@wrongstack/webui-server 0.306.3 → 0.306.4
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/index.js +203 -67
- package/dist/server/collaboration-ws-handler.d.ts +2 -0
- package/dist/server/entry.js +195 -65
- package/dist/server/index.d.ts +2 -0
- package/dist/server/pref-helpers.d.ts +1 -1
- package/dist/server/server-runtime.d.ts +1 -1
- package/dist/server/session-cleanup-scheduler.d.ts +26 -0
- package/dist/server/session-deletion.d.ts +13 -0
- package/package.json +11 -11
package/dist/index.js
CHANGED
|
@@ -1210,6 +1210,102 @@ function patchConfig(config, updates) {
|
|
|
1210
1210
|
return Object.freeze({ ...config, ...updates });
|
|
1211
1211
|
}
|
|
1212
1212
|
|
|
1213
|
+
// src/server/session-cleanup-scheduler.ts
|
|
1214
|
+
import { toErrorMessage } from "@wrongstack/core/utils";
|
|
1215
|
+
|
|
1216
|
+
// src/server/session-deletion.ts
|
|
1217
|
+
async function deleteWebUISession(ctx, sessionId) {
|
|
1218
|
+
if (sessionId === ctx.getActiveSessionId()) {
|
|
1219
|
+
throw new Error("Cannot delete the active session");
|
|
1220
|
+
}
|
|
1221
|
+
await ctx.getSessionStore().delete(sessionId);
|
|
1222
|
+
await ctx.refreshSessions().catch(() => void 0);
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
// src/server/session-cleanup-scheduler.ts
|
|
1226
|
+
var DEFAULT_EMPTY_SESSION_CLEANUP_INTERVAL_MS = 60 * 60 * 1e3;
|
|
1227
|
+
var EMPTY_SESSION_CLEANUP_INTERVAL_ENV = "WRONGSTACK_EMPTY_SESSION_CLEANUP_INTERVAL_MS";
|
|
1228
|
+
var MAX_SESSION_SCAN = 1e3;
|
|
1229
|
+
function resolveEmptySessionCleanupInterval(value = process.env[EMPTY_SESSION_CLEANUP_INTERVAL_ENV]) {
|
|
1230
|
+
if (value === void 0 || value.trim() === "") return DEFAULT_EMPTY_SESSION_CLEANUP_INTERVAL_MS;
|
|
1231
|
+
const parsed = Number(value);
|
|
1232
|
+
return Number.isFinite(parsed) && parsed >= 1e3 ? Math.floor(parsed) : DEFAULT_EMPTY_SESSION_CLEANUP_INTERVAL_MS;
|
|
1233
|
+
}
|
|
1234
|
+
async function cleanupOwnerlessEmptySessions(ctx) {
|
|
1235
|
+
const store = ctx.getSessionStore();
|
|
1236
|
+
let deleted = 0;
|
|
1237
|
+
let errors = 0;
|
|
1238
|
+
let sessions;
|
|
1239
|
+
try {
|
|
1240
|
+
sessions = await store.list(MAX_SESSION_SCAN);
|
|
1241
|
+
} catch (error2) {
|
|
1242
|
+
errors++;
|
|
1243
|
+
ctx.logger.error("Empty session cleanup failed to list sessions", {
|
|
1244
|
+
event: "webui.empty_session_cleanup_error",
|
|
1245
|
+
phase: "list",
|
|
1246
|
+
message: toErrorMessage(error2)
|
|
1247
|
+
});
|
|
1248
|
+
ctx.logger.info("Empty session cleanup completed", {
|
|
1249
|
+
event: "webui.empty_session_cleanup_completed",
|
|
1250
|
+
deleted,
|
|
1251
|
+
errors
|
|
1252
|
+
});
|
|
1253
|
+
return { deleted, errors };
|
|
1254
|
+
}
|
|
1255
|
+
for (const candidate of sessions) {
|
|
1256
|
+
const sessionId = candidate.id;
|
|
1257
|
+
if (sessionId === ctx.getActiveSessionId() || ctx.hasParticipants(sessionId)) continue;
|
|
1258
|
+
try {
|
|
1259
|
+
if (!store.isEmpty || !await store.isEmpty(sessionId)) continue;
|
|
1260
|
+
if (store !== ctx.getSessionStore() || sessionId === ctx.getActiveSessionId() || ctx.hasParticipants(sessionId)) {
|
|
1261
|
+
continue;
|
|
1262
|
+
}
|
|
1263
|
+
await deleteWebUISession(
|
|
1264
|
+
{
|
|
1265
|
+
getActiveSessionId: ctx.getActiveSessionId,
|
|
1266
|
+
getSessionStore: () => store,
|
|
1267
|
+
refreshSessions: ctx.refreshSessions
|
|
1268
|
+
},
|
|
1269
|
+
sessionId
|
|
1270
|
+
);
|
|
1271
|
+
deleted++;
|
|
1272
|
+
} catch (error2) {
|
|
1273
|
+
errors++;
|
|
1274
|
+
ctx.logger.error("Empty session cleanup failed for session", {
|
|
1275
|
+
event: "webui.empty_session_cleanup_error",
|
|
1276
|
+
phase: "delete",
|
|
1277
|
+
sessionId,
|
|
1278
|
+
message: toErrorMessage(error2)
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
ctx.logger.info("Empty session cleanup completed", {
|
|
1283
|
+
event: "webui.empty_session_cleanup_completed",
|
|
1284
|
+
deleted,
|
|
1285
|
+
errors
|
|
1286
|
+
});
|
|
1287
|
+
return { deleted, errors };
|
|
1288
|
+
}
|
|
1289
|
+
function scheduleOwnerlessEmptySessionCleanup(ctx, intervalMs = resolveEmptySessionCleanupInterval()) {
|
|
1290
|
+
let running = null;
|
|
1291
|
+
const runNow = () => {
|
|
1292
|
+
if (running) return running;
|
|
1293
|
+
running = cleanupOwnerlessEmptySessions(ctx).finally(() => {
|
|
1294
|
+
running = null;
|
|
1295
|
+
});
|
|
1296
|
+
return running;
|
|
1297
|
+
};
|
|
1298
|
+
const timer = setInterval(() => void runNow(), intervalMs);
|
|
1299
|
+
timer.unref?.();
|
|
1300
|
+
return {
|
|
1301
|
+
dispose: async () => {
|
|
1302
|
+
clearInterval(timer);
|
|
1303
|
+
await running;
|
|
1304
|
+
},
|
|
1305
|
+
runNow
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1213
1309
|
// src/server/brain-routes.ts
|
|
1214
1310
|
async function handleBrainRoute(ws, msg, handlers) {
|
|
1215
1311
|
switch (msg.type) {
|
|
@@ -1757,7 +1853,7 @@ function extractCodeMapFileTargets(projectRoot, toolName2, rawInput) {
|
|
|
1757
1853
|
|
|
1758
1854
|
// src/server/collaboration-ws-handler.ts
|
|
1759
1855
|
import { randomUUID } from "node:crypto";
|
|
1760
|
-
import { toErrorMessage } from "@wrongstack/core/utils";
|
|
1856
|
+
import { toErrorMessage as toErrorMessage2 } from "@wrongstack/core/utils";
|
|
1761
1857
|
var REPLAY_LIMIT = 50;
|
|
1762
1858
|
var PAUSE_TIMEOUT_MS = 6e4;
|
|
1763
1859
|
var CollaborationWebSocketHandler = class {
|
|
@@ -1792,6 +1888,10 @@ var CollaborationWebSocketHandler = class {
|
|
|
1792
1888
|
ws.on("close", () => this.handleDisconnect(ws));
|
|
1793
1889
|
ws.on("error", () => this.handleDisconnect(ws));
|
|
1794
1890
|
}
|
|
1891
|
+
/** True while at least one collaboration participant is attached to a session. */
|
|
1892
|
+
hasParticipants(sessionId) {
|
|
1893
|
+
return (this.bySession.get(sessionId)?.size ?? 0) > 0;
|
|
1894
|
+
}
|
|
1795
1895
|
dispose() {
|
|
1796
1896
|
for (const off of this.offs) off();
|
|
1797
1897
|
this.offs.length = 0;
|
|
@@ -1909,7 +2009,7 @@ var CollaborationWebSocketHandler = class {
|
|
|
1909
2009
|
this.broadcast(sessionId, this.stateMessage(sessionId));
|
|
1910
2010
|
if (this.reader) {
|
|
1911
2011
|
this.replayHistory(ws, sessionId).catch((err) => {
|
|
1912
|
-
this.logger.debug?.(`collab: replay failed for ${sessionId}: ${
|
|
2012
|
+
this.logger.debug?.(`collab: replay failed for ${sessionId}: ${toErrorMessage2(err)}`);
|
|
1913
2013
|
});
|
|
1914
2014
|
}
|
|
1915
2015
|
this.logger.debug?.(`collab: participant ${participant.participantId} joined ${sessionId}`);
|
|
@@ -2016,7 +2116,7 @@ var CollaborationWebSocketHandler = class {
|
|
|
2016
2116
|
}
|
|
2017
2117
|
});
|
|
2018
2118
|
} catch (err) {
|
|
2019
|
-
this.send(ws, this.errorMessage(`annotation rejected: ${
|
|
2119
|
+
this.send(ws, this.errorMessage(`annotation rejected: ${toErrorMessage2(err)}`));
|
|
2020
2120
|
}
|
|
2021
2121
|
}
|
|
2022
2122
|
async handleResolve(ws, raw) {
|
|
@@ -2068,7 +2168,7 @@ var CollaborationWebSocketHandler = class {
|
|
|
2068
2168
|
}
|
|
2069
2169
|
});
|
|
2070
2170
|
} catch (err) {
|
|
2071
|
-
this.send(ws, this.errorMessage(`resolve failed: ${
|
|
2171
|
+
this.send(ws, this.errorMessage(`resolve failed: ${toErrorMessage2(err)}`));
|
|
2072
2172
|
}
|
|
2073
2173
|
}
|
|
2074
2174
|
// ── Event subscription (live mirror) ───────────────────────────────────
|
|
@@ -2139,7 +2239,7 @@ var CollaborationWebSocketHandler = class {
|
|
|
2139
2239
|
seen++;
|
|
2140
2240
|
}
|
|
2141
2241
|
} catch (err) {
|
|
2142
|
-
this.logger.debug?.(`collab: session reader rejected ${sessionId}: ${
|
|
2242
|
+
this.logger.debug?.(`collab: session reader rejected ${sessionId}: ${toErrorMessage2(err)}`);
|
|
2143
2243
|
return;
|
|
2144
2244
|
}
|
|
2145
2245
|
const tail2 = seen <= REPLAY_LIMIT ? ring.slice(0, seen) : (
|
|
@@ -2222,7 +2322,7 @@ var CollaborationWebSocketHandler = class {
|
|
|
2222
2322
|
try {
|
|
2223
2323
|
sendSerialized(p.ws, data);
|
|
2224
2324
|
} catch (err) {
|
|
2225
|
-
this.logger.debug?.(`collab broadcast failed: ${
|
|
2325
|
+
this.logger.debug?.(`collab broadcast failed: ${toErrorMessage2(err)}`);
|
|
2226
2326
|
}
|
|
2227
2327
|
}
|
|
2228
2328
|
}
|
|
@@ -6910,7 +7010,7 @@ import {
|
|
|
6910
7010
|
PhaseOrchestrator,
|
|
6911
7011
|
PhaseStore
|
|
6912
7012
|
} from "@wrongstack/core/goal";
|
|
6913
|
-
import { toErrorMessage as
|
|
7013
|
+
import { toErrorMessage as toErrorMessage3 } from "@wrongstack/core/utils";
|
|
6914
7014
|
import { WorktreeManager } from "@wrongstack/core/worktree";
|
|
6915
7015
|
|
|
6916
7016
|
// src/server/git-process.ts
|
|
@@ -7163,7 +7263,7 @@ var GoalWebSocketHandler = class {
|
|
|
7163
7263
|
sendResult7(result);
|
|
7164
7264
|
} catch (err) {
|
|
7165
7265
|
if (mySeq !== this.assessSeq) return;
|
|
7166
|
-
this.logger.error(`[Goal] Assessment failed: ${
|
|
7266
|
+
this.logger.error(`[Goal] Assessment failed: ${toErrorMessage3(err)}`);
|
|
7167
7267
|
sendResult7({
|
|
7168
7268
|
realistic: true,
|
|
7169
7269
|
durationClaimed: null,
|
|
@@ -7172,7 +7272,7 @@ var GoalWebSocketHandler = class {
|
|
|
7172
7272
|
concerns: [],
|
|
7173
7273
|
raw: "",
|
|
7174
7274
|
parseFailed: true,
|
|
7175
|
-
parseError: `Assessment error: ${
|
|
7275
|
+
parseError: `Assessment error: ${toErrorMessage3(err)}`
|
|
7176
7276
|
});
|
|
7177
7277
|
}
|
|
7178
7278
|
}
|
|
@@ -7303,7 +7403,7 @@ Run npx tsc --noEmit to verify the fix. Output the fixed file paths.`;
|
|
|
7303
7403
|
);
|
|
7304
7404
|
this.broadcastState();
|
|
7305
7405
|
}).catch((err) => {
|
|
7306
|
-
this.logger.error(`[Goal] Aborted: ${
|
|
7406
|
+
this.logger.error(`[Goal] Aborted: ${toErrorMessage3(err)}`);
|
|
7307
7407
|
this.stopBroadcast();
|
|
7308
7408
|
this.broadcast({ type: "goal.failed", payload: { title, error: String(err) } });
|
|
7309
7409
|
});
|
|
@@ -7429,7 +7529,7 @@ Run npx tsc --noEmit to verify the fix. Output the fixed file paths.`;
|
|
|
7429
7529
|
}
|
|
7430
7530
|
this.logger.info(`[Goal] Planner produced no phases; using defaults for: ${goal}`);
|
|
7431
7531
|
} catch (err) {
|
|
7432
|
-
this.logger.error(`[Goal] Planning failed, using defaults: ${
|
|
7532
|
+
this.logger.error(`[Goal] Planning failed, using defaults: ${toErrorMessage3(err)}`);
|
|
7433
7533
|
}
|
|
7434
7534
|
return this.defaultPhases();
|
|
7435
7535
|
}
|
|
@@ -7490,7 +7590,7 @@ ${result_.finalText.slice(0, 2e3)}`
|
|
|
7490
7590
|
);
|
|
7491
7591
|
}
|
|
7492
7592
|
} catch (err) {
|
|
7493
|
-
this.logger.warn(`[Goal] Chimera review failed for "${task.title}": ${
|
|
7593
|
+
this.logger.warn(`[Goal] Chimera review failed for "${task.title}": ${toErrorMessage3(err)}`);
|
|
7494
7594
|
}
|
|
7495
7595
|
}
|
|
7496
7596
|
/**
|
|
@@ -9877,6 +9977,16 @@ function createHttpServer(opts) {
|
|
|
9877
9977
|
res.end("Unauthorized");
|
|
9878
9978
|
return;
|
|
9879
9979
|
}
|
|
9980
|
+
const isMutatingApiRequest = url.pathname.startsWith("/api/") && (req.method === "POST" || req.method === "PUT" || req.method === "PATCH" || req.method === "DELETE");
|
|
9981
|
+
if (isMutatingApiRequest && !req.headers.origin && !accessTokenOk) {
|
|
9982
|
+
res.writeHead(401, { "Content-Type": "application/json", "Cache-Control": "no-store" });
|
|
9983
|
+
res.end(
|
|
9984
|
+
JSON.stringify({
|
|
9985
|
+
error: "Unauthorized: a request without an Origin header must present the access token (X-WS-Token header or ws_token cookie)."
|
|
9986
|
+
})
|
|
9987
|
+
);
|
|
9988
|
+
return;
|
|
9989
|
+
}
|
|
9880
9990
|
if (shouldSetAuthCookie && opts.apiToken) {
|
|
9881
9991
|
setAuthCookieHeaders(res, opts.apiToken, secureCookies);
|
|
9882
9992
|
}
|
|
@@ -13800,7 +13910,7 @@ async function handleMemoryRoute(ctx, ws, message) {
|
|
|
13800
13910
|
|
|
13801
13911
|
// src/server/mode-operations.ts
|
|
13802
13912
|
import { ToolValidationError as ToolValidationError3 } from "@wrongstack/core/types";
|
|
13803
|
-
import { toErrorMessage as
|
|
13913
|
+
import { toErrorMessage as toErrorMessage4 } from "@wrongstack/core/utils";
|
|
13804
13914
|
function sendResult4(context, ws, success, message) {
|
|
13805
13915
|
context.send(ws, { type: "key.operation_result", payload: { success, message } });
|
|
13806
13916
|
}
|
|
@@ -13832,7 +13942,7 @@ function createModeOperations(context) {
|
|
|
13832
13942
|
} catch (error2) {
|
|
13833
13943
|
context.send(ws, {
|
|
13834
13944
|
type: "modes.list",
|
|
13835
|
-
payload: { modes: [], activeId: "default", error:
|
|
13945
|
+
payload: { modes: [], activeId: "default", error: toErrorMessage4(error2) }
|
|
13836
13946
|
});
|
|
13837
13947
|
}
|
|
13838
13948
|
},
|
|
@@ -13864,7 +13974,7 @@ function createModeOperations(context) {
|
|
|
13864
13974
|
await context.afterSwitch?.(id);
|
|
13865
13975
|
sendResult4(context, ws, true, `Switched to mode "${id}"`);
|
|
13866
13976
|
} catch (error2) {
|
|
13867
|
-
sendResult4(context, ws, false,
|
|
13977
|
+
sendResult4(context, ws, false, toErrorMessage4(error2));
|
|
13868
13978
|
}
|
|
13869
13979
|
}
|
|
13870
13980
|
};
|
|
@@ -13911,7 +14021,7 @@ import {
|
|
|
13911
14021
|
resolveConfiguredRefinerRef,
|
|
13912
14022
|
resolveEnhanceFallbackRef
|
|
13913
14023
|
} from "@wrongstack/core/execution";
|
|
13914
|
-
import { toErrorMessage as
|
|
14024
|
+
import { toErrorMessage as toErrorMessage5 } from "@wrongstack/core/utils";
|
|
13915
14025
|
function sendResult5(context, ws, payload, legacyResult = false) {
|
|
13916
14026
|
const message = { type: "model.switch_result", payload };
|
|
13917
14027
|
if (payload.success && context.broadcast) context.broadcast(message);
|
|
@@ -13969,7 +14079,7 @@ function createModelOperations(context) {
|
|
|
13969
14079
|
{
|
|
13970
14080
|
...requestId ? { requestId } : {},
|
|
13971
14081
|
success: false,
|
|
13972
|
-
message: `Switch failed: ${
|
|
14082
|
+
message: `Switch failed: ${toErrorMessage5(error2)}`,
|
|
13973
14083
|
provider,
|
|
13974
14084
|
model,
|
|
13975
14085
|
previousProvider,
|
|
@@ -14016,7 +14126,7 @@ function createModelOperations(context) {
|
|
|
14016
14126
|
payload: {
|
|
14017
14127
|
refined: text2,
|
|
14018
14128
|
english: text2,
|
|
14019
|
-
error: `Cannot use ${payload.provider}/${payload.model}: ${
|
|
14129
|
+
error: `Cannot use ${payload.provider}/${payload.model}: ${toErrorMessage5(error2)}`,
|
|
14020
14130
|
errorKind: "provider_error",
|
|
14021
14131
|
...fallbackRef ? { fallbackRef } : {}
|
|
14022
14132
|
}
|
|
@@ -14114,7 +14224,7 @@ function createModelOperations(context) {
|
|
|
14114
14224
|
JSON.stringify({
|
|
14115
14225
|
level: "error",
|
|
14116
14226
|
event: "model.refine.error",
|
|
14117
|
-
error:
|
|
14227
|
+
error: toErrorMessage5(error2),
|
|
14118
14228
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
14119
14229
|
})
|
|
14120
14230
|
);
|
|
@@ -14123,7 +14233,7 @@ function createModelOperations(context) {
|
|
|
14123
14233
|
payload: {
|
|
14124
14234
|
refined: text2,
|
|
14125
14235
|
english: text2,
|
|
14126
|
-
error:
|
|
14236
|
+
error: toErrorMessage5(error2),
|
|
14127
14237
|
errorKind: "provider_error",
|
|
14128
14238
|
...fallbackRef ? { fallbackRef } : {}
|
|
14129
14239
|
}
|
|
@@ -14812,7 +14922,7 @@ function createWebuiClientPresence(deps2) {
|
|
|
14812
14922
|
|
|
14813
14923
|
// src/server/brain-handlers.ts
|
|
14814
14924
|
import { BUILTIN_COUNCIL_PERSONAS } from "@wrongstack/core/execution";
|
|
14815
|
-
import { toErrorMessage as
|
|
14925
|
+
import { toErrorMessage as toErrorMessage6 } from "@wrongstack/core/utils";
|
|
14816
14926
|
var COUNCIL_PERSONA_CATALOG = Object.freeze(
|
|
14817
14927
|
BUILTIN_COUNCIL_PERSONAS.map(
|
|
14818
14928
|
(persona) => Object.freeze({
|
|
@@ -14901,7 +15011,7 @@ async function handleBrainConfigSet(ctx, ws, payload) {
|
|
|
14901
15011
|
payload: {
|
|
14902
15012
|
config: brainConfigPayload(ctx.brainRuntime),
|
|
14903
15013
|
persisted: false,
|
|
14904
|
-
error: `Invalid Brain setting: ${
|
|
15014
|
+
error: `Invalid Brain setting: ${toErrorMessage6(err)}`
|
|
14905
15015
|
}
|
|
14906
15016
|
});
|
|
14907
15017
|
}
|
|
@@ -14947,7 +15057,7 @@ async function handleBrainAsk(ctx, ws, question) {
|
|
|
14947
15057
|
payload: { sessionId: ctx.getSessionId?.(), question: q, decision }
|
|
14948
15058
|
});
|
|
14949
15059
|
} catch (err) {
|
|
14950
|
-
sendResult6(ctx, ws, false, `Brain consultation failed: ${
|
|
15060
|
+
sendResult6(ctx, ws, false, `Brain consultation failed: ${toErrorMessage6(err)}`);
|
|
14951
15061
|
}
|
|
14952
15062
|
}
|
|
14953
15063
|
|
|
@@ -15734,7 +15844,6 @@ function seedContextMeta(config, context) {
|
|
|
15734
15844
|
const hqConfig = config.hq;
|
|
15735
15845
|
meta["hqEnabled"] = hqConfig?.enabled === true;
|
|
15736
15846
|
meta["hqUrl"] = hqConfig?.url ?? "";
|
|
15737
|
-
meta["hqToken"] = hqConfig?.token ?? "";
|
|
15738
15847
|
meta["hqRawContent"] = hqConfig?.rawContent === true;
|
|
15739
15848
|
const tgExt = config.extensions?.["telegram"];
|
|
15740
15849
|
meta["tgConfigured"] = typeof tgExt?.["botToken"] === "string" && tgExt["botToken"].length > 0;
|
|
@@ -15828,7 +15937,6 @@ var PREF_KEYS = [
|
|
|
15828
15937
|
"auditLevel",
|
|
15829
15938
|
"hqEnabled",
|
|
15830
15939
|
"hqUrl",
|
|
15831
|
-
"hqToken",
|
|
15832
15940
|
"hqRawContent",
|
|
15833
15941
|
"tgConfigured",
|
|
15834
15942
|
"tgSessionEnd",
|
|
@@ -16390,7 +16498,7 @@ import * as fs16 from "node:fs/promises";
|
|
|
16390
16498
|
import * as path20 from "node:path";
|
|
16391
16499
|
import { TOKENS } from "@wrongstack/core/kernel";
|
|
16392
16500
|
import { DefaultSessionStore as DefaultSessionStore2 } from "@wrongstack/core/storage";
|
|
16393
|
-
import { toErrorMessage as
|
|
16501
|
+
import { toErrorMessage as toErrorMessage8, wstackGlobalRoot as wstackGlobalRoot2 } from "@wrongstack/core/utils";
|
|
16394
16502
|
import { makeProviderFromConfig } from "@wrongstack/providers";
|
|
16395
16503
|
|
|
16396
16504
|
// src/server/project-handlers.ts
|
|
@@ -16633,7 +16741,8 @@ function createProjectHandlers(ctx) {
|
|
|
16633
16741
|
// src/server/provider-handlers.ts
|
|
16634
16742
|
import { hasProviderCredential, resolveProviderModelList } from "@wrongstack/core/models";
|
|
16635
16743
|
import { DefaultSecretScrubber as DefaultSecretScrubber2 } from "@wrongstack/core/security";
|
|
16636
|
-
import {
|
|
16744
|
+
import { validateProviderBaseUrl } from "@wrongstack/core/tools";
|
|
16745
|
+
import { toErrorMessage as toErrorMessage7 } from "@wrongstack/core/utils";
|
|
16637
16746
|
import {
|
|
16638
16747
|
beginOAuthLogin
|
|
16639
16748
|
} from "@wrongstack/providers/oauth";
|
|
@@ -17221,6 +17330,13 @@ function createProviderOperations(deps2) {
|
|
|
17221
17330
|
sendOperationResult(ws, false, `Unknown provider "${payload.id}"`);
|
|
17222
17331
|
return;
|
|
17223
17332
|
}
|
|
17333
|
+
if (payload.baseUrl !== void 0 && payload.baseUrl !== "") {
|
|
17334
|
+
const invalid = validateProviderBaseUrl(payload.baseUrl);
|
|
17335
|
+
if (invalid) {
|
|
17336
|
+
sendOperationResult(ws, false, invalid);
|
|
17337
|
+
return;
|
|
17338
|
+
}
|
|
17339
|
+
}
|
|
17224
17340
|
if (payload.family !== void 0) cfg.family = payload.family;
|
|
17225
17341
|
if (payload.baseUrl !== void 0) cfg.baseUrl = payload.baseUrl;
|
|
17226
17342
|
if (payload.envVars !== void 0) cfg.envVars = payload.envVars;
|
|
@@ -17401,7 +17517,7 @@ function createProviderHandlers(deps2) {
|
|
|
17401
17517
|
JSON.stringify({
|
|
17402
17518
|
level: "error",
|
|
17403
17519
|
event: "webui.provider_save_failed",
|
|
17404
|
-
message:
|
|
17520
|
+
message: toErrorMessage7(error2),
|
|
17405
17521
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
17406
17522
|
})
|
|
17407
17523
|
);
|
|
@@ -19057,21 +19173,21 @@ function createSessionHandlers(ctx) {
|
|
|
19057
19173
|
deleteSession: async (ws, msg) => {
|
|
19058
19174
|
const { id } = msg.payload;
|
|
19059
19175
|
try {
|
|
19060
|
-
|
|
19061
|
-
|
|
19062
|
-
|
|
19063
|
-
|
|
19064
|
-
|
|
19065
|
-
|
|
19176
|
+
await deleteWebUISession(
|
|
19177
|
+
{
|
|
19178
|
+
getActiveSessionId: () => ctx.getSession().id,
|
|
19179
|
+
getSessionStore: ctx.getSessionStore,
|
|
19180
|
+
refreshSessions: async () => {
|
|
19181
|
+
const list = await ctx.getSessionStore().list(200);
|
|
19182
|
+
broadcastToAll({
|
|
19183
|
+
type: "sessions.list",
|
|
19184
|
+
payload: { sessions: toSessionHistoryEntries(list, ctx.getSession().id) }
|
|
19185
|
+
});
|
|
19186
|
+
}
|
|
19187
|
+
},
|
|
19188
|
+
id
|
|
19189
|
+
);
|
|
19066
19190
|
result(ws, true, `Session ${id} deleted`);
|
|
19067
|
-
try {
|
|
19068
|
-
const list = await store.list(200);
|
|
19069
|
-
broadcastToAll({
|
|
19070
|
-
type: "sessions.list",
|
|
19071
|
-
payload: { sessions: toSessionHistoryEntries(list, ctx.getSession().id) }
|
|
19072
|
-
});
|
|
19073
|
-
} catch {
|
|
19074
|
-
}
|
|
19075
19191
|
} catch (err) {
|
|
19076
19192
|
result(ws, false, errMessage(err));
|
|
19077
19193
|
}
|
|
@@ -19254,7 +19370,7 @@ async function applyEmbeddedModelSwitch(ctx, providerId, modelId) {
|
|
|
19254
19370
|
event: "models.refresh_failed",
|
|
19255
19371
|
provider: providerId,
|
|
19256
19372
|
model: modelId,
|
|
19257
|
-
message:
|
|
19373
|
+
message: toErrorMessage8(error2),
|
|
19258
19374
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
19259
19375
|
})
|
|
19260
19376
|
);
|
|
@@ -23376,7 +23492,7 @@ import {
|
|
|
23376
23492
|
expectDefined as expectDefined4,
|
|
23377
23493
|
sessionScopedPath as sessionScopedPath3,
|
|
23378
23494
|
startSharedHeapWatchdog,
|
|
23379
|
-
toErrorMessage as
|
|
23495
|
+
toErrorMessage as toErrorMessage15,
|
|
23380
23496
|
wstackGlobalRoot as wstackGlobalRoot4
|
|
23381
23497
|
} from "@wrongstack/core/utils";
|
|
23382
23498
|
import { makeProviderFromConfig as makeProviderFromConfig5 } from "@wrongstack/providers";
|
|
@@ -23413,7 +23529,7 @@ import {
|
|
|
23413
23529
|
} from "@wrongstack/core/types";
|
|
23414
23530
|
import {
|
|
23415
23531
|
estimateRequestTokensCalibrated,
|
|
23416
|
-
toErrorMessage as
|
|
23532
|
+
toErrorMessage as toErrorMessage11
|
|
23417
23533
|
} from "@wrongstack/core/utils";
|
|
23418
23534
|
import { makeLightSubagentFactory } from "@wrongstack/runtime";
|
|
23419
23535
|
import { getSageService, setupSage } from "@wrongstack/sage";
|
|
@@ -23569,7 +23685,7 @@ function sleep(ms) {
|
|
|
23569
23685
|
import { spawn as spawnChild } from "node:child_process";
|
|
23570
23686
|
import { createRequire as createRequire2 } from "node:module";
|
|
23571
23687
|
import { createCompatibilityTrustBoundary as createCompatibilityTrustBoundary2 } from "@wrongstack/core/security";
|
|
23572
|
-
import { buildChildEnv, toErrorMessage as
|
|
23688
|
+
import { buildChildEnv, toErrorMessage as toErrorMessage9 } from "@wrongstack/core/utils";
|
|
23573
23689
|
var MAX_SESSIONS_PER_CLIENT = 8;
|
|
23574
23690
|
var MAX_INPUT_BYTES = 1 << 16;
|
|
23575
23691
|
var DEFAULT_COLS = 80;
|
|
@@ -23685,8 +23801,8 @@ var TerminalWebSocketHandler = class {
|
|
|
23685
23801
|
...windowsPtyOptions()
|
|
23686
23802
|
});
|
|
23687
23803
|
} catch (err) {
|
|
23688
|
-
const msg = `Integrated terminal failed to start: ${
|
|
23689
|
-
this.logger.warn?.(`terminal spawn failed: ${
|
|
23804
|
+
const msg = `Integrated terminal failed to start: ${toErrorMessage9(err)}`;
|
|
23805
|
+
this.logger.warn?.(`terminal spawn failed: ${toErrorMessage9(err)}`);
|
|
23690
23806
|
this.send(ws, { type: "terminal.output", payload: { id: payload.id, data: `${msg}\r
|
|
23691
23807
|
` } });
|
|
23692
23808
|
this.send(ws, { type: "terminal.exit", payload: { id: payload.id, exitCode: -1 } });
|
|
@@ -23747,7 +23863,7 @@ var TerminalWebSocketHandler = class {
|
|
|
23747
23863
|
try {
|
|
23748
23864
|
pty.kill();
|
|
23749
23865
|
} catch (err) {
|
|
23750
|
-
this.logger.warn?.(`${reason} failed: ${
|
|
23866
|
+
this.logger.warn?.(`${reason} failed: ${toErrorMessage9(err)}`);
|
|
23751
23867
|
}
|
|
23752
23868
|
}
|
|
23753
23869
|
killWindowsProcessTree(pty, reason) {
|
|
@@ -23759,7 +23875,7 @@ var TerminalWebSocketHandler = class {
|
|
|
23759
23875
|
} catch {
|
|
23760
23876
|
}
|
|
23761
23877
|
} catch (err) {
|
|
23762
|
-
this.logger.warn?.(`${reason} taskkill failed: ${
|
|
23878
|
+
this.logger.warn?.(`${reason} taskkill failed: ${toErrorMessage9(err)}`);
|
|
23763
23879
|
}
|
|
23764
23880
|
}
|
|
23765
23881
|
send(ws, msg) {
|
|
@@ -23806,7 +23922,7 @@ function clampDim(value, fallback) {
|
|
|
23806
23922
|
|
|
23807
23923
|
// src/server/worktree-ws-handler.ts
|
|
23808
23924
|
import { join as join14, resolve as resolve14, sep as sep5 } from "node:path";
|
|
23809
|
-
import { toErrorMessage as
|
|
23925
|
+
import { toErrorMessage as toErrorMessage10 } from "@wrongstack/core/utils";
|
|
23810
23926
|
import { WorktreeManager as WorktreeManager3 } from "@wrongstack/core/worktree";
|
|
23811
23927
|
import { cleanupStaleSddWorktrees as cleanupStaleSddWorktrees2 } from "@wrongstack/sdd";
|
|
23812
23928
|
var MAX_ACTIVITY = 6;
|
|
@@ -23972,7 +24088,7 @@ var WorktreeWebSocketHandler = class {
|
|
|
23972
24088
|
}
|
|
23973
24089
|
});
|
|
23974
24090
|
} catch (err) {
|
|
23975
|
-
this.logger.debug?.(`worktree orphan scan failed: ${
|
|
24091
|
+
this.logger.debug?.(`worktree orphan scan failed: ${toErrorMessage10(err)}`);
|
|
23976
24092
|
this.broadcast({ type: "worktree.orphans", payload: { orphans: [], canClean: false } });
|
|
23977
24093
|
}
|
|
23978
24094
|
}
|
|
@@ -24234,7 +24350,7 @@ var WorktreeWebSocketHandler = class {
|
|
|
24234
24350
|
try {
|
|
24235
24351
|
sendSerialized(ws, data);
|
|
24236
24352
|
} catch (err) {
|
|
24237
|
-
this.logger.debug?.(`worktree broadcast failed: ${
|
|
24353
|
+
this.logger.debug?.(`worktree broadcast failed: ${toErrorMessage10(err)}`);
|
|
24238
24354
|
}
|
|
24239
24355
|
}
|
|
24240
24356
|
}
|
|
@@ -24343,7 +24459,7 @@ async function createAgentServices(input) {
|
|
|
24343
24459
|
const updateAutoCompactionMaxContext = async (newProvider, providerId = newProvider.id, providerCfg) => {
|
|
24344
24460
|
await modelsRegistry.refresh().catch((err) => {
|
|
24345
24461
|
logger.warn(
|
|
24346
|
-
`models.dev refresh failed for ${providerId}/${context.model}: ${
|
|
24462
|
+
`models.dev refresh failed for ${providerId}/${context.model}: ${toErrorMessage11(err)}; using cached catalog`
|
|
24347
24463
|
);
|
|
24348
24464
|
});
|
|
24349
24465
|
const currentConfig = input.config;
|
|
@@ -25185,7 +25301,7 @@ import {
|
|
|
25185
25301
|
import {
|
|
25186
25302
|
configureChildEnvGitIdentity,
|
|
25187
25303
|
sessionScopedPath as sessionScopedPath2,
|
|
25188
|
-
toErrorMessage as
|
|
25304
|
+
toErrorMessage as toErrorMessage13
|
|
25189
25305
|
} from "@wrongstack/core/utils";
|
|
25190
25306
|
import {
|
|
25191
25307
|
createVaultBackedMcpAuthorizationProviderFactory,
|
|
@@ -25267,7 +25383,7 @@ async function discoverAndMergeWebuiProviders(opts) {
|
|
|
25267
25383
|
|
|
25268
25384
|
// src/server/setup-screen.ts
|
|
25269
25385
|
import { expectDefined as expectDefined3 } from "@wrongstack/core/utils";
|
|
25270
|
-
import { toErrorMessage as
|
|
25386
|
+
import { toErrorMessage as toErrorMessage12 } from "@wrongstack/core/utils";
|
|
25271
25387
|
import { makeProviderFromConfig as makeProviderFromConfig3 } from "@wrongstack/providers";
|
|
25272
25388
|
var UNCONFIGURED_CAPABILITIES = {
|
|
25273
25389
|
tools: false,
|
|
@@ -25308,7 +25424,7 @@ function logCreateFailure(event, err) {
|
|
|
25308
25424
|
JSON.stringify({
|
|
25309
25425
|
level: "error",
|
|
25310
25426
|
event,
|
|
25311
|
-
message:
|
|
25427
|
+
message: toErrorMessage12(err),
|
|
25312
25428
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
25313
25429
|
})
|
|
25314
25430
|
);
|
|
@@ -25617,7 +25733,7 @@ async function createPreContextServices(input) {
|
|
|
25617
25733
|
await modelsRegistry.refresh();
|
|
25618
25734
|
logger.info("models.dev catalog refreshed");
|
|
25619
25735
|
} catch (err) {
|
|
25620
|
-
logger.warn(`models.dev refresh failed (${
|
|
25736
|
+
logger.warn(`models.dev refresh failed (${toErrorMessage13(err)}); using cached catalog`);
|
|
25621
25737
|
}
|
|
25622
25738
|
}
|
|
25623
25739
|
try {
|
|
@@ -25628,7 +25744,7 @@ async function createPreContextServices(input) {
|
|
|
25628
25744
|
logger
|
|
25629
25745
|
});
|
|
25630
25746
|
} catch (err) {
|
|
25631
|
-
logger.debug(`provider auto-discovery skipped: ${
|
|
25747
|
+
logger.debug(`provider auto-discovery skipped: ${toErrorMessage13(err)}`);
|
|
25632
25748
|
}
|
|
25633
25749
|
try {
|
|
25634
25750
|
await installCatalogModelOutputLimits({
|
|
@@ -25637,7 +25753,7 @@ async function createPreContextServices(input) {
|
|
|
25637
25753
|
log: (message) => logger.debug(message)
|
|
25638
25754
|
});
|
|
25639
25755
|
} catch (err) {
|
|
25640
|
-
logger.debug(`model output-limit index skipped: ${
|
|
25756
|
+
logger.debug(`model output-limit index skipped: ${toErrorMessage13(err)}`);
|
|
25641
25757
|
}
|
|
25642
25758
|
const events = opts.services?.events ?? new EventBus();
|
|
25643
25759
|
events.setLogger(logger);
|
|
@@ -25656,7 +25772,7 @@ async function createPreContextServices(input) {
|
|
|
25656
25772
|
JSON.stringify({
|
|
25657
25773
|
level: "warn",
|
|
25658
25774
|
event: "webui.provider_registry_load_failed",
|
|
25659
|
-
message:
|
|
25775
|
+
message: toErrorMessage13(err),
|
|
25660
25776
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
25661
25777
|
})
|
|
25662
25778
|
);
|
|
@@ -26291,7 +26407,7 @@ function buildRoutes(state, deps2, cb) {
|
|
|
26291
26407
|
import { createRequire as createRequire4 } from "node:module";
|
|
26292
26408
|
import * as path33 from "node:path";
|
|
26293
26409
|
import { fileURLToPath } from "node:url";
|
|
26294
|
-
import { toErrorMessage as
|
|
26410
|
+
import { toErrorMessage as toErrorMessage14 } from "@wrongstack/core/utils";
|
|
26295
26411
|
import { WebSocketServer } from "ws";
|
|
26296
26412
|
async function resolvePorts(opts) {
|
|
26297
26413
|
const surface = opts.surface ?? "webui";
|
|
@@ -26442,7 +26558,7 @@ function armEvents(wssPrimary, wssSecondary, wsHost, httpPort, setupInput, watch
|
|
|
26442
26558
|
level: "error",
|
|
26443
26559
|
event: "webui.ws_server_error",
|
|
26444
26560
|
host: wsHost,
|
|
26445
|
-
message:
|
|
26561
|
+
message: toErrorMessage14(err),
|
|
26446
26562
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
26447
26563
|
})
|
|
26448
26564
|
);
|
|
@@ -27089,7 +27205,7 @@ async function startWebUI(opts = {}) {
|
|
|
27089
27205
|
console.log(`[WebUI] Provider credentials reloaded from config.json (${activeId})`);
|
|
27090
27206
|
} catch (err) {
|
|
27091
27207
|
console.warn(
|
|
27092
|
-
`[WebUI] Credential hot-reload failed for ${activeId}: ${
|
|
27208
|
+
`[WebUI] Credential hot-reload failed for ${activeId}: ${toErrorMessage15(err)}`
|
|
27093
27209
|
);
|
|
27094
27210
|
}
|
|
27095
27211
|
},
|
|
@@ -27112,6 +27228,19 @@ async function startWebUI(opts = {}) {
|
|
|
27112
27228
|
})
|
|
27113
27229
|
});
|
|
27114
27230
|
const routes = buildRoutes(state, deps2, cb);
|
|
27231
|
+
const stopEmptySessionCleanup = scheduleOwnerlessEmptySessionCleanup({
|
|
27232
|
+
getSessionStore: state.getSessionStore,
|
|
27233
|
+
getActiveSessionId: () => state.getSession().id,
|
|
27234
|
+
hasParticipants: (sessionId) => collabHandler.hasParticipants(sessionId),
|
|
27235
|
+
refreshSessions: async () => {
|
|
27236
|
+
const list = await state.getSessionStore().list(200);
|
|
27237
|
+
broadcast(clients, {
|
|
27238
|
+
type: "sessions.list",
|
|
27239
|
+
payload: { sessions: toSessionHistoryEntries(list, state.getSession().id) }
|
|
27240
|
+
});
|
|
27241
|
+
},
|
|
27242
|
+
logger
|
|
27243
|
+
});
|
|
27115
27244
|
let kanbanSupervisorDispose = null;
|
|
27116
27245
|
const handleMessage = createMessageDispatcher({
|
|
27117
27246
|
state,
|
|
@@ -27196,7 +27325,8 @@ projectRoot: ${ev.projectRoot ?? "?"}`,
|
|
|
27196
27325
|
wssPrimary,
|
|
27197
27326
|
...wssSecondary ? [wssSecondary] : []
|
|
27198
27327
|
],
|
|
27199
|
-
onPreShutdown: () => {
|
|
27328
|
+
onPreShutdown: async () => {
|
|
27329
|
+
await stopEmptySessionCleanup.dispose();
|
|
27200
27330
|
kanbanSupervisorDispose?.();
|
|
27201
27331
|
kanbanSupervisorDispose = null;
|
|
27202
27332
|
},
|
|
@@ -27223,9 +27353,9 @@ projectRoot: ${ev.projectRoot ?? "?"}`,
|
|
|
27223
27353
|
eternalSubscription = null;
|
|
27224
27354
|
}
|
|
27225
27355
|
codebaseIndexing.dispose();
|
|
27226
|
-
await agentServices.runSageSessionHygiene().catch((err) => logger.warn(`sage session hygiene failed: ${
|
|
27356
|
+
await agentServices.runSageSessionHygiene().catch((err) => logger.warn(`sage session hygiene failed: ${toErrorMessage15(err)}`));
|
|
27227
27357
|
await memoryStore.dispose().catch(
|
|
27228
|
-
(err) => logger.warn(`sage connection disposal failed: ${
|
|
27358
|
+
(err) => logger.warn(`sage connection disposal failed: ${toErrorMessage15(err)}`)
|
|
27229
27359
|
);
|
|
27230
27360
|
await unregisterInstance(process.pid, path34.dirname(globalConfigPath));
|
|
27231
27361
|
}
|
|
@@ -27234,7 +27364,9 @@ projectRoot: ${ev.projectRoot ?? "?"}`,
|
|
|
27234
27364
|
export {
|
|
27235
27365
|
CLIENT_MESSAGE_TYPES,
|
|
27236
27366
|
CollaborationWebSocketHandler,
|
|
27367
|
+
DEFAULT_EMPTY_SESSION_CLEANUP_INTERVAL_MS,
|
|
27237
27368
|
DEFAULT_SURFACE_CONNECTION_CONFIG,
|
|
27369
|
+
EMPTY_SESSION_CLEANUP_INTERVAL_ENV,
|
|
27238
27370
|
GoalWebSocketHandler,
|
|
27239
27371
|
KANBAN_CLIENT_MESSAGE_TYPES,
|
|
27240
27372
|
PREF_KEYS,
|
|
@@ -27266,6 +27398,7 @@ export {
|
|
|
27266
27398
|
buildTaskGraphFromGoalPhase,
|
|
27267
27399
|
buildTaskGraphFromSddSnapshot,
|
|
27268
27400
|
buildWebUIAccessUrl,
|
|
27401
|
+
cleanupOwnerlessEmptySessions,
|
|
27269
27402
|
clearAnalyticsBuffer,
|
|
27270
27403
|
collectConnectionsHealth,
|
|
27271
27404
|
computeUsageCost,
|
|
@@ -27309,6 +27442,7 @@ export {
|
|
|
27309
27442
|
decodeSessionId,
|
|
27310
27443
|
defaultBaseDir,
|
|
27311
27444
|
deleteKey,
|
|
27445
|
+
deleteWebUISession,
|
|
27312
27446
|
enqueueBounded,
|
|
27313
27447
|
ensureDistDir,
|
|
27314
27448
|
ensureProjectDataDir,
|
|
@@ -27496,12 +27630,14 @@ export {
|
|
|
27496
27630
|
resolveAllPendingConfirms,
|
|
27497
27631
|
resolveAuthToken,
|
|
27498
27632
|
resolveDistDir,
|
|
27633
|
+
resolveEmptySessionCleanupInterval,
|
|
27499
27634
|
resolveProviderCatalogForModels,
|
|
27500
27635
|
resolveProviderModelMetadata,
|
|
27501
27636
|
resolveWorkingDirInsideProject,
|
|
27502
27637
|
resolveYoloEligiblePendingConfirms,
|
|
27503
27638
|
saveManifest,
|
|
27504
27639
|
saveProviders,
|
|
27640
|
+
scheduleOwnerlessEmptySessionCleanup,
|
|
27505
27641
|
seedContextMeta,
|
|
27506
27642
|
send,
|
|
27507
27643
|
sendResult2 as sendResult,
|