@wrongstack/webui 0.277.2 → 0.280.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/activity-BEjzb11U.js +5 -0
- package/dist/assets/activity-ByEwzh5Q.js +5 -0
- package/dist/assets/activity-CVWYwjgf.js +5 -0
- package/dist/assets/activity-Cjm0UoMi.js +5 -0
- package/dist/assets/activity-CqiCPjKb.js +5 -0
- package/dist/assets/activity-DN6Bmnaw.js +5 -0
- package/dist/assets/chat-CdLXf1yN.js +1 -0
- package/dist/assets/chat-Cfq81NCh.js +1 -0
- package/dist/assets/chat-Cvy-F2d6.js +1 -0
- package/dist/assets/chat-DNIePdaQ.js +1 -0
- package/dist/assets/chat-DhofDrgA.js +1 -0
- package/dist/assets/chat-cxuX4gFa.js +1 -0
- package/dist/assets/commandPalette-2M1hYRuH.js +1 -0
- package/dist/assets/commandPalette-BE_ljQwl.js +1 -0
- package/dist/assets/commandPalette-BYf22WAi.js +1 -0
- package/dist/assets/commandPalette-BZhacjvy.js +1 -0
- package/dist/assets/commandPalette-CTE9ITWg.js +1 -0
- package/dist/assets/commandPalette-RxWSjQIg.js +1 -0
- package/dist/assets/common-BAg8_MKg.js +1 -0
- package/dist/assets/common-BH1mb9Im.js +1 -0
- package/dist/assets/common-C29pCVfm.js +1 -0
- package/dist/assets/common-CrGjglH4.js +1 -0
- package/dist/assets/common-KzRPhobM.js +1 -0
- package/dist/assets/common-tmj0IcQS.js +1 -0
- package/dist/assets/index-BMDGZBnY.css +2 -0
- package/dist/assets/index-Ci_SxQGe.js +157 -0
- package/dist/assets/settings-BVvx94WK.js +1 -0
- package/dist/assets/settings-CQ4x-iFD.js +1 -0
- package/dist/assets/settings-CmHCQRVI.js +1 -0
- package/dist/assets/settings-DgGu17XI.js +1 -0
- package/dist/assets/settings-OOQ-99Tn.js +1 -0
- package/dist/assets/settings-TqprB9wO.js +1 -0
- package/dist/assets/setup-B5TfeR6q.js +1 -0
- package/dist/assets/setup-BBwNpiE3.js +1 -0
- package/dist/assets/setup-BT2iTMm4.js +1 -0
- package/dist/assets/setup-CVMjqrZh.js +1 -0
- package/dist/assets/setup-DOXm3hFc.js +1 -0
- package/dist/assets/setup-Dhb_xOpk.js +1 -0
- package/dist/assets/toasts-BCDGDcyq.js +1 -0
- package/dist/assets/toasts-BwQJwYLe.js +1 -0
- package/dist/assets/toasts-CFzweRH5.js +1 -0
- package/dist/assets/toasts-DDYQsJV8.js +1 -0
- package/dist/assets/toasts-Dm12Nq8t.js +1 -0
- package/dist/assets/toasts-frBt6fvE.js +1 -0
- package/dist/assets/{vendor-C604QguY.js → vendor-B_dTeUDT.js} +252 -252
- package/dist/index.html +3 -3
- package/dist/index.js +26536 -8227
- package/dist/index.js.map +1 -1
- package/dist/server/entry.js +875 -582
- package/dist/server/entry.js.map +1 -1
- package/dist/server/handlers.js.map +1 -1
- package/dist/server/index.d.ts +5 -0
- package/dist/server/index.js +870 -577
- package/dist/server/index.js.map +1 -1
- package/dist/types.d.ts +5 -2
- package/package.json +9 -6
- package/dist/assets/index-Bmo3qaVg.js +0 -141
- package/dist/assets/index-D8YeqUJA.css +0 -2
package/dist/server/entry.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// src/server/entry.ts
|
|
3
|
-
import { ToolValidationError as
|
|
3
|
+
import { ToolValidationError as ToolValidationError6 } from "@wrongstack/core";
|
|
4
4
|
|
|
5
5
|
// src/server/autophase-ws-handler.ts
|
|
6
6
|
import { spawnSync } from "child_process";
|
|
@@ -1401,11 +1401,11 @@ async function handleDesignVerify(ws, ctx) {
|
|
|
1401
1401
|
}
|
|
1402
1402
|
|
|
1403
1403
|
// src/server/eternal-iteration-broadcast.ts
|
|
1404
|
-
function createEternalSubscription(subscribe,
|
|
1404
|
+
function createEternalSubscription(subscribe, broadcast2, clientsRef) {
|
|
1405
1405
|
let disposed = false;
|
|
1406
1406
|
const dispose = subscribe((entry) => {
|
|
1407
1407
|
if (disposed) return;
|
|
1408
|
-
|
|
1408
|
+
broadcast2(clientsRef(), {
|
|
1409
1409
|
type: "eternal.iteration",
|
|
1410
1410
|
payload: { entry }
|
|
1411
1411
|
});
|
|
@@ -1477,21 +1477,21 @@ import * as fs3 from "fs/promises";
|
|
|
1477
1477
|
import * as path4 from "path";
|
|
1478
1478
|
import { ToolValidationError } from "@wrongstack/core";
|
|
1479
1479
|
function isPathInside(root, target) {
|
|
1480
|
-
const
|
|
1481
|
-
return
|
|
1480
|
+
const relative4 = path4.relative(root, target);
|
|
1481
|
+
return relative4 === "" || !relative4.startsWith("..") && !path4.isAbsolute(relative4);
|
|
1482
1482
|
}
|
|
1483
1483
|
async function resolveWorkingDirInsideProject(projectRoot, inputPath) {
|
|
1484
1484
|
const resolved = path4.resolve(projectRoot, inputPath);
|
|
1485
|
-
let
|
|
1485
|
+
let stat2;
|
|
1486
1486
|
try {
|
|
1487
|
-
|
|
1487
|
+
stat2 = await fs3.stat(resolved);
|
|
1488
1488
|
} catch {
|
|
1489
1489
|
throw new ToolValidationError({
|
|
1490
1490
|
message: `Directory not found or not accessible: ${resolved}`,
|
|
1491
1491
|
field: "path"
|
|
1492
1492
|
});
|
|
1493
1493
|
}
|
|
1494
|
-
if (!
|
|
1494
|
+
if (!stat2.isDirectory()) {
|
|
1495
1495
|
throw new ToolValidationError({
|
|
1496
1496
|
message: `Directory not found or not accessible: ${resolved}`,
|
|
1497
1497
|
field: "path"
|
|
@@ -1745,9 +1745,9 @@ async function handleGitInfo(ws, projectRoot) {
|
|
|
1745
1745
|
const cwd = projectRoot || void 0;
|
|
1746
1746
|
try {
|
|
1747
1747
|
const { execFile: ef } = await import("child_process");
|
|
1748
|
-
const git = (args) => new Promise((
|
|
1748
|
+
const git = (args) => new Promise((resolve10) => {
|
|
1749
1749
|
ef("git", args, { cwd, timeout: 3e3 }, (err, stdout) => {
|
|
1750
|
-
|
|
1750
|
+
resolve10(err ? "" : stdout.trim());
|
|
1751
1751
|
});
|
|
1752
1752
|
});
|
|
1753
1753
|
const [branchRaw, diffRaw, statusRaw, upstreamRaw] = await Promise.all([
|
|
@@ -1773,12 +1773,12 @@ async function handleGitInfo(ws, projectRoot) {
|
|
|
1773
1773
|
function makeGit(cwd) {
|
|
1774
1774
|
return async (args) => {
|
|
1775
1775
|
const { execFile: ef } = await import("child_process");
|
|
1776
|
-
return new Promise((
|
|
1776
|
+
return new Promise((resolve10) => {
|
|
1777
1777
|
ef(
|
|
1778
1778
|
"git",
|
|
1779
1779
|
args,
|
|
1780
1780
|
{ cwd, timeout: 5e3, maxBuffer: 1024 * 1024 * 16 },
|
|
1781
|
-
(err, stdout) =>
|
|
1781
|
+
(err, stdout) => resolve10(err ? "" : stdout)
|
|
1782
1782
|
);
|
|
1783
1783
|
});
|
|
1784
1784
|
};
|
|
@@ -1802,15 +1802,15 @@ async function handleGitChanges(ws, projectRoot) {
|
|
|
1802
1802
|
if (!m) continue;
|
|
1803
1803
|
const added = m[1] === "-" ? 0 : Number(m[1]);
|
|
1804
1804
|
const deleted = m[2] === "-" ? 0 : Number(m[2]);
|
|
1805
|
-
let
|
|
1806
|
-
if (
|
|
1805
|
+
let path23 = m[3] ?? "";
|
|
1806
|
+
if (path23 === "") {
|
|
1807
1807
|
i += 1;
|
|
1808
|
-
|
|
1808
|
+
path23 = parts[i + 1] ?? parts[i] ?? "";
|
|
1809
1809
|
i += 1;
|
|
1810
1810
|
}
|
|
1811
|
-
if (!
|
|
1812
|
-
const prev = counts.get(
|
|
1813
|
-
counts.set(
|
|
1811
|
+
if (!path23) continue;
|
|
1812
|
+
const prev = counts.get(path23) ?? { added: 0, deleted: 0 };
|
|
1813
|
+
counts.set(path23, { added: prev.added + added, deleted: prev.deleted + deleted });
|
|
1814
1814
|
}
|
|
1815
1815
|
};
|
|
1816
1816
|
parseNumstat(unstagedNumstat);
|
|
@@ -1822,7 +1822,7 @@ async function handleGitChanges(ws, projectRoot) {
|
|
|
1822
1822
|
if (!rec || rec.length < 3) continue;
|
|
1823
1823
|
const x = rec[0] ?? " ";
|
|
1824
1824
|
const y = rec[1] ?? " ";
|
|
1825
|
-
const
|
|
1825
|
+
const path23 = rec.slice(3);
|
|
1826
1826
|
const isRename = x === "R" || x === "C" || y === "R" || y === "C";
|
|
1827
1827
|
if (isRename) i += 1;
|
|
1828
1828
|
let status;
|
|
@@ -1834,13 +1834,13 @@ async function handleGitChanges(ws, projectRoot) {
|
|
|
1834
1834
|
else if (x === "D" || y === "D") status = "D";
|
|
1835
1835
|
else status = "M";
|
|
1836
1836
|
const staged = x !== " " && x !== "?";
|
|
1837
|
-
let added = counts.get(
|
|
1838
|
-
let deleted = counts.get(
|
|
1837
|
+
let added = counts.get(path23)?.added ?? 0;
|
|
1838
|
+
let deleted = counts.get(path23)?.deleted ?? 0;
|
|
1839
1839
|
if (status === "?") {
|
|
1840
1840
|
added = 0;
|
|
1841
1841
|
deleted = 0;
|
|
1842
1842
|
}
|
|
1843
|
-
files.push({ path:
|
|
1843
|
+
files.push({ path: path23, status, added, deleted, staged });
|
|
1844
1844
|
}
|
|
1845
1845
|
send(ws, { type: "git.changes", payload: { files } });
|
|
1846
1846
|
} catch (err) {
|
|
@@ -1851,22 +1851,22 @@ async function handleGitChanges(ws, projectRoot) {
|
|
|
1851
1851
|
}
|
|
1852
1852
|
}
|
|
1853
1853
|
var MAX_DIFF_BYTES = 2 * 1024 * 1024;
|
|
1854
|
-
async function handleGitDiff(ws, projectRoot,
|
|
1854
|
+
async function handleGitDiff(ws, projectRoot, path23) {
|
|
1855
1855
|
const cwd = projectRoot || void 0;
|
|
1856
|
-
const reply = (extra) => send(ws, { type: "git.diff", payload: { path:
|
|
1857
|
-
if (!
|
|
1856
|
+
const reply = (extra) => send(ws, { type: "git.diff", payload: { path: path23, ...extra } });
|
|
1857
|
+
if (!path23 || path23.includes("\0") || path23.includes("..") || nodePath.isAbsolute(path23)) {
|
|
1858
1858
|
reply({ oldText: "", newText: "", error: "invalid path" });
|
|
1859
1859
|
return;
|
|
1860
1860
|
}
|
|
1861
1861
|
try {
|
|
1862
1862
|
const git = makeGit(cwd);
|
|
1863
|
-
const { readFile:
|
|
1863
|
+
const { readFile: readFile10 } = await import("fs/promises");
|
|
1864
1864
|
const { join: join14 } = await import("path");
|
|
1865
|
-
const oldText = await git(["show", `HEAD:${
|
|
1865
|
+
const oldText = await git(["show", `HEAD:${path23}`]);
|
|
1866
1866
|
let newText = "";
|
|
1867
1867
|
try {
|
|
1868
|
-
const abs = cwd ? join14(cwd,
|
|
1869
|
-
const buf = await
|
|
1868
|
+
const abs = cwd ? join14(cwd, path23) : path23;
|
|
1869
|
+
const buf = await readFile10(abs);
|
|
1870
1870
|
if (buf.includes(0)) {
|
|
1871
1871
|
reply({ oldText: "", newText: "", binary: true });
|
|
1872
1872
|
return;
|
|
@@ -2068,7 +2068,7 @@ async function handleApiSessionEvents(res, globalRoot, sessionId, limit) {
|
|
|
2068
2068
|
return;
|
|
2069
2069
|
}
|
|
2070
2070
|
try {
|
|
2071
|
-
const { SessionRegistry, resolveWstackPaths:
|
|
2071
|
+
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, DefaultSessionStore: DefaultSessionStore2, DefaultSessionReader: DefaultSessionReader2 } = await import("@wrongstack/core");
|
|
2072
2072
|
const registry = new SessionRegistry(globalRoot);
|
|
2073
2073
|
const entry = await registry.get(sessionId);
|
|
2074
2074
|
if (!entry) {
|
|
@@ -2076,9 +2076,9 @@ async function handleApiSessionEvents(res, globalRoot, sessionId, limit) {
|
|
|
2076
2076
|
res.end(JSON.stringify({ error: "Session not found" }));
|
|
2077
2077
|
return;
|
|
2078
2078
|
}
|
|
2079
|
-
const paths =
|
|
2080
|
-
const store = new
|
|
2081
|
-
const reader = new
|
|
2079
|
+
const paths = resolveWstackPaths3({ projectRoot: entry.projectRoot, globalRoot });
|
|
2080
|
+
const store = new DefaultSessionStore2({ dir: paths.projectSessions });
|
|
2081
|
+
const reader = new DefaultSessionReader2({ store });
|
|
2082
2082
|
const rawEntries = [];
|
|
2083
2083
|
for await (const ev of reader.replay(sessionId)) {
|
|
2084
2084
|
const mapped = mapWatchEntry(ev);
|
|
@@ -2103,7 +2103,7 @@ async function handleApiSessionEvents(res, globalRoot, sessionId, limit) {
|
|
|
2103
2103
|
}
|
|
2104
2104
|
}
|
|
2105
2105
|
function readJsonBody(req) {
|
|
2106
|
-
return new Promise((
|
|
2106
|
+
return new Promise((resolve10, reject) => {
|
|
2107
2107
|
let data = "";
|
|
2108
2108
|
req.on("data", (chunk) => {
|
|
2109
2109
|
data += chunk;
|
|
@@ -2114,7 +2114,7 @@ function readJsonBody(req) {
|
|
|
2114
2114
|
});
|
|
2115
2115
|
req.on("end", () => {
|
|
2116
2116
|
try {
|
|
2117
|
-
|
|
2117
|
+
resolve10(data ? JSON.parse(data) : {});
|
|
2118
2118
|
} catch (err) {
|
|
2119
2119
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
2120
2120
|
}
|
|
@@ -2150,7 +2150,7 @@ async function handleApiSessionMessage(res, req, globalRoot, sessionId) {
|
|
|
2150
2150
|
const priority = ["low", "normal", "high"].includes(rawPriority) ? rawPriority : "high";
|
|
2151
2151
|
const subject = typeof body["subject"] === "string" && body["subject"].trim() ? body["subject"].trim() : "Message from Fleet HQ";
|
|
2152
2152
|
try {
|
|
2153
|
-
const { SessionRegistry, resolveWstackPaths:
|
|
2153
|
+
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, GlobalMailbox: GlobalMailbox4, mailboxSessionTag: mailboxSessionTag2 } = await import("@wrongstack/core");
|
|
2154
2154
|
const registry = new SessionRegistry(globalRoot);
|
|
2155
2155
|
const entry = await registry.get(sessionId);
|
|
2156
2156
|
if (!entry) {
|
|
@@ -2158,9 +2158,9 @@ async function handleApiSessionMessage(res, req, globalRoot, sessionId) {
|
|
|
2158
2158
|
res.end(JSON.stringify({ error: "Session not found" }));
|
|
2159
2159
|
return;
|
|
2160
2160
|
}
|
|
2161
|
-
const paths =
|
|
2162
|
-
const mailbox = new
|
|
2163
|
-
const to = `leader@${
|
|
2161
|
+
const paths = resolveWstackPaths3({ projectRoot: entry.projectRoot, globalRoot });
|
|
2162
|
+
const mailbox = new GlobalMailbox4(paths.projectDir);
|
|
2163
|
+
const to = `leader@${mailboxSessionTag2(sessionId)}`;
|
|
2164
2164
|
const sent = await mailbox.send({ from, to, type, subject, body: text, priority });
|
|
2165
2165
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
2166
2166
|
res.end(JSON.stringify({ ok: true, id: sent.id, to, type, delivered: entry.status }));
|
|
@@ -2176,7 +2176,7 @@ async function handleApiSessionMailbox(res, globalRoot, sessionId) {
|
|
|
2176
2176
|
return;
|
|
2177
2177
|
}
|
|
2178
2178
|
try {
|
|
2179
|
-
const { SessionRegistry, resolveWstackPaths:
|
|
2179
|
+
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, GlobalMailbox: GlobalMailbox4, mailboxSessionTag: mailboxSessionTag2 } = await import("@wrongstack/core");
|
|
2180
2180
|
const registry = new SessionRegistry(globalRoot);
|
|
2181
2181
|
const entry = await registry.get(sessionId);
|
|
2182
2182
|
if (!entry) {
|
|
@@ -2184,9 +2184,9 @@ async function handleApiSessionMailbox(res, globalRoot, sessionId) {
|
|
|
2184
2184
|
res.end(JSON.stringify({ error: "Session not found" }));
|
|
2185
2185
|
return;
|
|
2186
2186
|
}
|
|
2187
|
-
const paths =
|
|
2188
|
-
const mailbox = new
|
|
2189
|
-
const leaderAddr = `leader@${
|
|
2187
|
+
const paths = resolveWstackPaths3({ projectRoot: entry.projectRoot, globalRoot });
|
|
2188
|
+
const mailbox = new GlobalMailbox4(paths.projectDir);
|
|
2189
|
+
const leaderAddr = `leader@${mailboxSessionTag2(sessionId)}`;
|
|
2190
2190
|
const [inbound, outbound] = await Promise.all([
|
|
2191
2191
|
mailbox.query({ to: leaderAddr, limit: 50 }),
|
|
2192
2192
|
mailbox.query({ from: leaderAddr, limit: 50 })
|
|
@@ -2234,7 +2234,7 @@ async function handleApiSessionInterrupt(res, req, globalRoot, sessionId) {
|
|
|
2234
2234
|
const reason = typeof body["reason"] === "string" && body["reason"].trim() ? body["reason"].trim() : "Operator requested stop from Fleet HQ";
|
|
2235
2235
|
const from = typeof body["from"] === "string" && body["from"].trim() ? body["from"].trim() : "human@webui";
|
|
2236
2236
|
try {
|
|
2237
|
-
const { SessionRegistry, resolveWstackPaths:
|
|
2237
|
+
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, GlobalMailbox: GlobalMailbox4, mailboxSessionTag: mailboxSessionTag2 } = await import("@wrongstack/core");
|
|
2238
2238
|
const registry = new SessionRegistry(globalRoot);
|
|
2239
2239
|
const entry = await registry.get(sessionId);
|
|
2240
2240
|
if (!entry) {
|
|
@@ -2242,9 +2242,9 @@ async function handleApiSessionInterrupt(res, req, globalRoot, sessionId) {
|
|
|
2242
2242
|
res.end(JSON.stringify({ error: "Session not found" }));
|
|
2243
2243
|
return;
|
|
2244
2244
|
}
|
|
2245
|
-
const paths =
|
|
2246
|
-
const mailbox = new
|
|
2247
|
-
const to = `leader@${
|
|
2245
|
+
const paths = resolveWstackPaths3({ projectRoot: entry.projectRoot, globalRoot });
|
|
2246
|
+
const mailbox = new GlobalMailbox4(paths.projectDir);
|
|
2247
|
+
const to = `leader@${mailboxSessionTag2(sessionId)}`;
|
|
2248
2248
|
const sent = await mailbox.send({
|
|
2249
2249
|
from,
|
|
2250
2250
|
to,
|
|
@@ -2282,7 +2282,7 @@ async function handleApiFleetBroadcast(res, req, globalRoot) {
|
|
|
2282
2282
|
}
|
|
2283
2283
|
const from = typeof body["from"] === "string" && body["from"].trim() ? body["from"].trim() : "human@webui";
|
|
2284
2284
|
try {
|
|
2285
|
-
const { SessionRegistry, resolveWstackPaths:
|
|
2285
|
+
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, GlobalMailbox: GlobalMailbox4, mailboxSessionTag: mailboxSessionTag2 } = await import("@wrongstack/core");
|
|
2286
2286
|
const registry = new SessionRegistry(globalRoot);
|
|
2287
2287
|
const all = await registry.list();
|
|
2288
2288
|
const mySlug = all.find((s) => s.pid === process.pid)?.projectSlug;
|
|
@@ -2294,10 +2294,10 @@ async function handleApiFleetBroadcast(res, req, globalRoot) {
|
|
|
2294
2294
|
}
|
|
2295
2295
|
const mbByDir = /* @__PURE__ */ new Map();
|
|
2296
2296
|
const mailboxFor = (projectRoot) => {
|
|
2297
|
-
const dir =
|
|
2297
|
+
const dir = resolveWstackPaths3({ projectRoot, globalRoot }).projectDir;
|
|
2298
2298
|
let mb = mbByDir.get(dir);
|
|
2299
2299
|
if (!mb) {
|
|
2300
|
-
mb = new
|
|
2300
|
+
mb = new GlobalMailbox4(dir);
|
|
2301
2301
|
mbByDir.set(dir, mb);
|
|
2302
2302
|
}
|
|
2303
2303
|
return mb;
|
|
@@ -2309,7 +2309,7 @@ async function handleApiFleetBroadcast(res, req, globalRoot) {
|
|
|
2309
2309
|
const mb = mailboxFor(s.projectRoot);
|
|
2310
2310
|
await mb.send({
|
|
2311
2311
|
from,
|
|
2312
|
-
to: `leader@${
|
|
2312
|
+
to: `leader@${mailboxSessionTag2(s.sessionId)}`,
|
|
2313
2313
|
type: "steer",
|
|
2314
2314
|
subject: "Broadcast from Fleet HQ",
|
|
2315
2315
|
body: text,
|
|
@@ -2357,14 +2357,14 @@ function pushEvent(event) {
|
|
|
2357
2357
|
}
|
|
2358
2358
|
}
|
|
2359
2359
|
function parseBody(req) {
|
|
2360
|
-
return new Promise((
|
|
2360
|
+
return new Promise((resolve10, reject) => {
|
|
2361
2361
|
let body = "";
|
|
2362
2362
|
req.on("data", (chunk) => {
|
|
2363
2363
|
body += chunk.toString("utf-8");
|
|
2364
2364
|
});
|
|
2365
2365
|
req.on("end", () => {
|
|
2366
2366
|
try {
|
|
2367
|
-
|
|
2367
|
+
resolve10(JSON.parse(body));
|
|
2368
2368
|
} catch {
|
|
2369
2369
|
reject(new Error("Invalid JSON"));
|
|
2370
2370
|
}
|
|
@@ -2558,7 +2558,7 @@ function escapeHtmlAttr(value) {
|
|
|
2558
2558
|
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
2559
2559
|
}
|
|
2560
2560
|
function injectWsConfig(html, opts) {
|
|
2561
|
-
|
|
2561
|
+
const out = injectWsPort(html, opts.wsPort);
|
|
2562
2562
|
if (!opts.publicWsUrl || out.includes('name="wrongstack-ws-url"')) return out;
|
|
2563
2563
|
const tag = `<meta name="wrongstack-ws-url" content="${escapeHtmlAttr(opts.publicWsUrl)}" />`;
|
|
2564
2564
|
if (out.includes("</head>")) {
|
|
@@ -3241,16 +3241,16 @@ function openBrowser(url, platform = process.platform) {
|
|
|
3241
3241
|
import * as net from "net";
|
|
3242
3242
|
import { ToolValidationError as ToolValidationError3 } from "@wrongstack/core";
|
|
3243
3243
|
function isPortFree(host, port) {
|
|
3244
|
-
return new Promise((
|
|
3244
|
+
return new Promise((resolve10) => {
|
|
3245
3245
|
const srv = net.createServer();
|
|
3246
|
-
srv.once("error", () =>
|
|
3246
|
+
srv.once("error", () => resolve10(false));
|
|
3247
3247
|
srv.once("listening", () => {
|
|
3248
|
-
srv.close(() =>
|
|
3248
|
+
srv.close(() => resolve10(true));
|
|
3249
3249
|
});
|
|
3250
3250
|
try {
|
|
3251
3251
|
srv.listen(port, host);
|
|
3252
3252
|
} catch {
|
|
3253
|
-
|
|
3253
|
+
resolve10(false);
|
|
3254
3254
|
}
|
|
3255
3255
|
});
|
|
3256
3256
|
}
|
|
@@ -4128,6 +4128,7 @@ function validateMailboxMessagesPayload(payload) {
|
|
|
4128
4128
|
const limit = payload["limit"];
|
|
4129
4129
|
const agentId = payload["agentId"];
|
|
4130
4130
|
const unreadOnly = payload["unreadOnly"];
|
|
4131
|
+
const incompleteOnly = payload["incompleteOnly"];
|
|
4131
4132
|
if (limit !== void 0 && (typeof limit !== "number" || !Number.isFinite(limit) || limit < 1)) {
|
|
4132
4133
|
return {
|
|
4133
4134
|
ok: false,
|
|
@@ -4146,7 +4147,13 @@ function validateMailboxMessagesPayload(payload) {
|
|
|
4146
4147
|
message: "mailbox.messages payload.unreadOnly must be a boolean when provided"
|
|
4147
4148
|
};
|
|
4148
4149
|
}
|
|
4149
|
-
|
|
4150
|
+
if (incompleteOnly !== void 0 && typeof incompleteOnly !== "boolean") {
|
|
4151
|
+
return {
|
|
4152
|
+
ok: false,
|
|
4153
|
+
message: "mailbox.messages payload.incompleteOnly must be a boolean when provided"
|
|
4154
|
+
};
|
|
4155
|
+
}
|
|
4156
|
+
return { ok: true, value: { limit, agentId, unreadOnly, incompleteOnly } };
|
|
4150
4157
|
}
|
|
4151
4158
|
function validateMailboxAgentsPayload(payload) {
|
|
4152
4159
|
if (payload === void 0) return { ok: true, value: void 0 };
|
|
@@ -4281,7 +4288,7 @@ var NUMBER_PREF_KEYS = /* @__PURE__ */ new Set([
|
|
|
4281
4288
|
"enhanceDelayMs",
|
|
4282
4289
|
"tgLongToolMs"
|
|
4283
4290
|
]);
|
|
4284
|
-
var STRING_PREF_KEYS = /* @__PURE__ */ new Set(["hqUrl", "hqToken"]);
|
|
4291
|
+
var STRING_PREF_KEYS = /* @__PURE__ */ new Set(["hqUrl", "hqToken", "uiLocale"]);
|
|
4285
4292
|
var ENUM_PREF_KEYS = {
|
|
4286
4293
|
autonomy: AUTONOMY_VALUES,
|
|
4287
4294
|
contextStrategy: CONTEXT_STRATEGY_VALUES,
|
|
@@ -4294,6 +4301,37 @@ var ENUM_PREF_KEYS = {
|
|
|
4294
4301
|
reasoningEffort: REASONING_EFFORT_VALUES,
|
|
4295
4302
|
cacheTtl: CACHE_TTL_VALUES
|
|
4296
4303
|
};
|
|
4304
|
+
function validateModelRuntimeValue(modelRuntime, path23) {
|
|
4305
|
+
const reasoning = modelRuntime["reasoning"];
|
|
4306
|
+
if (reasoning !== void 0) {
|
|
4307
|
+
if (!isRecord(reasoning)) return `${path23}.reasoning must be an object when provided`;
|
|
4308
|
+
const mode = reasoning["mode"];
|
|
4309
|
+
const effort = reasoning["effort"];
|
|
4310
|
+
const preserve = reasoning["preserve"];
|
|
4311
|
+
if (mode !== void 0 && (typeof mode !== "string" || !REASONING_MODE_VALUES.has(mode))) {
|
|
4312
|
+
return `${path23}.reasoning.mode must be one of: ${Array.from(REASONING_MODE_VALUES).join(", ")}`;
|
|
4313
|
+
}
|
|
4314
|
+
if (effort !== void 0 && (typeof effort !== "string" || !REASONING_EFFORT_VALUES.has(effort))) {
|
|
4315
|
+
return `${path23}.reasoning.effort must be one of: ${Array.from(REASONING_EFFORT_VALUES).join(", ")}`;
|
|
4316
|
+
}
|
|
4317
|
+
if (preserve !== void 0 && typeof preserve !== "boolean") {
|
|
4318
|
+
return `${path23}.reasoning.preserve must be a boolean when provided`;
|
|
4319
|
+
}
|
|
4320
|
+
}
|
|
4321
|
+
const cache = modelRuntime["cache"];
|
|
4322
|
+
if (cache !== void 0) {
|
|
4323
|
+
if (!isRecord(cache)) return `${path23}.cache must be an object when provided`;
|
|
4324
|
+
const ttl = cache["ttl"];
|
|
4325
|
+
if (ttl !== void 0 && (typeof ttl !== "string" || !CACHE_TTL_VALUES.has(ttl) || ttl === "default")) {
|
|
4326
|
+
return `${path23}.cache.ttl must be one of: 5m, 1h`;
|
|
4327
|
+
}
|
|
4328
|
+
}
|
|
4329
|
+
const parameters = modelRuntime["parameters"];
|
|
4330
|
+
if (parameters !== void 0 && !isRecord(parameters)) {
|
|
4331
|
+
return `${path23}.parameters must be an object when provided`;
|
|
4332
|
+
}
|
|
4333
|
+
return null;
|
|
4334
|
+
}
|
|
4297
4335
|
function validatePreferenceValue(key, value) {
|
|
4298
4336
|
if (BOOLEAN_PREF_KEYS.has(key)) {
|
|
4299
4337
|
return typeof value === "boolean" ? null : `prefs.update payload.${key} must be a boolean`;
|
|
@@ -4319,6 +4357,7 @@ function validatePreferenceValue(key, value) {
|
|
|
4319
4357
|
const provider = entry["provider"];
|
|
4320
4358
|
const model = entry["model"];
|
|
4321
4359
|
const fallbackProfile = entry["fallbackProfile"];
|
|
4360
|
+
const modelRuntime = entry["modelRuntime"];
|
|
4322
4361
|
if (provider !== void 0 && typeof provider !== "string") {
|
|
4323
4362
|
return `prefs.update payload.${key}.provider must be a string when provided`;
|
|
4324
4363
|
}
|
|
@@ -4328,8 +4367,15 @@ function validatePreferenceValue(key, value) {
|
|
|
4328
4367
|
if (fallbackProfile !== void 0 && typeof fallbackProfile !== "string") {
|
|
4329
4368
|
return `prefs.update payload.${key}.fallbackProfile must be a string when provided`;
|
|
4330
4369
|
}
|
|
4331
|
-
if (
|
|
4332
|
-
return `prefs.update payload.${key}
|
|
4370
|
+
if (modelRuntime !== void 0 && !isRecord(modelRuntime)) {
|
|
4371
|
+
return `prefs.update payload.${key}.modelRuntime must be an object when provided`;
|
|
4372
|
+
}
|
|
4373
|
+
if (isRecord(modelRuntime)) {
|
|
4374
|
+
const runtimeError = validateModelRuntimeValue(modelRuntime, `prefs.update payload.${key}.modelRuntime`);
|
|
4375
|
+
if (runtimeError) return runtimeError;
|
|
4376
|
+
}
|
|
4377
|
+
if (model === void 0 && fallbackProfile === void 0 && modelRuntime === void 0) {
|
|
4378
|
+
return `prefs.update payload.${key} entries require model, fallbackProfile, or modelRuntime`;
|
|
4333
4379
|
}
|
|
4334
4380
|
}
|
|
4335
4381
|
return null;
|
|
@@ -4562,8 +4608,8 @@ function validateShellOpenPayload(payload) {
|
|
|
4562
4608
|
if (!isRecord(payload)) {
|
|
4563
4609
|
return { ok: false, message: "shell.open payload must be an object with string path" };
|
|
4564
4610
|
}
|
|
4565
|
-
const
|
|
4566
|
-
if (typeof
|
|
4611
|
+
const path23 = payload["path"];
|
|
4612
|
+
if (typeof path23 !== "string" || path23.trim().length === 0) {
|
|
4567
4613
|
return { ok: false, message: "shell.open payload.path must be a non-empty string" };
|
|
4568
4614
|
}
|
|
4569
4615
|
const target = payload["target"];
|
|
@@ -4573,48 +4619,20 @@ function validateShellOpenPayload(payload) {
|
|
|
4573
4619
|
message: 'shell.open payload.target must be "file" or "terminal" when provided'
|
|
4574
4620
|
};
|
|
4575
4621
|
}
|
|
4576
|
-
return { ok: true, value: { path:
|
|
4622
|
+
return { ok: true, value: { path: path23, target } };
|
|
4577
4623
|
}
|
|
4578
4624
|
function validateGitDiffPayload(payload) {
|
|
4579
4625
|
if (!isRecord(payload)) {
|
|
4580
4626
|
return { ok: false, message: "git.diff payload must be an object" };
|
|
4581
4627
|
}
|
|
4582
|
-
const
|
|
4583
|
-
if (
|
|
4628
|
+
const path23 = payload["path"];
|
|
4629
|
+
if (path23 === void 0 || path23 === null) {
|
|
4584
4630
|
return { ok: true, value: { path: "" } };
|
|
4585
4631
|
}
|
|
4586
|
-
if (typeof
|
|
4632
|
+
if (typeof path23 !== "string") {
|
|
4587
4633
|
return { ok: false, message: "git.diff payload.path must be a string when provided" };
|
|
4588
4634
|
}
|
|
4589
|
-
return { ok: true, value: { path:
|
|
4590
|
-
}
|
|
4591
|
-
function validateProjectsAddPayload(payload) {
|
|
4592
|
-
if (!isRecord(payload)) {
|
|
4593
|
-
return { ok: false, message: "projects.add payload must be an object with string root" };
|
|
4594
|
-
}
|
|
4595
|
-
const root = payload["root"];
|
|
4596
|
-
if (typeof root !== "string" || root.trim().length === 0) {
|
|
4597
|
-
return { ok: false, message: "projects.add payload.root must be a non-empty string" };
|
|
4598
|
-
}
|
|
4599
|
-
const name2 = payload["name"];
|
|
4600
|
-
if (name2 !== void 0 && typeof name2 !== "string") {
|
|
4601
|
-
return { ok: false, message: "projects.add payload.name must be a string when provided" };
|
|
4602
|
-
}
|
|
4603
|
-
return { ok: true, value: { root, name: typeof name2 === "string" ? name2 : void 0 } };
|
|
4604
|
-
}
|
|
4605
|
-
function validateProjectsSelectPayload(payload) {
|
|
4606
|
-
if (!isRecord(payload)) {
|
|
4607
|
-
return { ok: false, message: "projects.select payload must be an object with string root" };
|
|
4608
|
-
}
|
|
4609
|
-
const root = payload["root"];
|
|
4610
|
-
if (typeof root !== "string" || root.trim().length === 0) {
|
|
4611
|
-
return { ok: false, message: "projects.select payload.root must be a non-empty string" };
|
|
4612
|
-
}
|
|
4613
|
-
const name2 = payload["name"];
|
|
4614
|
-
if (name2 !== void 0 && typeof name2 !== "string") {
|
|
4615
|
-
return { ok: false, message: "projects.select payload.name must be a string when provided" };
|
|
4616
|
-
}
|
|
4617
|
-
return { ok: true, value: { root, name: typeof name2 === "string" ? name2 : void 0 } };
|
|
4635
|
+
return { ok: true, value: { path: path23 } };
|
|
4618
4636
|
}
|
|
4619
4637
|
|
|
4620
4638
|
// src/server/skills-handlers.ts
|
|
@@ -4787,7 +4805,13 @@ async function handleSkillsCreate(ws, ctx, msg) {
|
|
|
4787
4805
|
}
|
|
4788
4806
|
const createPayload = parsed.value;
|
|
4789
4807
|
try {
|
|
4790
|
-
const targetDir = createPayload.scope === "global" ? path11.join(
|
|
4808
|
+
const targetDir = createPayload.scope === "global" ? path11.join(
|
|
4809
|
+
ctx.globalSkillsDir ?? path11.join(wstackGlobalRoot(), "skills"),
|
|
4810
|
+
createPayload.name.trim()
|
|
4811
|
+
) : path11.join(
|
|
4812
|
+
ctx.projectSkillsDir ?? path11.join(ctx.projectRoot, ".wrongstack", "skills"),
|
|
4813
|
+
createPayload.name.trim()
|
|
4814
|
+
);
|
|
4791
4815
|
try {
|
|
4792
4816
|
await fs9.access(targetDir);
|
|
4793
4817
|
send(ws, { type: "skills.created", payload: { success: false, error: `Skill "${createPayload.name}" already exists` } });
|
|
@@ -4873,8 +4897,9 @@ async function handleSkillsEdit(ws, ctx, msg) {
|
|
|
4873
4897
|
send(ws, { type: "skills.edited", payload: { success: false, error: `Skill "${editPayload.name}" not found` } });
|
|
4874
4898
|
return;
|
|
4875
4899
|
}
|
|
4876
|
-
if (entry.
|
|
4877
|
-
|
|
4900
|
+
if (entry.source !== "project" && entry.source !== "user") {
|
|
4901
|
+
const label = entry.source === "bundled" ? "Bundled" : "Foreign (read-only)";
|
|
4902
|
+
send(ws, { type: "skills.edited", payload: { success: false, error: `${label} skills cannot be edited` } });
|
|
4878
4903
|
return;
|
|
4879
4904
|
}
|
|
4880
4905
|
await atomicWrite5(entry.path, editPayload.body);
|
|
@@ -5499,8 +5524,20 @@ function registerShutdownHandlers(res) {
|
|
|
5499
5524
|
import * as fs10 from "fs/promises";
|
|
5500
5525
|
import { watch as fsWatch } from "fs";
|
|
5501
5526
|
import * as path12 from "path";
|
|
5527
|
+
function statusProjectHashFromWatchFilename(projectsDir, filename) {
|
|
5528
|
+
const raw = String(filename);
|
|
5529
|
+
const relative4 = path12.isAbsolute(raw) ? path12.relative(projectsDir, raw) : raw;
|
|
5530
|
+
const parts = relative4.split(/[\\/]+/).filter(Boolean);
|
|
5531
|
+
if (parts.length < 2) return null;
|
|
5532
|
+
if (parts[parts.length - 1] !== "status.json") return null;
|
|
5533
|
+
return parts[parts.length - 2] ?? null;
|
|
5534
|
+
}
|
|
5535
|
+
function shouldLogWatcherStats() {
|
|
5536
|
+
const value = process.env["WRONGSTACK_WEBUI_WATCHER_STATS"]?.trim().toLowerCase();
|
|
5537
|
+
return value === "1" || value === "true" || value === "yes" || value === "on";
|
|
5538
|
+
}
|
|
5502
5539
|
function setupEvents(deps2) {
|
|
5503
|
-
const { events, broadcast:
|
|
5540
|
+
const { events, broadcast: broadcast2, clients, config, context, pendingConfirms, globalConfigPath, sessionBridge, wpaths, watcherMetrics, onFleetBroadcaster } = deps2;
|
|
5504
5541
|
const disposers = [];
|
|
5505
5542
|
const currentSessionId = () => context.session?.id ?? "";
|
|
5506
5543
|
const sessionPayload2 = (payload) => {
|
|
@@ -5519,19 +5556,19 @@ function setupEvents(deps2) {
|
|
|
5519
5556
|
};
|
|
5520
5557
|
events.on("iteration.started", (e) => {
|
|
5521
5558
|
const maxIt = typeof context.meta["maxIterations"] === "number" ? context.meta["maxIterations"] : config.tools?.maxIterations ?? 100;
|
|
5522
|
-
|
|
5559
|
+
broadcast2(clients, {
|
|
5523
5560
|
type: "iteration.started",
|
|
5524
5561
|
payload: sessionPayload2({ sessionId: e.sessionId, index: e.index, maxIterations: maxIt })
|
|
5525
5562
|
});
|
|
5526
5563
|
});
|
|
5527
5564
|
events.on("iteration.completed", (e) => {
|
|
5528
|
-
|
|
5565
|
+
broadcast2(clients, {
|
|
5529
5566
|
type: "iteration.completed",
|
|
5530
5567
|
payload: sessionPayload2({ sessionId: e.sessionId, index: e.index, totalIterations: e.index + 1 })
|
|
5531
5568
|
});
|
|
5532
5569
|
});
|
|
5533
5570
|
events.on("iteration.limit_reached", (e) => {
|
|
5534
|
-
|
|
5571
|
+
broadcast2(clients, {
|
|
5535
5572
|
type: "iteration.limit_reached",
|
|
5536
5573
|
payload: sessionPayload2({
|
|
5537
5574
|
sessionId: e.sessionId,
|
|
@@ -5541,19 +5578,19 @@ function setupEvents(deps2) {
|
|
|
5541
5578
|
});
|
|
5542
5579
|
});
|
|
5543
5580
|
events.on("provider.text_delta", (e) => {
|
|
5544
|
-
|
|
5581
|
+
broadcast2(clients, { type: "provider.text_delta", payload: sessionPayload2({ sessionId: e.sessionId, text: e.text, messageId: "current" }) });
|
|
5545
5582
|
});
|
|
5546
5583
|
events.on("provider.thinking_delta", (e) => {
|
|
5547
|
-
|
|
5584
|
+
broadcast2(clients, { type: "provider.thinking_delta", payload: sessionPayload2({ sessionId: e.sessionId, text: e.text }) });
|
|
5548
5585
|
});
|
|
5549
5586
|
events.on("provider.stream_error", (e) => {
|
|
5550
|
-
|
|
5587
|
+
broadcast2(clients, {
|
|
5551
5588
|
type: "provider.stream_error",
|
|
5552
5589
|
payload: sessionPayload2({ sessionId: e.sessionId, eventType: e.eventType, message: e.msg })
|
|
5553
5590
|
});
|
|
5554
5591
|
});
|
|
5555
5592
|
events.on("tool.started", (e) => {
|
|
5556
|
-
|
|
5593
|
+
broadcast2(clients, {
|
|
5557
5594
|
type: "tool.started",
|
|
5558
5595
|
payload: sessionPayload2({ sessionId: e.sessionId, id: e.id, name: e.name, input: e.input, messageId: `tool_${e.id}` })
|
|
5559
5596
|
});
|
|
@@ -5566,7 +5603,7 @@ function setupEvents(deps2) {
|
|
|
5566
5603
|
});
|
|
5567
5604
|
});
|
|
5568
5605
|
events.on("tool.progress", (e) => {
|
|
5569
|
-
|
|
5606
|
+
broadcast2(clients, {
|
|
5570
5607
|
type: "tool.progress",
|
|
5571
5608
|
// Nested `event` shape — the client handler reads `payload.event?.text`
|
|
5572
5609
|
// and early-returns on a falsy text, so a flat { eventType, text } payload
|
|
@@ -5583,7 +5620,7 @@ function setupEvents(deps2) {
|
|
|
5583
5620
|
});
|
|
5584
5621
|
});
|
|
5585
5622
|
events.on("tool.executed", (e) => {
|
|
5586
|
-
|
|
5623
|
+
broadcast2(clients, {
|
|
5587
5624
|
type: "tool.executed",
|
|
5588
5625
|
payload: sessionPayload2({ sessionId: e.sessionId, id: e.id, name: e.name, durationMs: e.durationMs, ok: e.ok, input: e.input, output: e.output })
|
|
5589
5626
|
});
|
|
@@ -5599,10 +5636,10 @@ function setupEvents(deps2) {
|
|
|
5599
5636
|
outputTokens: e.outputTokens,
|
|
5600
5637
|
outputLines: e.outputLines
|
|
5601
5638
|
});
|
|
5602
|
-
|
|
5639
|
+
broadcast2(clients, { type: "todos.updated", payload: sessionPayload2({ sessionId: e.sessionId, todos: [...context.todos] }) });
|
|
5603
5640
|
const sideEffects = context.sideEffects ?? [];
|
|
5604
5641
|
if (sideEffects.length > 0) {
|
|
5605
|
-
|
|
5642
|
+
broadcast2(clients, {
|
|
5606
5643
|
type: "side_effects",
|
|
5607
5644
|
payload: sessionPayload2({
|
|
5608
5645
|
sessionId: e.sessionId,
|
|
@@ -5624,7 +5661,7 @@ function setupEvents(deps2) {
|
|
|
5624
5661
|
if (typeof taskPath === "string" && taskPath) {
|
|
5625
5662
|
const { loadTasks } = await import("@wrongstack/core");
|
|
5626
5663
|
const file = await loadTasks(taskPath);
|
|
5627
|
-
|
|
5664
|
+
broadcast2(clients, { type: "tasks.updated", payload: sessionPayload2({ sessionId: e.sessionId, tasks: file?.tasks ?? [] }) });
|
|
5628
5665
|
}
|
|
5629
5666
|
} catch {
|
|
5630
5667
|
}
|
|
@@ -5633,7 +5670,7 @@ function setupEvents(deps2) {
|
|
|
5633
5670
|
if (typeof planPath === "string" && planPath) {
|
|
5634
5671
|
const { loadPlan } = await import("@wrongstack/core");
|
|
5635
5672
|
const plan = await loadPlan(planPath);
|
|
5636
|
-
|
|
5673
|
+
broadcast2(clients, { type: "plan.updated", payload: sessionPayload2({ sessionId: e.sessionId, plan: plan ?? { version: 1, sessionId: e.sessionId ?? context.session?.id ?? "", updatedAt: (/* @__PURE__ */ new Date()).toISOString(), items: [] } }) });
|
|
5637
5674
|
}
|
|
5638
5675
|
} catch {
|
|
5639
5676
|
}
|
|
@@ -5641,7 +5678,7 @@ function setupEvents(deps2) {
|
|
|
5641
5678
|
}
|
|
5642
5679
|
});
|
|
5643
5680
|
events.on("tool.loop_detected", (e) => {
|
|
5644
|
-
|
|
5681
|
+
broadcast2(clients, {
|
|
5645
5682
|
type: "tool.loop_detected",
|
|
5646
5683
|
payload: sessionPayload2({
|
|
5647
5684
|
sessionId: e.sessionId,
|
|
@@ -5653,19 +5690,19 @@ function setupEvents(deps2) {
|
|
|
5653
5690
|
});
|
|
5654
5691
|
});
|
|
5655
5692
|
events.on("trust.persisted", (e) => {
|
|
5656
|
-
|
|
5693
|
+
broadcast2(clients, {
|
|
5657
5694
|
type: "trust.persisted",
|
|
5658
5695
|
payload: sessionPayload2({ sessionId: e.sessionId, tool: e.tool, pattern: e.pattern, decision: e.decision })
|
|
5659
5696
|
});
|
|
5660
5697
|
});
|
|
5661
5698
|
events.on("delegate.started", (e) => {
|
|
5662
|
-
|
|
5699
|
+
broadcast2(clients, {
|
|
5663
5700
|
type: "delegate.started",
|
|
5664
5701
|
payload: sessionPayload2({ sessionId: e.sessionId, target: e.target, task: e.task })
|
|
5665
5702
|
});
|
|
5666
5703
|
});
|
|
5667
5704
|
events.on("delegate.completed", (e) => {
|
|
5668
|
-
|
|
5705
|
+
broadcast2(clients, {
|
|
5669
5706
|
type: "delegate.completed",
|
|
5670
5707
|
payload: sessionPayload2({
|
|
5671
5708
|
sessionId: e.sessionId,
|
|
@@ -5683,14 +5720,14 @@ function setupEvents(deps2) {
|
|
|
5683
5720
|
});
|
|
5684
5721
|
});
|
|
5685
5722
|
events.on("provider.response", (e) => {
|
|
5686
|
-
|
|
5723
|
+
broadcast2(clients, { type: "provider.response", payload: sessionPayload2({ sessionId: e.sessionId, usage: e.usage, stopReason: e.stopReason, messageId: "current" }) });
|
|
5687
5724
|
});
|
|
5688
5725
|
events.on("ctx.pct", (e) => {
|
|
5689
|
-
|
|
5726
|
+
broadcast2(clients, {
|
|
5690
5727
|
type: "ctx.pct",
|
|
5691
5728
|
payload: sessionPayload2({ sessionId: e.sessionId, load: e.load, tokens: e.tokens, maxContext: e.maxContext })
|
|
5692
5729
|
});
|
|
5693
|
-
|
|
5730
|
+
broadcast2(clients, {
|
|
5694
5731
|
type: "subagent.event",
|
|
5695
5732
|
payload: sessionPayload2({
|
|
5696
5733
|
sessionId: e.sessionId,
|
|
@@ -5703,33 +5740,37 @@ function setupEvents(deps2) {
|
|
|
5703
5740
|
});
|
|
5704
5741
|
});
|
|
5705
5742
|
events.on("ctx.max_context", (e) => {
|
|
5706
|
-
|
|
5743
|
+
broadcast2(clients, {
|
|
5707
5744
|
type: "ctx.max_context",
|
|
5708
5745
|
payload: sessionPayload2({ sessionId: e.sessionId, providerId: e.providerId, modelId: e.modelId, maxContext: e.maxContext })
|
|
5709
5746
|
});
|
|
5710
5747
|
});
|
|
5711
5748
|
events.on("token.threshold", (e) => {
|
|
5712
|
-
|
|
5749
|
+
broadcast2(clients, {
|
|
5713
5750
|
type: "token.threshold",
|
|
5714
5751
|
payload: sessionPayload2({ sessionId: e.sessionId, used: e.used, limit: e.limit })
|
|
5715
5752
|
});
|
|
5716
5753
|
});
|
|
5717
5754
|
events.on("token.cost_estimate_unavailable", (e) => {
|
|
5718
|
-
|
|
5755
|
+
broadcast2(clients, {
|
|
5719
5756
|
type: "token.cost_estimate_unavailable",
|
|
5720
5757
|
payload: sessionPayload2({ sessionId: e.sessionId, model: e.model })
|
|
5721
5758
|
});
|
|
5722
5759
|
});
|
|
5723
5760
|
events.on("context.repaired", (e) => {
|
|
5724
|
-
|
|
5761
|
+
broadcast2(clients, { type: "context.repaired", payload: sessionPayload2({ sessionId: e.sessionId, removedToolUses: e.removedToolUses, removedToolResults: e.removedToolResults, removedMessages: e.removedMessages }) });
|
|
5725
5762
|
});
|
|
5726
5763
|
events.on("tool.confirm_needed", (e) => {
|
|
5727
5764
|
const id = e.toolUseId ?? `confirm_${Date.now()}`;
|
|
5728
|
-
pendingConfirms.set(id,
|
|
5729
|
-
|
|
5765
|
+
pendingConfirms.set(id, {
|
|
5766
|
+
resolve: e.resolve,
|
|
5767
|
+
decisionSource: e.decisionSource,
|
|
5768
|
+
riskTier: e.riskTier
|
|
5769
|
+
});
|
|
5770
|
+
broadcast2(clients, { type: "tool.confirm_needed", payload: sessionPayload2({ sessionId: e.sessionId, id, toolName: e.tool?.name ?? "unknown", input: e.input, suggestedPattern: e.suggestedPattern, decisionSource: e.decisionSource, riskTier: e.riskTier }) });
|
|
5730
5771
|
});
|
|
5731
5772
|
events.on("error", (e) => {
|
|
5732
|
-
|
|
5773
|
+
broadcast2(clients, { type: "error", payload: sessionPayload2({ sessionId: e.sessionId, phase: e.phase, message: e.err instanceof Error ? e.err.message : String(e.err) }) });
|
|
5733
5774
|
appendForCurrentSession(e.sessionId, {
|
|
5734
5775
|
type: "error",
|
|
5735
5776
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -5738,13 +5779,13 @@ function setupEvents(deps2) {
|
|
|
5738
5779
|
});
|
|
5739
5780
|
});
|
|
5740
5781
|
events.on("session.damaged", (e) => {
|
|
5741
|
-
|
|
5782
|
+
broadcast2(clients, {
|
|
5742
5783
|
type: "session.damaged",
|
|
5743
5784
|
payload: { sessionId: e.sessionId, detail: e.detail }
|
|
5744
5785
|
});
|
|
5745
5786
|
});
|
|
5746
5787
|
events.on("session.rewound", (e) => {
|
|
5747
|
-
|
|
5788
|
+
broadcast2(clients, {
|
|
5748
5789
|
type: "session.rewound",
|
|
5749
5790
|
payload: sessionPayload2({
|
|
5750
5791
|
sessionId: e.sessionId,
|
|
@@ -5755,7 +5796,7 @@ function setupEvents(deps2) {
|
|
|
5755
5796
|
});
|
|
5756
5797
|
});
|
|
5757
5798
|
events.on("checkpoint.written", (e) => {
|
|
5758
|
-
|
|
5799
|
+
broadcast2(clients, {
|
|
5759
5800
|
type: "checkpoint.written",
|
|
5760
5801
|
payload: sessionPayload2({
|
|
5761
5802
|
sessionId: e.sessionId,
|
|
@@ -5767,19 +5808,19 @@ function setupEvents(deps2) {
|
|
|
5767
5808
|
});
|
|
5768
5809
|
});
|
|
5769
5810
|
events.on("in_flight.started", (e) => {
|
|
5770
|
-
|
|
5811
|
+
broadcast2(clients, {
|
|
5771
5812
|
type: "in_flight.started",
|
|
5772
5813
|
payload: sessionPayload2({ sessionId: e.sessionId, context: e.context, ts: e.ts })
|
|
5773
5814
|
});
|
|
5774
5815
|
});
|
|
5775
5816
|
events.on("in_flight.ended", (e) => {
|
|
5776
|
-
|
|
5817
|
+
broadcast2(clients, {
|
|
5777
5818
|
type: "in_flight.ended",
|
|
5778
5819
|
payload: sessionPayload2({ sessionId: e.sessionId, reason: e.reason, ts: e.ts })
|
|
5779
5820
|
});
|
|
5780
5821
|
});
|
|
5781
5822
|
events.on("provider.retry", (e) => {
|
|
5782
|
-
|
|
5823
|
+
broadcast2(clients, {
|
|
5783
5824
|
type: "provider.retry",
|
|
5784
5825
|
payload: sessionPayload2({
|
|
5785
5826
|
sessionId: e.sessionId,
|
|
@@ -5801,7 +5842,7 @@ function setupEvents(deps2) {
|
|
|
5801
5842
|
});
|
|
5802
5843
|
});
|
|
5803
5844
|
events.on("provider.error", (e) => {
|
|
5804
|
-
|
|
5845
|
+
broadcast2(clients, {
|
|
5805
5846
|
type: "provider.error",
|
|
5806
5847
|
payload: sessionPayload2({
|
|
5807
5848
|
sessionId: e.sessionId,
|
|
@@ -5821,7 +5862,7 @@ function setupEvents(deps2) {
|
|
|
5821
5862
|
});
|
|
5822
5863
|
});
|
|
5823
5864
|
events.on("provider.fallback", (e) => {
|
|
5824
|
-
|
|
5865
|
+
broadcast2(clients, {
|
|
5825
5866
|
type: "provider.fallback",
|
|
5826
5867
|
payload: sessionPayload2({
|
|
5827
5868
|
sessionId: e.sessionId,
|
|
@@ -5833,7 +5874,7 @@ function setupEvents(deps2) {
|
|
|
5833
5874
|
});
|
|
5834
5875
|
});
|
|
5835
5876
|
events.on("compaction.fired", (e) => {
|
|
5836
|
-
|
|
5877
|
+
broadcast2(clients, {
|
|
5837
5878
|
type: "context.compacted",
|
|
5838
5879
|
payload: sessionPayload2({
|
|
5839
5880
|
sessionId: e.sessionId,
|
|
@@ -5845,7 +5886,7 @@ function setupEvents(deps2) {
|
|
|
5845
5886
|
});
|
|
5846
5887
|
});
|
|
5847
5888
|
events.on("compaction.failed", (e) => {
|
|
5848
|
-
|
|
5889
|
+
broadcast2(clients, {
|
|
5849
5890
|
type: "compaction.failed",
|
|
5850
5891
|
payload: sessionPayload2({
|
|
5851
5892
|
sessionId: e.sessionId,
|
|
@@ -5860,25 +5901,25 @@ function setupEvents(deps2) {
|
|
|
5860
5901
|
});
|
|
5861
5902
|
});
|
|
5862
5903
|
events.on("mcp.server.connected", (e) => {
|
|
5863
|
-
|
|
5904
|
+
broadcast2(clients, {
|
|
5864
5905
|
type: "mcp.server.connected",
|
|
5865
5906
|
payload: { name: e.name, toolCount: e.toolCount }
|
|
5866
5907
|
});
|
|
5867
5908
|
});
|
|
5868
5909
|
events.on("mcp.server.reconnected", (e) => {
|
|
5869
|
-
|
|
5910
|
+
broadcast2(clients, {
|
|
5870
5911
|
type: "mcp.server.reconnected",
|
|
5871
5912
|
payload: { name: e.name, toolCount: e.toolCount }
|
|
5872
5913
|
});
|
|
5873
5914
|
});
|
|
5874
5915
|
events.on("mcp.server.disconnected", (e) => {
|
|
5875
|
-
|
|
5916
|
+
broadcast2(clients, {
|
|
5876
5917
|
type: "mcp.server.disconnected",
|
|
5877
5918
|
payload: { name: e.name, reason: e.reason }
|
|
5878
5919
|
});
|
|
5879
5920
|
});
|
|
5880
5921
|
events.on("coordinator.stats", (e) => {
|
|
5881
|
-
|
|
5922
|
+
broadcast2(clients, {
|
|
5882
5923
|
type: "coordinator.stats",
|
|
5883
5924
|
payload: sessionPayload2({
|
|
5884
5925
|
sessionId: e.sessionId,
|
|
@@ -5899,12 +5940,12 @@ function setupEvents(deps2) {
|
|
|
5899
5940
|
});
|
|
5900
5941
|
});
|
|
5901
5942
|
events.onPattern("mailbox.received", (_e, payload) => {
|
|
5902
|
-
|
|
5943
|
+
broadcast2(clients, { type: "mailbox.received", payload });
|
|
5903
5944
|
});
|
|
5904
5945
|
events.onPattern("mailbox.agent_registered", (_e, payload) => {
|
|
5905
|
-
|
|
5946
|
+
broadcast2(clients, { type: "mailbox.agent_registered", payload });
|
|
5906
5947
|
});
|
|
5907
|
-
const forwardSubagent = (kind, payload) =>
|
|
5948
|
+
const forwardSubagent = (kind, payload) => broadcast2(clients, { type: "subagent.event", payload: sessionPayload2({ kind, ...payload }) });
|
|
5908
5949
|
events.on("subagent.spawned", (e) => forwardSubagent("spawned", { sessionId: e.sessionId, subagentId: e.subagentId, taskId: e.taskId, name: e.name, provider: e.provider, model: e.model, description: e.description }));
|
|
5909
5950
|
events.on("subagent.task_started", (e) => forwardSubagent("task_started", { sessionId: e.sessionId, subagentId: e.subagentId, taskId: e.taskId, description: e.description }));
|
|
5910
5951
|
events.on("subagent.tool_executed", (e) => forwardSubagent("tool_executed", { sessionId: e.sessionId, subagentId: e.subagentId, toolName: e.name, durationMs: e.durationMs, ok: e.ok }));
|
|
@@ -5914,7 +5955,7 @@ function setupEvents(deps2) {
|
|
|
5914
5955
|
events.on("subagent.ctx_pct", (e) => forwardSubagent("ctx_pct", { sessionId: e.sessionId, subagentId: e.subagentId, load: e.load, tokens: e.tokens, maxContext: e.maxContext }));
|
|
5915
5956
|
events.on("subagent.task_completed", (e) => forwardSubagent("task_completed", { sessionId: e.sessionId, subagentId: e.subagentId, status: e.status, iterations: e.iterations, toolCalls: e.toolCalls, finalText: e.finalText, failureReason: e.error?.kind, error: e.error ? { kind: e.error.kind, message: e.error.message } : void 0 }));
|
|
5916
5957
|
events.on("agent.timeline.message", (e) => {
|
|
5917
|
-
|
|
5958
|
+
broadcast2(clients, {
|
|
5918
5959
|
type: "agent.timeline.message",
|
|
5919
5960
|
payload: sessionPayload2({
|
|
5920
5961
|
sessionId: e.sessionId,
|
|
@@ -5930,7 +5971,7 @@ function setupEvents(deps2) {
|
|
|
5930
5971
|
});
|
|
5931
5972
|
});
|
|
5932
5973
|
events.on("agent.status_changed", (e) => {
|
|
5933
|
-
|
|
5974
|
+
broadcast2(clients, {
|
|
5934
5975
|
type: "agent.status_changed",
|
|
5935
5976
|
payload: sessionPayload2({
|
|
5936
5977
|
sessionId: e.sessionId,
|
|
@@ -5999,19 +6040,19 @@ function setupEvents(deps2) {
|
|
|
5999
6040
|
}
|
|
6000
6041
|
});
|
|
6001
6042
|
events.onPattern("mailbox.*", (eventName, payload) => {
|
|
6002
|
-
|
|
6043
|
+
broadcast2(clients, {
|
|
6003
6044
|
type: "mailbox.event",
|
|
6004
6045
|
payload: sessionPayload2({ event: eventName, ...payload })
|
|
6005
6046
|
});
|
|
6006
6047
|
});
|
|
6007
6048
|
events.onPattern("brain.*", (eventName, payload) => {
|
|
6008
|
-
|
|
6049
|
+
broadcast2(clients, {
|
|
6009
6050
|
type: "brain.event",
|
|
6010
6051
|
payload: sessionPayload2({ event: eventName, ...payload })
|
|
6011
6052
|
});
|
|
6012
6053
|
});
|
|
6013
6054
|
events.on("client.status", async (e) => {
|
|
6014
|
-
|
|
6055
|
+
broadcast2(clients, { type: "client.status_update", payload: e });
|
|
6015
6056
|
if (wpaths?.projectStatus) {
|
|
6016
6057
|
try {
|
|
6017
6058
|
const statusFile = wpaths.projectStatus(e.projectHash);
|
|
@@ -6043,79 +6084,71 @@ function setupEvents(deps2) {
|
|
|
6043
6084
|
if (!watcherMetrics || watcherMetrics.broadcastsSent === 0) return 0;
|
|
6044
6085
|
return watcherMetrics.totalDebounceDelayMs / watcherMetrics.broadcastsSent;
|
|
6045
6086
|
};
|
|
6087
|
+
const logWatcherMetricsEnabled = shouldLogWatcherStats();
|
|
6046
6088
|
const logWatcherMetrics = () => {
|
|
6047
|
-
if (!watcherMetrics) return;
|
|
6089
|
+
if (!watcherMetrics || !logWatcherMetricsEnabled) return;
|
|
6048
6090
|
watcherMetrics.averageDebounceDelayMs = getAverageDebounceDelay();
|
|
6049
6091
|
console.log(
|
|
6050
6092
|
`[setup-events] File watcher stats: ${watcherMetrics.broadcastsSent} broadcasts, ${watcherMetrics.fileChangesDetected} file changes, ${watcherMetrics.debounceResets} debounce resets, avg delay: ${watcherMetrics.averageDebounceDelayMs.toFixed(1)}ms, ${watcherMetrics.activeProjects} active projects`
|
|
6051
6093
|
);
|
|
6052
6094
|
};
|
|
6053
|
-
const metricsInterval = setInterval(logWatcherMetrics, 6e4);
|
|
6095
|
+
const metricsInterval = logWatcherMetricsEnabled ? setInterval(logWatcherMetrics, 6e4) : void 0;
|
|
6054
6096
|
const broadcastStatus = (_projectHash, statusData, actualDelayMs) => {
|
|
6055
|
-
|
|
6097
|
+
broadcast2(clients, { type: "client.status_update", payload: statusData });
|
|
6056
6098
|
if (watcherMetrics) {
|
|
6057
6099
|
watcherMetrics.broadcastsSent++;
|
|
6058
6100
|
watcherMetrics.totalDebounceDelayMs += actualDelayMs;
|
|
6059
6101
|
watcherMetrics.averageDebounceDelayMs = getAverageDebounceDelay();
|
|
6060
6102
|
}
|
|
6061
6103
|
};
|
|
6062
|
-
const scheduleBroadcast = (
|
|
6104
|
+
const scheduleBroadcast = (projectHash, statusData) => {
|
|
6063
6105
|
const now = Date.now();
|
|
6064
|
-
const existing = pendingStatuses.get(
|
|
6106
|
+
const existing = pendingStatuses.get(projectHash);
|
|
6065
6107
|
if (existing && watcherMetrics) {
|
|
6066
6108
|
watcherMetrics.debounceResets++;
|
|
6067
6109
|
}
|
|
6068
|
-
pendingStatuses.set(
|
|
6110
|
+
pendingStatuses.set(projectHash, {
|
|
6069
6111
|
data: statusData,
|
|
6070
6112
|
firstWriteAt: existing ? existing.firstWriteAt : now
|
|
6071
6113
|
});
|
|
6072
|
-
const existingTimer = debounceTimers.get(
|
|
6114
|
+
const existingTimer = debounceTimers.get(projectHash);
|
|
6073
6115
|
if (existingTimer) {
|
|
6074
6116
|
clearTimeout(existingTimer);
|
|
6075
6117
|
}
|
|
6076
6118
|
const timer = setTimeout(() => {
|
|
6077
|
-
debounceTimers.delete(
|
|
6078
|
-
const pending = pendingStatuses.get(
|
|
6119
|
+
debounceTimers.delete(projectHash);
|
|
6120
|
+
const pending = pendingStatuses.get(projectHash);
|
|
6079
6121
|
if (pending) {
|
|
6080
6122
|
const actualDelay = Date.now() - pending.firstWriteAt;
|
|
6081
|
-
broadcastStatus(
|
|
6082
|
-
pendingStatuses.delete(
|
|
6123
|
+
broadcastStatus(projectHash, pending.data, actualDelay);
|
|
6124
|
+
pendingStatuses.delete(projectHash);
|
|
6083
6125
|
}
|
|
6084
6126
|
}, DEBOUNCE_MS);
|
|
6085
|
-
debounceTimers.set(
|
|
6127
|
+
debounceTimers.set(projectHash, timer);
|
|
6086
6128
|
};
|
|
6087
6129
|
let watcher;
|
|
6088
6130
|
const startWatcher = async () => {
|
|
6089
6131
|
try {
|
|
6090
6132
|
await fs10.mkdir(projectsDir, { recursive: true });
|
|
6091
6133
|
watcher = fsWatch(projectsDir, { persistent: true, recursive: true }, async (eventType, filename) => {
|
|
6092
|
-
if (eventType
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
if (statusData.projectHash) {
|
|
6106
|
-
const hash = String(statusData.projectHash);
|
|
6107
|
-
if (!knownProjectHashes.has(hash)) {
|
|
6108
|
-
knownProjectHashes.add(hash);
|
|
6109
|
-
if (watcherMetrics) watcherMetrics.activeProjects = knownProjectHashes.size;
|
|
6110
|
-
}
|
|
6111
|
-
}
|
|
6112
|
-
scheduleBroadcast(projectHash3, statusData);
|
|
6113
|
-
} catch {
|
|
6114
|
-
}
|
|
6115
|
-
}
|
|
6134
|
+
if (eventType !== "change" && eventType !== "rename") return;
|
|
6135
|
+
if (filename == null) return;
|
|
6136
|
+
const projectHash = statusProjectHashFromWatchFilename(projectsDir, filename);
|
|
6137
|
+
if (!projectHash) return;
|
|
6138
|
+
if (watcherMetrics) watcherMetrics.fileChangesDetected++;
|
|
6139
|
+
if (!knownProjectHashes.has(projectHash)) return;
|
|
6140
|
+
if (watcherMetrics) watcherMetrics.filesProcessed++;
|
|
6141
|
+
try {
|
|
6142
|
+
const targetFile = path12.join(projectsDir, projectHash, "status.json");
|
|
6143
|
+
const content = await fs10.readFile(targetFile, "utf-8");
|
|
6144
|
+
const statusData = JSON.parse(content);
|
|
6145
|
+
scheduleBroadcast(projectHash, statusData);
|
|
6146
|
+
} catch {
|
|
6116
6147
|
}
|
|
6117
6148
|
});
|
|
6118
|
-
|
|
6149
|
+
if (logWatcherMetricsEnabled) {
|
|
6150
|
+
console.log(`[setup-events] Watching ${projectsDir} for status.json changes (hash-filtered, debounced)`);
|
|
6151
|
+
}
|
|
6119
6152
|
} catch (err) {
|
|
6120
6153
|
console.error("[setup-events] Failed to start status file watcher:", err);
|
|
6121
6154
|
}
|
|
@@ -6131,14 +6164,14 @@ function setupEvents(deps2) {
|
|
|
6131
6164
|
});
|
|
6132
6165
|
startWatcher();
|
|
6133
6166
|
disposers.push(() => {
|
|
6134
|
-
clearInterval(metricsInterval);
|
|
6167
|
+
if (metricsInterval) clearInterval(metricsInterval);
|
|
6135
6168
|
logWatcherMetrics();
|
|
6136
6169
|
if (watcherMetrics) watcherMetrics.watcherActive = false;
|
|
6137
|
-
for (const [
|
|
6138
|
-
const timer = debounceTimers.get(
|
|
6170
|
+
for (const [projectHash, pending] of pendingStatuses) {
|
|
6171
|
+
const timer = debounceTimers.get(projectHash);
|
|
6139
6172
|
if (timer) {
|
|
6140
6173
|
clearTimeout(timer);
|
|
6141
|
-
broadcastStatus(
|
|
6174
|
+
broadcastStatus(projectHash, pending.data, 0);
|
|
6142
6175
|
}
|
|
6143
6176
|
}
|
|
6144
6177
|
for (const timer of debounceTimers.values()) {
|
|
@@ -6148,7 +6181,7 @@ function setupEvents(deps2) {
|
|
|
6148
6181
|
pendingStatuses.clear();
|
|
6149
6182
|
if (watcher) {
|
|
6150
6183
|
watcher.close();
|
|
6151
|
-
console.log("[setup-events] Closed status file watcher");
|
|
6184
|
+
if (logWatcherMetricsEnabled) console.log("[setup-events] Closed status file watcher");
|
|
6152
6185
|
}
|
|
6153
6186
|
});
|
|
6154
6187
|
}
|
|
@@ -6189,7 +6222,7 @@ function setupEvents(deps2) {
|
|
|
6189
6222
|
lastActivityAt: a.lastActivityAt
|
|
6190
6223
|
}))
|
|
6191
6224
|
}));
|
|
6192
|
-
|
|
6225
|
+
broadcast2(clients, { type: "sessions.status_update", payload: { sessions: live } });
|
|
6193
6226
|
} catch {
|
|
6194
6227
|
}
|
|
6195
6228
|
};
|
|
@@ -6236,6 +6269,115 @@ function computeUsageCost(usage, rates) {
|
|
|
6236
6269
|
return (usage.input * rates.input + usage.output * rates.output + (usage.cacheRead ?? 0) * rates.cacheRead) / 1e6;
|
|
6237
6270
|
}
|
|
6238
6271
|
|
|
6272
|
+
// src/server/model-catalog.ts
|
|
6273
|
+
import { capabilitiesFor } from "@wrongstack/providers";
|
|
6274
|
+
var SIBLING_CATALOG = {
|
|
6275
|
+
"anthropic-oauth": "anthropic",
|
|
6276
|
+
"openai-codex": "openai",
|
|
6277
|
+
"github-copilot": "openai"
|
|
6278
|
+
};
|
|
6279
|
+
async function resolveProviderCatalogForModels(modelsRegistry, providerId, cfg) {
|
|
6280
|
+
return await modelsRegistry.getProvider(providerId) ?? (cfg?.type && cfg.type !== providerId ? await modelsRegistry.getProvider(cfg.type) : void 0);
|
|
6281
|
+
}
|
|
6282
|
+
async function resolveProviderModelMetadata(modelsRegistry, providerId, modelId, cfg) {
|
|
6283
|
+
const direct = await modelsRegistry.getModel(providerId, modelId).catch(() => void 0);
|
|
6284
|
+
if (direct) return overlayConfiguredCapabilities(direct, cfg);
|
|
6285
|
+
if (cfg?.type && cfg.type !== providerId) {
|
|
6286
|
+
const typed = await modelsRegistry.getModel(cfg.type, modelId).catch(() => void 0);
|
|
6287
|
+
if (typed) return overlayConfiguredCapabilities(typed, cfg);
|
|
6288
|
+
}
|
|
6289
|
+
const sibling = cfg?.family ? SIBLING_CATALOG[cfg.family] : void 0;
|
|
6290
|
+
if (sibling) {
|
|
6291
|
+
const siblingModel = await modelsRegistry.getModel(sibling, modelId).catch(() => void 0);
|
|
6292
|
+
if (siblingModel) return overlayConfiguredCapabilities(siblingModel, cfg);
|
|
6293
|
+
const caps2 = await capabilitiesFor(
|
|
6294
|
+
modelsRegistry,
|
|
6295
|
+
sibling,
|
|
6296
|
+
modelId,
|
|
6297
|
+
cfg?.customModels
|
|
6298
|
+
).catch(() => void 0);
|
|
6299
|
+
if (caps2 && positiveNumber(caps2.maxContext)) {
|
|
6300
|
+
return syntheticModel(providerId, modelId, cfg, caps2);
|
|
6301
|
+
}
|
|
6302
|
+
}
|
|
6303
|
+
const catalogId = cfg?.type && cfg.type !== providerId ? cfg.type : providerId;
|
|
6304
|
+
const caps = await capabilitiesFor(
|
|
6305
|
+
modelsRegistry,
|
|
6306
|
+
catalogId,
|
|
6307
|
+
modelId,
|
|
6308
|
+
cfg?.customModels
|
|
6309
|
+
).catch(() => void 0);
|
|
6310
|
+
if (caps && positiveNumber(caps.maxContext)) {
|
|
6311
|
+
return syntheticModel(providerId, modelId, cfg, caps);
|
|
6312
|
+
}
|
|
6313
|
+
const crossCatalog = await findModelAcrossCatalog(modelsRegistry, modelId);
|
|
6314
|
+
if (crossCatalog) return overlayConfiguredCapabilities(crossCatalog, cfg);
|
|
6315
|
+
return syntheticModel(providerId, modelId, cfg);
|
|
6316
|
+
}
|
|
6317
|
+
async function findModelAcrossCatalog(modelsRegistry, modelId) {
|
|
6318
|
+
if (typeof modelsRegistry.listProviders !== "function" || typeof modelsRegistry.getModel !== "function") {
|
|
6319
|
+
return void 0;
|
|
6320
|
+
}
|
|
6321
|
+
const providers = await modelsRegistry.listProviders().catch(() => []);
|
|
6322
|
+
for (const provider of providers) {
|
|
6323
|
+
if (!provider.models.some((model) => model.id === modelId)) continue;
|
|
6324
|
+
const resolved = await modelsRegistry.getModel(provider.id, modelId).catch(() => void 0);
|
|
6325
|
+
if (resolved?.capabilities?.maxContext) return resolved;
|
|
6326
|
+
}
|
|
6327
|
+
return void 0;
|
|
6328
|
+
}
|
|
6329
|
+
function syntheticModel(providerId, modelId, cfg, capabilities) {
|
|
6330
|
+
const custom = cfg?.customModels?.[modelId];
|
|
6331
|
+
const configuredMax = positiveNumber(readConfiguredMaxContext(cfg));
|
|
6332
|
+
const customCaps = custom?.capabilities;
|
|
6333
|
+
const maxContext = positiveNumber(customCaps?.maxContext) ?? positiveNumber(capabilities?.maxContext) ?? configuredMax ?? 0;
|
|
6334
|
+
const isKnown = Boolean(custom) || Boolean(cfg?.models?.includes(modelId)) || maxContext > 0 || Boolean(capabilities);
|
|
6335
|
+
if (!isKnown) return void 0;
|
|
6336
|
+
return {
|
|
6337
|
+
providerId,
|
|
6338
|
+
modelId,
|
|
6339
|
+
capabilities: {
|
|
6340
|
+
tools: readConfiguredBool(cfg, "tools") ?? capabilities?.tools ?? customCaps?.tools ?? false,
|
|
6341
|
+
vision: readConfiguredBool(cfg, "vision") ?? capabilities?.vision ?? customCaps?.vision ?? false,
|
|
6342
|
+
reasoning: readConfiguredBool(cfg, "reasoning") ?? capabilities?.reasoning ?? customCaps?.reasoning ?? false,
|
|
6343
|
+
maxContext,
|
|
6344
|
+
maxOutput: custom?.maxOutput ?? customCaps?.maxOutput ?? capabilities?.maxOutput
|
|
6345
|
+
},
|
|
6346
|
+
cost: void 0
|
|
6347
|
+
};
|
|
6348
|
+
}
|
|
6349
|
+
function overlayConfiguredCapabilities(model, cfg) {
|
|
6350
|
+
const custom = cfg?.customModels?.[model.modelId];
|
|
6351
|
+
const configuredMax = positiveNumber(readConfiguredMaxContext(cfg));
|
|
6352
|
+
const customCaps = custom?.capabilities;
|
|
6353
|
+
if (!configuredMax && !customCaps && custom?.maxOutput === void 0) return model;
|
|
6354
|
+
return {
|
|
6355
|
+
...model,
|
|
6356
|
+
capabilities: {
|
|
6357
|
+
...model.capabilities,
|
|
6358
|
+
...customCaps,
|
|
6359
|
+
maxContext: customCaps?.maxContext ?? configuredMax ?? model.capabilities.maxContext,
|
|
6360
|
+
maxOutput: custom?.maxOutput ?? customCaps?.maxOutput ?? model.capabilities.maxOutput
|
|
6361
|
+
}
|
|
6362
|
+
};
|
|
6363
|
+
}
|
|
6364
|
+
function readConfiguredMaxContext(providerConfig) {
|
|
6365
|
+
if (!providerConfig || typeof providerConfig !== "object") return void 0;
|
|
6366
|
+
const capabilities = providerConfig.capabilities;
|
|
6367
|
+
if (!capabilities || typeof capabilities !== "object") return void 0;
|
|
6368
|
+
return capabilities.maxContext;
|
|
6369
|
+
}
|
|
6370
|
+
function readConfiguredBool(providerConfig, key) {
|
|
6371
|
+
if (!providerConfig || typeof providerConfig !== "object") return void 0;
|
|
6372
|
+
const capabilities = providerConfig.capabilities;
|
|
6373
|
+
if (!capabilities || typeof capabilities !== "object") return void 0;
|
|
6374
|
+
const value = capabilities[key];
|
|
6375
|
+
return typeof value === "boolean" ? value : void 0;
|
|
6376
|
+
}
|
|
6377
|
+
function positiveNumber(value) {
|
|
6378
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
|
|
6379
|
+
}
|
|
6380
|
+
|
|
6239
6381
|
// src/server/server-runtime.ts
|
|
6240
6382
|
import { toErrorMessage as toErrorMessage3 } from "@wrongstack/core/utils";
|
|
6241
6383
|
async function resolvePorts(opts) {
|
|
@@ -6268,7 +6410,12 @@ function createSessionStartPayload(g) {
|
|
|
6268
6410
|
let outputCost = 0;
|
|
6269
6411
|
let cacheReadCost = 0;
|
|
6270
6412
|
try {
|
|
6271
|
-
const m = await
|
|
6413
|
+
const m = await resolveProviderModelMetadata(
|
|
6414
|
+
g.modelsRegistry,
|
|
6415
|
+
config.provider,
|
|
6416
|
+
config.model,
|
|
6417
|
+
config.providers?.[config.provider]
|
|
6418
|
+
);
|
|
6272
6419
|
maxContext = m?.capabilities?.maxContext ?? 0;
|
|
6273
6420
|
if (!maxContext) {
|
|
6274
6421
|
try {
|
|
@@ -6411,7 +6558,7 @@ function registerShutdown(deps2) {
|
|
|
6411
6558
|
}
|
|
6412
6559
|
|
|
6413
6560
|
// src/server/pre-context-services.ts
|
|
6414
|
-
import * as
|
|
6561
|
+
import * as path15 from "path";
|
|
6415
6562
|
import { createRequire } from "module";
|
|
6416
6563
|
import { WebSocket as WebSocket2 } from "ws";
|
|
6417
6564
|
import {
|
|
@@ -6449,13 +6596,14 @@ import { buildProviderFactoriesFromRegistry } from "@wrongstack/providers";
|
|
|
6449
6596
|
import { createDefaultContainer } from "@wrongstack/runtime";
|
|
6450
6597
|
import {
|
|
6451
6598
|
builtinToolsPack,
|
|
6599
|
+
configureDangerBypass,
|
|
6452
6600
|
configureExecPolicy,
|
|
6453
6601
|
forgetTool,
|
|
6454
6602
|
relatedMemoryTool,
|
|
6455
6603
|
rememberTool,
|
|
6456
6604
|
searchMemoryTool
|
|
6457
6605
|
} from "@wrongstack/tools";
|
|
6458
|
-
import {
|
|
6606
|
+
import { toErrorMessage as toErrorMessage5 } from "@wrongstack/core/utils";
|
|
6459
6607
|
|
|
6460
6608
|
// src/server/setup-screen.ts
|
|
6461
6609
|
import { expectDefined as expectDefined2 } from "@wrongstack/core";
|
|
@@ -6537,6 +6685,7 @@ function seedContextMeta(config, context) {
|
|
|
6537
6685
|
meta["favoriteModelsOnly"] = config.favoriteModelsOnly === true;
|
|
6538
6686
|
meta["modelMatrix"] = config.modelMatrix ?? {};
|
|
6539
6687
|
meta["fallbackAuto"] = config.fallbackAuto !== false;
|
|
6688
|
+
if (typeof config.uiLocale === "string" && config.uiLocale) meta["uiLocale"] = config.uiLocale;
|
|
6540
6689
|
meta["featureMcp"] = config.features.mcp !== false;
|
|
6541
6690
|
meta["featurePlugins"] = config.features.plugins !== false;
|
|
6542
6691
|
meta["featureMemory"] = config.features.memory !== false;
|
|
@@ -6575,10 +6724,117 @@ function seedContextMeta(config, context) {
|
|
|
6575
6724
|
meta["tgLongToolMs"] = typeof tgMs === "number" ? tgMs : 3e4;
|
|
6576
6725
|
}
|
|
6577
6726
|
|
|
6727
|
+
// src/server/model-auto-discovery.ts
|
|
6728
|
+
import * as fs11 from "fs/promises";
|
|
6729
|
+
import * as path14 from "path";
|
|
6730
|
+
import { COMPATIBLE_PRESETS, discoverOpenAICompatibleModels } from "@wrongstack/providers";
|
|
6731
|
+
function isOverlayRegistry(value) {
|
|
6732
|
+
return !!value && typeof value === "object" && typeof value.mergeOverlay === "function";
|
|
6733
|
+
}
|
|
6734
|
+
function resolveKey(cfg) {
|
|
6735
|
+
if (Array.isArray(cfg.apiKeys) && cfg.apiKeys.length > 0) {
|
|
6736
|
+
const active = cfg.activeKey ? cfg.apiKeys.find((key) => key.label === cfg.activeKey) : void 0;
|
|
6737
|
+
return (active ?? cfg.apiKeys[0])?.apiKey;
|
|
6738
|
+
}
|
|
6739
|
+
return cfg.apiKey && cfg.apiKey.length > 0 ? cfg.apiKey : void 0;
|
|
6740
|
+
}
|
|
6741
|
+
function eligibleProviders(config) {
|
|
6742
|
+
const out = [];
|
|
6743
|
+
for (const [id, cfg] of Object.entries(config.providers ?? {})) {
|
|
6744
|
+
const preset = COMPATIBLE_PRESETS[id];
|
|
6745
|
+
const enabled = cfg.autoDiscoverModels ?? preset?.autoDiscover ?? false;
|
|
6746
|
+
if (!enabled) continue;
|
|
6747
|
+
const baseUrl = cfg.baseUrl ?? preset?.defaultBaseUrl;
|
|
6748
|
+
if (!baseUrl) continue;
|
|
6749
|
+
out.push({ id, cfg, baseUrl, apiKey: resolveKey(cfg) });
|
|
6750
|
+
}
|
|
6751
|
+
return out;
|
|
6752
|
+
}
|
|
6753
|
+
async function readCache(file) {
|
|
6754
|
+
try {
|
|
6755
|
+
return JSON.parse(await fs11.readFile(file, "utf8"));
|
|
6756
|
+
} catch {
|
|
6757
|
+
return {};
|
|
6758
|
+
}
|
|
6759
|
+
}
|
|
6760
|
+
async function discoverAndMergeWebuiProviders(opts) {
|
|
6761
|
+
const registry = opts.registry;
|
|
6762
|
+
if (!isOverlayRegistry(registry)) return;
|
|
6763
|
+
const targets = eligibleProviders(opts.config);
|
|
6764
|
+
if (targets.length === 0) return;
|
|
6765
|
+
const cacheFile = path14.join(opts.cacheDir, "discovered-models-cache.json");
|
|
6766
|
+
const cache = await readCache(cacheFile);
|
|
6767
|
+
let cacheDirty = false;
|
|
6768
|
+
await Promise.all(
|
|
6769
|
+
targets.map(async ({ id, cfg, baseUrl, apiKey }) => {
|
|
6770
|
+
const cacheKey = `${id}\0${baseUrl}`;
|
|
6771
|
+
const provider = await discoverOpenAICompatibleModels(id, {
|
|
6772
|
+
baseUrl,
|
|
6773
|
+
apiKey,
|
|
6774
|
+
headers: cfg.headers,
|
|
6775
|
+
providerName: id,
|
|
6776
|
+
fetchImpl: opts.fetchImpl
|
|
6777
|
+
});
|
|
6778
|
+
if (provider) {
|
|
6779
|
+
cache[cacheKey] = { fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), provider };
|
|
6780
|
+
cacheDirty = true;
|
|
6781
|
+
registry.mergeOverlay({ [id]: provider });
|
|
6782
|
+
opts.logger?.info?.(
|
|
6783
|
+
`auto-discovered ${Object.keys(provider.models).length} models for "${id}" from ${baseUrl}`
|
|
6784
|
+
);
|
|
6785
|
+
return;
|
|
6786
|
+
}
|
|
6787
|
+
const cached = cache[cacheKey];
|
|
6788
|
+
if (cached) {
|
|
6789
|
+
registry.mergeOverlay({ [id]: cached.provider });
|
|
6790
|
+
opts.logger?.warn?.(
|
|
6791
|
+
`auto-discovery for "${id}" failed; using ${Object.keys(cached.provider.models).length} cached models from ${cached.fetchedAt}`
|
|
6792
|
+
);
|
|
6793
|
+
} else {
|
|
6794
|
+
opts.logger?.warn?.(
|
|
6795
|
+
`auto-discovery for "${id}" failed and no cache available (server at ${baseUrl} unreachable?)`
|
|
6796
|
+
);
|
|
6797
|
+
}
|
|
6798
|
+
})
|
|
6799
|
+
);
|
|
6800
|
+
if (cacheDirty) {
|
|
6801
|
+
try {
|
|
6802
|
+
await fs11.mkdir(path14.dirname(cacheFile), { recursive: true });
|
|
6803
|
+
await fs11.writeFile(cacheFile, JSON.stringify(cache), "utf8");
|
|
6804
|
+
} catch {
|
|
6805
|
+
opts.logger?.debug?.("provider auto-discovery cache write failed");
|
|
6806
|
+
}
|
|
6807
|
+
}
|
|
6808
|
+
}
|
|
6809
|
+
|
|
6578
6810
|
// src/server/pre-context-services.ts
|
|
6811
|
+
var GITHUB_PROVIDERS_OVERLAY_URL = "https://raw.githubusercontent.com/WrongStack/WrongStack/main/packages/cli/data/providers.json";
|
|
6579
6812
|
async function createPreContextServices(input) {
|
|
6580
|
-
const { config, wpaths, logger, opts,
|
|
6581
|
-
const modelsRegistry = opts.services?.modelsRegistry ?? new DefaultModelsRegistry({
|
|
6813
|
+
const { config, wpaths, logger, opts, projectRoot, workingDir, needsProvider } = input;
|
|
6814
|
+
const modelsRegistry = opts.services?.modelsRegistry ?? new DefaultModelsRegistry({
|
|
6815
|
+
cacheFile: wpaths.modelsCache,
|
|
6816
|
+
ttlSeconds: 0,
|
|
6817
|
+
overlayUrl: GITHUB_PROVIDERS_OVERLAY_URL,
|
|
6818
|
+
overlayCacheFile: wpaths.modelsOverlayCache
|
|
6819
|
+
});
|
|
6820
|
+
if (!opts.services?.modelsRegistry) {
|
|
6821
|
+
try {
|
|
6822
|
+
await modelsRegistry.refresh();
|
|
6823
|
+
logger.info("models.dev catalog refreshed");
|
|
6824
|
+
} catch (err) {
|
|
6825
|
+
logger.warn(`models.dev refresh failed (${toErrorMessage5(err)}); using cached catalog`);
|
|
6826
|
+
}
|
|
6827
|
+
}
|
|
6828
|
+
try {
|
|
6829
|
+
await discoverAndMergeWebuiProviders({
|
|
6830
|
+
config,
|
|
6831
|
+
registry: modelsRegistry,
|
|
6832
|
+
cacheDir: path15.dirname(wpaths.modelsCache),
|
|
6833
|
+
logger
|
|
6834
|
+
});
|
|
6835
|
+
} catch (err) {
|
|
6836
|
+
logger.debug(`provider auto-discovery skipped: ${toErrorMessage5(err)}`);
|
|
6837
|
+
}
|
|
6582
6838
|
const container = createDefaultContainer({ config, wpaths, logger, modelsRegistry });
|
|
6583
6839
|
const configStore = opts.services?.configStore ?? container.resolve(TOKENS.ConfigStore);
|
|
6584
6840
|
const providerRegistry = new ProviderRegistry();
|
|
@@ -6609,6 +6865,7 @@ async function createPreContextServices(input) {
|
|
|
6609
6865
|
applyToolDescriptionModes(toolRegistry, config.tools?.descriptionMode);
|
|
6610
6866
|
applyToolResultRenderModes(toolRegistry, config.tools?.resultRenderMode);
|
|
6611
6867
|
configureExecPolicy(config.tools?.exec ?? {});
|
|
6868
|
+
configureDangerBypass(config.tools?.exec?.danger ?? {});
|
|
6612
6869
|
console.log("[WebUI] Tool registry loaded:", toolRegistry.list().length, "tools");
|
|
6613
6870
|
const mcpRegistry = new MCPRegistry({ toolRegistry, events, log: logger, cacheDir: wpaths.cacheDir });
|
|
6614
6871
|
if (config.features.mcp && config.mcpServers) {
|
|
@@ -6619,7 +6876,7 @@ async function createPreContextServices(input) {
|
|
|
6619
6876
|
});
|
|
6620
6877
|
}
|
|
6621
6878
|
}
|
|
6622
|
-
|
|
6879
|
+
const sessionStore = opts.services?.session ?? new DefaultSessionStore({ dir: wpaths.projectSessions });
|
|
6623
6880
|
if (!opts.services?.session) {
|
|
6624
6881
|
sessionStore.prune(DEFAULT_SESSION_PRUNE_DAYS).then((count) => {
|
|
6625
6882
|
if (count > 0) logger.info(`Pruned ${count} old session${count === 1 ? "" : "s"}.`);
|
|
@@ -6641,7 +6898,7 @@ async function createPreContextServices(input) {
|
|
|
6641
6898
|
sessionId: session.id,
|
|
6642
6899
|
projectSlug: wpaths.projectSlug,
|
|
6643
6900
|
projectRoot,
|
|
6644
|
-
projectName:
|
|
6901
|
+
projectName: path15.basename(projectRoot),
|
|
6645
6902
|
workingDir,
|
|
6646
6903
|
clientType: "webui",
|
|
6647
6904
|
pid: process.pid,
|
|
@@ -6657,12 +6914,21 @@ async function createPreContextServices(input) {
|
|
|
6657
6914
|
statusTracker.start();
|
|
6658
6915
|
let stopHqSessionBridge;
|
|
6659
6916
|
let hqTelemetryPublisher;
|
|
6917
|
+
const stopHqAuxBridges = [];
|
|
6660
6918
|
try {
|
|
6661
|
-
const {
|
|
6919
|
+
const {
|
|
6920
|
+
createHqPublisherFromEnv,
|
|
6921
|
+
startSessionTelemetryBridge,
|
|
6922
|
+
startFleetTelemetryBridge,
|
|
6923
|
+
startBrainTelemetryBridge,
|
|
6924
|
+
startWorktreeTelemetryBridge,
|
|
6925
|
+
startToolTelemetryBridge,
|
|
6926
|
+
startCostTelemetryBridge
|
|
6927
|
+
} = await import("@wrongstack/core");
|
|
6662
6928
|
const hqTelemetry = createHqPublisherFromEnv({
|
|
6663
6929
|
clientKind: "webui",
|
|
6664
6930
|
projectRoot,
|
|
6665
|
-
projectName:
|
|
6931
|
+
projectName: path15.basename(projectRoot),
|
|
6666
6932
|
appConfig: config,
|
|
6667
6933
|
socketFactory: (url) => new WebSocket2(url)
|
|
6668
6934
|
});
|
|
@@ -6674,11 +6940,41 @@ async function createPreContextServices(input) {
|
|
|
6674
6940
|
events,
|
|
6675
6941
|
sessionId: session.id,
|
|
6676
6942
|
projectRoot,
|
|
6677
|
-
projectName:
|
|
6943
|
+
projectName: path15.basename(projectRoot),
|
|
6678
6944
|
globalRoot: wpaths.globalRoot,
|
|
6679
6945
|
initialAgents: statusTracker?.getAgents(),
|
|
6680
6946
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6681
6947
|
});
|
|
6948
|
+
try {
|
|
6949
|
+
stopHqAuxBridges.push(
|
|
6950
|
+
startFleetTelemetryBridge({ events, publisher: hqTelemetry, runId: session.id, sessionId: session.id })
|
|
6951
|
+
);
|
|
6952
|
+
} catch {
|
|
6953
|
+
}
|
|
6954
|
+
try {
|
|
6955
|
+
stopHqAuxBridges.push(
|
|
6956
|
+
startBrainTelemetryBridge({ events, publisher: hqTelemetry, sessionId: session.id })
|
|
6957
|
+
);
|
|
6958
|
+
} catch {
|
|
6959
|
+
}
|
|
6960
|
+
try {
|
|
6961
|
+
stopHqAuxBridges.push(
|
|
6962
|
+
startWorktreeTelemetryBridge({ events, publisher: hqTelemetry, sessionId: session.id })
|
|
6963
|
+
);
|
|
6964
|
+
} catch {
|
|
6965
|
+
}
|
|
6966
|
+
try {
|
|
6967
|
+
stopHqAuxBridges.push(
|
|
6968
|
+
startToolTelemetryBridge({ events, publisher: hqTelemetry, projectRoot, sessionId: session.id })
|
|
6969
|
+
);
|
|
6970
|
+
} catch {
|
|
6971
|
+
}
|
|
6972
|
+
try {
|
|
6973
|
+
stopHqAuxBridges.push(
|
|
6974
|
+
startCostTelemetryBridge({ events, publisher: hqTelemetry, sessionId: session.id })
|
|
6975
|
+
);
|
|
6976
|
+
} catch {
|
|
6977
|
+
}
|
|
6682
6978
|
}
|
|
6683
6979
|
} catch {
|
|
6684
6980
|
}
|
|
@@ -6688,6 +6984,12 @@ async function createPreContextServices(input) {
|
|
|
6688
6984
|
await registry.markClosing();
|
|
6689
6985
|
statusTracker?.stop();
|
|
6690
6986
|
stopHqSessionBridge?.();
|
|
6987
|
+
for (const stop of stopHqAuxBridges) {
|
|
6988
|
+
try {
|
|
6989
|
+
stop();
|
|
6990
|
+
} catch {
|
|
6991
|
+
}
|
|
6992
|
+
}
|
|
6691
6993
|
hqTelemetryPublisher?.close();
|
|
6692
6994
|
} catch {
|
|
6693
6995
|
}
|
|
@@ -6717,22 +7019,27 @@ async function createPreContextServices(input) {
|
|
|
6717
7019
|
const customModeStore = createCustomModeStore(wpaths.configDir);
|
|
6718
7020
|
await customModeStore.load();
|
|
6719
7021
|
console.log("[WebUI] Custom context modes loaded:", customModeStore.list().filter((m) => m.custom).length, "custom");
|
|
6720
|
-
const resolvedModel = await
|
|
7022
|
+
const resolvedModel = await resolveProviderModelMetadata(
|
|
7023
|
+
modelsRegistry,
|
|
7024
|
+
config.provider,
|
|
7025
|
+
config.model,
|
|
7026
|
+
config.providers?.[config.provider]
|
|
7027
|
+
);
|
|
6721
7028
|
const modelCapabilities = resolvedModel?.capabilities ? { maxContextTokens: resolvedModel.capabilities.maxContext, supportsTools: resolvedModel.capabilities.tools, supportsVision: resolvedModel.capabilities.vision, supportsReasoning: resolvedModel.capabilities.reasoning } : void 0;
|
|
6722
7029
|
const modelCapabilitiesRef = { current: modelCapabilities };
|
|
6723
7030
|
const skillLoader = config.features.skills ? new DefaultSkillLoader({ paths: wpaths }) : void 0;
|
|
6724
7031
|
const skillInstaller = config.features.skills ? new SkillInstaller({
|
|
6725
|
-
manifestPath:
|
|
6726
|
-
projectSkillsDir:
|
|
6727
|
-
globalSkillsDir:
|
|
6728
|
-
projectHash: projectHash
|
|
7032
|
+
manifestPath: path15.join(wpaths.globalRoot, "installed-skills.json"),
|
|
7033
|
+
projectSkillsDir: wpaths.inProjectSkills,
|
|
7034
|
+
globalSkillsDir: wpaths.globalSkills,
|
|
7035
|
+
projectHash: wpaths.projectHash,
|
|
6729
7036
|
skillLoader
|
|
6730
7037
|
}) : void 0;
|
|
6731
7038
|
const promptsEnabled = config.features.prompts !== false;
|
|
6732
7039
|
const bundledPromptsDir = promptsEnabled ? (() => {
|
|
6733
7040
|
try {
|
|
6734
7041
|
const req = createRequire(import.meta.url);
|
|
6735
|
-
return
|
|
7042
|
+
return path15.join(path15.dirname(req.resolve("@wrongstack/core/package.json")), "data", "prompts");
|
|
6736
7043
|
} catch {
|
|
6737
7044
|
return void 0;
|
|
6738
7045
|
}
|
|
@@ -6749,6 +7056,11 @@ async function createPreContextServices(input) {
|
|
|
6749
7056
|
modelCapabilities: () => modelCapabilitiesRef.current,
|
|
6750
7057
|
instructionPaths: { globalDir: wpaths.globalInstructions, projectDir: wpaths.inProjectInstructions }
|
|
6751
7058
|
});
|
|
7059
|
+
if (container.has(TOKENS.SystemPromptBuilder)) {
|
|
7060
|
+
container.override(TOKENS.SystemPromptBuilder, () => systemPromptBuilder, { owner: "webui" });
|
|
7061
|
+
} else {
|
|
7062
|
+
container.bind(TOKENS.SystemPromptBuilder, () => systemPromptBuilder, { owner: "webui" });
|
|
7063
|
+
}
|
|
6752
7064
|
let onlineAgents = [];
|
|
6753
7065
|
try {
|
|
6754
7066
|
const systemMailbox = new GlobalMailbox(wpaths.projectDir);
|
|
@@ -6810,11 +7122,11 @@ async function createPreContextServices(input) {
|
|
|
6810
7122
|
}
|
|
6811
7123
|
|
|
6812
7124
|
// src/server/start-webui.ts
|
|
6813
|
-
import * as
|
|
7125
|
+
import * as path22 from "path";
|
|
6814
7126
|
import {
|
|
6815
|
-
createDefaultPipelines
|
|
7127
|
+
createDefaultPipelines,
|
|
6816
7128
|
createSessionEventBridge,
|
|
6817
|
-
DEFAULT_CONTEXT_WINDOW_MODE_ID as
|
|
7129
|
+
DEFAULT_CONTEXT_WINDOW_MODE_ID as DEFAULT_CONTEXT_WINDOW_MODE_ID2,
|
|
6818
7130
|
expectDefined as expectDefined3,
|
|
6819
7131
|
resolveSessionLoggingConfig,
|
|
6820
7132
|
watchProviderConfig
|
|
@@ -7588,8 +7900,8 @@ var CollaborationWebSocketHandler = class {
|
|
|
7588
7900
|
};
|
|
7589
7901
|
|
|
7590
7902
|
// src/server/codebase-indexing.ts
|
|
7591
|
-
import * as
|
|
7592
|
-
import * as
|
|
7903
|
+
import * as fs12 from "fs";
|
|
7904
|
+
import * as path16 from "path";
|
|
7593
7905
|
import {
|
|
7594
7906
|
cancelPendingReindexes,
|
|
7595
7907
|
enqueueReindex,
|
|
@@ -7638,11 +7950,11 @@ function setupWebUICodebaseIndexing(deps2) {
|
|
|
7638
7950
|
let watcher;
|
|
7639
7951
|
if (idx.watchExternal) {
|
|
7640
7952
|
try {
|
|
7641
|
-
watcher =
|
|
7953
|
+
watcher = fs12.watch(deps2.projectRoot, { recursive: true }, (_event, filename) => {
|
|
7642
7954
|
if (!filename) return;
|
|
7643
7955
|
const rel = filename.toString();
|
|
7644
7956
|
if (isIgnored(rel)) return;
|
|
7645
|
-
const abs =
|
|
7957
|
+
const abs = path16.resolve(deps2.projectRoot, rel);
|
|
7646
7958
|
enqueueFile(abs);
|
|
7647
7959
|
});
|
|
7648
7960
|
watcher.on("error", (err) => deps2.logger.debug(`webui codebase index watcher error: ${err}`));
|
|
@@ -7655,7 +7967,7 @@ function setupWebUICodebaseIndexing(deps2) {
|
|
|
7655
7967
|
}
|
|
7656
7968
|
function enqueueFile(filePath) {
|
|
7657
7969
|
if (!idx.onEdit && !idx.watchExternal) return;
|
|
7658
|
-
const abs =
|
|
7970
|
+
const abs = path16.isAbsolute(filePath) ? path16.normalize(filePath) : path16.resolve(deps2.projectRoot, filePath);
|
|
7659
7971
|
if (!isInside2(deps2.projectRoot, abs) || !isIndexableFile(abs)) return;
|
|
7660
7972
|
enqueueReindex({
|
|
7661
7973
|
projectRoot: deps2.projectRoot,
|
|
@@ -7692,75 +8004,150 @@ function isIgnored(rel) {
|
|
|
7692
8004
|
return rel.split(/[/\\]/).some((seg) => IGNORE_DIRS.has(seg));
|
|
7693
8005
|
}
|
|
7694
8006
|
function isInside2(root, target) {
|
|
7695
|
-
const normalizedRoot =
|
|
7696
|
-
const normalizedTarget =
|
|
7697
|
-
return normalizedTarget === normalizedRoot || normalizedTarget.startsWith(normalizedRoot +
|
|
8007
|
+
const normalizedRoot = path16.resolve(root);
|
|
8008
|
+
const normalizedTarget = path16.resolve(target);
|
|
8009
|
+
return normalizedTarget === normalizedRoot || normalizedTarget.startsWith(normalizedRoot + path16.sep);
|
|
7698
8010
|
}
|
|
7699
8011
|
|
|
7700
8012
|
// src/server/discover-mailbox-bridge.ts
|
|
7701
|
-
import {
|
|
8013
|
+
import { spawn as spawn3 } from "child_process";
|
|
8014
|
+
import { createRequire as createRequire2 } from "module";
|
|
8015
|
+
import { resolveProjectDir, wstackGlobalRoot as wstackGlobalRoot2 } from "@wrongstack/core";
|
|
7702
8016
|
import { readLiveLock } from "@wrongstack/core/coordination";
|
|
8017
|
+
var MAILBOX_BRIDGE_BOOT_TIMEOUT_MS = 5e3;
|
|
7703
8018
|
async function discoverMailboxBridgeForWebui(params) {
|
|
7704
8019
|
const mode = params.config?.features?.mailboxBridge ?? "auto";
|
|
7705
8020
|
if (mode === "off") return;
|
|
7706
|
-
const projectDir = resolveProjectDir(params.projectRoot,
|
|
7707
|
-
|
|
8021
|
+
const projectDir = resolveProjectDir(params.projectRoot, wstackGlobalRoot2());
|
|
8022
|
+
let result = await readLiveLock(projectDir);
|
|
8023
|
+
let spawnedPid = null;
|
|
8024
|
+
if (result.kind !== "live") {
|
|
8025
|
+
spawnedPid = spawnMailboxBridge(params.projectRoot, params.logger);
|
|
8026
|
+
if (spawnedPid !== null) {
|
|
8027
|
+
const live = await waitForLiveMailboxBridge(projectDir, MAILBOX_BRIDGE_BOOT_TIMEOUT_MS);
|
|
8028
|
+
if (live) {
|
|
8029
|
+
stashBridge(params, live, projectDir, "spawned", spawnedPid);
|
|
8030
|
+
params.logger.debug("webui spawned mailbox bridge", {
|
|
8031
|
+
url: live.url,
|
|
8032
|
+
lockPath: projectDir,
|
|
8033
|
+
childPid: spawnedPid
|
|
8034
|
+
});
|
|
8035
|
+
return;
|
|
8036
|
+
}
|
|
8037
|
+
result = await readLiveLock(projectDir);
|
|
8038
|
+
}
|
|
8039
|
+
}
|
|
7708
8040
|
switch (result.kind) {
|
|
7709
8041
|
case "live": {
|
|
7710
8042
|
params.logger.debug("webui joined existing mailbox bridge", {
|
|
7711
8043
|
url: result.lock.url,
|
|
7712
8044
|
lockPath: projectDir
|
|
7713
8045
|
});
|
|
7714
|
-
params.
|
|
7715
|
-
url: result.lock.url,
|
|
7716
|
-
token: result.lock.token,
|
|
7717
|
-
lockPath: projectDir,
|
|
7718
|
-
childPid: null,
|
|
7719
|
-
source: "joined"
|
|
7720
|
-
};
|
|
8046
|
+
stashBridge(params, result.lock, projectDir, "joined", null);
|
|
7721
8047
|
break;
|
|
7722
8048
|
}
|
|
7723
8049
|
case "probe-failed": {
|
|
7724
8050
|
params.logger.warn(
|
|
7725
8051
|
"mailbox bridge present but /healthz unreachable; webui will start without external-agent connectivity",
|
|
7726
|
-
{ url: result.lock.url, lockPath: projectDir }
|
|
8052
|
+
{ url: result.lock.url, lockPath: projectDir, spawnedPid }
|
|
7727
8053
|
);
|
|
7728
|
-
params.
|
|
7729
|
-
url: result.lock.url,
|
|
7730
|
-
token: result.lock.token,
|
|
7731
|
-
lockPath: projectDir,
|
|
7732
|
-
childPid: null,
|
|
7733
|
-
source: "unhealthy"
|
|
7734
|
-
};
|
|
8054
|
+
stashBridge(params, result.lock, projectDir, "unhealthy", null);
|
|
7735
8055
|
break;
|
|
7736
8056
|
}
|
|
7737
8057
|
case "absent": {
|
|
7738
|
-
params.logger.
|
|
7739
|
-
"
|
|
7740
|
-
{ projectDir }
|
|
8058
|
+
params.logger.warn(
|
|
8059
|
+
"mailbox bridge unavailable; webui will start without external-agent connectivity. Run `wstack mailbox serve` or a CLI surface to bring one up.",
|
|
8060
|
+
{ projectDir, spawnedPid }
|
|
7741
8061
|
);
|
|
7742
8062
|
break;
|
|
7743
8063
|
}
|
|
7744
8064
|
}
|
|
7745
8065
|
}
|
|
8066
|
+
function stashBridge(params, lock, lockPath, source, childPid) {
|
|
8067
|
+
params.ctx.meta["mailboxBridge"] = {
|
|
8068
|
+
url: lock.url,
|
|
8069
|
+
token: lock.token,
|
|
8070
|
+
lockPath,
|
|
8071
|
+
childPid,
|
|
8072
|
+
source
|
|
8073
|
+
};
|
|
8074
|
+
}
|
|
8075
|
+
async function waitForLiveMailboxBridge(projectDir, timeoutMs) {
|
|
8076
|
+
const deadline = Date.now() + timeoutMs;
|
|
8077
|
+
while (Date.now() < deadline) {
|
|
8078
|
+
await sleep(120);
|
|
8079
|
+
const result = await readLiveLock(projectDir);
|
|
8080
|
+
if (result.kind === "live") return result.lock;
|
|
8081
|
+
}
|
|
8082
|
+
return null;
|
|
8083
|
+
}
|
|
8084
|
+
function spawnMailboxBridge(projectRoot, logger) {
|
|
8085
|
+
const invocation = mailboxServeInvocation();
|
|
8086
|
+
try {
|
|
8087
|
+
const child = spawn3(invocation.command, invocation.args, {
|
|
8088
|
+
cwd: projectRoot,
|
|
8089
|
+
detached: process.platform !== "win32",
|
|
8090
|
+
env: process.env,
|
|
8091
|
+
stdio: "ignore",
|
|
8092
|
+
windowsHide: true,
|
|
8093
|
+
...invocation.windowsVerbatimArguments ? { windowsVerbatimArguments: invocation.windowsVerbatimArguments } : {}
|
|
8094
|
+
});
|
|
8095
|
+
child.once("error", (err) => {
|
|
8096
|
+
logger.warn("failed to spawn mailbox bridge for webui", {
|
|
8097
|
+
command: invocation.command,
|
|
8098
|
+
err: err instanceof Error ? err.message : String(err)
|
|
8099
|
+
});
|
|
8100
|
+
});
|
|
8101
|
+
child.unref();
|
|
8102
|
+
return child.pid ?? null;
|
|
8103
|
+
} catch (err) {
|
|
8104
|
+
logger.warn("failed to spawn mailbox bridge for webui", {
|
|
8105
|
+
command: invocation.command,
|
|
8106
|
+
err: err instanceof Error ? err.message : String(err)
|
|
8107
|
+
});
|
|
8108
|
+
return null;
|
|
8109
|
+
}
|
|
8110
|
+
}
|
|
8111
|
+
function mailboxServeInvocation() {
|
|
8112
|
+
const explicitCliEntry = process.env["WRONGSTACK_CLI_ENTRY"];
|
|
8113
|
+
if (explicitCliEntry) {
|
|
8114
|
+
return { command: process.execPath, args: [explicitCliEntry, "mailbox", "serve"] };
|
|
8115
|
+
}
|
|
8116
|
+
try {
|
|
8117
|
+
const require2 = createRequire2(import.meta.url);
|
|
8118
|
+
const cliEntry = require2.resolve("@wrongstack/cli");
|
|
8119
|
+
return { command: process.execPath, args: [cliEntry, "mailbox", "serve"] };
|
|
8120
|
+
} catch {
|
|
8121
|
+
return {
|
|
8122
|
+
command: process.platform === "win32" ? "wstack.cmd" : "wstack",
|
|
8123
|
+
args: ["mailbox", "serve"]
|
|
8124
|
+
};
|
|
8125
|
+
}
|
|
8126
|
+
}
|
|
8127
|
+
function sleep(ms) {
|
|
8128
|
+
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
8129
|
+
}
|
|
7746
8130
|
|
|
7747
8131
|
// src/server/terminal-ws-handler.ts
|
|
7748
|
-
import {
|
|
8132
|
+
import { spawn as spawnChild } from "child_process";
|
|
8133
|
+
import { createRequire as createRequire3 } from "module";
|
|
7749
8134
|
import { toErrorMessage as toErrorMessage7 } from "@wrongstack/core/utils";
|
|
7750
8135
|
var MAX_SESSIONS_PER_CLIENT = 8;
|
|
7751
8136
|
var DEFAULT_COLS = 80;
|
|
7752
8137
|
var DEFAULT_ROWS = 24;
|
|
7753
|
-
var requireFromHere =
|
|
8138
|
+
var requireFromHere = createRequire3(import.meta.url);
|
|
7754
8139
|
var cachedNodePty;
|
|
7755
8140
|
var TerminalWebSocketHandler = class {
|
|
7756
|
-
constructor(getCwd, logger, loadNodePty = defaultLoadNodePty) {
|
|
8141
|
+
constructor(getCwd, logger, loadNodePty = defaultLoadNodePty, killProcessTree = defaultKillProcessTree) {
|
|
7757
8142
|
this.getCwd = getCwd;
|
|
7758
8143
|
this.logger = logger;
|
|
7759
8144
|
this.loadNodePty = loadNodePty;
|
|
8145
|
+
this.killProcessTree = killProcessTree;
|
|
7760
8146
|
}
|
|
7761
8147
|
getCwd;
|
|
7762
8148
|
logger;
|
|
7763
8149
|
loadNodePty;
|
|
8150
|
+
killProcessTree;
|
|
7764
8151
|
/** ws → (terminalId → pty). */
|
|
7765
8152
|
sessions = /* @__PURE__ */ new Map();
|
|
7766
8153
|
addClient(ws) {
|
|
@@ -7822,10 +8209,14 @@ var TerminalWebSocketHandler = class {
|
|
|
7822
8209
|
cols: clampDim(payload.cols, DEFAULT_COLS),
|
|
7823
8210
|
rows: clampDim(payload.rows, DEFAULT_ROWS),
|
|
7824
8211
|
cwd: this.getCwd(),
|
|
7825
|
-
env: process.env
|
|
8212
|
+
env: process.env,
|
|
8213
|
+
...windowsPtyOptions()
|
|
7826
8214
|
});
|
|
7827
8215
|
} catch (err) {
|
|
8216
|
+
const msg = `Integrated terminal failed to start: ${toErrorMessage7(err)}`;
|
|
7828
8217
|
this.logger.warn?.(`terminal spawn failed: ${toErrorMessage7(err)}`);
|
|
8218
|
+
this.send(ws, { type: "terminal.output", payload: { id: payload.id, data: `${msg}\r
|
|
8219
|
+
` } });
|
|
7829
8220
|
this.send(ws, { type: "terminal.exit", payload: { id: payload.id, exitCode: -1 } });
|
|
7830
8221
|
return;
|
|
7831
8222
|
}
|
|
@@ -7858,21 +8249,38 @@ var TerminalWebSocketHandler = class {
|
|
|
7858
8249
|
const pty = map?.get(id);
|
|
7859
8250
|
if (!pty) return;
|
|
7860
8251
|
map?.delete(id);
|
|
7861
|
-
|
|
7862
|
-
pty.kill();
|
|
7863
|
-
} catch {
|
|
7864
|
-
}
|
|
8252
|
+
this.killPty(pty, "terminal close");
|
|
7865
8253
|
}
|
|
7866
8254
|
disposeClient(ws) {
|
|
7867
8255
|
const map = this.sessions.get(ws);
|
|
7868
8256
|
if (!map) return;
|
|
7869
8257
|
for (const pty of map.values()) {
|
|
8258
|
+
this.killPty(pty, "terminal client dispose");
|
|
8259
|
+
}
|
|
8260
|
+
this.sessions.delete(ws);
|
|
8261
|
+
}
|
|
8262
|
+
killPty(pty, reason) {
|
|
8263
|
+
if (process.platform === "win32" && isPositivePid(pty.pid)) {
|
|
8264
|
+
this.killWindowsProcessTree(pty, reason);
|
|
8265
|
+
return;
|
|
8266
|
+
}
|
|
8267
|
+
try {
|
|
8268
|
+
pty.kill();
|
|
8269
|
+
} catch (err) {
|
|
8270
|
+
this.logger.warn?.(`${reason} failed: ${toErrorMessage7(err)}`);
|
|
8271
|
+
}
|
|
8272
|
+
}
|
|
8273
|
+
killWindowsProcessTree(pty, reason) {
|
|
8274
|
+
try {
|
|
8275
|
+
this.killProcessTree(pty.pid);
|
|
7870
8276
|
try {
|
|
7871
|
-
pty.
|
|
8277
|
+
pty.write("");
|
|
8278
|
+
pty.write("exit\r");
|
|
7872
8279
|
} catch {
|
|
7873
8280
|
}
|
|
8281
|
+
} catch (err) {
|
|
8282
|
+
this.logger.warn?.(`${reason} taskkill failed: ${toErrorMessage7(err)}`);
|
|
7874
8283
|
}
|
|
7875
|
-
this.sessions.delete(ws);
|
|
7876
8284
|
}
|
|
7877
8285
|
send(ws, msg) {
|
|
7878
8286
|
try {
|
|
@@ -7896,6 +8304,21 @@ function isStr(v) {
|
|
|
7896
8304
|
function numOrUndef(v) {
|
|
7897
8305
|
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
7898
8306
|
}
|
|
8307
|
+
function windowsPtyOptions() {
|
|
8308
|
+
if (process.platform !== "win32") return {};
|
|
8309
|
+
if (process.env.WRONGSTACK_TERMINAL_USE_CONPTY_DLL !== "1") return {};
|
|
8310
|
+
return { useConptyDll: true };
|
|
8311
|
+
}
|
|
8312
|
+
function defaultKillProcessTree(pid) {
|
|
8313
|
+
const child = spawnChild("taskkill", ["/PID", String(pid), "/T", "/F"], {
|
|
8314
|
+
stdio: "ignore",
|
|
8315
|
+
windowsHide: true
|
|
8316
|
+
});
|
|
8317
|
+
child.once("error", () => void 0);
|
|
8318
|
+
}
|
|
8319
|
+
function isPositivePid(value) {
|
|
8320
|
+
return Number.isInteger(value) && Number(value) > 0;
|
|
8321
|
+
}
|
|
7899
8322
|
function clampDim(value, fallback) {
|
|
7900
8323
|
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
7901
8324
|
return Math.max(1, Math.min(1e3, Math.floor(value)));
|
|
@@ -7918,20 +8341,14 @@ async function createAgentServices(input) {
|
|
|
7918
8341
|
providerRegistry,
|
|
7919
8342
|
modelsRegistry,
|
|
7920
8343
|
events,
|
|
7921
|
-
mcpRegistry,
|
|
7922
|
-
skillLoader,
|
|
7923
|
-
skillInstaller,
|
|
7924
|
-
tokenCounter,
|
|
7925
8344
|
modelCapabilitiesRef
|
|
7926
8345
|
} = input;
|
|
7927
8346
|
const collabBus = new CollaborationBusCtor();
|
|
7928
8347
|
const pipelines = input.pipelines;
|
|
7929
|
-
const collabPause = collabPauseMiddleware(collabBus, { logger });
|
|
7930
|
-
Object.defineProperty(collabPause, "name", { value: "collab-pause" });
|
|
7931
|
-
pipelines.toolCall.prepend(collabPause);
|
|
7932
8348
|
const collabInject = collabInjectMiddleware(collabBus, { logger });
|
|
7933
|
-
Object.defineProperty(collabInject, "name", { value: "collab-inject" });
|
|
7934
8349
|
pipelines.toolCall.prepend(collabInject);
|
|
8350
|
+
const collabPause = collabPauseMiddleware(collabBus, { logger });
|
|
8351
|
+
pipelines.toolCall.prepend(collabPause);
|
|
7935
8352
|
installDesignStudioMiddleware({ pipelines, ctx: context });
|
|
7936
8353
|
const codebaseIndexing = setupWebUICodebaseIndexing({
|
|
7937
8354
|
config,
|
|
@@ -7952,7 +8369,12 @@ async function createAgentServices(input) {
|
|
|
7952
8369
|
let effectiveMaxContext = config.context?.effectiveMaxContext ?? 0;
|
|
7953
8370
|
if (!effectiveMaxContext) {
|
|
7954
8371
|
try {
|
|
7955
|
-
const m = await
|
|
8372
|
+
const m = await resolveProviderModelMetadata(
|
|
8373
|
+
modelsRegistry,
|
|
8374
|
+
config.provider,
|
|
8375
|
+
context.model,
|
|
8376
|
+
config.providers?.[config.provider]
|
|
8377
|
+
);
|
|
7956
8378
|
effectiveMaxContext = m?.capabilities?.maxContext ?? 0;
|
|
7957
8379
|
} catch {
|
|
7958
8380
|
}
|
|
@@ -7983,15 +8405,21 @@ async function createAgentServices(input) {
|
|
|
7983
8405
|
);
|
|
7984
8406
|
pipelines.contextWindow.use({ name: "AutoCompaction", handler: autoCompactor.handler() });
|
|
7985
8407
|
}
|
|
7986
|
-
const updateAutoCompactionMaxContext = async (newProvider) => {
|
|
8408
|
+
const updateAutoCompactionMaxContext = async (newProvider, providerId = newProvider.id, providerCfg) => {
|
|
7987
8409
|
await modelsRegistry.refresh().catch((err) => {
|
|
7988
8410
|
logger.warn(
|
|
7989
|
-
`models.dev refresh failed for ${
|
|
8411
|
+
`models.dev refresh failed for ${providerId}/${context.model}: ${toErrorMessage8(err)}; using cached catalog`
|
|
7990
8412
|
);
|
|
7991
8413
|
});
|
|
7992
|
-
|
|
8414
|
+
const currentConfig = input.config;
|
|
8415
|
+
let newMaxContext = currentConfig.context?.effectiveMaxContext ?? newProvider.capabilities.maxContext;
|
|
7993
8416
|
try {
|
|
7994
|
-
const m = await
|
|
8417
|
+
const m = await resolveProviderModelMetadata(
|
|
8418
|
+
modelsRegistry,
|
|
8419
|
+
providerId,
|
|
8420
|
+
context.model,
|
|
8421
|
+
providerCfg ?? currentConfig.providers?.[providerId]
|
|
8422
|
+
);
|
|
7995
8423
|
newMaxContext = m?.capabilities?.maxContext ?? newMaxContext;
|
|
7996
8424
|
} catch {
|
|
7997
8425
|
}
|
|
@@ -8200,6 +8628,24 @@ async function createAgentServices(input) {
|
|
|
8200
8628
|
};
|
|
8201
8629
|
}
|
|
8202
8630
|
|
|
8631
|
+
// src/server/pending-confirms.ts
|
|
8632
|
+
function isDestructivePendingConfirm(confirm) {
|
|
8633
|
+
return confirm.riskTier === "destructive" || confirm.decisionSource === "yolo_destructive";
|
|
8634
|
+
}
|
|
8635
|
+
function resolveYoloEligiblePendingConfirms(pendingConfirms) {
|
|
8636
|
+
for (const [id, confirm] of pendingConfirms) {
|
|
8637
|
+
if (isDestructivePendingConfirm(confirm)) continue;
|
|
8638
|
+
pendingConfirms.delete(id);
|
|
8639
|
+
confirm.resolve("yes");
|
|
8640
|
+
}
|
|
8641
|
+
}
|
|
8642
|
+
function resolveAllPendingConfirms(pendingConfirms, decision) {
|
|
8643
|
+
for (const [id, confirm] of pendingConfirms) {
|
|
8644
|
+
pendingConfirms.delete(id);
|
|
8645
|
+
confirm.resolve(decision);
|
|
8646
|
+
}
|
|
8647
|
+
}
|
|
8648
|
+
|
|
8203
8649
|
// src/server/connection-handler.ts
|
|
8204
8650
|
var REPLAY_MESSAGE_CAP = 2e3;
|
|
8205
8651
|
var RATE_LIMIT_MESSAGES = Number.parseInt(process.env["WEBUI_RATE_LIMIT"] ?? "0", 10);
|
|
@@ -8311,12 +8757,7 @@ function createConnectionHandler(opts) {
|
|
|
8311
8757
|
const closing = opts.clients.get(ws);
|
|
8312
8758
|
opts.clients.delete(ws);
|
|
8313
8759
|
if (closing) rateLimits.delete(closing.connId);
|
|
8314
|
-
|
|
8315
|
-
for (const [, resolve11] of opts.pendingConfirms) {
|
|
8316
|
-
resolve11("no");
|
|
8317
|
-
}
|
|
8318
|
-
opts.pendingConfirms.clear();
|
|
8319
|
-
}
|
|
8760
|
+
resolveAllPendingConfirms(opts.pendingConfirms, "no");
|
|
8320
8761
|
});
|
|
8321
8762
|
ws.on("error", (err) => {
|
|
8322
8763
|
console.warn(
|
|
@@ -8331,6 +8772,9 @@ function createConnectionHandler(opts) {
|
|
|
8331
8772
|
};
|
|
8332
8773
|
}
|
|
8333
8774
|
|
|
8775
|
+
// src/server/message-dispatcher.ts
|
|
8776
|
+
import path17 from "path";
|
|
8777
|
+
|
|
8334
8778
|
// src/server/autophase-routes.ts
|
|
8335
8779
|
async function handleAutoPhaseRoute(_ws, msg, handlers) {
|
|
8336
8780
|
if (!msg.type.startsWith("autophase.")) return false;
|
|
@@ -8357,15 +8801,15 @@ async function handleBrainRoute(ws, msg, handlers) {
|
|
|
8357
8801
|
|
|
8358
8802
|
// src/server/goal-handlers.ts
|
|
8359
8803
|
import { resolveWstackPaths } from "@wrongstack/core/utils";
|
|
8360
|
-
async function handleGoalGet(projectRoot,
|
|
8804
|
+
async function handleGoalGet(projectRoot, broadcast2) {
|
|
8361
8805
|
try {
|
|
8362
8806
|
const goalPath = resolveWstackPaths({ projectRoot }).projectGoal;
|
|
8363
|
-
const { readFile:
|
|
8364
|
-
const raw = await
|
|
8807
|
+
const { readFile: readFile10 } = await import("fs/promises");
|
|
8808
|
+
const raw = await readFile10(goalPath, "utf8");
|
|
8365
8809
|
const goal = JSON.parse(raw);
|
|
8366
|
-
|
|
8810
|
+
broadcast2({ type: "goal.updated", payload: goal });
|
|
8367
8811
|
} catch {
|
|
8368
|
-
|
|
8812
|
+
broadcast2({ type: "goal.updated", payload: null });
|
|
8369
8813
|
}
|
|
8370
8814
|
}
|
|
8371
8815
|
|
|
@@ -9025,6 +9469,16 @@ function createMessageDispatcher(opts) {
|
|
|
9025
9469
|
broadcast: (m) => broadcast(state.getClients(), m)
|
|
9026
9470
|
};
|
|
9027
9471
|
}
|
|
9472
|
+
function makeSkillsContext() {
|
|
9473
|
+
const projectRoot = state.getProjectRoot();
|
|
9474
|
+
return {
|
|
9475
|
+
skillLoader: deps2.skillLoader,
|
|
9476
|
+
skillInstaller: deps2.skillInstaller,
|
|
9477
|
+
projectRoot,
|
|
9478
|
+
projectSkillsDir: path17.join(projectRoot, ".wrongstack", "skills"),
|
|
9479
|
+
globalSkillsDir: deps2.wpaths.globalSkills
|
|
9480
|
+
};
|
|
9481
|
+
}
|
|
9028
9482
|
function messageSessionId(msg) {
|
|
9029
9483
|
const payload = msg.payload;
|
|
9030
9484
|
return payload && typeof payload === "object" && typeof payload.sessionId === "string" ? payload.sessionId : void 0;
|
|
@@ -9137,10 +9591,10 @@ function createMessageDispatcher(opts) {
|
|
|
9137
9591
|
case "tool.confirm_result": {
|
|
9138
9592
|
if (!ensureCurrentSession(ws, msg, "tool.confirm_result")) return;
|
|
9139
9593
|
const { id, decision } = msg.payload;
|
|
9140
|
-
const
|
|
9141
|
-
if (
|
|
9594
|
+
const confirm = pendingConfirms.get(id);
|
|
9595
|
+
if (confirm) {
|
|
9142
9596
|
pendingConfirms.delete(id);
|
|
9143
|
-
|
|
9597
|
+
confirm.resolve(decision);
|
|
9144
9598
|
}
|
|
9145
9599
|
break;
|
|
9146
9600
|
}
|
|
@@ -9198,60 +9652,28 @@ function createMessageDispatcher(opts) {
|
|
|
9198
9652
|
throw new Error("handleMcpRoute did not claim mcp.discover \u2014 check chain order");
|
|
9199
9653
|
// Skills — full request→response cycle lives in skills-handlers.ts.
|
|
9200
9654
|
case "skills.list":
|
|
9201
|
-
await handleSkillsList(ws,
|
|
9202
|
-
skillLoader: deps2.skillLoader,
|
|
9203
|
-
skillInstaller: deps2.skillInstaller,
|
|
9204
|
-
projectRoot: state.getProjectRoot()
|
|
9205
|
-
});
|
|
9655
|
+
await handleSkillsList(ws, makeSkillsContext());
|
|
9206
9656
|
break;
|
|
9207
9657
|
case "skills.content":
|
|
9208
|
-
await handleSkillsContent(ws,
|
|
9209
|
-
skillLoader: deps2.skillLoader,
|
|
9210
|
-
skillInstaller: deps2.skillInstaller,
|
|
9211
|
-
projectRoot: state.getProjectRoot()
|
|
9212
|
-
}, msg);
|
|
9658
|
+
await handleSkillsContent(ws, makeSkillsContext(), msg);
|
|
9213
9659
|
break;
|
|
9214
9660
|
case "skills.install":
|
|
9215
|
-
await handleSkillsInstall(ws,
|
|
9216
|
-
skillLoader: deps2.skillLoader,
|
|
9217
|
-
skillInstaller: deps2.skillInstaller,
|
|
9218
|
-
projectRoot: state.getProjectRoot()
|
|
9219
|
-
}, msg);
|
|
9661
|
+
await handleSkillsInstall(ws, makeSkillsContext(), msg);
|
|
9220
9662
|
break;
|
|
9221
9663
|
case "skills.uninstall":
|
|
9222
|
-
await handleSkillsUninstall(ws,
|
|
9223
|
-
skillLoader: deps2.skillLoader,
|
|
9224
|
-
skillInstaller: deps2.skillInstaller,
|
|
9225
|
-
projectRoot: state.getProjectRoot()
|
|
9226
|
-
}, msg);
|
|
9664
|
+
await handleSkillsUninstall(ws, makeSkillsContext(), msg);
|
|
9227
9665
|
break;
|
|
9228
9666
|
case "skills.update":
|
|
9229
|
-
await handleSkillsUpdate(ws,
|
|
9230
|
-
skillLoader: deps2.skillLoader,
|
|
9231
|
-
skillInstaller: deps2.skillInstaller,
|
|
9232
|
-
projectRoot: state.getProjectRoot()
|
|
9233
|
-
}, msg);
|
|
9667
|
+
await handleSkillsUpdate(ws, makeSkillsContext(), msg);
|
|
9234
9668
|
break;
|
|
9235
9669
|
case "skills.create":
|
|
9236
|
-
await handleSkillsCreate(ws,
|
|
9237
|
-
skillLoader: deps2.skillLoader,
|
|
9238
|
-
skillInstaller: deps2.skillInstaller,
|
|
9239
|
-
projectRoot: state.getProjectRoot()
|
|
9240
|
-
}, msg);
|
|
9670
|
+
await handleSkillsCreate(ws, makeSkillsContext(), msg);
|
|
9241
9671
|
break;
|
|
9242
9672
|
case "skills.edit":
|
|
9243
|
-
await handleSkillsEdit(ws,
|
|
9244
|
-
skillLoader: deps2.skillLoader,
|
|
9245
|
-
skillInstaller: deps2.skillInstaller,
|
|
9246
|
-
projectRoot: state.getProjectRoot()
|
|
9247
|
-
}, msg);
|
|
9673
|
+
await handleSkillsEdit(ws, makeSkillsContext(), msg);
|
|
9248
9674
|
break;
|
|
9249
9675
|
case "skills.export":
|
|
9250
|
-
await handleSkillsExport(ws,
|
|
9251
|
-
skillLoader: deps2.skillLoader,
|
|
9252
|
-
skillInstaller: deps2.skillInstaller,
|
|
9253
|
-
projectRoot: state.getProjectRoot()
|
|
9254
|
-
});
|
|
9676
|
+
await handleSkillsExport(ws, makeSkillsContext());
|
|
9255
9677
|
break;
|
|
9256
9678
|
// Prompt library — shared handlers (prompts-handlers.ts).
|
|
9257
9679
|
case "prompts.list":
|
|
@@ -9365,7 +9787,12 @@ function createMessageDispatcher(opts) {
|
|
|
9365
9787
|
const session = state.getSession();
|
|
9366
9788
|
const usage = deps2.tokenCounter.total();
|
|
9367
9789
|
const cacheStats = deps2.tokenCounter.cacheStats();
|
|
9368
|
-
const m = await
|
|
9790
|
+
const m = await resolveProviderModelMetadata(
|
|
9791
|
+
deps2.modelsRegistry,
|
|
9792
|
+
config.provider,
|
|
9793
|
+
config.model,
|
|
9794
|
+
config.providers?.[config.provider]
|
|
9795
|
+
).catch(() => null);
|
|
9369
9796
|
const cost = computeUsageCost(usage, getCostRates(m));
|
|
9370
9797
|
send(ws, {
|
|
9371
9798
|
type: "stats.get",
|
|
@@ -9456,7 +9883,7 @@ function createMessageDispatcher(opts) {
|
|
|
9456
9883
|
// src/server/pref-helpers.ts
|
|
9457
9884
|
import { atomicWrite as atomicWrite6 } from "@wrongstack/core/utils";
|
|
9458
9885
|
import { decryptConfigSecrets as decryptConfigSecrets2, encryptConfigSecrets as encryptConfigSecrets2 } from "@wrongstack/core/security";
|
|
9459
|
-
import * as
|
|
9886
|
+
import * as fs13 from "fs/promises";
|
|
9460
9887
|
var PREF_KEYS = [
|
|
9461
9888
|
"autonomy",
|
|
9462
9889
|
"autonomyDelayMs",
|
|
@@ -9482,6 +9909,7 @@ var PREF_KEYS = [
|
|
|
9482
9909
|
"tokenSavingTier",
|
|
9483
9910
|
"maxConcurrent",
|
|
9484
9911
|
"titleAnimation",
|
|
9912
|
+
"uiLocale",
|
|
9485
9913
|
"logLevel",
|
|
9486
9914
|
"auditLevel",
|
|
9487
9915
|
"hqEnabled",
|
|
@@ -9515,7 +9943,7 @@ async function updateGlobalConfig(deps2, holder, mutate, errorLabel) {
|
|
|
9515
9943
|
const write = async () => {
|
|
9516
9944
|
let raw;
|
|
9517
9945
|
try {
|
|
9518
|
-
raw = await
|
|
9946
|
+
raw = await fs13.readFile(globalConfigPath, "utf8");
|
|
9519
9947
|
} catch {
|
|
9520
9948
|
raw = "{}";
|
|
9521
9949
|
}
|
|
@@ -9557,7 +9985,10 @@ async function persistPrefsToConfig(deps2, holder, payload) {
|
|
|
9557
9985
|
setAutonomy("autoProceedDelayMs", payload["autonomyDelayMs"]);
|
|
9558
9986
|
if (typeof payload["autoProceedMaxIterations"] === "number")
|
|
9559
9987
|
setAutonomy("autoProceedMaxIterations", payload["autoProceedMaxIterations"]);
|
|
9560
|
-
if (typeof payload["yolo"] === "boolean")
|
|
9988
|
+
if (typeof payload["yolo"] === "boolean") {
|
|
9989
|
+
setAutonomy("yolo", payload["yolo"]);
|
|
9990
|
+
decrypted.yolo = payload["yolo"];
|
|
9991
|
+
}
|
|
9561
9992
|
if (typeof payload["chime"] === "boolean") setAutonomy("chime", payload["chime"]);
|
|
9562
9993
|
if (typeof payload["confirmExit"] === "boolean")
|
|
9563
9994
|
setAutonomy("confirmExit", payload["confirmExit"]);
|
|
@@ -9572,6 +10003,7 @@ async function persistPrefsToConfig(deps2, holder, payload) {
|
|
|
9572
10003
|
if (autonomyTouched) decrypted.autonomy = autonomyCfg;
|
|
9573
10004
|
if (typeof payload["nextPrediction"] === "boolean")
|
|
9574
10005
|
decrypted.nextPrediction = payload["nextPrediction"];
|
|
10006
|
+
if (typeof payload["uiLocale"] === "string") decrypted.uiLocale = payload["uiLocale"];
|
|
9575
10007
|
if (Array.isArray(payload["fallbackModels"]))
|
|
9576
10008
|
decrypted.fallbackModels = payload["fallbackModels"];
|
|
9577
10009
|
if (payload["fallbackProfiles"] && typeof payload["fallbackProfiles"] === "object" && !Array.isArray(payload["fallbackProfiles"])) {
|
|
@@ -9689,16 +10121,16 @@ async function persistPrefsToConfig(deps2, holder, payload) {
|
|
|
9689
10121
|
}
|
|
9690
10122
|
|
|
9691
10123
|
// src/server/projects-manifest.ts
|
|
9692
|
-
import * as
|
|
9693
|
-
import * as
|
|
10124
|
+
import * as fs14 from "fs/promises";
|
|
10125
|
+
import * as path18 from "path";
|
|
9694
10126
|
import { projectSlug } from "@wrongstack/core";
|
|
9695
10127
|
function projectsJsonPath(globalConfigPath) {
|
|
9696
|
-
const base =
|
|
9697
|
-
return
|
|
10128
|
+
const base = path18.dirname(globalConfigPath);
|
|
10129
|
+
return path18.join(base, "projects.json");
|
|
9698
10130
|
}
|
|
9699
10131
|
async function loadManifest(globalConfigPath) {
|
|
9700
10132
|
try {
|
|
9701
|
-
const raw = await
|
|
10133
|
+
const raw = await fs14.readFile(projectsJsonPath(globalConfigPath), "utf8");
|
|
9702
10134
|
const parsed = JSON.parse(raw);
|
|
9703
10135
|
return { projects: parsed.projects ?? [] };
|
|
9704
10136
|
} catch {
|
|
@@ -9707,16 +10139,16 @@ async function loadManifest(globalConfigPath) {
|
|
|
9707
10139
|
}
|
|
9708
10140
|
async function saveManifest(manifest, globalConfigPath) {
|
|
9709
10141
|
const file = projectsJsonPath(globalConfigPath);
|
|
9710
|
-
await
|
|
9711
|
-
await
|
|
10142
|
+
await fs14.mkdir(path18.dirname(file), { recursive: true });
|
|
10143
|
+
await fs14.writeFile(file, JSON.stringify(manifest, null, 2), "utf8");
|
|
9712
10144
|
}
|
|
9713
10145
|
function generateProjectSlug(rootPath) {
|
|
9714
10146
|
return projectSlug(rootPath);
|
|
9715
10147
|
}
|
|
9716
10148
|
async function ensureProjectDataDir(slug, globalConfigPath) {
|
|
9717
|
-
const base =
|
|
9718
|
-
const dir =
|
|
9719
|
-
await
|
|
10149
|
+
const base = path18.dirname(globalConfigPath);
|
|
10150
|
+
const dir = path18.join(base, "projects", slug);
|
|
10151
|
+
await fs14.mkdir(dir, { recursive: true });
|
|
9720
10152
|
return dir;
|
|
9721
10153
|
}
|
|
9722
10154
|
|
|
@@ -9750,7 +10182,7 @@ function projectSavedProviders(providers) {
|
|
|
9750
10182
|
}
|
|
9751
10183
|
var probeScrubber = new DefaultSecretScrubber();
|
|
9752
10184
|
function createProviderHandlers(deps2) {
|
|
9753
|
-
const { globalConfigPath, vault, broadcast:
|
|
10185
|
+
const { globalConfigPath, vault, broadcast: broadcast2, clients } = deps2;
|
|
9754
10186
|
let configWriteLock = deps2.getConfigWriteLock();
|
|
9755
10187
|
async function loadConfigProviders() {
|
|
9756
10188
|
return loadSavedProviders(globalConfigPath, vault);
|
|
@@ -9838,7 +10270,7 @@ function createProviderHandlers(deps2) {
|
|
|
9838
10270
|
}
|
|
9839
10271
|
}
|
|
9840
10272
|
function broadcastSaved(providers) {
|
|
9841
|
-
|
|
10273
|
+
broadcast2(clients, {
|
|
9842
10274
|
type: "providers.saved",
|
|
9843
10275
|
payload: { providers: projectSavedProviders(providers) }
|
|
9844
10276
|
});
|
|
@@ -10031,7 +10463,7 @@ function createProviderHandlers(deps2) {
|
|
|
10031
10463
|
}
|
|
10032
10464
|
|
|
10033
10465
|
// src/server/routes.ts
|
|
10034
|
-
import
|
|
10466
|
+
import path21 from "path";
|
|
10035
10467
|
import {
|
|
10036
10468
|
enhanceUserPrompt,
|
|
10037
10469
|
gatedEnhancerReasoning,
|
|
@@ -10046,14 +10478,19 @@ async function handleMailboxMessages(ws, deps2, payload) {
|
|
|
10046
10478
|
try {
|
|
10047
10479
|
const dir = resolveProjectDir2(deps2.projectRoot, deps2.globalRoot);
|
|
10048
10480
|
const mb = new GlobalMailbox3(dir);
|
|
10481
|
+
const limit = payload?.limit ?? 30;
|
|
10482
|
+
const unreadForAgent = payload?.unreadOnly === true && payload.agentId !== void 0;
|
|
10049
10483
|
const messages = await mb.query({
|
|
10050
|
-
limit: payload?.limit
|
|
10484
|
+
limit: payload?.unreadOnly === true && payload.agentId === void 0 ? Math.max(limit * 5, 100) : limit,
|
|
10485
|
+
to: payload?.agentId,
|
|
10486
|
+
unreadBy: unreadForAgent ? payload.agentId : void 0,
|
|
10051
10487
|
incompleteOnly: payload?.incompleteOnly ?? false
|
|
10052
10488
|
});
|
|
10489
|
+
const visibleMessages = payload?.unreadOnly === true && payload.agentId === void 0 ? messages.filter((m) => Object.keys(m.readBy).length === 0).slice(0, limit) : messages;
|
|
10053
10490
|
send(ws, {
|
|
10054
10491
|
type: "mailbox.messages",
|
|
10055
10492
|
payload: {
|
|
10056
|
-
messages:
|
|
10493
|
+
messages: visibleMessages.map((m) => ({
|
|
10057
10494
|
id: m.id,
|
|
10058
10495
|
from: m.from,
|
|
10059
10496
|
to: m.to,
|
|
@@ -10215,15 +10652,7 @@ function createModeHandlers(ctx) {
|
|
|
10215
10652
|
}
|
|
10216
10653
|
|
|
10217
10654
|
// src/server/project-handlers.ts
|
|
10218
|
-
import * as
|
|
10219
|
-
import * as path17 from "path";
|
|
10220
|
-
import {
|
|
10221
|
-
DefaultSessionStore as DefaultSessionStore2,
|
|
10222
|
-
DefaultSystemPromptBuilder as DefaultSystemPromptBuilder3,
|
|
10223
|
-
getSessionRegistry as getSessionRegistry2,
|
|
10224
|
-
resolveWstackPaths as resolveWstackPaths3,
|
|
10225
|
-
ToolValidationError as ToolValidationError6
|
|
10226
|
-
} from "@wrongstack/core";
|
|
10655
|
+
import * as path19 from "path";
|
|
10227
10656
|
function createProjectHandlers(ctx) {
|
|
10228
10657
|
return {
|
|
10229
10658
|
listProjects: async (ws) => {
|
|
@@ -10235,213 +10664,29 @@ function createProjectHandlers(ctx) {
|
|
|
10235
10664
|
}
|
|
10236
10665
|
},
|
|
10237
10666
|
addProject: async (ws, msg) => {
|
|
10238
|
-
const
|
|
10239
|
-
|
|
10240
|
-
|
|
10241
|
-
|
|
10242
|
-
|
|
10243
|
-
|
|
10244
|
-
|
|
10245
|
-
|
|
10246
|
-
const { root: addRoot, name: displayName } = parsed.value;
|
|
10247
|
-
try {
|
|
10248
|
-
const resolved = path17.resolve(addRoot);
|
|
10249
|
-
await fs14.access(resolved);
|
|
10250
|
-
const stat3 = await fs14.stat(resolved);
|
|
10251
|
-
if (!stat3.isDirectory()) {
|
|
10252
|
-
throw new ToolValidationError6({ message: `Not a directory: ${resolved}`, field: "path" });
|
|
10253
|
-
}
|
|
10254
|
-
const manifest = await loadManifest(ctx.globalConfigPath);
|
|
10255
|
-
const existing = manifest.projects.find((p) => p.root === resolved);
|
|
10256
|
-
if (existing) {
|
|
10257
|
-
send(ws, {
|
|
10258
|
-
type: "projects.added",
|
|
10259
|
-
payload: {
|
|
10260
|
-
name: existing.name,
|
|
10261
|
-
root: existing.root,
|
|
10262
|
-
slug: existing.slug,
|
|
10263
|
-
message: `Already registered as "${existing.name}"`
|
|
10264
|
-
}
|
|
10265
|
-
});
|
|
10266
|
-
return;
|
|
10667
|
+
const payload = msg.payload;
|
|
10668
|
+
send(ws, {
|
|
10669
|
+
type: "projects.added",
|
|
10670
|
+
payload: {
|
|
10671
|
+
name: typeof payload?.name === "string" ? payload.name : "",
|
|
10672
|
+
root: typeof payload?.root === "string" ? payload.root : "",
|
|
10673
|
+
slug: "",
|
|
10674
|
+
message: "Project registration is disabled in WebUI. Open/register projects from the launcher or desktop shell."
|
|
10267
10675
|
}
|
|
10268
|
-
|
|
10269
|
-
const slug = generateProjectSlug(resolved);
|
|
10270
|
-
await ensureProjectDataDir(slug, ctx.globalConfigPath);
|
|
10271
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10272
|
-
manifest.projects.push({ name: name2, root: resolved, slug, lastSeen: now, createdAt: now });
|
|
10273
|
-
await saveManifest(manifest, ctx.globalConfigPath);
|
|
10274
|
-
send(ws, {
|
|
10275
|
-
type: "projects.added",
|
|
10276
|
-
payload: { name: name2, root: resolved, slug, message: `Registered project "${name2}"` }
|
|
10277
|
-
});
|
|
10278
|
-
} catch (err) {
|
|
10279
|
-
send(ws, {
|
|
10280
|
-
type: "projects.added",
|
|
10281
|
-
payload: { name: path17.basename(addRoot), root: addRoot, slug: "", message: errMessage(err) }
|
|
10282
|
-
});
|
|
10283
|
-
}
|
|
10676
|
+
});
|
|
10284
10677
|
},
|
|
10285
10678
|
selectProject: async (ws, msg) => {
|
|
10286
|
-
const
|
|
10287
|
-
|
|
10288
|
-
|
|
10289
|
-
|
|
10290
|
-
|
|
10291
|
-
|
|
10292
|
-
|
|
10293
|
-
|
|
10294
|
-
|
|
10295
|
-
try {
|
|
10296
|
-
const resolved = path17.resolve(selRoot);
|
|
10297
|
-
try {
|
|
10298
|
-
await fs14.access(resolved);
|
|
10299
|
-
const stat3 = await fs14.stat(resolved);
|
|
10300
|
-
if (!stat3.isDirectory()) {
|
|
10301
|
-
throw new ToolValidationError6({ message: `Not a directory: ${resolved}`, field: "path" });
|
|
10302
|
-
}
|
|
10303
|
-
} catch (err) {
|
|
10304
|
-
send(ws, {
|
|
10305
|
-
type: "projects.selected",
|
|
10306
|
-
payload: {
|
|
10307
|
-
root: selRoot,
|
|
10308
|
-
name: selName || path17.basename(selRoot),
|
|
10309
|
-
message: `Cannot switch: ${errMessage(err)}`
|
|
10310
|
-
}
|
|
10311
|
-
});
|
|
10312
|
-
return;
|
|
10313
|
-
}
|
|
10314
|
-
const manifest = await loadManifest(ctx.globalConfigPath);
|
|
10315
|
-
const entry = manifest.projects.find((p) => p.root === resolved);
|
|
10316
|
-
if (entry) {
|
|
10317
|
-
entry.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
|
|
10318
|
-
entry.lastWorkingDir = resolved;
|
|
10319
|
-
} else {
|
|
10320
|
-
const name2 = selName?.trim() || path17.basename(resolved);
|
|
10321
|
-
const slug = generateProjectSlug(resolved);
|
|
10322
|
-
manifest.projects.push({
|
|
10323
|
-
name: name2,
|
|
10324
|
-
root: resolved,
|
|
10325
|
-
slug,
|
|
10326
|
-
lastSeen: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10327
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10328
|
-
lastWorkingDir: resolved
|
|
10329
|
-
});
|
|
10330
|
-
await ensureProjectDataDir(slug, ctx.globalConfigPath);
|
|
10331
|
-
}
|
|
10332
|
-
await saveManifest(manifest, ctx.globalConfigPath);
|
|
10333
|
-
ctx.abortRunLock();
|
|
10334
|
-
ctx.setProjectRoot(resolved);
|
|
10335
|
-
ctx.setWorkingDir(resolved);
|
|
10336
|
-
ctx.context.cwd = resolved;
|
|
10337
|
-
ctx.context.projectRoot = resolved;
|
|
10338
|
-
const switchSlug = entry?.slug ?? generateProjectSlug(resolved);
|
|
10339
|
-
try {
|
|
10340
|
-
const modeId = ctx.getModeId();
|
|
10341
|
-
const switchMode = modeId === "default" ? void 0 : await ctx.modeStore.getMode(modeId);
|
|
10342
|
-
const switchPaths = resolveWstackPaths3({
|
|
10343
|
-
projectRoot: resolved,
|
|
10344
|
-
globalRoot: ctx.wpaths.globalRoot
|
|
10345
|
-
});
|
|
10346
|
-
const switchBuilder = new DefaultSystemPromptBuilder3({
|
|
10347
|
-
memoryStore: ctx.memoryStore,
|
|
10348
|
-
skillLoader: ctx.skillLoader,
|
|
10349
|
-
modeStore: ctx.modeStore,
|
|
10350
|
-
modeId,
|
|
10351
|
-
modePrompt: switchMode?.prompt ?? "",
|
|
10352
|
-
modelCapabilities: ctx.modelCapabilities,
|
|
10353
|
-
instructionPaths: {
|
|
10354
|
-
globalDir: switchPaths.globalInstructions,
|
|
10355
|
-
projectDir: switchPaths.inProjectInstructions
|
|
10356
|
-
}
|
|
10357
|
-
});
|
|
10358
|
-
ctx.context.systemPrompt = await switchBuilder.build({
|
|
10359
|
-
cwd: resolved,
|
|
10360
|
-
projectRoot: resolved,
|
|
10361
|
-
tools: ctx.toolRegistry.list(),
|
|
10362
|
-
provider: ctx.config.provider,
|
|
10363
|
-
model: ctx.config.model
|
|
10364
|
-
});
|
|
10365
|
-
} catch {
|
|
10366
|
-
}
|
|
10367
|
-
const newSessionsDir = path17.join(
|
|
10368
|
-
path17.dirname(ctx.globalConfigPath),
|
|
10369
|
-
"projects",
|
|
10370
|
-
switchSlug,
|
|
10371
|
-
"sessions"
|
|
10372
|
-
);
|
|
10373
|
-
await fs14.mkdir(newSessionsDir, { recursive: true });
|
|
10374
|
-
const newSessionStore = new DefaultSessionStore2({ dir: newSessionsDir });
|
|
10375
|
-
const oldSession = ctx.getSession();
|
|
10376
|
-
const oldSessionId = oldSession.id;
|
|
10377
|
-
try {
|
|
10378
|
-
await oldSession.append({
|
|
10379
|
-
type: "session_end",
|
|
10380
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10381
|
-
usage: ctx.tokenCounter.total()
|
|
10382
|
-
});
|
|
10383
|
-
await oldSession.close();
|
|
10384
|
-
} catch {
|
|
10385
|
-
}
|
|
10386
|
-
ctx.setSessionStore(newSessionStore);
|
|
10387
|
-
const newSession = await newSessionStore.create({
|
|
10388
|
-
id: "",
|
|
10389
|
-
title: "",
|
|
10390
|
-
model: ctx.config.model,
|
|
10391
|
-
provider: ctx.config.provider
|
|
10392
|
-
});
|
|
10393
|
-
ctx.setSession(newSession);
|
|
10394
|
-
ctx.context.session = newSession;
|
|
10395
|
-
ctx.context.state.replaceMessages([]);
|
|
10396
|
-
ctx.context.state.replaceTodos([]);
|
|
10397
|
-
ctx.context.readFiles.clear();
|
|
10398
|
-
ctx.context.fileMtimes.clear();
|
|
10399
|
-
ctx.tokenCounter.reset();
|
|
10400
|
-
ctx.setSessionStartedAt(Date.now());
|
|
10401
|
-
try {
|
|
10402
|
-
const registry = getSessionRegistry2(ctx.wpaths.globalRoot);
|
|
10403
|
-
await registry.register({
|
|
10404
|
-
sessionId: newSession.id,
|
|
10405
|
-
projectSlug: switchSlug,
|
|
10406
|
-
projectRoot: resolved,
|
|
10407
|
-
projectName: path17.basename(resolved),
|
|
10408
|
-
workingDir: resolved,
|
|
10409
|
-
clientType: "webui",
|
|
10410
|
-
pid: process.pid,
|
|
10411
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10412
|
-
});
|
|
10413
|
-
} catch {
|
|
10679
|
+
const payload = msg.payload;
|
|
10680
|
+
const root = typeof payload?.root === "string" ? payload.root : "";
|
|
10681
|
+
const name2 = typeof payload?.name === "string" ? payload.name : root ? path19.basename(root) : "";
|
|
10682
|
+
send(ws, {
|
|
10683
|
+
type: "projects.selected",
|
|
10684
|
+
payload: {
|
|
10685
|
+
root,
|
|
10686
|
+
name: name2,
|
|
10687
|
+
message: "Project switching is disabled in WebUI. Open a project from the launcher or desktop shell instead."
|
|
10414
10688
|
}
|
|
10415
|
-
|
|
10416
|
-
type: "projects.selected",
|
|
10417
|
-
payload: {
|
|
10418
|
-
root: resolved,
|
|
10419
|
-
name: selName || path17.basename(resolved),
|
|
10420
|
-
message: `Switched to ${selName || path17.basename(resolved)}`
|
|
10421
|
-
}
|
|
10422
|
-
});
|
|
10423
|
-
broadcast(ctx.clients, {
|
|
10424
|
-
type: "subagent.event",
|
|
10425
|
-
payload: { kind: "session_stopped", sessionId: oldSessionId }
|
|
10426
|
-
});
|
|
10427
|
-
broadcast(ctx.clients, {
|
|
10428
|
-
type: "session.start",
|
|
10429
|
-
payload: {
|
|
10430
|
-
...await ctx.sessionStartPayload(),
|
|
10431
|
-
reset: true,
|
|
10432
|
-
clearedSessionId: oldSessionId
|
|
10433
|
-
}
|
|
10434
|
-
});
|
|
10435
|
-
} catch (err) {
|
|
10436
|
-
send(ws, {
|
|
10437
|
-
type: "projects.selected",
|
|
10438
|
-
payload: {
|
|
10439
|
-
root: selRoot,
|
|
10440
|
-
name: selName || path17.basename(selRoot),
|
|
10441
|
-
message: errMessage(err)
|
|
10442
|
-
}
|
|
10443
|
-
});
|
|
10444
|
-
}
|
|
10689
|
+
});
|
|
10445
10690
|
},
|
|
10446
10691
|
setWorkingDir: async (ws, msg) => {
|
|
10447
10692
|
const parsed = validateWorkingDirSetPayload(msg.payload);
|
|
@@ -10468,9 +10713,9 @@ function createProjectHandlers(ctx) {
|
|
|
10468
10713
|
}
|
|
10469
10714
|
|
|
10470
10715
|
// src/server/session-handlers.ts
|
|
10471
|
-
import * as
|
|
10716
|
+
import * as path20 from "path";
|
|
10472
10717
|
import {
|
|
10473
|
-
DEFAULT_CONTEXT_WINDOW_MODE_ID
|
|
10718
|
+
DEFAULT_CONTEXT_WINDOW_MODE_ID,
|
|
10474
10719
|
repairToolUseAdjacency,
|
|
10475
10720
|
resolveContextWindowPolicy as resolveContextWindowPolicy3
|
|
10476
10721
|
} from "@wrongstack/core";
|
|
@@ -10552,7 +10797,7 @@ function createSessionHandlers(ctx) {
|
|
|
10552
10797
|
type: "context.debug",
|
|
10553
10798
|
payload: sessionPayload2({
|
|
10554
10799
|
...breakdown,
|
|
10555
|
-
mode: ctx.context.meta["contextWindowMode"] ??
|
|
10800
|
+
mode: ctx.context.meta["contextWindowMode"] ?? DEFAULT_CONTEXT_WINDOW_MODE_ID,
|
|
10556
10801
|
policy: ctx.context.meta["contextWindowPolicy"]
|
|
10557
10802
|
})
|
|
10558
10803
|
});
|
|
@@ -10606,7 +10851,7 @@ function createSessionHandlers(ctx) {
|
|
|
10606
10851
|
},
|
|
10607
10852
|
listContextModes: async (ws, msg) => {
|
|
10608
10853
|
if (!ensureCurrentSession(ws, msg, "context.modes.list")) return;
|
|
10609
|
-
const active = String(ctx.context.meta["contextWindowMode"] ??
|
|
10854
|
+
const active = String(ctx.context.meta["contextWindowMode"] ?? DEFAULT_CONTEXT_WINDOW_MODE_ID);
|
|
10610
10855
|
const allModes = ctx.customModeStore.list().map((m) => ({
|
|
10611
10856
|
id: m.id,
|
|
10612
10857
|
name: m.name,
|
|
@@ -10696,8 +10941,8 @@ function createSessionHandlers(ctx) {
|
|
|
10696
10941
|
}
|
|
10697
10942
|
const { id } = parsed.value;
|
|
10698
10943
|
if (String(ctx.context.meta["contextWindowMode"] ?? "") === id) {
|
|
10699
|
-
ctx.context.meta["contextWindowMode"] =
|
|
10700
|
-
ctx.context.meta["contextWindowPolicy"] = resolveContextWindowPolicy3({},
|
|
10944
|
+
ctx.context.meta["contextWindowMode"] = DEFAULT_CONTEXT_WINDOW_MODE_ID;
|
|
10945
|
+
ctx.context.meta["contextWindowPolicy"] = resolveContextWindowPolicy3({}, DEFAULT_CONTEXT_WINDOW_MODE_ID);
|
|
10701
10946
|
}
|
|
10702
10947
|
const result = ctx.customModeStore.remove(id);
|
|
10703
10948
|
sendResult(ws, result.ok, result.error ?? `Mode "${id}" deleted`);
|
|
@@ -10788,7 +11033,7 @@ function createSessionHandlers(ctx) {
|
|
|
10788
11033
|
const { DefaultSessionRewinder } = await import("@wrongstack/core");
|
|
10789
11034
|
const projectRoot = ctx.getProjectRoot();
|
|
10790
11035
|
const rewinder = new DefaultSessionRewinder(
|
|
10791
|
-
|
|
11036
|
+
path20.join(projectRoot, ".wrongstack", "sessions"),
|
|
10792
11037
|
projectRoot
|
|
10793
11038
|
);
|
|
10794
11039
|
const checkpoints = await rewinder.listCheckpoints(ctx.getSession().id);
|
|
@@ -10804,7 +11049,7 @@ function createSessionHandlers(ctx) {
|
|
|
10804
11049
|
const { DefaultSessionRewinder } = await import("@wrongstack/core");
|
|
10805
11050
|
const projectRoot = ctx.getProjectRoot();
|
|
10806
11051
|
const rewinder = new DefaultSessionRewinder(
|
|
10807
|
-
|
|
11052
|
+
path20.join(projectRoot, ".wrongstack", "sessions"),
|
|
10808
11053
|
projectRoot
|
|
10809
11054
|
);
|
|
10810
11055
|
await rewinder.rewindToCheckpoint(ctx.getSession().id, checkpointIndex);
|
|
@@ -10822,6 +11067,29 @@ function createSessionHandlers(ctx) {
|
|
|
10822
11067
|
}
|
|
10823
11068
|
|
|
10824
11069
|
// src/server/routes.ts
|
|
11070
|
+
async function enrichProviderModelDescriptors(modelsRegistry, providerId, cfg, models) {
|
|
11071
|
+
return Promise.all(
|
|
11072
|
+
models.map(async (model) => {
|
|
11073
|
+
if (model.contextWindow && model.capabilities.length > 0) return model;
|
|
11074
|
+
const resolved = await resolveProviderModelMetadata(
|
|
11075
|
+
modelsRegistry,
|
|
11076
|
+
providerId,
|
|
11077
|
+
model.id,
|
|
11078
|
+
cfg
|
|
11079
|
+
).catch(() => void 0);
|
|
11080
|
+
if (!resolved) return model;
|
|
11081
|
+
const capabilities = new Set(model.capabilities);
|
|
11082
|
+
if (resolved.capabilities.tools) capabilities.add("tools");
|
|
11083
|
+
if (resolved.capabilities.reasoning) capabilities.add("reasoning");
|
|
11084
|
+
if (resolved.capabilities.vision) capabilities.add("vision");
|
|
11085
|
+
return {
|
|
11086
|
+
...model,
|
|
11087
|
+
contextWindow: model.contextWindow || resolved.capabilities.maxContext || void 0,
|
|
11088
|
+
capabilities: [...capabilities]
|
|
11089
|
+
};
|
|
11090
|
+
})
|
|
11091
|
+
);
|
|
11092
|
+
}
|
|
10825
11093
|
function buildRoutes(state, deps2, cb) {
|
|
10826
11094
|
const providerHandlers = createProviderHandlers({
|
|
10827
11095
|
globalConfigPath: deps2.globalConfigPath,
|
|
@@ -10863,13 +11131,22 @@ function buildRoutes(state, deps2, cb) {
|
|
|
10863
11131
|
const providerId = msg.payload.providerId;
|
|
10864
11132
|
const saved = await providerHandlers.loadConfigProviders();
|
|
10865
11133
|
const cfg = saved[providerId];
|
|
10866
|
-
const
|
|
10867
|
-
|
|
11134
|
+
const provider = await resolveProviderCatalogForModels(
|
|
11135
|
+
deps2.modelsRegistry,
|
|
11136
|
+
providerId,
|
|
11137
|
+
cfg
|
|
11138
|
+
);
|
|
11139
|
+
const models = await enrichProviderModelDescriptors(
|
|
11140
|
+
deps2.modelsRegistry,
|
|
11141
|
+
providerId,
|
|
11142
|
+
cfg,
|
|
11143
|
+
resolveProviderModelList(cfg?.models, provider)
|
|
11144
|
+
);
|
|
10868
11145
|
send(ws, {
|
|
10869
11146
|
type: "provider.models",
|
|
10870
11147
|
payload: {
|
|
10871
11148
|
provider: providerId,
|
|
10872
|
-
models
|
|
11149
|
+
models
|
|
10873
11150
|
}
|
|
10874
11151
|
});
|
|
10875
11152
|
},
|
|
@@ -10889,7 +11166,7 @@ function buildRoutes(state, deps2, cb) {
|
|
|
10889
11166
|
const providerCfg = newCfg.providers?.[newProvider] ?? { type: newProvider };
|
|
10890
11167
|
const newProv = deps2.providerRegistry.has(newProvider) ? deps2.providerRegistry.create({ ...providerCfg, type: newProvider }) : makeProviderFromConfig2(newProvider, providerCfg);
|
|
10891
11168
|
deps2.context.provider = newProv;
|
|
10892
|
-
await cb.updateAutoCompactionMaxContext(newProv);
|
|
11169
|
+
await cb.updateAutoCompactionMaxContext(newProv, newProvider, providerCfg);
|
|
10893
11170
|
await cb.updateGlobalConfig((config) => {
|
|
10894
11171
|
config.provider = newProvider;
|
|
10895
11172
|
config.model = newModel;
|
|
@@ -10925,7 +11202,12 @@ function buildRoutes(state, deps2, cb) {
|
|
|
10925
11202
|
try {
|
|
10926
11203
|
const history = recentTextTurns(deps2.context.messages);
|
|
10927
11204
|
const cfg = state.getConfig();
|
|
10928
|
-
const resolved = await
|
|
11205
|
+
const resolved = await resolveProviderModelMetadata(
|
|
11206
|
+
deps2.modelsRegistry,
|
|
11207
|
+
cfg.provider ?? "",
|
|
11208
|
+
cfg.model ?? "",
|
|
11209
|
+
cfg.providers?.[cfg.provider ?? ""]
|
|
11210
|
+
).catch(() => void 0);
|
|
10929
11211
|
const reasoning = gatedEnhancerReasoning(resolved?.capabilities?.reasoningConfig);
|
|
10930
11212
|
const result = await enhanceUserPrompt({
|
|
10931
11213
|
provider: deps2.context.provider,
|
|
@@ -11037,6 +11319,7 @@ function buildRoutes(state, deps2, cb) {
|
|
|
11037
11319
|
void cb.persistPrefsToConfig(payload);
|
|
11038
11320
|
if (typeof payload["yolo"] === "boolean") {
|
|
11039
11321
|
deps2.permissionPolicy.setYolo?.(payload["yolo"]);
|
|
11322
|
+
if (payload["yolo"] === true) resolveYoloEligiblePendingConfirms(deps2.pendingConfirms);
|
|
11040
11323
|
}
|
|
11041
11324
|
const cfg = state.getConfig();
|
|
11042
11325
|
const features = cfg.features ?? {};
|
|
@@ -11107,7 +11390,7 @@ function buildRoutes(state, deps2, cb) {
|
|
|
11107
11390
|
}
|
|
11108
11391
|
return handleMailboxMessages(
|
|
11109
11392
|
ws,
|
|
11110
|
-
{ projectRoot: state.getProjectRoot(), globalRoot:
|
|
11393
|
+
{ projectRoot: state.getProjectRoot(), globalRoot: path21.dirname(deps2.globalConfigPath) },
|
|
11111
11394
|
parsed.value
|
|
11112
11395
|
);
|
|
11113
11396
|
},
|
|
@@ -11119,13 +11402,13 @@ function buildRoutes(state, deps2, cb) {
|
|
|
11119
11402
|
}
|
|
11120
11403
|
return handleMailboxAgents(
|
|
11121
11404
|
ws,
|
|
11122
|
-
{ projectRoot: state.getProjectRoot(), globalRoot:
|
|
11405
|
+
{ projectRoot: state.getProjectRoot(), globalRoot: path21.dirname(deps2.globalConfigPath) },
|
|
11123
11406
|
parsed.value
|
|
11124
11407
|
);
|
|
11125
11408
|
},
|
|
11126
11409
|
clear: (ws) => handleMailboxClear(
|
|
11127
11410
|
ws,
|
|
11128
|
-
{ projectRoot: state.getProjectRoot(), globalRoot:
|
|
11411
|
+
{ projectRoot: state.getProjectRoot(), globalRoot: path21.dirname(deps2.globalConfigPath) }
|
|
11129
11412
|
),
|
|
11130
11413
|
purge: (ws, msg) => {
|
|
11131
11414
|
const parsed = validateMailboxPurgePayload(msg.payload);
|
|
@@ -11135,7 +11418,7 @@ function buildRoutes(state, deps2, cb) {
|
|
|
11135
11418
|
}
|
|
11136
11419
|
return handleMailboxPurge(
|
|
11137
11420
|
ws,
|
|
11138
|
-
{ projectRoot: state.getProjectRoot(), globalRoot:
|
|
11421
|
+
{ projectRoot: state.getProjectRoot(), globalRoot: path21.dirname(deps2.globalConfigPath) },
|
|
11139
11422
|
parsed.value
|
|
11140
11423
|
);
|
|
11141
11424
|
}
|
|
@@ -11281,9 +11564,8 @@ async function startWebUI(opts = {}) {
|
|
|
11281
11564
|
let sessionStore = preContext.sessionStore;
|
|
11282
11565
|
let session = preContext.session;
|
|
11283
11566
|
let sessionStartedAt = preContext.sessionStartedAt;
|
|
11284
|
-
let statusTracker = preContext.statusTracker;
|
|
11285
11567
|
let modeId = preContext.modeId;
|
|
11286
|
-
|
|
11568
|
+
const needsSetup = preContext.needsSetup;
|
|
11287
11569
|
const prefSnapshot2 = () => prefSnapshot(context.meta);
|
|
11288
11570
|
const persistPrefsToConfig2 = async (payload) => persistPrefsToConfig(prefHelperDeps, configWriteLock, payload);
|
|
11289
11571
|
const agentServices = await createAgentServices({
|
|
@@ -11306,7 +11588,7 @@ async function startWebUI(opts = {}) {
|
|
|
11306
11588
|
skillLoader,
|
|
11307
11589
|
skillInstaller,
|
|
11308
11590
|
tokenCounter,
|
|
11309
|
-
pipelines:
|
|
11591
|
+
pipelines: createDefaultPipelines(),
|
|
11310
11592
|
modelCapabilitiesRef,
|
|
11311
11593
|
sessionGetter: () => session,
|
|
11312
11594
|
sessionReader,
|
|
@@ -11332,13 +11614,16 @@ async function startWebUI(opts = {}) {
|
|
|
11332
11614
|
collabHandler,
|
|
11333
11615
|
updateAutoCompactionMaxContext
|
|
11334
11616
|
} = agentServices;
|
|
11617
|
+
if (typeof context.meta["yolo"] === "boolean") {
|
|
11618
|
+
permissionPolicy.setYolo?.(context.meta["yolo"]);
|
|
11619
|
+
}
|
|
11335
11620
|
const sessionStartPayload = createSessionStartPayload({
|
|
11336
11621
|
getConfig: () => config,
|
|
11337
11622
|
getSessionId: () => session.id,
|
|
11338
11623
|
getProjectRoot: () => projectRoot,
|
|
11339
11624
|
getWorkingDir: () => workingDir,
|
|
11340
11625
|
getModeId: () => modeId,
|
|
11341
|
-
getContextMode: () => String(context.meta["contextWindowMode"] ??
|
|
11626
|
+
getContextMode: () => String(context.meta["contextWindowMode"] ?? DEFAULT_CONTEXT_WINDOW_MODE_ID2),
|
|
11342
11627
|
getNeedsSetup: () => needsSetup,
|
|
11343
11628
|
modelsRegistry
|
|
11344
11629
|
});
|
|
@@ -11369,8 +11654,6 @@ async function startWebUI(opts = {}) {
|
|
|
11369
11654
|
averageDebounceDelayMs: 0,
|
|
11370
11655
|
watcherActive: false
|
|
11371
11656
|
};
|
|
11372
|
-
let disposeEvents = null;
|
|
11373
|
-
let fleetBroadcast = null;
|
|
11374
11657
|
const eventArming = armEvents(wssPrimary, wssSecondary, wsHost, wsPort, {
|
|
11375
11658
|
events,
|
|
11376
11659
|
broadcast,
|
|
@@ -11383,21 +11666,21 @@ async function startWebUI(opts = {}) {
|
|
|
11383
11666
|
wpaths
|
|
11384
11667
|
}, watcherMetricsRef);
|
|
11385
11668
|
async function touchProjectEntry(root, workDir) {
|
|
11386
|
-
const resolved =
|
|
11669
|
+
const resolved = path22.resolve(root);
|
|
11387
11670
|
const manifest = await loadManifest(globalConfigPath);
|
|
11388
11671
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
11389
|
-
const existing = manifest.projects.find((p) =>
|
|
11672
|
+
const existing = manifest.projects.find((p) => path22.resolve(p.root) === resolved);
|
|
11390
11673
|
if (existing) {
|
|
11391
11674
|
existing.lastSeen = now;
|
|
11392
|
-
if (workDir) existing.lastWorkingDir =
|
|
11675
|
+
if (workDir) existing.lastWorkingDir = path22.resolve(workDir);
|
|
11393
11676
|
} else {
|
|
11394
11677
|
manifest.projects.push({
|
|
11395
|
-
name:
|
|
11678
|
+
name: path22.basename(resolved),
|
|
11396
11679
|
root: resolved,
|
|
11397
11680
|
slug: generateProjectSlug(resolved),
|
|
11398
11681
|
createdAt: now,
|
|
11399
11682
|
lastSeen: now,
|
|
11400
|
-
lastWorkingDir: workDir ?
|
|
11683
|
+
lastWorkingDir: workDir ? path22.resolve(workDir) : void 0
|
|
11401
11684
|
});
|
|
11402
11685
|
}
|
|
11403
11686
|
await saveManifest(manifest, globalConfigPath);
|
|
@@ -11461,6 +11744,7 @@ async function startWebUI(opts = {}) {
|
|
|
11461
11744
|
configStore,
|
|
11462
11745
|
tokenCounter,
|
|
11463
11746
|
permissionPolicy,
|
|
11747
|
+
pendingConfirms,
|
|
11464
11748
|
pipelines,
|
|
11465
11749
|
logger,
|
|
11466
11750
|
memoryStore,
|
|
@@ -11502,6 +11786,7 @@ async function startWebUI(opts = {}) {
|
|
|
11502
11786
|
let lastActiveCfg = JSON.stringify(
|
|
11503
11787
|
state.getConfig().providers?.[deps2.context.provider.id] ?? null
|
|
11504
11788
|
);
|
|
11789
|
+
let lastUiLocale = state.getConfig().uiLocale;
|
|
11505
11790
|
const credentialWatcher = watchProviderConfig(
|
|
11506
11791
|
globalConfigPath,
|
|
11507
11792
|
vault,
|
|
@@ -11522,6 +11807,14 @@ async function startWebUI(opts = {}) {
|
|
|
11522
11807
|
type: "providers.saved",
|
|
11523
11808
|
payload: { providers: projectSavedProviders(snapshot.providers) }
|
|
11524
11809
|
});
|
|
11810
|
+
const newUi = snapshot.uiLocale;
|
|
11811
|
+
if (newUi !== lastUiLocale) {
|
|
11812
|
+
lastUiLocale = newUi;
|
|
11813
|
+
if (newUi) {
|
|
11814
|
+
deps2.context.meta["uiLocale"] = newUi;
|
|
11815
|
+
broadcast(clients, { type: "prefs.updated", payload: { uiLocale: newUi } });
|
|
11816
|
+
}
|
|
11817
|
+
}
|
|
11525
11818
|
const activeId = deps2.context.provider.id;
|
|
11526
11819
|
const newCfgStr = JSON.stringify(snapshot.providers[activeId] ?? null);
|
|
11527
11820
|
if (newCfgStr === lastActiveCfg) return;
|
|
@@ -11609,7 +11902,7 @@ async function startWebUI(opts = {}) {
|
|
|
11609
11902
|
eternalSubscription = null;
|
|
11610
11903
|
}
|
|
11611
11904
|
codebaseIndexing.dispose();
|
|
11612
|
-
return unregisterInstance(process.pid,
|
|
11905
|
+
return unregisterInstance(process.pid, path22.dirname(globalConfigPath));
|
|
11613
11906
|
}
|
|
11614
11907
|
});
|
|
11615
11908
|
}
|
|
@@ -11624,7 +11917,7 @@ function readArg(names) {
|
|
|
11624
11917
|
if (current === name2) {
|
|
11625
11918
|
const next = argv[i + 1];
|
|
11626
11919
|
if (!next || next.startsWith("-")) {
|
|
11627
|
-
throw new
|
|
11920
|
+
throw new ToolValidationError6({ message: `${name2} requires a value`, field: name2 });
|
|
11628
11921
|
}
|
|
11629
11922
|
return next;
|
|
11630
11923
|
}
|
|
@@ -11637,14 +11930,14 @@ function parsePort(value, fallback, label) {
|
|
|
11637
11930
|
if (value === void 0) return fallback;
|
|
11638
11931
|
const parsed = Number.parseInt(value, 10);
|
|
11639
11932
|
if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) {
|
|
11640
|
-
throw new
|
|
11933
|
+
throw new ToolValidationError6({
|
|
11641
11934
|
message: `${label} must be a port between 1 and 65535`,
|
|
11642
11935
|
field: "port"
|
|
11643
11936
|
});
|
|
11644
11937
|
}
|
|
11645
11938
|
return parsed;
|
|
11646
11939
|
}
|
|
11647
|
-
function
|
|
11940
|
+
function envFlag2(name2) {
|
|
11648
11941
|
const value = process.env[name2]?.trim().toLowerCase();
|
|
11649
11942
|
return value === "1" || value === "true" || value === "yes" || value === "on";
|
|
11650
11943
|
}
|
|
@@ -11703,7 +11996,7 @@ if (argv.includes("--help") || argv.includes("-h")) {
|
|
|
11703
11996
|
process.exit(1);
|
|
11704
11997
|
}
|
|
11705
11998
|
const open = argv.includes("--open") || argv.includes("-o") || process.env["WEBUI_OPEN"] === "1";
|
|
11706
|
-
const requireToken = argv.includes("--require-token") ||
|
|
11999
|
+
const requireToken = argv.includes("--require-token") || envFlag2("WEBUI_REQUIRE_TOKEN");
|
|
11707
12000
|
console.log(`[WebUI] Starting standalone server on ${wsHost} (http:${httpPort}, ws:${wsPort})...`);
|
|
11708
12001
|
startWebUI({
|
|
11709
12002
|
wsPort,
|