@wrongstack/webui-server 0.306.2 → 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
|
@@ -95,6 +95,8 @@ export declare class CollaborationWebSocketHandler {
|
|
|
95
95
|
*/
|
|
96
96
|
bus?: CollaborationBus | undefined, options?: CollaborationHandlerOptions);
|
|
97
97
|
addClient(ws: WebSocket): void;
|
|
98
|
+
/** True while at least one collaboration participant is attached to a session. */
|
|
99
|
+
hasParticipants(sessionId: string): boolean;
|
|
98
100
|
dispose(): void;
|
|
99
101
|
/**
|
|
100
102
|
* Dispatch a parsed client message. Returns true when the message was
|
package/dist/server/entry.js
CHANGED
|
@@ -1167,6 +1167,102 @@ function patchConfig(config, updates) {
|
|
|
1167
1167
|
return Object.freeze({ ...config, ...updates });
|
|
1168
1168
|
}
|
|
1169
1169
|
|
|
1170
|
+
// src/server/session-cleanup-scheduler.ts
|
|
1171
|
+
import { toErrorMessage } from "@wrongstack/core/utils";
|
|
1172
|
+
|
|
1173
|
+
// src/server/session-deletion.ts
|
|
1174
|
+
async function deleteWebUISession(ctx, sessionId) {
|
|
1175
|
+
if (sessionId === ctx.getActiveSessionId()) {
|
|
1176
|
+
throw new Error("Cannot delete the active session");
|
|
1177
|
+
}
|
|
1178
|
+
await ctx.getSessionStore().delete(sessionId);
|
|
1179
|
+
await ctx.refreshSessions().catch(() => void 0);
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
// src/server/session-cleanup-scheduler.ts
|
|
1183
|
+
var DEFAULT_EMPTY_SESSION_CLEANUP_INTERVAL_MS = 60 * 60 * 1e3;
|
|
1184
|
+
var EMPTY_SESSION_CLEANUP_INTERVAL_ENV = "WRONGSTACK_EMPTY_SESSION_CLEANUP_INTERVAL_MS";
|
|
1185
|
+
var MAX_SESSION_SCAN = 1e3;
|
|
1186
|
+
function resolveEmptySessionCleanupInterval(value = process.env[EMPTY_SESSION_CLEANUP_INTERVAL_ENV]) {
|
|
1187
|
+
if (value === void 0 || value.trim() === "") return DEFAULT_EMPTY_SESSION_CLEANUP_INTERVAL_MS;
|
|
1188
|
+
const parsed = Number(value);
|
|
1189
|
+
return Number.isFinite(parsed) && parsed >= 1e3 ? Math.floor(parsed) : DEFAULT_EMPTY_SESSION_CLEANUP_INTERVAL_MS;
|
|
1190
|
+
}
|
|
1191
|
+
async function cleanupOwnerlessEmptySessions(ctx) {
|
|
1192
|
+
const store = ctx.getSessionStore();
|
|
1193
|
+
let deleted = 0;
|
|
1194
|
+
let errors = 0;
|
|
1195
|
+
let sessions;
|
|
1196
|
+
try {
|
|
1197
|
+
sessions = await store.list(MAX_SESSION_SCAN);
|
|
1198
|
+
} catch (error2) {
|
|
1199
|
+
errors++;
|
|
1200
|
+
ctx.logger.error("Empty session cleanup failed to list sessions", {
|
|
1201
|
+
event: "webui.empty_session_cleanup_error",
|
|
1202
|
+
phase: "list",
|
|
1203
|
+
message: toErrorMessage(error2)
|
|
1204
|
+
});
|
|
1205
|
+
ctx.logger.info("Empty session cleanup completed", {
|
|
1206
|
+
event: "webui.empty_session_cleanup_completed",
|
|
1207
|
+
deleted,
|
|
1208
|
+
errors
|
|
1209
|
+
});
|
|
1210
|
+
return { deleted, errors };
|
|
1211
|
+
}
|
|
1212
|
+
for (const candidate of sessions) {
|
|
1213
|
+
const sessionId = candidate.id;
|
|
1214
|
+
if (sessionId === ctx.getActiveSessionId() || ctx.hasParticipants(sessionId)) continue;
|
|
1215
|
+
try {
|
|
1216
|
+
if (!store.isEmpty || !await store.isEmpty(sessionId)) continue;
|
|
1217
|
+
if (store !== ctx.getSessionStore() || sessionId === ctx.getActiveSessionId() || ctx.hasParticipants(sessionId)) {
|
|
1218
|
+
continue;
|
|
1219
|
+
}
|
|
1220
|
+
await deleteWebUISession(
|
|
1221
|
+
{
|
|
1222
|
+
getActiveSessionId: ctx.getActiveSessionId,
|
|
1223
|
+
getSessionStore: () => store,
|
|
1224
|
+
refreshSessions: ctx.refreshSessions
|
|
1225
|
+
},
|
|
1226
|
+
sessionId
|
|
1227
|
+
);
|
|
1228
|
+
deleted++;
|
|
1229
|
+
} catch (error2) {
|
|
1230
|
+
errors++;
|
|
1231
|
+
ctx.logger.error("Empty session cleanup failed for session", {
|
|
1232
|
+
event: "webui.empty_session_cleanup_error",
|
|
1233
|
+
phase: "delete",
|
|
1234
|
+
sessionId,
|
|
1235
|
+
message: toErrorMessage(error2)
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
ctx.logger.info("Empty session cleanup completed", {
|
|
1240
|
+
event: "webui.empty_session_cleanup_completed",
|
|
1241
|
+
deleted,
|
|
1242
|
+
errors
|
|
1243
|
+
});
|
|
1244
|
+
return { deleted, errors };
|
|
1245
|
+
}
|
|
1246
|
+
function scheduleOwnerlessEmptySessionCleanup(ctx, intervalMs = resolveEmptySessionCleanupInterval()) {
|
|
1247
|
+
let running = null;
|
|
1248
|
+
const runNow = () => {
|
|
1249
|
+
if (running) return running;
|
|
1250
|
+
running = cleanupOwnerlessEmptySessions(ctx).finally(() => {
|
|
1251
|
+
running = null;
|
|
1252
|
+
});
|
|
1253
|
+
return running;
|
|
1254
|
+
};
|
|
1255
|
+
const timer = setInterval(() => void runNow(), intervalMs);
|
|
1256
|
+
timer.unref?.();
|
|
1257
|
+
return {
|
|
1258
|
+
dispose: async () => {
|
|
1259
|
+
clearInterval(timer);
|
|
1260
|
+
await running;
|
|
1261
|
+
},
|
|
1262
|
+
runNow
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1170
1266
|
// src/server/brain-routes.ts
|
|
1171
1267
|
async function handleBrainRoute(ws, msg, handlers) {
|
|
1172
1268
|
switch (msg.type) {
|
|
@@ -1714,7 +1810,7 @@ function extractCodeMapFileTargets(projectRoot, toolName2, rawInput) {
|
|
|
1714
1810
|
|
|
1715
1811
|
// src/server/collaboration-ws-handler.ts
|
|
1716
1812
|
import { randomUUID } from "node:crypto";
|
|
1717
|
-
import { toErrorMessage } from "@wrongstack/core/utils";
|
|
1813
|
+
import { toErrorMessage as toErrorMessage2 } from "@wrongstack/core/utils";
|
|
1718
1814
|
var REPLAY_LIMIT = 50;
|
|
1719
1815
|
var PAUSE_TIMEOUT_MS = 6e4;
|
|
1720
1816
|
var CollaborationWebSocketHandler = class {
|
|
@@ -1749,6 +1845,10 @@ var CollaborationWebSocketHandler = class {
|
|
|
1749
1845
|
ws.on("close", () => this.handleDisconnect(ws));
|
|
1750
1846
|
ws.on("error", () => this.handleDisconnect(ws));
|
|
1751
1847
|
}
|
|
1848
|
+
/** True while at least one collaboration participant is attached to a session. */
|
|
1849
|
+
hasParticipants(sessionId) {
|
|
1850
|
+
return (this.bySession.get(sessionId)?.size ?? 0) > 0;
|
|
1851
|
+
}
|
|
1752
1852
|
dispose() {
|
|
1753
1853
|
for (const off of this.offs) off();
|
|
1754
1854
|
this.offs.length = 0;
|
|
@@ -1866,7 +1966,7 @@ var CollaborationWebSocketHandler = class {
|
|
|
1866
1966
|
this.broadcast(sessionId, this.stateMessage(sessionId));
|
|
1867
1967
|
if (this.reader) {
|
|
1868
1968
|
this.replayHistory(ws, sessionId).catch((err) => {
|
|
1869
|
-
this.logger.debug?.(`collab: replay failed for ${sessionId}: ${
|
|
1969
|
+
this.logger.debug?.(`collab: replay failed for ${sessionId}: ${toErrorMessage2(err)}`);
|
|
1870
1970
|
});
|
|
1871
1971
|
}
|
|
1872
1972
|
this.logger.debug?.(`collab: participant ${participant.participantId} joined ${sessionId}`);
|
|
@@ -1973,7 +2073,7 @@ var CollaborationWebSocketHandler = class {
|
|
|
1973
2073
|
}
|
|
1974
2074
|
});
|
|
1975
2075
|
} catch (err) {
|
|
1976
|
-
this.send(ws, this.errorMessage(`annotation rejected: ${
|
|
2076
|
+
this.send(ws, this.errorMessage(`annotation rejected: ${toErrorMessage2(err)}`));
|
|
1977
2077
|
}
|
|
1978
2078
|
}
|
|
1979
2079
|
async handleResolve(ws, raw) {
|
|
@@ -2025,7 +2125,7 @@ var CollaborationWebSocketHandler = class {
|
|
|
2025
2125
|
}
|
|
2026
2126
|
});
|
|
2027
2127
|
} catch (err) {
|
|
2028
|
-
this.send(ws, this.errorMessage(`resolve failed: ${
|
|
2128
|
+
this.send(ws, this.errorMessage(`resolve failed: ${toErrorMessage2(err)}`));
|
|
2029
2129
|
}
|
|
2030
2130
|
}
|
|
2031
2131
|
// ── Event subscription (live mirror) ───────────────────────────────────
|
|
@@ -2096,7 +2196,7 @@ var CollaborationWebSocketHandler = class {
|
|
|
2096
2196
|
seen++;
|
|
2097
2197
|
}
|
|
2098
2198
|
} catch (err) {
|
|
2099
|
-
this.logger.debug?.(`collab: session reader rejected ${sessionId}: ${
|
|
2199
|
+
this.logger.debug?.(`collab: session reader rejected ${sessionId}: ${toErrorMessage2(err)}`);
|
|
2100
2200
|
return;
|
|
2101
2201
|
}
|
|
2102
2202
|
const tail2 = seen <= REPLAY_LIMIT ? ring.slice(0, seen) : (
|
|
@@ -2179,7 +2279,7 @@ var CollaborationWebSocketHandler = class {
|
|
|
2179
2279
|
try {
|
|
2180
2280
|
sendSerialized(p.ws, data);
|
|
2181
2281
|
} catch (err) {
|
|
2182
|
-
this.logger.debug?.(`collab broadcast failed: ${
|
|
2282
|
+
this.logger.debug?.(`collab broadcast failed: ${toErrorMessage2(err)}`);
|
|
2183
2283
|
}
|
|
2184
2284
|
}
|
|
2185
2285
|
}
|
|
@@ -6824,7 +6924,7 @@ import {
|
|
|
6824
6924
|
PhaseOrchestrator,
|
|
6825
6925
|
PhaseStore
|
|
6826
6926
|
} from "@wrongstack/core/goal";
|
|
6827
|
-
import { toErrorMessage as
|
|
6927
|
+
import { toErrorMessage as toErrorMessage3 } from "@wrongstack/core/utils";
|
|
6828
6928
|
import { WorktreeManager } from "@wrongstack/core/worktree";
|
|
6829
6929
|
|
|
6830
6930
|
// src/server/git-process.ts
|
|
@@ -7077,7 +7177,7 @@ var GoalWebSocketHandler = class {
|
|
|
7077
7177
|
sendResult7(result);
|
|
7078
7178
|
} catch (err) {
|
|
7079
7179
|
if (mySeq !== this.assessSeq) return;
|
|
7080
|
-
this.logger.error(`[Goal] Assessment failed: ${
|
|
7180
|
+
this.logger.error(`[Goal] Assessment failed: ${toErrorMessage3(err)}`);
|
|
7081
7181
|
sendResult7({
|
|
7082
7182
|
realistic: true,
|
|
7083
7183
|
durationClaimed: null,
|
|
@@ -7086,7 +7186,7 @@ var GoalWebSocketHandler = class {
|
|
|
7086
7186
|
concerns: [],
|
|
7087
7187
|
raw: "",
|
|
7088
7188
|
parseFailed: true,
|
|
7089
|
-
parseError: `Assessment error: ${
|
|
7189
|
+
parseError: `Assessment error: ${toErrorMessage3(err)}`
|
|
7090
7190
|
});
|
|
7091
7191
|
}
|
|
7092
7192
|
}
|
|
@@ -7217,7 +7317,7 @@ Run npx tsc --noEmit to verify the fix. Output the fixed file paths.`;
|
|
|
7217
7317
|
);
|
|
7218
7318
|
this.broadcastState();
|
|
7219
7319
|
}).catch((err) => {
|
|
7220
|
-
this.logger.error(`[Goal] Aborted: ${
|
|
7320
|
+
this.logger.error(`[Goal] Aborted: ${toErrorMessage3(err)}`);
|
|
7221
7321
|
this.stopBroadcast();
|
|
7222
7322
|
this.broadcast({ type: "goal.failed", payload: { title, error: String(err) } });
|
|
7223
7323
|
});
|
|
@@ -7343,7 +7443,7 @@ Run npx tsc --noEmit to verify the fix. Output the fixed file paths.`;
|
|
|
7343
7443
|
}
|
|
7344
7444
|
this.logger.info(`[Goal] Planner produced no phases; using defaults for: ${goal}`);
|
|
7345
7445
|
} catch (err) {
|
|
7346
|
-
this.logger.error(`[Goal] Planning failed, using defaults: ${
|
|
7446
|
+
this.logger.error(`[Goal] Planning failed, using defaults: ${toErrorMessage3(err)}`);
|
|
7347
7447
|
}
|
|
7348
7448
|
return this.defaultPhases();
|
|
7349
7449
|
}
|
|
@@ -7404,7 +7504,7 @@ ${result_.finalText.slice(0, 2e3)}`
|
|
|
7404
7504
|
);
|
|
7405
7505
|
}
|
|
7406
7506
|
} catch (err) {
|
|
7407
|
-
this.logger.warn(`[Goal] Chimera review failed for "${task.title}": ${
|
|
7507
|
+
this.logger.warn(`[Goal] Chimera review failed for "${task.title}": ${toErrorMessage3(err)}`);
|
|
7408
7508
|
}
|
|
7409
7509
|
}
|
|
7410
7510
|
/**
|
|
@@ -9783,6 +9883,16 @@ function createHttpServer(opts) {
|
|
|
9783
9883
|
res.end("Unauthorized");
|
|
9784
9884
|
return;
|
|
9785
9885
|
}
|
|
9886
|
+
const isMutatingApiRequest = url.pathname.startsWith("/api/") && (req.method === "POST" || req.method === "PUT" || req.method === "PATCH" || req.method === "DELETE");
|
|
9887
|
+
if (isMutatingApiRequest && !req.headers.origin && !accessTokenOk) {
|
|
9888
|
+
res.writeHead(401, { "Content-Type": "application/json", "Cache-Control": "no-store" });
|
|
9889
|
+
res.end(
|
|
9890
|
+
JSON.stringify({
|
|
9891
|
+
error: "Unauthorized: a request without an Origin header must present the access token (X-WS-Token header or ws_token cookie)."
|
|
9892
|
+
})
|
|
9893
|
+
);
|
|
9894
|
+
return;
|
|
9895
|
+
}
|
|
9786
9896
|
if (shouldSetAuthCookie && opts.apiToken) {
|
|
9787
9897
|
setAuthCookieHeaders(res, opts.apiToken, secureCookies);
|
|
9788
9898
|
}
|
|
@@ -13560,7 +13670,7 @@ async function handleMemoryRoute(ctx, ws, message) {
|
|
|
13560
13670
|
|
|
13561
13671
|
// src/server/mode-operations.ts
|
|
13562
13672
|
import { ToolValidationError as ToolValidationError3 } from "@wrongstack/core/types";
|
|
13563
|
-
import { toErrorMessage as
|
|
13673
|
+
import { toErrorMessage as toErrorMessage4 } from "@wrongstack/core/utils";
|
|
13564
13674
|
function sendResult4(context, ws, success, message) {
|
|
13565
13675
|
context.send(ws, { type: "key.operation_result", payload: { success, message } });
|
|
13566
13676
|
}
|
|
@@ -13592,7 +13702,7 @@ function createModeOperations(context) {
|
|
|
13592
13702
|
} catch (error2) {
|
|
13593
13703
|
context.send(ws, {
|
|
13594
13704
|
type: "modes.list",
|
|
13595
|
-
payload: { modes: [], activeId: "default", error:
|
|
13705
|
+
payload: { modes: [], activeId: "default", error: toErrorMessage4(error2) }
|
|
13596
13706
|
});
|
|
13597
13707
|
}
|
|
13598
13708
|
},
|
|
@@ -13624,7 +13734,7 @@ function createModeOperations(context) {
|
|
|
13624
13734
|
await context.afterSwitch?.(id);
|
|
13625
13735
|
sendResult4(context, ws, true, `Switched to mode "${id}"`);
|
|
13626
13736
|
} catch (error2) {
|
|
13627
|
-
sendResult4(context, ws, false,
|
|
13737
|
+
sendResult4(context, ws, false, toErrorMessage4(error2));
|
|
13628
13738
|
}
|
|
13629
13739
|
}
|
|
13630
13740
|
};
|
|
@@ -13671,7 +13781,7 @@ import {
|
|
|
13671
13781
|
resolveConfiguredRefinerRef,
|
|
13672
13782
|
resolveEnhanceFallbackRef
|
|
13673
13783
|
} from "@wrongstack/core/execution";
|
|
13674
|
-
import { toErrorMessage as
|
|
13784
|
+
import { toErrorMessage as toErrorMessage5 } from "@wrongstack/core/utils";
|
|
13675
13785
|
function sendResult5(context, ws, payload, legacyResult = false) {
|
|
13676
13786
|
const message = { type: "model.switch_result", payload };
|
|
13677
13787
|
if (payload.success && context.broadcast) context.broadcast(message);
|
|
@@ -13729,7 +13839,7 @@ function createModelOperations(context) {
|
|
|
13729
13839
|
{
|
|
13730
13840
|
...requestId ? { requestId } : {},
|
|
13731
13841
|
success: false,
|
|
13732
|
-
message: `Switch failed: ${
|
|
13842
|
+
message: `Switch failed: ${toErrorMessage5(error2)}`,
|
|
13733
13843
|
provider,
|
|
13734
13844
|
model,
|
|
13735
13845
|
previousProvider,
|
|
@@ -13776,7 +13886,7 @@ function createModelOperations(context) {
|
|
|
13776
13886
|
payload: {
|
|
13777
13887
|
refined: text,
|
|
13778
13888
|
english: text,
|
|
13779
|
-
error: `Cannot use ${payload.provider}/${payload.model}: ${
|
|
13889
|
+
error: `Cannot use ${payload.provider}/${payload.model}: ${toErrorMessage5(error2)}`,
|
|
13780
13890
|
errorKind: "provider_error",
|
|
13781
13891
|
...fallbackRef ? { fallbackRef } : {}
|
|
13782
13892
|
}
|
|
@@ -13874,7 +13984,7 @@ function createModelOperations(context) {
|
|
|
13874
13984
|
JSON.stringify({
|
|
13875
13985
|
level: "error",
|
|
13876
13986
|
event: "model.refine.error",
|
|
13877
|
-
error:
|
|
13987
|
+
error: toErrorMessage5(error2),
|
|
13878
13988
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
13879
13989
|
})
|
|
13880
13990
|
);
|
|
@@ -13883,7 +13993,7 @@ function createModelOperations(context) {
|
|
|
13883
13993
|
payload: {
|
|
13884
13994
|
refined: text,
|
|
13885
13995
|
english: text,
|
|
13886
|
-
error:
|
|
13996
|
+
error: toErrorMessage5(error2),
|
|
13887
13997
|
errorKind: "provider_error",
|
|
13888
13998
|
...fallbackRef ? { fallbackRef } : {}
|
|
13889
13999
|
}
|
|
@@ -14002,7 +14112,7 @@ function formatExternalAccessUrls(opts) {
|
|
|
14002
14112
|
|
|
14003
14113
|
// src/server/brain-handlers.ts
|
|
14004
14114
|
import { BUILTIN_COUNCIL_PERSONAS } from "@wrongstack/core/execution";
|
|
14005
|
-
import { toErrorMessage as
|
|
14115
|
+
import { toErrorMessage as toErrorMessage6 } from "@wrongstack/core/utils";
|
|
14006
14116
|
var COUNCIL_PERSONA_CATALOG = Object.freeze(
|
|
14007
14117
|
BUILTIN_COUNCIL_PERSONAS.map(
|
|
14008
14118
|
(persona) => Object.freeze({
|
|
@@ -14091,7 +14201,7 @@ async function handleBrainConfigSet(ctx, ws, payload) {
|
|
|
14091
14201
|
payload: {
|
|
14092
14202
|
config: brainConfigPayload(ctx.brainRuntime),
|
|
14093
14203
|
persisted: false,
|
|
14094
|
-
error: `Invalid Brain setting: ${
|
|
14204
|
+
error: `Invalid Brain setting: ${toErrorMessage6(err)}`
|
|
14095
14205
|
}
|
|
14096
14206
|
});
|
|
14097
14207
|
}
|
|
@@ -14137,7 +14247,7 @@ async function handleBrainAsk(ctx, ws, question) {
|
|
|
14137
14247
|
payload: { sessionId: ctx.getSessionId?.(), question: q, decision }
|
|
14138
14248
|
});
|
|
14139
14249
|
} catch (err) {
|
|
14140
|
-
sendResult6(ctx, ws, false, `Brain consultation failed: ${
|
|
14250
|
+
sendResult6(ctx, ws, false, `Brain consultation failed: ${toErrorMessage6(err)}`);
|
|
14141
14251
|
}
|
|
14142
14252
|
}
|
|
14143
14253
|
|
|
@@ -14530,7 +14640,6 @@ function seedContextMeta(config, context) {
|
|
|
14530
14640
|
const hqConfig = config.hq;
|
|
14531
14641
|
meta["hqEnabled"] = hqConfig?.enabled === true;
|
|
14532
14642
|
meta["hqUrl"] = hqConfig?.url ?? "";
|
|
14533
|
-
meta["hqToken"] = hqConfig?.token ?? "";
|
|
14534
14643
|
meta["hqRawContent"] = hqConfig?.rawContent === true;
|
|
14535
14644
|
const tgExt = config.extensions?.["telegram"];
|
|
14536
14645
|
meta["tgConfigured"] = typeof tgExt?.["botToken"] === "string" && tgExt["botToken"].length > 0;
|
|
@@ -14624,7 +14733,6 @@ var PREF_KEYS = [
|
|
|
14624
14733
|
"auditLevel",
|
|
14625
14734
|
"hqEnabled",
|
|
14626
14735
|
"hqUrl",
|
|
14627
|
-
"hqToken",
|
|
14628
14736
|
"hqRawContent",
|
|
14629
14737
|
"tgConfigured",
|
|
14630
14738
|
"tgSessionEnd",
|
|
@@ -15421,7 +15529,8 @@ function createProjectHandlers(ctx) {
|
|
|
15421
15529
|
// src/server/provider-handlers.ts
|
|
15422
15530
|
import { hasProviderCredential, resolveProviderModelList } from "@wrongstack/core/models";
|
|
15423
15531
|
import { DefaultSecretScrubber as DefaultSecretScrubber2 } from "@wrongstack/core/security";
|
|
15424
|
-
import {
|
|
15532
|
+
import { validateProviderBaseUrl } from "@wrongstack/core/tools";
|
|
15533
|
+
import { toErrorMessage as toErrorMessage7 } from "@wrongstack/core/utils";
|
|
15425
15534
|
import {
|
|
15426
15535
|
beginOAuthLogin
|
|
15427
15536
|
} from "@wrongstack/providers/oauth";
|
|
@@ -16009,6 +16118,13 @@ function createProviderOperations(deps2) {
|
|
|
16009
16118
|
sendOperationResult(ws, false, `Unknown provider "${payload.id}"`);
|
|
16010
16119
|
return;
|
|
16011
16120
|
}
|
|
16121
|
+
if (payload.baseUrl !== void 0 && payload.baseUrl !== "") {
|
|
16122
|
+
const invalid = validateProviderBaseUrl(payload.baseUrl);
|
|
16123
|
+
if (invalid) {
|
|
16124
|
+
sendOperationResult(ws, false, invalid);
|
|
16125
|
+
return;
|
|
16126
|
+
}
|
|
16127
|
+
}
|
|
16012
16128
|
if (payload.family !== void 0) cfg.family = payload.family;
|
|
16013
16129
|
if (payload.baseUrl !== void 0) cfg.baseUrl = payload.baseUrl;
|
|
16014
16130
|
if (payload.envVars !== void 0) cfg.envVars = payload.envVars;
|
|
@@ -16189,7 +16305,7 @@ function createProviderHandlers(deps2) {
|
|
|
16189
16305
|
JSON.stringify({
|
|
16190
16306
|
level: "error",
|
|
16191
16307
|
event: "webui.provider_save_failed",
|
|
16192
|
-
message:
|
|
16308
|
+
message: toErrorMessage7(error2),
|
|
16193
16309
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
16194
16310
|
})
|
|
16195
16311
|
);
|
|
@@ -17591,21 +17707,21 @@ function createSessionHandlers(ctx) {
|
|
|
17591
17707
|
deleteSession: async (ws, msg) => {
|
|
17592
17708
|
const { id } = msg.payload;
|
|
17593
17709
|
try {
|
|
17594
|
-
|
|
17595
|
-
|
|
17596
|
-
|
|
17597
|
-
|
|
17598
|
-
|
|
17599
|
-
|
|
17710
|
+
await deleteWebUISession(
|
|
17711
|
+
{
|
|
17712
|
+
getActiveSessionId: () => ctx.getSession().id,
|
|
17713
|
+
getSessionStore: ctx.getSessionStore,
|
|
17714
|
+
refreshSessions: async () => {
|
|
17715
|
+
const list = await ctx.getSessionStore().list(200);
|
|
17716
|
+
broadcastToAll({
|
|
17717
|
+
type: "sessions.list",
|
|
17718
|
+
payload: { sessions: toSessionHistoryEntries(list, ctx.getSession().id) }
|
|
17719
|
+
});
|
|
17720
|
+
}
|
|
17721
|
+
},
|
|
17722
|
+
id
|
|
17723
|
+
);
|
|
17600
17724
|
result(ws, true, `Session ${id} deleted`);
|
|
17601
|
-
try {
|
|
17602
|
-
const list = await store.list(200);
|
|
17603
|
-
broadcastToAll({
|
|
17604
|
-
type: "sessions.list",
|
|
17605
|
-
payload: { sessions: toSessionHistoryEntries(list, ctx.getSession().id) }
|
|
17606
|
-
});
|
|
17607
|
-
} catch {
|
|
17608
|
-
}
|
|
17609
17725
|
} catch (err) {
|
|
17610
17726
|
result(ws, false, errMessage(err));
|
|
17611
17727
|
}
|
|
@@ -21259,7 +21375,7 @@ import {
|
|
|
21259
21375
|
expectDefined as expectDefined3,
|
|
21260
21376
|
sessionScopedPath as sessionScopedPath3,
|
|
21261
21377
|
startSharedHeapWatchdog,
|
|
21262
|
-
toErrorMessage as
|
|
21378
|
+
toErrorMessage as toErrorMessage14,
|
|
21263
21379
|
wstackGlobalRoot as wstackGlobalRoot2
|
|
21264
21380
|
} from "@wrongstack/core/utils";
|
|
21265
21381
|
import { makeProviderFromConfig as makeProviderFromConfig3 } from "@wrongstack/providers";
|
|
@@ -21296,7 +21412,7 @@ import {
|
|
|
21296
21412
|
} from "@wrongstack/core/types";
|
|
21297
21413
|
import {
|
|
21298
21414
|
estimateRequestTokensCalibrated,
|
|
21299
|
-
toErrorMessage as
|
|
21415
|
+
toErrorMessage as toErrorMessage10
|
|
21300
21416
|
} from "@wrongstack/core/utils";
|
|
21301
21417
|
import { makeLightSubagentFactory } from "@wrongstack/runtime";
|
|
21302
21418
|
import { getSageService, setupSage } from "@wrongstack/sage";
|
|
@@ -21452,7 +21568,7 @@ function sleep(ms) {
|
|
|
21452
21568
|
import { spawn as spawnChild } from "node:child_process";
|
|
21453
21569
|
import { createRequire as createRequire2 } from "node:module";
|
|
21454
21570
|
import { createCompatibilityTrustBoundary as createCompatibilityTrustBoundary2 } from "@wrongstack/core/security";
|
|
21455
|
-
import { buildChildEnv, toErrorMessage as
|
|
21571
|
+
import { buildChildEnv, toErrorMessage as toErrorMessage8 } from "@wrongstack/core/utils";
|
|
21456
21572
|
var MAX_SESSIONS_PER_CLIENT = 8;
|
|
21457
21573
|
var MAX_INPUT_BYTES = 1 << 16;
|
|
21458
21574
|
var DEFAULT_COLS = 80;
|
|
@@ -21568,8 +21684,8 @@ var TerminalWebSocketHandler = class {
|
|
|
21568
21684
|
...windowsPtyOptions()
|
|
21569
21685
|
});
|
|
21570
21686
|
} catch (err) {
|
|
21571
|
-
const msg = `Integrated terminal failed to start: ${
|
|
21572
|
-
this.logger.warn?.(`terminal spawn failed: ${
|
|
21687
|
+
const msg = `Integrated terminal failed to start: ${toErrorMessage8(err)}`;
|
|
21688
|
+
this.logger.warn?.(`terminal spawn failed: ${toErrorMessage8(err)}`);
|
|
21573
21689
|
this.send(ws, { type: "terminal.output", payload: { id: payload.id, data: `${msg}\r
|
|
21574
21690
|
` } });
|
|
21575
21691
|
this.send(ws, { type: "terminal.exit", payload: { id: payload.id, exitCode: -1 } });
|
|
@@ -21630,7 +21746,7 @@ var TerminalWebSocketHandler = class {
|
|
|
21630
21746
|
try {
|
|
21631
21747
|
pty.kill();
|
|
21632
21748
|
} catch (err) {
|
|
21633
|
-
this.logger.warn?.(`${reason} failed: ${
|
|
21749
|
+
this.logger.warn?.(`${reason} failed: ${toErrorMessage8(err)}`);
|
|
21634
21750
|
}
|
|
21635
21751
|
}
|
|
21636
21752
|
killWindowsProcessTree(pty, reason) {
|
|
@@ -21642,7 +21758,7 @@ var TerminalWebSocketHandler = class {
|
|
|
21642
21758
|
} catch {
|
|
21643
21759
|
}
|
|
21644
21760
|
} catch (err) {
|
|
21645
|
-
this.logger.warn?.(`${reason} taskkill failed: ${
|
|
21761
|
+
this.logger.warn?.(`${reason} taskkill failed: ${toErrorMessage8(err)}`);
|
|
21646
21762
|
}
|
|
21647
21763
|
}
|
|
21648
21764
|
send(ws, msg) {
|
|
@@ -21689,7 +21805,7 @@ function clampDim(value, fallback) {
|
|
|
21689
21805
|
|
|
21690
21806
|
// src/server/worktree-ws-handler.ts
|
|
21691
21807
|
import { join as join11, resolve as resolve13, sep as sep5 } from "node:path";
|
|
21692
|
-
import { toErrorMessage as
|
|
21808
|
+
import { toErrorMessage as toErrorMessage9 } from "@wrongstack/core/utils";
|
|
21693
21809
|
import { WorktreeManager as WorktreeManager3 } from "@wrongstack/core/worktree";
|
|
21694
21810
|
import { cleanupStaleSddWorktrees as cleanupStaleSddWorktrees2 } from "@wrongstack/sdd";
|
|
21695
21811
|
var MAX_ACTIVITY = 6;
|
|
@@ -21855,7 +21971,7 @@ var WorktreeWebSocketHandler = class {
|
|
|
21855
21971
|
}
|
|
21856
21972
|
});
|
|
21857
21973
|
} catch (err) {
|
|
21858
|
-
this.logger.debug?.(`worktree orphan scan failed: ${
|
|
21974
|
+
this.logger.debug?.(`worktree orphan scan failed: ${toErrorMessage9(err)}`);
|
|
21859
21975
|
this.broadcast({ type: "worktree.orphans", payload: { orphans: [], canClean: false } });
|
|
21860
21976
|
}
|
|
21861
21977
|
}
|
|
@@ -22117,7 +22233,7 @@ var WorktreeWebSocketHandler = class {
|
|
|
22117
22233
|
try {
|
|
22118
22234
|
sendSerialized(ws, data);
|
|
22119
22235
|
} catch (err) {
|
|
22120
|
-
this.logger.debug?.(`worktree broadcast failed: ${
|
|
22236
|
+
this.logger.debug?.(`worktree broadcast failed: ${toErrorMessage9(err)}`);
|
|
22121
22237
|
}
|
|
22122
22238
|
}
|
|
22123
22239
|
}
|
|
@@ -22226,7 +22342,7 @@ async function createAgentServices(input) {
|
|
|
22226
22342
|
const updateAutoCompactionMaxContext = async (newProvider, providerId = newProvider.id, providerCfg) => {
|
|
22227
22343
|
await modelsRegistry.refresh().catch((err) => {
|
|
22228
22344
|
logger.warn(
|
|
22229
|
-
`models.dev refresh failed for ${providerId}/${context.model}: ${
|
|
22345
|
+
`models.dev refresh failed for ${providerId}/${context.model}: ${toErrorMessage10(err)}; using cached catalog`
|
|
22230
22346
|
);
|
|
22231
22347
|
});
|
|
22232
22348
|
const currentConfig = input.config;
|
|
@@ -23068,7 +23184,7 @@ import {
|
|
|
23068
23184
|
import {
|
|
23069
23185
|
configureChildEnvGitIdentity,
|
|
23070
23186
|
sessionScopedPath as sessionScopedPath2,
|
|
23071
|
-
toErrorMessage as
|
|
23187
|
+
toErrorMessage as toErrorMessage12
|
|
23072
23188
|
} from "@wrongstack/core/utils";
|
|
23073
23189
|
import {
|
|
23074
23190
|
createVaultBackedMcpAuthorizationProviderFactory,
|
|
@@ -23150,7 +23266,7 @@ async function discoverAndMergeWebuiProviders(opts) {
|
|
|
23150
23266
|
|
|
23151
23267
|
// src/server/setup-screen.ts
|
|
23152
23268
|
import { expectDefined as expectDefined2 } from "@wrongstack/core/utils";
|
|
23153
|
-
import { toErrorMessage as
|
|
23269
|
+
import { toErrorMessage as toErrorMessage11 } from "@wrongstack/core/utils";
|
|
23154
23270
|
import { makeProviderFromConfig } from "@wrongstack/providers";
|
|
23155
23271
|
var UNCONFIGURED_CAPABILITIES = {
|
|
23156
23272
|
tools: false,
|
|
@@ -23191,7 +23307,7 @@ function logCreateFailure(event, err) {
|
|
|
23191
23307
|
JSON.stringify({
|
|
23192
23308
|
level: "error",
|
|
23193
23309
|
event,
|
|
23194
|
-
message:
|
|
23310
|
+
message: toErrorMessage11(err),
|
|
23195
23311
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
23196
23312
|
})
|
|
23197
23313
|
);
|
|
@@ -23500,7 +23616,7 @@ async function createPreContextServices(input) {
|
|
|
23500
23616
|
await modelsRegistry.refresh();
|
|
23501
23617
|
logger.info("models.dev catalog refreshed");
|
|
23502
23618
|
} catch (err) {
|
|
23503
|
-
logger.warn(`models.dev refresh failed (${
|
|
23619
|
+
logger.warn(`models.dev refresh failed (${toErrorMessage12(err)}); using cached catalog`);
|
|
23504
23620
|
}
|
|
23505
23621
|
}
|
|
23506
23622
|
try {
|
|
@@ -23511,7 +23627,7 @@ async function createPreContextServices(input) {
|
|
|
23511
23627
|
logger
|
|
23512
23628
|
});
|
|
23513
23629
|
} catch (err) {
|
|
23514
|
-
logger.debug(`provider auto-discovery skipped: ${
|
|
23630
|
+
logger.debug(`provider auto-discovery skipped: ${toErrorMessage12(err)}`);
|
|
23515
23631
|
}
|
|
23516
23632
|
try {
|
|
23517
23633
|
await installCatalogModelOutputLimits({
|
|
@@ -23520,7 +23636,7 @@ async function createPreContextServices(input) {
|
|
|
23520
23636
|
log: (message) => logger.debug(message)
|
|
23521
23637
|
});
|
|
23522
23638
|
} catch (err) {
|
|
23523
|
-
logger.debug(`model output-limit index skipped: ${
|
|
23639
|
+
logger.debug(`model output-limit index skipped: ${toErrorMessage12(err)}`);
|
|
23524
23640
|
}
|
|
23525
23641
|
const events = opts.services?.events ?? new EventBus();
|
|
23526
23642
|
events.setLogger(logger);
|
|
@@ -23539,7 +23655,7 @@ async function createPreContextServices(input) {
|
|
|
23539
23655
|
JSON.stringify({
|
|
23540
23656
|
level: "warn",
|
|
23541
23657
|
event: "webui.provider_registry_load_failed",
|
|
23542
|
-
message:
|
|
23658
|
+
message: toErrorMessage12(err),
|
|
23543
23659
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
23544
23660
|
})
|
|
23545
23661
|
);
|
|
@@ -24174,7 +24290,7 @@ function buildRoutes(state, deps2, cb) {
|
|
|
24174
24290
|
import { createRequire as createRequire4 } from "node:module";
|
|
24175
24291
|
import * as path28 from "node:path";
|
|
24176
24292
|
import { fileURLToPath } from "node:url";
|
|
24177
|
-
import { toErrorMessage as
|
|
24293
|
+
import { toErrorMessage as toErrorMessage13 } from "@wrongstack/core/utils";
|
|
24178
24294
|
import { WebSocketServer } from "ws";
|
|
24179
24295
|
async function resolvePorts(opts) {
|
|
24180
24296
|
const surface = opts.surface ?? "webui";
|
|
@@ -24325,7 +24441,7 @@ function armEvents(wssPrimary, wssSecondary, wsHost, httpPort, setupInput, watch
|
|
|
24325
24441
|
level: "error",
|
|
24326
24442
|
event: "webui.ws_server_error",
|
|
24327
24443
|
host: wsHost,
|
|
24328
|
-
message:
|
|
24444
|
+
message: toErrorMessage13(err),
|
|
24329
24445
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
24330
24446
|
})
|
|
24331
24447
|
);
|
|
@@ -24972,7 +25088,7 @@ async function startWebUI(opts = {}) {
|
|
|
24972
25088
|
console.log(`[WebUI] Provider credentials reloaded from config.json (${activeId})`);
|
|
24973
25089
|
} catch (err) {
|
|
24974
25090
|
console.warn(
|
|
24975
|
-
`[WebUI] Credential hot-reload failed for ${activeId}: ${
|
|
25091
|
+
`[WebUI] Credential hot-reload failed for ${activeId}: ${toErrorMessage14(err)}`
|
|
24976
25092
|
);
|
|
24977
25093
|
}
|
|
24978
25094
|
},
|
|
@@ -24995,6 +25111,19 @@ async function startWebUI(opts = {}) {
|
|
|
24995
25111
|
})
|
|
24996
25112
|
});
|
|
24997
25113
|
const routes = buildRoutes(state, deps2, cb);
|
|
25114
|
+
const stopEmptySessionCleanup = scheduleOwnerlessEmptySessionCleanup({
|
|
25115
|
+
getSessionStore: state.getSessionStore,
|
|
25116
|
+
getActiveSessionId: () => state.getSession().id,
|
|
25117
|
+
hasParticipants: (sessionId) => collabHandler.hasParticipants(sessionId),
|
|
25118
|
+
refreshSessions: async () => {
|
|
25119
|
+
const list = await state.getSessionStore().list(200);
|
|
25120
|
+
broadcast(clients, {
|
|
25121
|
+
type: "sessions.list",
|
|
25122
|
+
payload: { sessions: toSessionHistoryEntries(list, state.getSession().id) }
|
|
25123
|
+
});
|
|
25124
|
+
},
|
|
25125
|
+
logger
|
|
25126
|
+
});
|
|
24998
25127
|
let kanbanSupervisorDispose = null;
|
|
24999
25128
|
const handleMessage = createMessageDispatcher({
|
|
25000
25129
|
state,
|
|
@@ -25079,7 +25208,8 @@ projectRoot: ${ev.projectRoot ?? "?"}`,
|
|
|
25079
25208
|
wssPrimary,
|
|
25080
25209
|
...wssSecondary ? [wssSecondary] : []
|
|
25081
25210
|
],
|
|
25082
|
-
onPreShutdown: () => {
|
|
25211
|
+
onPreShutdown: async () => {
|
|
25212
|
+
await stopEmptySessionCleanup.dispose();
|
|
25083
25213
|
kanbanSupervisorDispose?.();
|
|
25084
25214
|
kanbanSupervisorDispose = null;
|
|
25085
25215
|
},
|
|
@@ -25106,9 +25236,9 @@ projectRoot: ${ev.projectRoot ?? "?"}`,
|
|
|
25106
25236
|
eternalSubscription = null;
|
|
25107
25237
|
}
|
|
25108
25238
|
codebaseIndexing.dispose();
|
|
25109
|
-
await agentServices.runSageSessionHygiene().catch((err) => logger.warn(`sage session hygiene failed: ${
|
|
25239
|
+
await agentServices.runSageSessionHygiene().catch((err) => logger.warn(`sage session hygiene failed: ${toErrorMessage14(err)}`));
|
|
25110
25240
|
await memoryStore.dispose().catch(
|
|
25111
|
-
(err) => logger.warn(`sage connection disposal failed: ${
|
|
25241
|
+
(err) => logger.warn(`sage connection disposal failed: ${toErrorMessage14(err)}`)
|
|
25112
25242
|
);
|
|
25113
25243
|
await unregisterInstance(process.pid, path29.dirname(globalConfigPath));
|
|
25114
25244
|
}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
*/
|
|
11
11
|
export { type AutonomyRouteHandlers, createAutonomyRouteHandlers, handleAutonomyRoute, } from './autonomy-routes.js';
|
|
12
12
|
export { bootConfig, patchConfig } from './boot.js';
|
|
13
|
+
export { cleanupOwnerlessEmptySessions, DEFAULT_EMPTY_SESSION_CLEANUP_INTERVAL_MS, EMPTY_SESSION_CLEANUP_INTERVAL_ENV, resolveEmptySessionCleanupInterval, scheduleOwnerlessEmptySessionCleanup, } from './session-cleanup-scheduler.js';
|
|
14
|
+
export { deleteWebUISession } from './session-deletion.js';
|
|
13
15
|
export type { BrainRouteHandlers } from './brain-routes.js';
|
|
14
16
|
export { handleBrainRoute } from './brain-routes.js';
|
|
15
17
|
export { type ChronicleRouteContext, handleChronicleRoute, } from './chronicle-routes.js';
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import type { SecretVault } from '@wrongstack/core/types';
|
|
19
19
|
/** Pref keys exposed to the settings panel via prefs.get / prefs.updated. */
|
|
20
|
-
export declare const PREF_KEYS: readonly ['autonomy', 'autonomyDelayMs', 'autoProceedMaxIterations', 'yolo', 'maxIterations', 'chime', 'confirmExit', 'nextPrediction', 'nextStepsTool', 'enhanceEnabled', 'enhanceDelayMs', 'enhanceLanguage', 'featureMcp', 'featurePlugins', 'featureMemory', 'featureSkills', 'featureModelsRegistry', 'indexOnStart', 'contextAutoCompact', 'contextStrategy', 'contextMode', 'tokenSavingTier', 'maxConcurrent', 'titleAnimation', 'uiLocale', 'logLevel', 'auditLevel', 'hqEnabled', 'hqUrl', '
|
|
20
|
+
export declare const PREF_KEYS: readonly ['autonomy', 'autonomyDelayMs', 'autoProceedMaxIterations', 'yolo', 'maxIterations', 'chime', 'confirmExit', 'nextPrediction', 'nextStepsTool', 'enhanceEnabled', 'enhanceDelayMs', 'enhanceLanguage', 'featureMcp', 'featurePlugins', 'featureMemory', 'featureSkills', 'featureModelsRegistry', 'indexOnStart', 'contextAutoCompact', 'contextStrategy', 'contextMode', 'tokenSavingTier', 'maxConcurrent', 'titleAnimation', 'uiLocale', 'logLevel', 'auditLevel', 'hqEnabled', 'hqUrl', 'hqRawContent', 'tgConfigured', 'tgSessionEnd', 'tgDelegate', 'tgLongToolMs', 'reasoningMode', 'reasoningEffort', 'reasoningPreserve', 'cacheTtl', 'fallbackModels', 'fallbackProfiles', 'favoriteModels', 'favoriteModelsOnly', 'modelAvailabilitySchedule', 'modelMatrix', 'fallbackAuto', 'refinerProvider', 'refinerModel', 'refinerFallbackProfile', 'thinkingWord', 'statuslineMode', 'animationStyle', 'showModelReasoning', 'breakerEnabled', 'breakerAutoKillResetMs', 'fsAccess', 'debugStream', 'chimeraEnabled', 'chimeraProvider', 'chimeraModel', 'chimeraMaxFiles', 'chimeraAutoFix', 'autoReviewEnabled', 'autoReviewProvider', 'autoReviewModel', 'autoReviewFallbackProfile', 'autoReviewModelSelection', 'autoReviewFallbackModels', 'autoReviewDebounceMs', 'autoReviewMaxFilesPerBatch', 'autoReviewMaxConcurrentReviews', 'autoReviewCascadeOn', 'groupToolCalls', 'showThinkingLogs', 'autoCollapseInput', 'pluginsEnabled', 'fleetChatVerbosity'];
|
|
21
21
|
export interface PrefHelperDeps {
|
|
22
22
|
/** Path to the active profile config; the sole settings mutation target. */
|
|
23
23
|
profileConfigPath: string;
|
|
@@ -14,7 +14,7 @@ import type { Config, ModelsRegistry } from '@wrongstack/core/types';
|
|
|
14
14
|
import { type WebSocket, WebSocketServer } from 'ws';
|
|
15
15
|
import { type FileWatcherMetrics, setupEvents } from './setup-events.js';
|
|
16
16
|
import type { ConnectedClient } from './types.js';
|
|
17
|
-
interface ResolvedPorts {
|
|
17
|
+
export interface ResolvedPorts {
|
|
18
18
|
wsHost: string;
|
|
19
19
|
httpPort: number;
|
|
20
20
|
publicUrl: string | undefined;
|