@wrongstack/webui-server 0.285.0 → 0.286.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/index.js +827 -301
- package/dist/index.js.map +4 -4
- package/dist/server/backend-services.d.ts +12 -23
- package/dist/server/backend-services.d.ts.map +1 -1
- package/dist/server/brain-routes.d.ts +2 -0
- package/dist/server/brain-routes.d.ts.map +1 -1
- package/dist/server/collaboration-ws-handler.d.ts +27 -19
- package/dist/server/collaboration-ws-handler.d.ts.map +1 -1
- package/dist/server/context-meta.d.ts.map +1 -1
- package/dist/server/entry.js +823 -301
- package/dist/server/entry.js.map +4 -4
- package/dist/server/handlers.js.map +2 -2
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/kanban-routes.d.ts.map +1 -1
- package/dist/server/mailbox-handlers.d.ts +12 -0
- package/dist/server/mailbox-handlers.d.ts.map +1 -1
- package/dist/server/mailbox-routes.d.ts +1 -0
- package/dist/server/mailbox-routes.d.ts.map +1 -1
- package/dist/server/mcp-handlers.d.ts +10 -1
- package/dist/server/mcp-handlers.d.ts.map +1 -1
- package/dist/server/mcp-routes.d.ts +4 -0
- package/dist/server/mcp-routes.d.ts.map +1 -1
- package/dist/server/message-dispatcher.d.ts.map +1 -1
- package/dist/server/pre-context-services.d.ts +3 -1
- package/dist/server/pre-context-services.d.ts.map +1 -1
- package/dist/server/pref-helpers.d.ts +1 -1
- package/dist/server/pref-helpers.d.ts.map +1 -1
- package/dist/server/routes.d.ts +6 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/server-runtime.d.ts.map +1 -1
- package/dist/server/session-handlers.d.ts +13 -0
- package/dist/server/session-handlers.d.ts.map +1 -1
- package/dist/server/setup-events.d.ts +1 -1
- package/dist/server/setup-events.d.ts.map +1 -1
- package/dist/server/standalone-session-identity.d.ts +35 -0
- package/dist/server/standalone-session-identity.d.ts.map +1 -0
- package/dist/server/start-webui.d.ts.map +1 -1
- package/dist/server/ws-payload-validation.d.ts +4 -0
- package/dist/server/ws-payload-validation.d.ts.map +1 -1
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -2996,7 +2996,7 @@ function mapStatus(raw) {
|
|
|
2996
2996
|
return "stopped";
|
|
2997
2997
|
}
|
|
2998
2998
|
}
|
|
2999
|
-
function toView(info) {
|
|
2999
|
+
function toView(info, health) {
|
|
3000
3000
|
const view = {
|
|
3001
3001
|
name: info.name,
|
|
3002
3002
|
transport: info.transport,
|
|
@@ -3008,6 +3008,7 @@ function toView(info) {
|
|
|
3008
3008
|
};
|
|
3009
3009
|
if (info.description !== void 0) view.description = info.description;
|
|
3010
3010
|
if (info.lazy !== void 0) view.lazy = info.lazy;
|
|
3011
|
+
if (health !== void 0) view.health = health;
|
|
3011
3012
|
return view;
|
|
3012
3013
|
}
|
|
3013
3014
|
function deps(ws, globalConfigPath, registry) {
|
|
@@ -3033,7 +3034,13 @@ async function handleMcpList(ws, _msg, globalConfigPath, mcpRegistry) {
|
|
|
3033
3034
|
registry: mcpRegistry,
|
|
3034
3035
|
presets: allServers()
|
|
3035
3036
|
});
|
|
3036
|
-
|
|
3037
|
+
const health = new Map(
|
|
3038
|
+
(typeof mcpRegistry.operationalHealth === "function" ? mcpRegistry.operationalHealth() : []).map((item) => [item.name, item])
|
|
3039
|
+
);
|
|
3040
|
+
send(ws, {
|
|
3041
|
+
type: "mcp.list",
|
|
3042
|
+
payload: { servers: servers.map((server) => toView(server, health.get(server.name))) }
|
|
3043
|
+
});
|
|
3037
3044
|
}
|
|
3038
3045
|
async function handleMcpAdd(ws, msg, globalConfigPath, mcpRegistry) {
|
|
3039
3046
|
const d = deps(ws, globalConfigPath, mcpRegistry);
|
|
@@ -3183,6 +3190,99 @@ async function handleMcpDiscover(ws, msg, globalConfigPath, mcpRegistry) {
|
|
|
3183
3190
|
payload: { success: result.ok, message: result.message }
|
|
3184
3191
|
});
|
|
3185
3192
|
}
|
|
3193
|
+
async function handleMcpResources(ws, msg, _globalConfigPath, mcpRegistry) {
|
|
3194
|
+
if (!mcpRegistry) return sendContentError(ws, "resources", "", "MCP registry is not available.");
|
|
3195
|
+
const payload = payloadRecord(msg);
|
|
3196
|
+
let serverName = "";
|
|
3197
|
+
try {
|
|
3198
|
+
serverName = requiredPayloadString(payload, "name");
|
|
3199
|
+
const refresh = payload["refresh"] === true;
|
|
3200
|
+
const [resources, resourceTemplates] = await Promise.all([
|
|
3201
|
+
mcpRegistry.listResources(serverName, { refresh }),
|
|
3202
|
+
mcpRegistry.listResourceTemplates(serverName, { refresh })
|
|
3203
|
+
]);
|
|
3204
|
+
send(ws, {
|
|
3205
|
+
type: "mcp.resources",
|
|
3206
|
+
payload: { name: serverName, resources, resourceTemplates }
|
|
3207
|
+
});
|
|
3208
|
+
} catch (err) {
|
|
3209
|
+
sendContentError(ws, "resources", serverName, errorMessage(err));
|
|
3210
|
+
}
|
|
3211
|
+
}
|
|
3212
|
+
async function handleMcpPrompts(ws, msg, _globalConfigPath, mcpRegistry) {
|
|
3213
|
+
if (!mcpRegistry) return sendContentError(ws, "prompts", "", "MCP registry is not available.");
|
|
3214
|
+
const payload = payloadRecord(msg);
|
|
3215
|
+
let serverName = "";
|
|
3216
|
+
try {
|
|
3217
|
+
serverName = requiredPayloadString(payload, "name");
|
|
3218
|
+
const prompts = await mcpRegistry.listPrompts(serverName, {
|
|
3219
|
+
refresh: payload["refresh"] === true
|
|
3220
|
+
});
|
|
3221
|
+
send(ws, { type: "mcp.prompts", payload: { name: serverName, prompts } });
|
|
3222
|
+
} catch (err) {
|
|
3223
|
+
sendContentError(ws, "prompts", serverName, errorMessage(err));
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
async function handleMcpResourceRead(ws, msg, _globalConfigPath, mcpRegistry) {
|
|
3227
|
+
if (!mcpRegistry)
|
|
3228
|
+
return sendContentError(ws, "resource.read", "", "MCP registry is not available.");
|
|
3229
|
+
const payload = payloadRecord(msg);
|
|
3230
|
+
let serverName = "";
|
|
3231
|
+
try {
|
|
3232
|
+
serverName = requiredPayloadString(payload, "name");
|
|
3233
|
+
const insertion = await mcpRegistry.selectResourceForInsertion(
|
|
3234
|
+
serverName,
|
|
3235
|
+
requiredPayloadString(payload, "uri")
|
|
3236
|
+
);
|
|
3237
|
+
send(ws, { type: "mcp.content.selected", payload: insertion });
|
|
3238
|
+
} catch (err) {
|
|
3239
|
+
sendContentError(ws, "resource.read", serverName, errorMessage(err));
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3242
|
+
async function handleMcpPromptGet(ws, msg, _globalConfigPath, mcpRegistry) {
|
|
3243
|
+
if (!mcpRegistry) return sendContentError(ws, "prompt.get", "", "MCP registry is not available.");
|
|
3244
|
+
const payload = payloadRecord(msg);
|
|
3245
|
+
let serverName = "";
|
|
3246
|
+
try {
|
|
3247
|
+
serverName = requiredPayloadString(payload, "name");
|
|
3248
|
+
const insertion = await mcpRegistry.selectPromptForInsertion(
|
|
3249
|
+
serverName,
|
|
3250
|
+
requiredPayloadString(payload, "prompt"),
|
|
3251
|
+
promptArguments(payload["arguments"])
|
|
3252
|
+
);
|
|
3253
|
+
send(ws, { type: "mcp.content.selected", payload: insertion });
|
|
3254
|
+
} catch (err) {
|
|
3255
|
+
sendContentError(ws, "prompt.get", serverName, errorMessage(err));
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
function payloadRecord(msg) {
|
|
3259
|
+
return msg.payload && typeof msg.payload === "object" && !Array.isArray(msg.payload) ? msg.payload : {};
|
|
3260
|
+
}
|
|
3261
|
+
function requiredPayloadString(payload, field) {
|
|
3262
|
+
const value = payload[field];
|
|
3263
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
3264
|
+
throw new Error(`MCP payload field "${field}" must be a non-empty string`);
|
|
3265
|
+
}
|
|
3266
|
+
return value;
|
|
3267
|
+
}
|
|
3268
|
+
function promptArguments(value) {
|
|
3269
|
+
if (value === void 0) return void 0;
|
|
3270
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
3271
|
+
throw new Error("MCP prompt arguments must be an object");
|
|
3272
|
+
}
|
|
3273
|
+
const args = {};
|
|
3274
|
+
for (const [key, item] of Object.entries(value)) {
|
|
3275
|
+
if (typeof item !== "string") throw new Error(`MCP prompt argument "${key}" must be a string`);
|
|
3276
|
+
args[key] = item;
|
|
3277
|
+
}
|
|
3278
|
+
return args;
|
|
3279
|
+
}
|
|
3280
|
+
function sendContentError(ws, action, name2, error) {
|
|
3281
|
+
send(ws, { type: "mcp.content.error", payload: { action, name: name2, error } });
|
|
3282
|
+
}
|
|
3283
|
+
function errorMessage(err) {
|
|
3284
|
+
return err instanceof Error ? err.message : String(err);
|
|
3285
|
+
}
|
|
3186
3286
|
|
|
3187
3287
|
// src/server/memory-handlers.ts
|
|
3188
3288
|
async function handleMemoryList(ws, memoryStore) {
|
|
@@ -4272,6 +4372,16 @@ function validateBrainAskPayload(payload) {
|
|
|
4272
4372
|
}
|
|
4273
4373
|
return { ok: true, value: { question: question.trim() } };
|
|
4274
4374
|
}
|
|
4375
|
+
function validateBrainConfigSetPayload(payload) {
|
|
4376
|
+
if (!isRecord(payload)) {
|
|
4377
|
+
return { ok: false, message: "brain.config.set payload must be an object with a patch object" };
|
|
4378
|
+
}
|
|
4379
|
+
const patch = payload["patch"];
|
|
4380
|
+
if (!isRecord(patch)) {
|
|
4381
|
+
return { ok: false, message: "brain.config.set payload.patch must be an object" };
|
|
4382
|
+
}
|
|
4383
|
+
return { ok: true, value: { patch } };
|
|
4384
|
+
}
|
|
4275
4385
|
function validateAutonomySwitchPayload(payload) {
|
|
4276
4386
|
if (!isRecord(payload)) {
|
|
4277
4387
|
return { ok: false, message: "autonomy.switch payload must be an object with string mode" };
|
|
@@ -4333,7 +4443,9 @@ var BOOLEAN_PREF_KEYS = /* @__PURE__ */ new Set([
|
|
|
4333
4443
|
"hqEnabled",
|
|
4334
4444
|
"hqRawContent",
|
|
4335
4445
|
"fallbackAuto",
|
|
4336
|
-
"favoriteModelsOnly"
|
|
4446
|
+
"favoriteModelsOnly",
|
|
4447
|
+
"breakerEnabled",
|
|
4448
|
+
"debugStream"
|
|
4337
4449
|
]);
|
|
4338
4450
|
var STRING_ARRAY_PREF_KEYS = /* @__PURE__ */ new Set(["fallbackModels", "favoriteModels"]);
|
|
4339
4451
|
var STRING_ARRAY_RECORD_PREF_KEYS = /* @__PURE__ */ new Set(["fallbackProfiles"]);
|
|
@@ -4344,9 +4456,17 @@ var NUMBER_PREF_KEYS = /* @__PURE__ */ new Set([
|
|
|
4344
4456
|
"maxIterations",
|
|
4345
4457
|
"maxConcurrent",
|
|
4346
4458
|
"enhanceDelayMs",
|
|
4347
|
-
"tgLongToolMs"
|
|
4459
|
+
"tgLongToolMs",
|
|
4460
|
+
"breakerAutoKillResetMs"
|
|
4461
|
+
]);
|
|
4462
|
+
var STRING_PREF_KEYS = /* @__PURE__ */ new Set([
|
|
4463
|
+
"hqUrl",
|
|
4464
|
+
"hqToken",
|
|
4465
|
+
"uiLocale",
|
|
4466
|
+
"thinkingWord",
|
|
4467
|
+
"refinerProvider",
|
|
4468
|
+
"refinerModel"
|
|
4348
4469
|
]);
|
|
4349
|
-
var STRING_PREF_KEYS = /* @__PURE__ */ new Set(["hqUrl", "hqToken", "uiLocale"]);
|
|
4350
4470
|
var ENUM_PREF_KEYS = {
|
|
4351
4471
|
autonomy: AUTONOMY_VALUES,
|
|
4352
4472
|
contextStrategy: CONTEXT_STRATEGY_VALUES,
|
|
@@ -4357,7 +4477,10 @@ var ENUM_PREF_KEYS = {
|
|
|
4357
4477
|
auditLevel: AUDIT_LEVEL_VALUES,
|
|
4358
4478
|
reasoningMode: REASONING_MODE_VALUES,
|
|
4359
4479
|
reasoningEffort: REASONING_EFFORT_VALUES,
|
|
4360
|
-
cacheTtl: CACHE_TTL_VALUES
|
|
4480
|
+
cacheTtl: CACHE_TTL_VALUES,
|
|
4481
|
+
statuslineMode: /* @__PURE__ */ new Set(["minimum", "detailed", "no-color"]),
|
|
4482
|
+
animationStyle: /* @__PURE__ */ new Set(["rainbow", "wave", "pulse", "dots", "breathe", "cycle"]),
|
|
4483
|
+
fsAccess: /* @__PURE__ */ new Set(["unrestricted", "project"])
|
|
4361
4484
|
};
|
|
4362
4485
|
function validateModelRuntimeValue(modelRuntime, path23) {
|
|
4363
4486
|
const reasoning = modelRuntime["reasoning"];
|
|
@@ -5756,6 +5879,10 @@ function shouldLogWatcherStats() {
|
|
|
5756
5879
|
function setupEvents(deps2) {
|
|
5757
5880
|
const { events, broadcast: broadcast2, clients, config, context, pendingConfirms, globalConfigPath, sessionBridge, wpaths, watcherMetrics, onFleetBroadcaster } = deps2;
|
|
5758
5881
|
const disposers = [];
|
|
5882
|
+
let disposed = false;
|
|
5883
|
+
const on = (event, listener) => {
|
|
5884
|
+
disposers.push(events.on(event, listener));
|
|
5885
|
+
};
|
|
5759
5886
|
const currentSessionId = () => context.session?.id ?? "";
|
|
5760
5887
|
const sessionPayload2 = (payload) => {
|
|
5761
5888
|
const provided = payload["sessionId"];
|
|
@@ -5771,20 +5898,20 @@ function setupEvents(deps2) {
|
|
|
5771
5898
|
sessionBridge?.append(event).catch(() => {
|
|
5772
5899
|
});
|
|
5773
5900
|
};
|
|
5774
|
-
|
|
5901
|
+
on("iteration.started", (e) => {
|
|
5775
5902
|
const maxIt = typeof context.meta["maxIterations"] === "number" ? context.meta["maxIterations"] : config.tools?.maxIterations ?? 100;
|
|
5776
5903
|
broadcast2(clients, {
|
|
5777
5904
|
type: "iteration.started",
|
|
5778
5905
|
payload: sessionPayload2({ sessionId: e.sessionId, index: e.index, maxIterations: maxIt })
|
|
5779
5906
|
});
|
|
5780
5907
|
});
|
|
5781
|
-
|
|
5908
|
+
on("iteration.completed", (e) => {
|
|
5782
5909
|
broadcast2(clients, {
|
|
5783
5910
|
type: "iteration.completed",
|
|
5784
5911
|
payload: sessionPayload2({ sessionId: e.sessionId, index: e.index, totalIterations: e.index + 1 })
|
|
5785
5912
|
});
|
|
5786
5913
|
});
|
|
5787
|
-
|
|
5914
|
+
on("iteration.limit_reached", (e) => {
|
|
5788
5915
|
broadcast2(clients, {
|
|
5789
5916
|
type: "iteration.limit_reached",
|
|
5790
5917
|
payload: sessionPayload2({
|
|
@@ -5794,19 +5921,19 @@ function setupEvents(deps2) {
|
|
|
5794
5921
|
})
|
|
5795
5922
|
});
|
|
5796
5923
|
});
|
|
5797
|
-
|
|
5924
|
+
on("provider.text_delta", (e) => {
|
|
5798
5925
|
broadcast2(clients, { type: "provider.text_delta", payload: sessionPayload2({ sessionId: e.sessionId, text: e.text, messageId: "current" }) });
|
|
5799
5926
|
});
|
|
5800
|
-
|
|
5927
|
+
on("provider.thinking_delta", (e) => {
|
|
5801
5928
|
broadcast2(clients, { type: "provider.thinking_delta", payload: sessionPayload2({ sessionId: e.sessionId, text: e.text }) });
|
|
5802
5929
|
});
|
|
5803
|
-
|
|
5930
|
+
on("provider.stream_error", (e) => {
|
|
5804
5931
|
broadcast2(clients, {
|
|
5805
5932
|
type: "provider.stream_error",
|
|
5806
5933
|
payload: sessionPayload2({ sessionId: e.sessionId, eventType: e.eventType, message: e.msg })
|
|
5807
5934
|
});
|
|
5808
5935
|
});
|
|
5809
|
-
|
|
5936
|
+
on("tool.started", (e) => {
|
|
5810
5937
|
broadcast2(clients, {
|
|
5811
5938
|
type: "tool.started",
|
|
5812
5939
|
payload: sessionPayload2({ sessionId: e.sessionId, id: e.id, name: e.name, input: e.input, messageId: `tool_${e.id}` })
|
|
@@ -5819,7 +5946,7 @@ function setupEvents(deps2) {
|
|
|
5819
5946
|
input: e.input
|
|
5820
5947
|
});
|
|
5821
5948
|
});
|
|
5822
|
-
|
|
5949
|
+
on("tool.progress", (e) => {
|
|
5823
5950
|
broadcast2(clients, {
|
|
5824
5951
|
type: "tool.progress",
|
|
5825
5952
|
// Nested `event` shape — the client handler reads `payload.event?.text`
|
|
@@ -5840,7 +5967,7 @@ function setupEvents(deps2) {
|
|
|
5840
5967
|
}
|
|
5841
5968
|
});
|
|
5842
5969
|
});
|
|
5843
|
-
|
|
5970
|
+
on("tool.executed", (e) => {
|
|
5844
5971
|
broadcast2(clients, {
|
|
5845
5972
|
type: "tool.executed",
|
|
5846
5973
|
payload: sessionPayload2({ sessionId: e.sessionId, id: e.id, name: e.name, durationMs: e.durationMs, ok: e.ok, input: e.input, output: e.output })
|
|
@@ -5898,7 +6025,7 @@ function setupEvents(deps2) {
|
|
|
5898
6025
|
})();
|
|
5899
6026
|
}
|
|
5900
6027
|
});
|
|
5901
|
-
|
|
6028
|
+
on("tool.loop_detected", (e) => {
|
|
5902
6029
|
broadcast2(clients, {
|
|
5903
6030
|
type: "tool.loop_detected",
|
|
5904
6031
|
payload: sessionPayload2({
|
|
@@ -5910,19 +6037,19 @@ function setupEvents(deps2) {
|
|
|
5910
6037
|
})
|
|
5911
6038
|
});
|
|
5912
6039
|
});
|
|
5913
|
-
|
|
6040
|
+
on("trust.persisted", (e) => {
|
|
5914
6041
|
broadcast2(clients, {
|
|
5915
6042
|
type: "trust.persisted",
|
|
5916
6043
|
payload: sessionPayload2({ sessionId: e.sessionId, tool: e.tool, pattern: e.pattern, decision: e.decision })
|
|
5917
6044
|
});
|
|
5918
6045
|
});
|
|
5919
|
-
|
|
6046
|
+
on("delegate.started", (e) => {
|
|
5920
6047
|
broadcast2(clients, {
|
|
5921
6048
|
type: "delegate.started",
|
|
5922
6049
|
payload: sessionPayload2({ sessionId: e.sessionId, target: e.target, task: e.task })
|
|
5923
6050
|
});
|
|
5924
6051
|
});
|
|
5925
|
-
|
|
6052
|
+
on("delegate.completed", (e) => {
|
|
5926
6053
|
broadcast2(clients, {
|
|
5927
6054
|
type: "delegate.completed",
|
|
5928
6055
|
payload: sessionPayload2({
|
|
@@ -5940,7 +6067,7 @@ function setupEvents(deps2) {
|
|
|
5940
6067
|
})
|
|
5941
6068
|
});
|
|
5942
6069
|
});
|
|
5943
|
-
|
|
6070
|
+
on("provider.response", (e) => {
|
|
5944
6071
|
broadcast2(clients, {
|
|
5945
6072
|
type: "provider.response",
|
|
5946
6073
|
payload: sessionPayload2({
|
|
@@ -5952,7 +6079,7 @@ function setupEvents(deps2) {
|
|
|
5952
6079
|
})
|
|
5953
6080
|
});
|
|
5954
6081
|
});
|
|
5955
|
-
|
|
6082
|
+
on("ctx.pct", (e) => {
|
|
5956
6083
|
broadcast2(clients, {
|
|
5957
6084
|
type: "ctx.pct",
|
|
5958
6085
|
payload: sessionPayload2({ sessionId: e.sessionId, load: e.load, tokens: e.tokens, maxContext: e.maxContext })
|
|
@@ -5969,28 +6096,28 @@ function setupEvents(deps2) {
|
|
|
5969
6096
|
})
|
|
5970
6097
|
});
|
|
5971
6098
|
});
|
|
5972
|
-
|
|
6099
|
+
on("ctx.max_context", (e) => {
|
|
5973
6100
|
broadcast2(clients, {
|
|
5974
6101
|
type: "ctx.max_context",
|
|
5975
6102
|
payload: sessionPayload2({ sessionId: e.sessionId, providerId: e.providerId, modelId: e.modelId, maxContext: e.maxContext })
|
|
5976
6103
|
});
|
|
5977
6104
|
});
|
|
5978
|
-
|
|
6105
|
+
on("token.threshold", (e) => {
|
|
5979
6106
|
broadcast2(clients, {
|
|
5980
6107
|
type: "token.threshold",
|
|
5981
6108
|
payload: sessionPayload2({ sessionId: e.sessionId, used: e.used, limit: e.limit })
|
|
5982
6109
|
});
|
|
5983
6110
|
});
|
|
5984
|
-
|
|
6111
|
+
on("token.cost_estimate_unavailable", (e) => {
|
|
5985
6112
|
broadcast2(clients, {
|
|
5986
6113
|
type: "token.cost_estimate_unavailable",
|
|
5987
6114
|
payload: sessionPayload2({ sessionId: e.sessionId, model: e.model })
|
|
5988
6115
|
});
|
|
5989
6116
|
});
|
|
5990
|
-
|
|
6117
|
+
on("context.repaired", (e) => {
|
|
5991
6118
|
broadcast2(clients, { type: "context.repaired", payload: sessionPayload2({ sessionId: e.sessionId, removedToolUses: e.removedToolUses, removedToolResults: e.removedToolResults, removedMessages: e.removedMessages }) });
|
|
5992
6119
|
});
|
|
5993
|
-
|
|
6120
|
+
on("tool.confirm_needed", (e) => {
|
|
5994
6121
|
const id = e.toolUseId ?? `confirm_${Date.now()}`;
|
|
5995
6122
|
const payload = sessionPayload2({ sessionId: e.sessionId, id, toolName: e.tool?.name ?? "unknown", input: e.input, suggestedPattern: e.suggestedPattern, decisionSource: e.decisionSource, riskTier: e.riskTier });
|
|
5996
6123
|
pendingConfirms.set(id, {
|
|
@@ -6001,7 +6128,7 @@ function setupEvents(deps2) {
|
|
|
6001
6128
|
});
|
|
6002
6129
|
broadcast2(clients, { type: "tool.confirm_needed", payload });
|
|
6003
6130
|
});
|
|
6004
|
-
|
|
6131
|
+
on("error", (e) => {
|
|
6005
6132
|
broadcast2(clients, { type: "error", payload: sessionPayload2({ sessionId: e.sessionId, phase: e.phase, message: e.err instanceof Error ? e.err.message : String(e.err) }) });
|
|
6006
6133
|
appendForCurrentSession(e.sessionId, {
|
|
6007
6134
|
type: "error",
|
|
@@ -6010,13 +6137,13 @@ function setupEvents(deps2) {
|
|
|
6010
6137
|
phase: e.phase
|
|
6011
6138
|
});
|
|
6012
6139
|
});
|
|
6013
|
-
|
|
6140
|
+
on("session.damaged", (e) => {
|
|
6014
6141
|
broadcast2(clients, {
|
|
6015
6142
|
type: "session.damaged",
|
|
6016
6143
|
payload: { sessionId: e.sessionId, detail: e.detail }
|
|
6017
6144
|
});
|
|
6018
6145
|
});
|
|
6019
|
-
|
|
6146
|
+
on("session.rewound", (e) => {
|
|
6020
6147
|
broadcast2(clients, {
|
|
6021
6148
|
type: "session.rewound",
|
|
6022
6149
|
payload: sessionPayload2({
|
|
@@ -6027,7 +6154,7 @@ function setupEvents(deps2) {
|
|
|
6027
6154
|
})
|
|
6028
6155
|
});
|
|
6029
6156
|
});
|
|
6030
|
-
|
|
6157
|
+
on("checkpoint.written", (e) => {
|
|
6031
6158
|
broadcast2(clients, {
|
|
6032
6159
|
type: "checkpoint.written",
|
|
6033
6160
|
payload: sessionPayload2({
|
|
@@ -6039,19 +6166,19 @@ function setupEvents(deps2) {
|
|
|
6039
6166
|
})
|
|
6040
6167
|
});
|
|
6041
6168
|
});
|
|
6042
|
-
|
|
6169
|
+
on("in_flight.started", (e) => {
|
|
6043
6170
|
broadcast2(clients, {
|
|
6044
6171
|
type: "in_flight.started",
|
|
6045
6172
|
payload: sessionPayload2({ sessionId: e.sessionId, context: e.context, ts: e.ts })
|
|
6046
6173
|
});
|
|
6047
6174
|
});
|
|
6048
|
-
|
|
6175
|
+
on("in_flight.ended", (e) => {
|
|
6049
6176
|
broadcast2(clients, {
|
|
6050
6177
|
type: "in_flight.ended",
|
|
6051
6178
|
payload: sessionPayload2({ sessionId: e.sessionId, reason: e.reason, ts: e.ts })
|
|
6052
6179
|
});
|
|
6053
6180
|
});
|
|
6054
|
-
|
|
6181
|
+
on("provider.retry", (e) => {
|
|
6055
6182
|
broadcast2(clients, {
|
|
6056
6183
|
type: "provider.retry",
|
|
6057
6184
|
payload: sessionPayload2({
|
|
@@ -6073,7 +6200,7 @@ function setupEvents(deps2) {
|
|
|
6073
6200
|
description: e.description
|
|
6074
6201
|
});
|
|
6075
6202
|
});
|
|
6076
|
-
|
|
6203
|
+
on("provider.error", (e) => {
|
|
6077
6204
|
broadcast2(clients, {
|
|
6078
6205
|
type: "provider.error",
|
|
6079
6206
|
payload: sessionPayload2({
|
|
@@ -6093,7 +6220,7 @@ function setupEvents(deps2) {
|
|
|
6093
6220
|
retryable: e.retryable
|
|
6094
6221
|
});
|
|
6095
6222
|
});
|
|
6096
|
-
|
|
6223
|
+
on("provider.fallback", (e) => {
|
|
6097
6224
|
broadcast2(clients, {
|
|
6098
6225
|
type: "provider.fallback",
|
|
6099
6226
|
payload: sessionPayload2({
|
|
@@ -6105,7 +6232,7 @@ function setupEvents(deps2) {
|
|
|
6105
6232
|
})
|
|
6106
6233
|
});
|
|
6107
6234
|
});
|
|
6108
|
-
|
|
6235
|
+
on("compaction.fired", (e) => {
|
|
6109
6236
|
broadcast2(clients, {
|
|
6110
6237
|
type: "context.compacted",
|
|
6111
6238
|
payload: sessionPayload2({
|
|
@@ -6117,7 +6244,7 @@ function setupEvents(deps2) {
|
|
|
6117
6244
|
})
|
|
6118
6245
|
});
|
|
6119
6246
|
});
|
|
6120
|
-
|
|
6247
|
+
on("compaction.failed", (e) => {
|
|
6121
6248
|
broadcast2(clients, {
|
|
6122
6249
|
type: "compaction.failed",
|
|
6123
6250
|
payload: sessionPayload2({
|
|
@@ -6132,25 +6259,25 @@ function setupEvents(deps2) {
|
|
|
6132
6259
|
})
|
|
6133
6260
|
});
|
|
6134
6261
|
});
|
|
6135
|
-
|
|
6262
|
+
on("mcp.server.connected", (e) => {
|
|
6136
6263
|
broadcast2(clients, {
|
|
6137
6264
|
type: "mcp.server.connected",
|
|
6138
6265
|
payload: { name: e.name, toolCount: e.toolCount }
|
|
6139
6266
|
});
|
|
6140
6267
|
});
|
|
6141
|
-
|
|
6268
|
+
on("mcp.server.reconnected", (e) => {
|
|
6142
6269
|
broadcast2(clients, {
|
|
6143
6270
|
type: "mcp.server.reconnected",
|
|
6144
6271
|
payload: { name: e.name, toolCount: e.toolCount }
|
|
6145
6272
|
});
|
|
6146
6273
|
});
|
|
6147
|
-
|
|
6274
|
+
on("mcp.server.disconnected", (e) => {
|
|
6148
6275
|
broadcast2(clients, {
|
|
6149
6276
|
type: "mcp.server.disconnected",
|
|
6150
6277
|
payload: { name: e.name, reason: e.reason }
|
|
6151
6278
|
});
|
|
6152
6279
|
});
|
|
6153
|
-
|
|
6280
|
+
on("coordinator.stats", (e) => {
|
|
6154
6281
|
broadcast2(clients, {
|
|
6155
6282
|
type: "coordinator.stats",
|
|
6156
6283
|
payload: sessionPayload2({
|
|
@@ -6178,16 +6305,16 @@ function setupEvents(deps2) {
|
|
|
6178
6305
|
broadcast2(clients, { type: "mailbox.agent_registered", payload });
|
|
6179
6306
|
});
|
|
6180
6307
|
const forwardSubagent = (kind, payload) => broadcast2(clients, { type: "subagent.event", payload: sessionPayload2({ kind, ...payload }) });
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6308
|
+
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 }));
|
|
6309
|
+
on("subagent.task_started", (e) => forwardSubagent("task_started", { sessionId: e.sessionId, subagentId: e.subagentId, taskId: e.taskId, description: e.description }));
|
|
6310
|
+
on("subagent.tool_executed", (e) => forwardSubagent("tool_executed", { sessionId: e.sessionId, subagentId: e.subagentId, toolName: e.name, durationMs: e.durationMs, ok: e.ok }));
|
|
6311
|
+
on("subagent.iteration_summary", (e) => forwardSubagent("iteration_summary", { sessionId: e.sessionId, subagentId: e.subagentId, iteration: e.iteration, toolCalls: e.toolCalls, costUsd: e.costUsd, currentTool: e.currentTool, partialText: e.partialText }));
|
|
6312
|
+
on("subagent.budget_warning", (e) => forwardSubagent("budget_warning", { sessionId: e.sessionId, subagentId: e.subagentId, budgetKind: e.kind, used: e.used, limit: e.limit }));
|
|
6313
|
+
on("subagent.budget_extended", (e) => forwardSubagent("budget_extended", { sessionId: e.sessionId, subagentId: e.subagentId, budgetKind: e.kind, newLimit: e.newLimit, totalExtensions: e.totalExtensions }));
|
|
6314
|
+
on("subagent.ctx_pct", (e) => forwardSubagent("ctx_pct", { sessionId: e.sessionId, subagentId: e.subagentId, load: e.load, tokens: e.tokens, maxContext: e.maxContext }));
|
|
6315
|
+
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 }));
|
|
6316
|
+
on("subagent.removed", (e) => forwardSubagent("removed", { sessionId: e.sessionId, subagentId: e.subagentId, reason: e.reason }));
|
|
6317
|
+
on("agent.timeline.message", (e) => {
|
|
6191
6318
|
broadcast2(clients, {
|
|
6192
6319
|
type: "agent.timeline.message",
|
|
6193
6320
|
payload: sessionPayload2({
|
|
@@ -6203,7 +6330,7 @@ function setupEvents(deps2) {
|
|
|
6203
6330
|
})
|
|
6204
6331
|
});
|
|
6205
6332
|
});
|
|
6206
|
-
|
|
6333
|
+
on("agent.status_changed", (e) => {
|
|
6207
6334
|
broadcast2(clients, {
|
|
6208
6335
|
type: "agent.status_changed",
|
|
6209
6336
|
payload: sessionPayload2({
|
|
@@ -6218,7 +6345,7 @@ function setupEvents(deps2) {
|
|
|
6218
6345
|
});
|
|
6219
6346
|
});
|
|
6220
6347
|
let leaderSpawned = false;
|
|
6221
|
-
|
|
6348
|
+
on("iteration.started", (e) => {
|
|
6222
6349
|
if (!leaderSpawned) {
|
|
6223
6350
|
leaderSpawned = true;
|
|
6224
6351
|
const provider = context.provider?.id ?? "unknown";
|
|
@@ -6232,7 +6359,7 @@ function setupEvents(deps2) {
|
|
|
6232
6359
|
});
|
|
6233
6360
|
}
|
|
6234
6361
|
});
|
|
6235
|
-
|
|
6362
|
+
on("tool.executed", (e) => {
|
|
6236
6363
|
forwardSubagent("tool_executed", {
|
|
6237
6364
|
sessionId: e.sessionId,
|
|
6238
6365
|
subagentId: "leader",
|
|
@@ -6241,7 +6368,7 @@ function setupEvents(deps2) {
|
|
|
6241
6368
|
ok: e.ok
|
|
6242
6369
|
});
|
|
6243
6370
|
});
|
|
6244
|
-
|
|
6371
|
+
on("provider.response", (e) => {
|
|
6245
6372
|
if (e.usage?.input != null) {
|
|
6246
6373
|
const maxCtx = context.provider.capabilities.maxContext;
|
|
6247
6374
|
const rawLoad = maxCtx > 0 ? e.usage.input / maxCtx : 0;
|
|
@@ -6258,7 +6385,7 @@ function setupEvents(deps2) {
|
|
|
6258
6385
|
});
|
|
6259
6386
|
}
|
|
6260
6387
|
});
|
|
6261
|
-
|
|
6388
|
+
on("iteration.completed", (e) => {
|
|
6262
6389
|
if (!leaderSpawned) {
|
|
6263
6390
|
leaderSpawned = true;
|
|
6264
6391
|
const provider = context.provider?.id ?? "unknown";
|
|
@@ -6290,7 +6417,7 @@ function setupEvents(deps2) {
|
|
|
6290
6417
|
payload: sessionPayload2({ event: eventName, ...payload })
|
|
6291
6418
|
});
|
|
6292
6419
|
});
|
|
6293
|
-
|
|
6420
|
+
on("client.status", async (e) => {
|
|
6294
6421
|
broadcast2(clients, { type: "client.status_update", payload: e });
|
|
6295
6422
|
if (wpaths?.projectStatus) {
|
|
6296
6423
|
try {
|
|
@@ -6376,6 +6503,7 @@ function setupEvents(deps2) {
|
|
|
6376
6503
|
const startWatcher = async () => {
|
|
6377
6504
|
try {
|
|
6378
6505
|
await fs10.mkdir(projectsDir, { recursive: true });
|
|
6506
|
+
if (disposed) return;
|
|
6379
6507
|
watcher = fsWatch(projectsDir, { persistent: true, recursive: true }, async (eventType, filename) => {
|
|
6380
6508
|
if (eventType !== "change" && eventType !== "rename") return;
|
|
6381
6509
|
if (filename == null) return;
|
|
@@ -6406,7 +6534,7 @@ function setupEvents(deps2) {
|
|
|
6406
6534
|
);
|
|
6407
6535
|
}
|
|
6408
6536
|
};
|
|
6409
|
-
|
|
6537
|
+
on("client.status", (e) => {
|
|
6410
6538
|
if (e.projectHash) {
|
|
6411
6539
|
const hash = String(e.projectHash);
|
|
6412
6540
|
if (!knownProjectHashes.has(hash)) {
|
|
@@ -6446,7 +6574,9 @@ function setupEvents(deps2) {
|
|
|
6446
6574
|
const registry = new SessionRegistry(globalRoot);
|
|
6447
6575
|
const sessions = await registry.list();
|
|
6448
6576
|
const mySlug = sessions.find((s) => s.pid === process.pid)?.projectSlug;
|
|
6449
|
-
const live = sessions.filter(
|
|
6577
|
+
const live = sessions.filter(
|
|
6578
|
+
(s) => s.status === "active" || s.status === "idle"
|
|
6579
|
+
).filter((s) => mySlug ? s.projectSlug === mySlug : true).map((s) => ({
|
|
6450
6580
|
sessionId: s.sessionId,
|
|
6451
6581
|
projectName: s.projectName,
|
|
6452
6582
|
projectSlug: s.projectSlug,
|
|
@@ -6500,6 +6630,8 @@ function setupEvents(deps2) {
|
|
|
6500
6630
|
void broadcastSessions();
|
|
6501
6631
|
}
|
|
6502
6632
|
return () => {
|
|
6633
|
+
if (disposed) return;
|
|
6634
|
+
disposed = true;
|
|
6503
6635
|
for (const dispose of disposers) {
|
|
6504
6636
|
try {
|
|
6505
6637
|
dispose();
|
|
@@ -6726,7 +6858,7 @@ function createWsServers(ports, accessToken) {
|
|
|
6726
6858
|
allowedHostnames: publicHostnames,
|
|
6727
6859
|
allowBrowserUrlToken: Boolean(ports.publicWsUrl)
|
|
6728
6860
|
});
|
|
6729
|
-
const WS_MAX_PAYLOAD =
|
|
6861
|
+
const WS_MAX_PAYLOAD = 20 * 1024 * 1024;
|
|
6730
6862
|
const wssPrimary = new WebSocketServer({
|
|
6731
6863
|
port: ports.wsPort,
|
|
6732
6864
|
host: ports.wsHost,
|
|
@@ -6834,12 +6966,10 @@ function registerShutdown(deps2) {
|
|
|
6834
6966
|
}
|
|
6835
6967
|
|
|
6836
6968
|
// src/server/pre-context-services.ts
|
|
6837
|
-
import * as
|
|
6969
|
+
import * as path16 from "node:path";
|
|
6838
6970
|
import { createRequire as createRequire2 } from "node:module";
|
|
6839
|
-
import { WebSocket as WebSocket2 } from "ws";
|
|
6840
6971
|
import { DefaultTokenCounter } from "@wrongstack/core/infrastructure";
|
|
6841
6972
|
import {
|
|
6842
|
-
AgentStatusTracker,
|
|
6843
6973
|
AnnotationsStore,
|
|
6844
6974
|
DEFAULT_SESSION_PRUNE_DAYS,
|
|
6845
6975
|
DefaultModelsRegistry,
|
|
@@ -6850,7 +6980,6 @@ import {
|
|
|
6850
6980
|
DefaultSkillLoader,
|
|
6851
6981
|
DefaultSystemPromptBuilder,
|
|
6852
6982
|
EventBus,
|
|
6853
|
-
FleetNotifier,
|
|
6854
6983
|
GlobalMailbox,
|
|
6855
6984
|
PromptUsageStore,
|
|
6856
6985
|
ProviderRegistry,
|
|
@@ -6861,8 +6990,8 @@ import {
|
|
|
6861
6990
|
applyToolDescriptionModes,
|
|
6862
6991
|
applyToolResultRenderModes,
|
|
6863
6992
|
configureChildEnvGitIdentity,
|
|
6993
|
+
getSessionRegistry as getSessionRegistry2,
|
|
6864
6994
|
resolveContextWindowPolicy,
|
|
6865
|
-
getSessionRegistry,
|
|
6866
6995
|
makeMailboxTool,
|
|
6867
6996
|
makeMailInboxTool,
|
|
6868
6997
|
makeFleetStatusTool,
|
|
@@ -6871,7 +7000,12 @@ import {
|
|
|
6871
7000
|
import {
|
|
6872
7001
|
createSuperMemoryTools
|
|
6873
7002
|
} from "@wrongstack/super-memory";
|
|
6874
|
-
import {
|
|
7003
|
+
import {
|
|
7004
|
+
MCPAuthorizationManager,
|
|
7005
|
+
MCPRegistry,
|
|
7006
|
+
MCPVaultTokenStore,
|
|
7007
|
+
createVaultBackedMcpAuthorizationProviderFactory
|
|
7008
|
+
} from "@wrongstack/mcp";
|
|
6875
7009
|
import { buildProviderFactoriesFromRegistry } from "@wrongstack/providers";
|
|
6876
7010
|
import { createDefaultContainer } from "@wrongstack/runtime";
|
|
6877
7011
|
import {
|
|
@@ -6883,7 +7017,7 @@ import {
|
|
|
6883
7017
|
rememberTool,
|
|
6884
7018
|
searchMemoryTool
|
|
6885
7019
|
} from "@wrongstack/tools";
|
|
6886
|
-
import { toErrorMessage as toErrorMessage5 } from "@wrongstack/core/utils";
|
|
7020
|
+
import { sessionScopedPath, toErrorMessage as toErrorMessage5 } from "@wrongstack/core/utils";
|
|
6887
7021
|
|
|
6888
7022
|
// src/server/setup-screen.ts
|
|
6889
7023
|
import { expectDefined as expectDefined2 } from "@wrongstack/core";
|
|
@@ -7021,6 +7155,20 @@ function seedContextMeta(config, context) {
|
|
|
7021
7155
|
meta["reasoningPreserve"] = mr.reasoning?.preserve === true;
|
|
7022
7156
|
meta["cacheTtl"] = mr.cache?.ttl ?? "default";
|
|
7023
7157
|
}
|
|
7158
|
+
meta["refinerProvider"] = autonomyCfg["refinerProvider"] ?? "";
|
|
7159
|
+
meta["refinerModel"] = autonomyCfg["refinerModel"] ?? "";
|
|
7160
|
+
meta["thinkingWord"] = autonomyCfg["thinkingWord"] ?? "thinking";
|
|
7161
|
+
meta["statuslineMode"] = autonomyCfg["statuslineMode"] ?? "detailed";
|
|
7162
|
+
meta["animationStyle"] = autonomyCfg["animationStyle"] ?? "rainbow";
|
|
7163
|
+
meta["breakerEnabled"] = config.circuitBreaker?.enabled === true;
|
|
7164
|
+
meta["breakerAutoKillResetMs"] = config.circuitBreaker?.autoKillResetMs ?? 6e4;
|
|
7165
|
+
{
|
|
7166
|
+
const featuresAllow = config.features?.allowOutsideProjectRoot;
|
|
7167
|
+
const toolsRestrict = config.tools?.restrictToProjectRoot;
|
|
7168
|
+
const allow = featuresAllow !== void 0 ? featuresAllow : toolsRestrict !== void 0 ? !toolsRestrict : true;
|
|
7169
|
+
meta["fsAccess"] = allow ? "unrestricted" : "project";
|
|
7170
|
+
}
|
|
7171
|
+
meta["debugStream"] = config.debugStream === true;
|
|
7024
7172
|
const hqConfig = config.hq;
|
|
7025
7173
|
meta["hqEnabled"] = hqConfig?.enabled === true;
|
|
7026
7174
|
meta["hqUrl"] = hqConfig?.url ?? "";
|
|
@@ -7117,10 +7265,188 @@ async function discoverAndMergeWebuiProviders(opts) {
|
|
|
7117
7265
|
}
|
|
7118
7266
|
}
|
|
7119
7267
|
|
|
7268
|
+
// src/server/standalone-session-identity.ts
|
|
7269
|
+
import * as path15 from "node:path";
|
|
7270
|
+
import {
|
|
7271
|
+
AgentStatusTracker,
|
|
7272
|
+
FleetNotifier,
|
|
7273
|
+
getSessionRegistry,
|
|
7274
|
+
RecoveryLock
|
|
7275
|
+
} from "@wrongstack/core";
|
|
7276
|
+
import { WebSocket as WebSocket2 } from "ws";
|
|
7277
|
+
async function createStandaloneSessionIdentityLifecycle(opts) {
|
|
7278
|
+
const { paths, events, logger } = opts;
|
|
7279
|
+
const registry = opts.sessionRegistry ?? getSessionRegistry(paths.globalRoot);
|
|
7280
|
+
const fleetNotifier = new FleetNotifier({
|
|
7281
|
+
baseDir: paths.globalRoot,
|
|
7282
|
+
projectRoot: paths.projectRoot,
|
|
7283
|
+
selfPid: process.pid
|
|
7284
|
+
});
|
|
7285
|
+
let activeSessionId = opts.initialSessionId;
|
|
7286
|
+
let stopped = false;
|
|
7287
|
+
let transition = Promise.resolve();
|
|
7288
|
+
const statusTracker = new AgentStatusTracker({
|
|
7289
|
+
events,
|
|
7290
|
+
registry,
|
|
7291
|
+
sessionId: () => activeSessionId,
|
|
7292
|
+
onUpdate: () => fleetNotifier.notify()
|
|
7293
|
+
});
|
|
7294
|
+
const register = async (sessionId) => {
|
|
7295
|
+
try {
|
|
7296
|
+
await registry.register({
|
|
7297
|
+
sessionId,
|
|
7298
|
+
projectSlug: paths.projectSlug,
|
|
7299
|
+
projectRoot: paths.projectRoot,
|
|
7300
|
+
projectName: path15.basename(paths.projectRoot),
|
|
7301
|
+
workingDir: opts.workingDir,
|
|
7302
|
+
clientType: "webui",
|
|
7303
|
+
pid: process.pid,
|
|
7304
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7305
|
+
agents: statusTracker.getAgents()
|
|
7306
|
+
});
|
|
7307
|
+
fleetNotifier.notify();
|
|
7308
|
+
} catch (err) {
|
|
7309
|
+
logger.debug?.(`WebUI session registry update failed: ${errorMessage2(err)}`);
|
|
7310
|
+
}
|
|
7311
|
+
};
|
|
7312
|
+
await register(activeSessionId);
|
|
7313
|
+
statusTracker.start();
|
|
7314
|
+
const recoveryLock = opts.manageRecoveryLock === false ? void 0 : new RecoveryLock({ dir: paths.projectSessions, sessionStore: opts.sessionStore });
|
|
7315
|
+
let ownsRecoveryLock = false;
|
|
7316
|
+
if (recoveryLock) {
|
|
7317
|
+
try {
|
|
7318
|
+
await recoveryLock.write(activeSessionId);
|
|
7319
|
+
ownsRecoveryLock = true;
|
|
7320
|
+
} catch {
|
|
7321
|
+
}
|
|
7322
|
+
}
|
|
7323
|
+
let stopHqBridges = () => {
|
|
7324
|
+
};
|
|
7325
|
+
let closeHqPublisher = () => {
|
|
7326
|
+
};
|
|
7327
|
+
let restartHqBridges = (_sessionId) => {
|
|
7328
|
+
};
|
|
7329
|
+
if (opts.enableHqTelemetry !== false) {
|
|
7330
|
+
try {
|
|
7331
|
+
const core = await import("@wrongstack/core");
|
|
7332
|
+
const publisher = core.createHqPublisherFromEnv({
|
|
7333
|
+
clientKind: "webui",
|
|
7334
|
+
projectRoot: paths.projectRoot,
|
|
7335
|
+
projectName: path15.basename(paths.projectRoot),
|
|
7336
|
+
appConfig: opts.config,
|
|
7337
|
+
socketFactory: (url) => new WebSocket2(url)
|
|
7338
|
+
});
|
|
7339
|
+
if (publisher) {
|
|
7340
|
+
publisher.connect();
|
|
7341
|
+
closeHqPublisher = () => publisher.close();
|
|
7342
|
+
restartHqBridges = (sessionId) => {
|
|
7343
|
+
stopHqBridges();
|
|
7344
|
+
const stops = [];
|
|
7345
|
+
const add = (start) => {
|
|
7346
|
+
try {
|
|
7347
|
+
stops.push(start());
|
|
7348
|
+
} catch {
|
|
7349
|
+
}
|
|
7350
|
+
};
|
|
7351
|
+
add(
|
|
7352
|
+
() => core.startSessionTelemetryBridge({
|
|
7353
|
+
publisher,
|
|
7354
|
+
events,
|
|
7355
|
+
sessionId,
|
|
7356
|
+
projectRoot: paths.projectRoot,
|
|
7357
|
+
projectName: path15.basename(paths.projectRoot),
|
|
7358
|
+
globalRoot: paths.globalRoot,
|
|
7359
|
+
initialAgents: statusTracker.getAgents(),
|
|
7360
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7361
|
+
})
|
|
7362
|
+
);
|
|
7363
|
+
add(
|
|
7364
|
+
() => core.startFleetTelemetryBridge({
|
|
7365
|
+
events,
|
|
7366
|
+
publisher,
|
|
7367
|
+
runId: sessionId,
|
|
7368
|
+
sessionId
|
|
7369
|
+
})
|
|
7370
|
+
);
|
|
7371
|
+
add(() => core.startBrainTelemetryBridge({ events, publisher, sessionId }));
|
|
7372
|
+
add(() => core.startWorktreeTelemetryBridge({ events, publisher, sessionId }));
|
|
7373
|
+
add(
|
|
7374
|
+
() => core.startToolTelemetryBridge({
|
|
7375
|
+
events,
|
|
7376
|
+
publisher,
|
|
7377
|
+
projectRoot: paths.projectRoot,
|
|
7378
|
+
sessionId
|
|
7379
|
+
})
|
|
7380
|
+
);
|
|
7381
|
+
add(() => core.startCostTelemetryBridge({ events, publisher, sessionId }));
|
|
7382
|
+
stopHqBridges = () => {
|
|
7383
|
+
for (const stop2 of stops.splice(0)) {
|
|
7384
|
+
try {
|
|
7385
|
+
stop2();
|
|
7386
|
+
} catch {
|
|
7387
|
+
}
|
|
7388
|
+
}
|
|
7389
|
+
};
|
|
7390
|
+
};
|
|
7391
|
+
restartHqBridges(activeSessionId);
|
|
7392
|
+
}
|
|
7393
|
+
} catch (err) {
|
|
7394
|
+
logger.debug?.(`WebUI HQ telemetry unavailable: ${errorMessage2(err)}`);
|
|
7395
|
+
}
|
|
7396
|
+
}
|
|
7397
|
+
const repointRecovery = async (sessionId) => {
|
|
7398
|
+
if (!recoveryLock) return;
|
|
7399
|
+
try {
|
|
7400
|
+
if (ownsRecoveryLock) await recoveryLock.clear();
|
|
7401
|
+
await recoveryLock.write(sessionId);
|
|
7402
|
+
ownsRecoveryLock = true;
|
|
7403
|
+
} catch {
|
|
7404
|
+
ownsRecoveryLock = false;
|
|
7405
|
+
}
|
|
7406
|
+
};
|
|
7407
|
+
const activate = async (sessionId) => {
|
|
7408
|
+
if (stopped || sessionId === activeSessionId) return;
|
|
7409
|
+
transition = transition.then(async () => {
|
|
7410
|
+
if (stopped || sessionId === activeSessionId) return;
|
|
7411
|
+
activeSessionId = sessionId;
|
|
7412
|
+
await register(sessionId);
|
|
7413
|
+
await repointRecovery(sessionId);
|
|
7414
|
+
try {
|
|
7415
|
+
restartHqBridges(sessionId);
|
|
7416
|
+
} catch (err) {
|
|
7417
|
+
logger.debug?.(`WebUI HQ session swap failed: ${errorMessage2(err)}`);
|
|
7418
|
+
}
|
|
7419
|
+
});
|
|
7420
|
+
await transition;
|
|
7421
|
+
};
|
|
7422
|
+
const stop = async () => {
|
|
7423
|
+
if (stopped) return;
|
|
7424
|
+
stopped = true;
|
|
7425
|
+
await transition.catch(() => void 0);
|
|
7426
|
+
statusTracker.stop();
|
|
7427
|
+
stopHqBridges();
|
|
7428
|
+
closeHqPublisher();
|
|
7429
|
+
fleetNotifier.dispose();
|
|
7430
|
+
try {
|
|
7431
|
+
await registry.markClosing();
|
|
7432
|
+
await registry.unregister();
|
|
7433
|
+
} catch {
|
|
7434
|
+
}
|
|
7435
|
+
if (recoveryLock && ownsRecoveryLock) {
|
|
7436
|
+
await recoveryLock.clear().catch(() => void 0);
|
|
7437
|
+
ownsRecoveryLock = false;
|
|
7438
|
+
}
|
|
7439
|
+
};
|
|
7440
|
+
return { statusTracker, activate, stop };
|
|
7441
|
+
}
|
|
7442
|
+
function errorMessage2(err) {
|
|
7443
|
+
return err instanceof Error ? err.message : String(err);
|
|
7444
|
+
}
|
|
7445
|
+
|
|
7120
7446
|
// src/server/pre-context-services.ts
|
|
7121
7447
|
var GITHUB_PROVIDERS_OVERLAY_URL = "https://raw.githubusercontent.com/WrongStack/WrongStack/main/packages/cli/data/providers.json";
|
|
7122
7448
|
async function createPreContextServices(input) {
|
|
7123
|
-
const { config, wpaths, logger, opts, projectRoot, workingDir, needsProvider } = input;
|
|
7449
|
+
const { config, wpaths, logger, opts, vault, projectRoot, workingDir, needsProvider } = input;
|
|
7124
7450
|
const modelsRegistry = opts.services?.modelsRegistry ?? new DefaultModelsRegistry({
|
|
7125
7451
|
cacheFile: wpaths.modelsCache,
|
|
7126
7452
|
ttlSeconds: 0,
|
|
@@ -7139,7 +7465,7 @@ async function createPreContextServices(input) {
|
|
|
7139
7465
|
await discoverAndMergeWebuiProviders({
|
|
7140
7466
|
config,
|
|
7141
7467
|
registry: modelsRegistry,
|
|
7142
|
-
cacheDir:
|
|
7468
|
+
cacheDir: path16.dirname(wpaths.modelsCache),
|
|
7143
7469
|
logger
|
|
7144
7470
|
});
|
|
7145
7471
|
} catch (err) {
|
|
@@ -7182,10 +7508,23 @@ async function createPreContextServices(input) {
|
|
|
7182
7508
|
configureDangerBypass(config.tools?.exec?.danger ?? {});
|
|
7183
7509
|
configureChildEnvGitIdentity(config.git?.identity ?? null);
|
|
7184
7510
|
console.log("[WebUI] Tool registry loaded:", toolRegistry.list().length, "tools");
|
|
7185
|
-
const
|
|
7511
|
+
const mcpTokenStore = new MCPVaultTokenStore(
|
|
7512
|
+
path16.join(wpaths.projectDir, "mcp-auth.json"),
|
|
7513
|
+
vault
|
|
7514
|
+
);
|
|
7515
|
+
const mcpAuthorizationManager = new MCPAuthorizationManager({ store: mcpTokenStore });
|
|
7516
|
+
const mcpRegistry = new MCPRegistry({
|
|
7517
|
+
toolRegistry,
|
|
7518
|
+
events,
|
|
7519
|
+
log: logger,
|
|
7520
|
+
cacheDir: wpaths.cacheDir,
|
|
7521
|
+
authorizationProviderFactory: createVaultBackedMcpAuthorizationProviderFactory({
|
|
7522
|
+
store: mcpTokenStore
|
|
7523
|
+
}),
|
|
7524
|
+
authorizationManager: mcpAuthorizationManager
|
|
7525
|
+
});
|
|
7186
7526
|
if (config.features.mcp && config.mcpServers) {
|
|
7187
7527
|
for (const [name2, cfg] of Object.entries(config.mcpServers)) {
|
|
7188
|
-
if (cfg.enabled === false) continue;
|
|
7189
7528
|
void mcpRegistry.start({ ...cfg, name: name2 }).catch((err) => {
|
|
7190
7529
|
logger.warn(`MCP server "${name2}" failed to start at boot`, err);
|
|
7191
7530
|
});
|
|
@@ -7200,7 +7539,7 @@ async function createPreContextServices(input) {
|
|
|
7200
7539
|
// surface via the SessionRegistry.
|
|
7201
7540
|
isSessionInUse: async (sessionId) => {
|
|
7202
7541
|
try {
|
|
7203
|
-
const registry =
|
|
7542
|
+
const registry = getSessionRegistry2(wpaths.globalRoot);
|
|
7204
7543
|
const live = await registry.listByProject(wpaths.projectSlug);
|
|
7205
7544
|
const hit = live.find((e) => e.sessionId === sessionId);
|
|
7206
7545
|
if (hit) {
|
|
@@ -7225,120 +7564,22 @@ async function createPreContextServices(input) {
|
|
|
7225
7564
|
await input.touchProject(projectRoot, workingDir);
|
|
7226
7565
|
} catch {
|
|
7227
7566
|
}
|
|
7228
|
-
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
|
|
7233
|
-
|
|
7567
|
+
const sessionIdentity = await createStandaloneSessionIdentityLifecycle({
|
|
7568
|
+
config,
|
|
7569
|
+
events,
|
|
7570
|
+
logger,
|
|
7571
|
+
paths: {
|
|
7572
|
+
globalRoot: wpaths.globalRoot,
|
|
7234
7573
|
projectRoot,
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
|
|
7243
|
-
|
|
7244
|
-
registry,
|
|
7245
|
-
sessionId: () => session.id,
|
|
7246
|
-
onUpdate: () => fleetNotifier.notify()
|
|
7247
|
-
});
|
|
7248
|
-
statusTracker.start();
|
|
7249
|
-
let stopHqSessionBridge;
|
|
7250
|
-
let hqTelemetryPublisher;
|
|
7251
|
-
const stopHqAuxBridges = [];
|
|
7252
|
-
try {
|
|
7253
|
-
const {
|
|
7254
|
-
createHqPublisherFromEnv,
|
|
7255
|
-
startSessionTelemetryBridge,
|
|
7256
|
-
startFleetTelemetryBridge,
|
|
7257
|
-
startBrainTelemetryBridge,
|
|
7258
|
-
startWorktreeTelemetryBridge,
|
|
7259
|
-
startToolTelemetryBridge,
|
|
7260
|
-
startCostTelemetryBridge
|
|
7261
|
-
} = await import("@wrongstack/core");
|
|
7262
|
-
const hqTelemetry = createHqPublisherFromEnv({
|
|
7263
|
-
clientKind: "webui",
|
|
7264
|
-
projectRoot,
|
|
7265
|
-
projectName: path15.basename(projectRoot),
|
|
7266
|
-
appConfig: config,
|
|
7267
|
-
socketFactory: (url) => new WebSocket2(url)
|
|
7268
|
-
});
|
|
7269
|
-
if (hqTelemetry) {
|
|
7270
|
-
hqTelemetry.connect();
|
|
7271
|
-
hqTelemetryPublisher = hqTelemetry;
|
|
7272
|
-
stopHqSessionBridge = startSessionTelemetryBridge({
|
|
7273
|
-
publisher: hqTelemetry,
|
|
7274
|
-
events,
|
|
7275
|
-
sessionId: session.id,
|
|
7276
|
-
projectRoot,
|
|
7277
|
-
projectName: path15.basename(projectRoot),
|
|
7278
|
-
globalRoot: wpaths.globalRoot,
|
|
7279
|
-
initialAgents: statusTracker?.getAgents(),
|
|
7280
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7281
|
-
});
|
|
7282
|
-
try {
|
|
7283
|
-
stopHqAuxBridges.push(
|
|
7284
|
-
startFleetTelemetryBridge({ events, publisher: hqTelemetry, runId: session.id, sessionId: session.id })
|
|
7285
|
-
);
|
|
7286
|
-
} catch {
|
|
7287
|
-
}
|
|
7288
|
-
try {
|
|
7289
|
-
stopHqAuxBridges.push(
|
|
7290
|
-
startBrainTelemetryBridge({ events, publisher: hqTelemetry, sessionId: session.id })
|
|
7291
|
-
);
|
|
7292
|
-
} catch {
|
|
7293
|
-
}
|
|
7294
|
-
try {
|
|
7295
|
-
stopHqAuxBridges.push(
|
|
7296
|
-
startWorktreeTelemetryBridge({ events, publisher: hqTelemetry, sessionId: session.id })
|
|
7297
|
-
);
|
|
7298
|
-
} catch {
|
|
7299
|
-
}
|
|
7300
|
-
try {
|
|
7301
|
-
stopHqAuxBridges.push(
|
|
7302
|
-
startToolTelemetryBridge({ events, publisher: hqTelemetry, projectRoot, sessionId: session.id })
|
|
7303
|
-
);
|
|
7304
|
-
} catch {
|
|
7305
|
-
}
|
|
7306
|
-
try {
|
|
7307
|
-
stopHqAuxBridges.push(
|
|
7308
|
-
startCostTelemetryBridge({ events, publisher: hqTelemetry, sessionId: session.id })
|
|
7309
|
-
);
|
|
7310
|
-
} catch {
|
|
7311
|
-
}
|
|
7312
|
-
}
|
|
7313
|
-
} catch {
|
|
7314
|
-
}
|
|
7315
|
-
const stopTracking = async () => {
|
|
7316
|
-
try {
|
|
7317
|
-
fleetNotifier.dispose();
|
|
7318
|
-
await registry.markClosing();
|
|
7319
|
-
statusTracker?.stop();
|
|
7320
|
-
stopHqSessionBridge?.();
|
|
7321
|
-
for (const stop of stopHqAuxBridges) {
|
|
7322
|
-
try {
|
|
7323
|
-
stop();
|
|
7324
|
-
} catch {
|
|
7325
|
-
}
|
|
7326
|
-
}
|
|
7327
|
-
hqTelemetryPublisher?.close();
|
|
7328
|
-
} catch {
|
|
7329
|
-
}
|
|
7330
|
-
};
|
|
7331
|
-
process.once("beforeExit", () => {
|
|
7332
|
-
void stopTracking();
|
|
7333
|
-
});
|
|
7334
|
-
process.once("SIGINT", () => {
|
|
7335
|
-
void stopTracking();
|
|
7336
|
-
});
|
|
7337
|
-
process.once("SIGTERM", () => {
|
|
7338
|
-
void stopTracking();
|
|
7339
|
-
});
|
|
7340
|
-
} catch {
|
|
7341
|
-
}
|
|
7574
|
+
projectSlug: wpaths.projectSlug,
|
|
7575
|
+
projectSessions: wpaths.projectSessions
|
|
7576
|
+
},
|
|
7577
|
+
workingDir,
|
|
7578
|
+
initialSessionId: session.id,
|
|
7579
|
+
sessionStore,
|
|
7580
|
+
manageRecoveryLock: !opts.services?.session
|
|
7581
|
+
});
|
|
7582
|
+
const statusTracker = sessionIdentity.statusTracker;
|
|
7342
7583
|
let context;
|
|
7343
7584
|
const tokenCounter = new DefaultTokenCounter({
|
|
7344
7585
|
registry: modelsRegistry,
|
|
@@ -7363,7 +7604,7 @@ async function createPreContextServices(input) {
|
|
|
7363
7604
|
const modelCapabilitiesRef = { current: modelCapabilities };
|
|
7364
7605
|
const skillLoader = config.features.skills ? new DefaultSkillLoader({ paths: wpaths }) : void 0;
|
|
7365
7606
|
const skillInstaller = config.features.skills ? new SkillInstaller({
|
|
7366
|
-
manifestPath:
|
|
7607
|
+
manifestPath: path16.join(wpaths.globalRoot, "installed-skills.json"),
|
|
7367
7608
|
projectSkillsDir: wpaths.inProjectSkills,
|
|
7368
7609
|
globalSkillsDir: wpaths.globalSkills,
|
|
7369
7610
|
projectHash: wpaths.projectHash,
|
|
@@ -7373,7 +7614,7 @@ async function createPreContextServices(input) {
|
|
|
7373
7614
|
const bundledPromptsDir = promptsEnabled ? (() => {
|
|
7374
7615
|
try {
|
|
7375
7616
|
const req = createRequire2(import.meta.url);
|
|
7376
|
-
return
|
|
7617
|
+
return path16.join(path16.dirname(req.resolve("@wrongstack/core/package.json")), "data", "prompts");
|
|
7377
7618
|
} catch {
|
|
7378
7619
|
return void 0;
|
|
7379
7620
|
}
|
|
@@ -7425,6 +7666,14 @@ async function createPreContextServices(input) {
|
|
|
7425
7666
|
const initialContextPolicy = resolveContextWindowPolicy(config.context);
|
|
7426
7667
|
context.meta["contextWindowMode"] = initialContextPolicy.id;
|
|
7427
7668
|
context.meta["contextWindowPolicy"] = initialContextPolicy;
|
|
7669
|
+
context.state.setMeta(
|
|
7670
|
+
"plan.path",
|
|
7671
|
+
sessionScopedPath(wpaths.projectSessions, session.id, ".plan.json")
|
|
7672
|
+
);
|
|
7673
|
+
context.state.setMeta(
|
|
7674
|
+
"task.path",
|
|
7675
|
+
sessionScopedPath(wpaths.projectSessions, session.id, ".tasks.json")
|
|
7676
|
+
);
|
|
7428
7677
|
seedContextMeta(config, context);
|
|
7429
7678
|
return {
|
|
7430
7679
|
modelsRegistry,
|
|
@@ -7441,6 +7690,7 @@ async function createPreContextServices(input) {
|
|
|
7441
7690
|
session,
|
|
7442
7691
|
sessionStartedAt,
|
|
7443
7692
|
statusTracker,
|
|
7693
|
+
sessionIdentity,
|
|
7444
7694
|
tokenCounter,
|
|
7445
7695
|
modeStore,
|
|
7446
7696
|
modeId,
|
|
@@ -7490,6 +7740,7 @@ function patchConfig(config, updates) {
|
|
|
7490
7740
|
}
|
|
7491
7741
|
|
|
7492
7742
|
// src/server/backend-services.ts
|
|
7743
|
+
import { join as join13 } from "node:path";
|
|
7493
7744
|
import {
|
|
7494
7745
|
Agent,
|
|
7495
7746
|
AutoCompactionMiddleware as AutoCompactionMiddlewareCtor,
|
|
@@ -7503,9 +7754,10 @@ import {
|
|
|
7503
7754
|
installDesignStudioMiddleware,
|
|
7504
7755
|
ObservableBrainArbiter as ObservableBrainArbiterCtor,
|
|
7505
7756
|
resolveContextWindowPolicy as resolveContextWindowPolicy2,
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
7757
|
+
BrainDecisionLedger,
|
|
7758
|
+
createBrainRuntime,
|
|
7759
|
+
resolveBrainConfigDefaults,
|
|
7760
|
+
EscalationRoutingBrainArbiter,
|
|
7509
7761
|
GlobalMailbox as GlobalMailbox2,
|
|
7510
7762
|
mailboxSessionTag,
|
|
7511
7763
|
SessionMemoryConsolidator,
|
|
@@ -7519,12 +7771,13 @@ import { toErrorMessage as toErrorMessage6 } from "@wrongstack/core/utils";
|
|
|
7519
7771
|
var REPLAY_LIMIT = 50;
|
|
7520
7772
|
var PAUSE_TIMEOUT_MS = 6e4;
|
|
7521
7773
|
var CollaborationWebSocketHandler = class {
|
|
7522
|
-
constructor(events, logger, reader, annotations, bus) {
|
|
7774
|
+
constructor(events, logger, reader, annotations, bus, options = {}) {
|
|
7523
7775
|
this.events = events;
|
|
7524
7776
|
this.logger = logger;
|
|
7525
7777
|
this.reader = reader;
|
|
7526
7778
|
this.annotations = annotations;
|
|
7527
7779
|
this.bus = bus;
|
|
7780
|
+
this.options = options;
|
|
7528
7781
|
this.subscribe();
|
|
7529
7782
|
this.bus?.onInjectionConsumed((info) => this.broadcastInjectionConsumed(info));
|
|
7530
7783
|
}
|
|
@@ -7533,6 +7786,7 @@ var CollaborationWebSocketHandler = class {
|
|
|
7533
7786
|
reader;
|
|
7534
7787
|
annotations;
|
|
7535
7788
|
bus;
|
|
7789
|
+
options;
|
|
7536
7790
|
clients = /* @__PURE__ */ new Set();
|
|
7537
7791
|
/** sessionId → participants currently watching it. */
|
|
7538
7792
|
bySession = /* @__PURE__ */ new Map();
|
|
@@ -7560,11 +7814,16 @@ var CollaborationWebSocketHandler = class {
|
|
|
7560
7814
|
handleMessage(ws, msg) {
|
|
7561
7815
|
if (msg.type === "collab.join") {
|
|
7562
7816
|
const payload = msg.payload;
|
|
7563
|
-
if (
|
|
7817
|
+
if (typeof payload?.sessionId !== "string" || payload.sessionId.trim().length === 0) {
|
|
7564
7818
|
this.send(ws, this.errorMessage("collab.join requires sessionId"));
|
|
7565
7819
|
return true;
|
|
7566
7820
|
}
|
|
7567
|
-
|
|
7821
|
+
const role = payload.role ?? "observer";
|
|
7822
|
+
if (role !== "observer" && role !== "annotator" && role !== "controller") {
|
|
7823
|
+
this.send(ws, this.errorMessage(`unknown collaboration role '${String(role)}'`));
|
|
7824
|
+
return true;
|
|
7825
|
+
}
|
|
7826
|
+
this.join(ws, payload.sessionId, role);
|
|
7568
7827
|
return true;
|
|
7569
7828
|
}
|
|
7570
7829
|
if (msg.type === "collab.leave") {
|
|
@@ -7599,6 +7858,20 @@ var CollaborationWebSocketHandler = class {
|
|
|
7599
7858
|
}
|
|
7600
7859
|
// ── Join / leave flow ──────────────────────────────────────────────────
|
|
7601
7860
|
join(ws, sessionId, role) {
|
|
7861
|
+
const activeSessionId = this.options.getActiveSessionId?.();
|
|
7862
|
+
if (activeSessionId !== void 0 && sessionId !== activeSessionId) {
|
|
7863
|
+
this.send(
|
|
7864
|
+
ws,
|
|
7865
|
+
this.errorMessage(
|
|
7866
|
+
`collab.join sessionId mismatch (active: ${activeSessionId})`
|
|
7867
|
+
)
|
|
7868
|
+
);
|
|
7869
|
+
return;
|
|
7870
|
+
}
|
|
7871
|
+
if (this.findParticipant(ws)) {
|
|
7872
|
+
this.send(ws, this.errorMessage("collab.join requires leaving the current session first"));
|
|
7873
|
+
return;
|
|
7874
|
+
}
|
|
7602
7875
|
if (role === "controller" && !this.bus) {
|
|
7603
7876
|
this.send(
|
|
7604
7877
|
ws,
|
|
@@ -7617,6 +7890,13 @@ var CollaborationWebSocketHandler = class {
|
|
|
7617
7890
|
);
|
|
7618
7891
|
return;
|
|
7619
7892
|
}
|
|
7893
|
+
if (role !== "observer" && this.options.authorizeRole?.({ ws, sessionId, requestedRole: role }) !== true) {
|
|
7894
|
+
this.send(
|
|
7895
|
+
ws,
|
|
7896
|
+
this.errorMessage(`role '${role}' requires explicit server authorization`)
|
|
7897
|
+
);
|
|
7898
|
+
return;
|
|
7899
|
+
}
|
|
7620
7900
|
const participant = {
|
|
7621
7901
|
participantId: randomUUID(),
|
|
7622
7902
|
ws,
|
|
@@ -7870,17 +8150,13 @@ var CollaborationWebSocketHandler = class {
|
|
|
7870
8150
|
type: "collab.event",
|
|
7871
8151
|
payload: { kind, payload, at: (/* @__PURE__ */ new Date()).toISOString() }
|
|
7872
8152
|
};
|
|
7873
|
-
const
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
`collab broadcast failed: ${toErrorMessage6(err)}`
|
|
7881
|
-
);
|
|
7882
|
-
}
|
|
7883
|
-
}
|
|
8153
|
+
const activeSessionId = this.options.getActiveSessionId?.();
|
|
8154
|
+
if (activeSessionId !== void 0) {
|
|
8155
|
+
this.broadcast(activeSessionId, msg);
|
|
8156
|
+
return;
|
|
8157
|
+
}
|
|
8158
|
+
for (const sessionId of this.bySession.keys()) {
|
|
8159
|
+
this.broadcast(sessionId, msg);
|
|
7884
8160
|
}
|
|
7885
8161
|
}
|
|
7886
8162
|
/**
|
|
@@ -8204,7 +8480,8 @@ var CollaborationWebSocketHandler = class {
|
|
|
8204
8480
|
* Bus callback: a queued injection was spliced into a real tool call. Re-emit
|
|
8205
8481
|
* `collab.injection.granted` with phase `'consumed'` and the now-known tool
|
|
8206
8482
|
* name. The injection carries no sessionId, so resolve it from the author's
|
|
8207
|
-
* current
|
|
8483
|
+
* current participant. If the author has left, fail closed rather than leak
|
|
8484
|
+
* the payload to unrelated sessions.
|
|
8208
8485
|
*/
|
|
8209
8486
|
broadcastInjectionConsumed(info) {
|
|
8210
8487
|
let sessionId = null;
|
|
@@ -8233,14 +8510,16 @@ var CollaborationWebSocketHandler = class {
|
|
|
8233
8510
|
if (sessionId) {
|
|
8234
8511
|
this.broadcast(sessionId, message(sessionId));
|
|
8235
8512
|
} else {
|
|
8236
|
-
|
|
8513
|
+
this.logger.debug?.(
|
|
8514
|
+
`collab: consumed injection ${info.toolUseId} has no live author; broadcast suppressed`
|
|
8515
|
+
);
|
|
8237
8516
|
}
|
|
8238
8517
|
}
|
|
8239
8518
|
};
|
|
8240
8519
|
|
|
8241
8520
|
// src/server/codebase-indexing.ts
|
|
8242
8521
|
import * as fs12 from "node:fs";
|
|
8243
|
-
import * as
|
|
8522
|
+
import * as path17 from "node:path";
|
|
8244
8523
|
import {
|
|
8245
8524
|
cancelPendingReindexes,
|
|
8246
8525
|
enqueueReindex,
|
|
@@ -8293,7 +8572,7 @@ function setupWebUICodebaseIndexing(deps2) {
|
|
|
8293
8572
|
if (!filename) return;
|
|
8294
8573
|
const rel = filename.toString();
|
|
8295
8574
|
if (isIgnored(rel)) return;
|
|
8296
|
-
const abs =
|
|
8575
|
+
const abs = path17.resolve(deps2.projectRoot, rel);
|
|
8297
8576
|
enqueueFile(abs);
|
|
8298
8577
|
});
|
|
8299
8578
|
watcher.on("error", (err) => deps2.logger.debug(`webui codebase index watcher error: ${err}`));
|
|
@@ -8306,7 +8585,7 @@ function setupWebUICodebaseIndexing(deps2) {
|
|
|
8306
8585
|
}
|
|
8307
8586
|
function enqueueFile(filePath) {
|
|
8308
8587
|
if (!idx.onEdit && !idx.watchExternal) return;
|
|
8309
|
-
const abs =
|
|
8588
|
+
const abs = path17.isAbsolute(filePath) ? path17.normalize(filePath) : path17.resolve(deps2.projectRoot, filePath);
|
|
8310
8589
|
if (!isInside2(deps2.projectRoot, abs) || !isIndexableFile(abs)) return;
|
|
8311
8590
|
enqueueReindex({
|
|
8312
8591
|
projectRoot: deps2.projectRoot,
|
|
@@ -8343,9 +8622,9 @@ function isIgnored(rel) {
|
|
|
8343
8622
|
return rel.split(/[/\\]/).some((seg) => IGNORE_DIRS.has(seg));
|
|
8344
8623
|
}
|
|
8345
8624
|
function isInside2(root, target) {
|
|
8346
|
-
const normalizedRoot =
|
|
8347
|
-
const normalizedTarget =
|
|
8348
|
-
return normalizedTarget === normalizedRoot || normalizedTarget.startsWith(normalizedRoot +
|
|
8625
|
+
const normalizedRoot = path17.resolve(root);
|
|
8626
|
+
const normalizedTarget = path17.resolve(target);
|
|
8627
|
+
return normalizedTarget === normalizedRoot || normalizedTarget.startsWith(normalizedRoot + path17.sep);
|
|
8349
8628
|
}
|
|
8350
8629
|
|
|
8351
8630
|
// src/server/discover-mailbox-bridge.ts
|
|
@@ -8878,21 +9157,59 @@ async function createAgentServices(input) {
|
|
|
8878
9157
|
agent.extensions.register(new SessionMemoryConsolidator({ memoryStore }));
|
|
8879
9158
|
}
|
|
8880
9159
|
console.log("[WebUI] Agent initialized");
|
|
8881
|
-
const
|
|
8882
|
-
|
|
8883
|
-
|
|
8884
|
-
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
9160
|
+
const brainCfg = resolveBrainConfigDefaults(config.brain, {
|
|
9161
|
+
fallbackModels: config.fallbackModels
|
|
9162
|
+
});
|
|
9163
|
+
const brainLedgerPath = join13(wpaths.projectDir, "brain-ledger.jsonl");
|
|
9164
|
+
let brainLedgerEnabled = brainCfg.ledger?.enabled !== false;
|
|
9165
|
+
let brainLedger;
|
|
9166
|
+
const startBrainLedger = () => {
|
|
9167
|
+
if (brainLedger) return;
|
|
9168
|
+
brainLedger = new BrainDecisionLedger({ events, filePath: brainLedgerPath });
|
|
9169
|
+
void brainLedger.start();
|
|
9170
|
+
};
|
|
9171
|
+
if (brainLedgerEnabled) startBrainLedger();
|
|
9172
|
+
const brainRuntime = createBrainRuntime({
|
|
9173
|
+
initialConfig: brainCfg,
|
|
9174
|
+
defaultProviderId: config.provider,
|
|
9175
|
+
sessionProvider: () => provider,
|
|
9176
|
+
sessionModel: () => context.model,
|
|
9177
|
+
resolveProvider: (providerId) => {
|
|
9178
|
+
const savedCfg = config.providers?.[providerId] ?? {};
|
|
9179
|
+
return providerRegistry.create({
|
|
9180
|
+
...savedCfg,
|
|
9181
|
+
apiKey: savedCfg.apiKey ?? config.apiKey,
|
|
9182
|
+
baseUrl: savedCfg.baseUrl ?? config.baseUrl,
|
|
9183
|
+
type: providerId
|
|
9184
|
+
});
|
|
9185
|
+
},
|
|
9186
|
+
ledger: {
|
|
9187
|
+
getPath: () => brainLedgerEnabled ? brainLedgerPath : void 0,
|
|
9188
|
+
isEnabled: () => brainLedgerEnabled,
|
|
9189
|
+
setEnabled: (on) => {
|
|
9190
|
+
brainLedgerEnabled = on;
|
|
9191
|
+
if (on) {
|
|
9192
|
+
startBrainLedger();
|
|
9193
|
+
} else {
|
|
9194
|
+
void brainLedger?.stop();
|
|
9195
|
+
brainLedger = void 0;
|
|
9196
|
+
}
|
|
9197
|
+
},
|
|
9198
|
+
failureStreakFor: (request) => brainLedger?.failureStreakFor(request) ?? 0,
|
|
9199
|
+
getDecisionDigest: (request) => brainLedger?.digestFor(request)
|
|
9200
|
+
},
|
|
9201
|
+
persist: input.persistBrainConfig
|
|
9202
|
+
});
|
|
9203
|
+
const brainSettings = {
|
|
9204
|
+
get maxAutoRisk() {
|
|
9205
|
+
return brainRuntime.getMaxAutoRisk();
|
|
9206
|
+
},
|
|
9207
|
+
set maxAutoRisk(level) {
|
|
9208
|
+
void brainRuntime.apply({ maxAutoRisk: level }).persisted;
|
|
9209
|
+
}
|
|
8889
9210
|
};
|
|
8890
9211
|
const brain = new ObservableBrainArbiterCtor(
|
|
8891
|
-
|
|
8892
|
-
policy: new DefaultBrainArbiter(),
|
|
8893
|
-
autonomous: autonomousBrain,
|
|
8894
|
-
getMaxAutoRisk: () => brainSettings.maxAutoRisk
|
|
8895
|
-
}),
|
|
9212
|
+
new EscalationRoutingBrainArbiter(brainRuntime.arbiter, void 0, () => "headless"),
|
|
8896
9213
|
events
|
|
8897
9214
|
);
|
|
8898
9215
|
container.bind(TOKENS2.BrainArbiter, () => brain);
|
|
@@ -8932,6 +9249,12 @@ async function createAgentServices(input) {
|
|
|
8932
9249
|
const brainMonitor = new BrainMonitor({
|
|
8933
9250
|
events,
|
|
8934
9251
|
brain,
|
|
9252
|
+
toolFailureStreak: brainCfg.monitor?.toolFailureStreak,
|
|
9253
|
+
errorStormCount: brainCfg.monitor?.errorStormCount,
|
|
9254
|
+
stallMs: brainCfg.monitor?.stallMs,
|
|
9255
|
+
fileChurnThreshold: brainCfg.monitor?.fileChurnThreshold,
|
|
9256
|
+
fileChurnWindowMs: brainCfg.monitor?.fileChurnWindowMs,
|
|
9257
|
+
cooldownMs: brainCfg.monitor?.cooldownMs,
|
|
8935
9258
|
sessionId: () => context.session?.id,
|
|
8936
9259
|
intervene: async ({ subject, body }) => {
|
|
8937
9260
|
const tag = mailboxSessionTag(input.sessionGetter().id);
|
|
@@ -8996,7 +9319,10 @@ async function createAgentServices(input) {
|
|
|
8996
9319
|
logger,
|
|
8997
9320
|
input.sessionReader,
|
|
8998
9321
|
input.annotationsStore,
|
|
8999
|
-
collabBus
|
|
9322
|
+
collabBus,
|
|
9323
|
+
{
|
|
9324
|
+
getActiveSessionId: () => context.session.id
|
|
9325
|
+
}
|
|
9000
9326
|
);
|
|
9001
9327
|
return {
|
|
9002
9328
|
collabBus,
|
|
@@ -9008,8 +9334,13 @@ async function createAgentServices(input) {
|
|
|
9008
9334
|
pipelines,
|
|
9009
9335
|
brain,
|
|
9010
9336
|
brainSettings,
|
|
9337
|
+
brainRuntime,
|
|
9011
9338
|
brainLog,
|
|
9012
9339
|
brainMonitor,
|
|
9340
|
+
// Getter: ledger toggles swap the instance, shutdown must stop the LIVE one.
|
|
9341
|
+
get brainLedger() {
|
|
9342
|
+
return brainLedger;
|
|
9343
|
+
},
|
|
9013
9344
|
codebaseIndexing,
|
|
9014
9345
|
autoPhaseHandler,
|
|
9015
9346
|
specsHandler,
|
|
@@ -9185,7 +9516,18 @@ function createConnectionHandler(opts) {
|
|
|
9185
9516
|
}
|
|
9186
9517
|
|
|
9187
9518
|
// src/server/message-dispatcher.ts
|
|
9188
|
-
import
|
|
9519
|
+
import path18 from "node:path";
|
|
9520
|
+
import {
|
|
9521
|
+
buildUserContentBlocks,
|
|
9522
|
+
IncomingImageError,
|
|
9523
|
+
parseIncomingImages
|
|
9524
|
+
} from "@wrongstack/core/utils";
|
|
9525
|
+
import {
|
|
9526
|
+
createToolVisionAdapters,
|
|
9527
|
+
ImageInputUnsupportedError,
|
|
9528
|
+
routeImagesForModel,
|
|
9529
|
+
VisionUrlBlockedError
|
|
9530
|
+
} from "@wrongstack/runtime/vision";
|
|
9189
9531
|
|
|
9190
9532
|
// src/server/autophase-routes.ts
|
|
9191
9533
|
async function handleAutoPhaseRoute(_ws, msg, handlers) {
|
|
@@ -9206,6 +9548,12 @@ async function handleBrainRoute(ws, msg, handlers) {
|
|
|
9206
9548
|
case "brain.ask":
|
|
9207
9549
|
await handlers.ask(ws, msg);
|
|
9208
9550
|
return true;
|
|
9551
|
+
case "brain.config.get":
|
|
9552
|
+
await handlers.configGet(ws, msg);
|
|
9553
|
+
return true;
|
|
9554
|
+
case "brain.config.set":
|
|
9555
|
+
await handlers.configSet(ws, msg);
|
|
9556
|
+
return true;
|
|
9209
9557
|
default:
|
|
9210
9558
|
return false;
|
|
9211
9559
|
}
|
|
@@ -9936,6 +10284,9 @@ async function handleKanbanRoute(ws, msg, ctx) {
|
|
|
9936
10284
|
"Kanban agent dispatch is only available from the CLI-hosted WebUI runtime."
|
|
9937
10285
|
);
|
|
9938
10286
|
return true;
|
|
10287
|
+
case "kanban.capabilities":
|
|
10288
|
+
ok(ws, type, { dispatchSupported: false });
|
|
10289
|
+
return true;
|
|
9939
10290
|
case "kanban.task.remove": {
|
|
9940
10291
|
const boardId = payload?.boardId;
|
|
9941
10292
|
const taskId = payload?.taskId;
|
|
@@ -10015,6 +10366,9 @@ async function handleMailboxRoute(ws, msg, handlers) {
|
|
|
10015
10366
|
case "mailbox.purge":
|
|
10016
10367
|
await handlers.purge(ws, msg);
|
|
10017
10368
|
return true;
|
|
10369
|
+
case "mailbox.compact":
|
|
10370
|
+
await handlers.compact(ws, msg);
|
|
10371
|
+
return true;
|
|
10018
10372
|
default:
|
|
10019
10373
|
return false;
|
|
10020
10374
|
}
|
|
@@ -10053,6 +10407,18 @@ async function handleMcpRoute(ws, msg, handlers) {
|
|
|
10053
10407
|
case "mcp.discover":
|
|
10054
10408
|
await handlers.discover(ws, msg);
|
|
10055
10409
|
return true;
|
|
10410
|
+
case "mcp.resources":
|
|
10411
|
+
await handlers.resources(ws, msg);
|
|
10412
|
+
return true;
|
|
10413
|
+
case "mcp.prompts":
|
|
10414
|
+
await handlers.prompts(ws, msg);
|
|
10415
|
+
return true;
|
|
10416
|
+
case "mcp.resource.read":
|
|
10417
|
+
await handlers.resourceRead(ws, msg);
|
|
10418
|
+
return true;
|
|
10419
|
+
case "mcp.prompt.get":
|
|
10420
|
+
await handlers.promptGet(ws, msg);
|
|
10421
|
+
return true;
|
|
10056
10422
|
default:
|
|
10057
10423
|
return false;
|
|
10058
10424
|
}
|
|
@@ -10442,7 +10808,7 @@ function createMessageDispatcher(opts) {
|
|
|
10442
10808
|
skillLoader: deps2.skillLoader,
|
|
10443
10809
|
skillInstaller: deps2.skillInstaller,
|
|
10444
10810
|
projectRoot,
|
|
10445
|
-
projectSkillsDir:
|
|
10811
|
+
projectSkillsDir: path18.join(projectRoot, ".wrongstack", "skills"),
|
|
10446
10812
|
globalSkillsDir: deps2.wpaths.globalSkills
|
|
10447
10813
|
};
|
|
10448
10814
|
}
|
|
@@ -10515,7 +10881,8 @@ function createMessageDispatcher(opts) {
|
|
|
10515
10881
|
}
|
|
10516
10882
|
case "user_message": {
|
|
10517
10883
|
if (!ensureCurrentSession(ws, msg, "user_message")) return;
|
|
10518
|
-
const
|
|
10884
|
+
const userPayload = msg.payload;
|
|
10885
|
+
const content = userPayload.content;
|
|
10519
10886
|
if (runLock.get()) {
|
|
10520
10887
|
send(ws, {
|
|
10521
10888
|
type: "error",
|
|
@@ -10529,8 +10896,24 @@ function createMessageDispatcher(opts) {
|
|
|
10529
10896
|
const thisRun = new AbortController();
|
|
10530
10897
|
runLock.set(thisRun);
|
|
10531
10898
|
try {
|
|
10899
|
+
let input = content;
|
|
10900
|
+
const imageBlocks = parseIncomingImages(userPayload.images, userPayload.imageBase64);
|
|
10901
|
+
if (imageBlocks.length > 0) {
|
|
10902
|
+
const routed = await routeImagesForModel(
|
|
10903
|
+
buildUserContentBlocks(content, imageBlocks),
|
|
10904
|
+
{
|
|
10905
|
+
supportsVision: deps2.agent.ctx.provider.capabilities.vision,
|
|
10906
|
+
adapters: () => createToolVisionAdapters(deps2.agent.tools),
|
|
10907
|
+
ctx: deps2.agent.ctx,
|
|
10908
|
+
signal: thisRun.signal,
|
|
10909
|
+
providerId: deps2.agent.ctx.provider.id,
|
|
10910
|
+
model: deps2.agent.ctx.model
|
|
10911
|
+
}
|
|
10912
|
+
);
|
|
10913
|
+
input = routed.blocks;
|
|
10914
|
+
}
|
|
10532
10915
|
const maxIt = typeof deps2.context.meta["maxIterations"] === "number" ? deps2.context.meta["maxIterations"] : void 0;
|
|
10533
|
-
const result = await deps2.agent.run(
|
|
10916
|
+
const result = await deps2.agent.run(input, {
|
|
10534
10917
|
signal: thisRun.signal,
|
|
10535
10918
|
maxIterations: maxIt
|
|
10536
10919
|
});
|
|
@@ -10548,13 +10931,24 @@ function createMessageDispatcher(opts) {
|
|
|
10548
10931
|
})
|
|
10549
10932
|
});
|
|
10550
10933
|
} catch (err) {
|
|
10551
|
-
|
|
10552
|
-
|
|
10553
|
-
|
|
10554
|
-
|
|
10555
|
-
|
|
10556
|
-
|
|
10557
|
-
|
|
10934
|
+
if (err instanceof IncomingImageError || err instanceof ImageInputUnsupportedError || err instanceof VisionUrlBlockedError) {
|
|
10935
|
+
send(ws, {
|
|
10936
|
+
type: "error",
|
|
10937
|
+
payload: sessionPayload2({
|
|
10938
|
+
phase: "user_message",
|
|
10939
|
+
...err instanceof ImageInputUnsupportedError ? { code: "vision_unsupported" } : {},
|
|
10940
|
+
message: err.message
|
|
10941
|
+
})
|
|
10942
|
+
});
|
|
10943
|
+
} else {
|
|
10944
|
+
send(ws, {
|
|
10945
|
+
type: "error",
|
|
10946
|
+
payload: sessionPayload2({
|
|
10947
|
+
phase: "agent.run",
|
|
10948
|
+
message: errMessage(err)
|
|
10949
|
+
})
|
|
10950
|
+
});
|
|
10951
|
+
}
|
|
10558
10952
|
} finally {
|
|
10559
10953
|
if (runLock.get() === thisRun) {
|
|
10560
10954
|
runLock.set(null);
|
|
@@ -10602,7 +10996,7 @@ function createMessageDispatcher(opts) {
|
|
|
10602
10996
|
break;
|
|
10603
10997
|
}
|
|
10604
10998
|
case "tool.disable": {
|
|
10605
|
-
const name2 =
|
|
10999
|
+
const name2 = msg.payload?.name;
|
|
10606
11000
|
if (!name2) {
|
|
10607
11001
|
send(ws, { type: "error", payload: { message: "tool.disable requires a name" } });
|
|
10608
11002
|
break;
|
|
@@ -10617,7 +11011,7 @@ function createMessageDispatcher(opts) {
|
|
|
10617
11011
|
break;
|
|
10618
11012
|
}
|
|
10619
11013
|
case "tool.enable": {
|
|
10620
|
-
const name2 =
|
|
11014
|
+
const name2 = msg.payload?.name;
|
|
10621
11015
|
if (!name2) {
|
|
10622
11016
|
send(ws, { type: "error", payload: { message: "tool.enable requires a name" } });
|
|
10623
11017
|
break;
|
|
@@ -10661,6 +11055,14 @@ function createMessageDispatcher(opts) {
|
|
|
10661
11055
|
throw new Error("handleMcpRoute did not claim mcp.restart \u2014 check chain order");
|
|
10662
11056
|
case "mcp.discover":
|
|
10663
11057
|
throw new Error("handleMcpRoute did not claim mcp.discover \u2014 check chain order");
|
|
11058
|
+
case "mcp.resources":
|
|
11059
|
+
throw new Error("handleMcpRoute did not claim mcp.resources \u2014 check chain order");
|
|
11060
|
+
case "mcp.prompts":
|
|
11061
|
+
throw new Error("handleMcpRoute did not claim mcp.prompts \u2014 check chain order");
|
|
11062
|
+
case "mcp.resource.read":
|
|
11063
|
+
throw new Error("handleMcpRoute did not claim mcp.resource.read \u2014 check chain order");
|
|
11064
|
+
case "mcp.prompt.get":
|
|
11065
|
+
throw new Error("handleMcpRoute did not claim mcp.prompt.get \u2014 check chain order");
|
|
10664
11066
|
// Skills — full request→response cycle lives in skills-handlers.ts.
|
|
10665
11067
|
case "skills.list":
|
|
10666
11068
|
await handleSkillsList(ws, makeSkillsContext());
|
|
@@ -10961,7 +11363,19 @@ var PREF_KEYS = [
|
|
|
10961
11363
|
"favoriteModels",
|
|
10962
11364
|
"favoriteModelsOnly",
|
|
10963
11365
|
"modelMatrix",
|
|
10964
|
-
"fallbackAuto"
|
|
11366
|
+
"fallbackAuto",
|
|
11367
|
+
// Refiner + TUI visual prefs (parity with the CLI's embedded server —
|
|
11368
|
+
// these were browser-editable there but rejected as unknown keys here).
|
|
11369
|
+
"refinerProvider",
|
|
11370
|
+
"refinerModel",
|
|
11371
|
+
"thinkingWord",
|
|
11372
|
+
"statuslineMode",
|
|
11373
|
+
"animationStyle",
|
|
11374
|
+
// Safety / system prefs (parity with /settings breaker, fs-access, debug-stream).
|
|
11375
|
+
"breakerEnabled",
|
|
11376
|
+
"breakerAutoKillResetMs",
|
|
11377
|
+
"fsAccess",
|
|
11378
|
+
"debugStream"
|
|
10965
11379
|
];
|
|
10966
11380
|
function prefSnapshot(contextMeta) {
|
|
10967
11381
|
const snapshot = {};
|
|
@@ -11032,6 +11446,16 @@ async function persistPrefsToConfig(deps2, holder, payload) {
|
|
|
11032
11446
|
setAutonomy("enhanceDelayMs", payload["enhanceDelayMs"]);
|
|
11033
11447
|
if (typeof payload["enhanceLanguage"] === "string")
|
|
11034
11448
|
setAutonomy("enhanceLanguage", payload["enhanceLanguage"]);
|
|
11449
|
+
if (typeof payload["refinerProvider"] === "string")
|
|
11450
|
+
setAutonomy("refinerProvider", payload["refinerProvider"]);
|
|
11451
|
+
if (typeof payload["refinerModel"] === "string")
|
|
11452
|
+
setAutonomy("refinerModel", payload["refinerModel"]);
|
|
11453
|
+
if (typeof payload["thinkingWord"] === "string")
|
|
11454
|
+
setAutonomy("thinkingWord", payload["thinkingWord"]);
|
|
11455
|
+
if (typeof payload["statuslineMode"] === "string")
|
|
11456
|
+
setAutonomy("statuslineMode", payload["statuslineMode"]);
|
|
11457
|
+
if (typeof payload["animationStyle"] === "string")
|
|
11458
|
+
setAutonomy("animationStyle", payload["animationStyle"]);
|
|
11035
11459
|
if (autonomyTouched) decrypted.autonomy = autonomyCfg;
|
|
11036
11460
|
if (typeof payload["nextPrediction"] === "boolean")
|
|
11037
11461
|
decrypted.nextPrediction = payload["nextPrediction"];
|
|
@@ -11149,16 +11573,34 @@ async function persistPrefsToConfig(deps2, holder, payload) {
|
|
|
11149
11573
|
}
|
|
11150
11574
|
decrypted.modelRuntime = mr;
|
|
11151
11575
|
}
|
|
11576
|
+
if (typeof payload["breakerEnabled"] === "boolean" || typeof payload["breakerAutoKillResetMs"] === "number") {
|
|
11577
|
+
const cb = decrypted.circuitBreaker ?? {};
|
|
11578
|
+
if (typeof payload["breakerEnabled"] === "boolean") cb.enabled = payload["breakerEnabled"];
|
|
11579
|
+
if (typeof payload["breakerAutoKillResetMs"] === "number")
|
|
11580
|
+
cb.autoKillResetMs = payload["breakerAutoKillResetMs"];
|
|
11581
|
+
decrypted.circuitBreaker = cb;
|
|
11582
|
+
}
|
|
11583
|
+
if (payload["fsAccess"] === "unrestricted" || payload["fsAccess"] === "project") {
|
|
11584
|
+
const restrict = payload["fsAccess"] === "project";
|
|
11585
|
+
const toolsCfg = decrypted.tools ?? {};
|
|
11586
|
+
toolsCfg.restrictToProjectRoot = restrict;
|
|
11587
|
+
decrypted.tools = toolsCfg;
|
|
11588
|
+
const featsCfg = decrypted.features ?? {};
|
|
11589
|
+
featsCfg.allowOutsideProjectRoot = !restrict;
|
|
11590
|
+
decrypted.features = featsCfg;
|
|
11591
|
+
}
|
|
11592
|
+
if (typeof payload["debugStream"] === "boolean")
|
|
11593
|
+
decrypted.debugStream = payload["debugStream"];
|
|
11152
11594
|
}, "prefs");
|
|
11153
11595
|
}
|
|
11154
11596
|
|
|
11155
11597
|
// src/server/projects-manifest.ts
|
|
11156
11598
|
import * as fs14 from "node:fs/promises";
|
|
11157
|
-
import * as
|
|
11599
|
+
import * as path19 from "node:path";
|
|
11158
11600
|
import { projectSlug } from "@wrongstack/core";
|
|
11159
11601
|
function projectsJsonPath(globalConfigPath) {
|
|
11160
|
-
const base =
|
|
11161
|
-
return
|
|
11602
|
+
const base = path19.dirname(globalConfigPath);
|
|
11603
|
+
return path19.join(base, "projects.json");
|
|
11162
11604
|
}
|
|
11163
11605
|
async function loadManifest(globalConfigPath) {
|
|
11164
11606
|
try {
|
|
@@ -11171,15 +11613,15 @@ async function loadManifest(globalConfigPath) {
|
|
|
11171
11613
|
}
|
|
11172
11614
|
async function saveManifest(manifest, globalConfigPath) {
|
|
11173
11615
|
const file = projectsJsonPath(globalConfigPath);
|
|
11174
|
-
await fs14.mkdir(
|
|
11616
|
+
await fs14.mkdir(path19.dirname(file), { recursive: true });
|
|
11175
11617
|
await fs14.writeFile(file, JSON.stringify(manifest, null, 2), "utf8");
|
|
11176
11618
|
}
|
|
11177
11619
|
function generateProjectSlug(rootPath) {
|
|
11178
11620
|
return projectSlug(rootPath);
|
|
11179
11621
|
}
|
|
11180
11622
|
async function ensureProjectDataDir(slug, globalConfigPath) {
|
|
11181
|
-
const base =
|
|
11182
|
-
const dir =
|
|
11623
|
+
const base = path19.dirname(globalConfigPath);
|
|
11624
|
+
const dir = path19.join(base, "projects", slug);
|
|
11183
11625
|
await fs14.mkdir(dir, { recursive: true });
|
|
11184
11626
|
return dir;
|
|
11185
11627
|
}
|
|
@@ -11596,6 +12038,16 @@ async function handleMailboxPurge(ws, deps2, opts) {
|
|
|
11596
12038
|
send(ws, { type: "mailbox.purged", payload: { error: errMessage(err) } });
|
|
11597
12039
|
}
|
|
11598
12040
|
}
|
|
12041
|
+
async function handleMailboxCompact(ws, deps2, opts) {
|
|
12042
|
+
try {
|
|
12043
|
+
const dir = resolveProjectDir2(deps2.projectRoot, deps2.globalRoot);
|
|
12044
|
+
const mb = new GlobalMailbox3(dir);
|
|
12045
|
+
const result = await mb.autoCompact(opts);
|
|
12046
|
+
send(ws, { type: "mailbox.compacted", payload: result });
|
|
12047
|
+
} catch (err) {
|
|
12048
|
+
send(ws, { type: "mailbox.compacted", payload: { error: errMessage(err) } });
|
|
12049
|
+
}
|
|
12050
|
+
}
|
|
11599
12051
|
|
|
11600
12052
|
// src/server/mode-handlers.ts
|
|
11601
12053
|
import {
|
|
@@ -11684,7 +12136,7 @@ function createModeHandlers(ctx) {
|
|
|
11684
12136
|
}
|
|
11685
12137
|
|
|
11686
12138
|
// src/server/project-handlers.ts
|
|
11687
|
-
import * as
|
|
12139
|
+
import * as path20 from "node:path";
|
|
11688
12140
|
function createProjectHandlers(ctx) {
|
|
11689
12141
|
return {
|
|
11690
12142
|
listProjects: async (ws) => {
|
|
@@ -11710,7 +12162,7 @@ function createProjectHandlers(ctx) {
|
|
|
11710
12162
|
selectProject: async (ws, msg) => {
|
|
11711
12163
|
const payload = msg.payload;
|
|
11712
12164
|
const root = typeof payload?.root === "string" ? payload.root : "";
|
|
11713
|
-
const name2 = typeof payload?.name === "string" ? payload.name : root ?
|
|
12165
|
+
const name2 = typeof payload?.name === "string" ? payload.name : root ? path20.basename(root) : "";
|
|
11714
12166
|
send(ws, {
|
|
11715
12167
|
type: "projects.selected",
|
|
11716
12168
|
payload: {
|
|
@@ -11745,12 +12197,12 @@ function createProjectHandlers(ctx) {
|
|
|
11745
12197
|
}
|
|
11746
12198
|
|
|
11747
12199
|
// src/server/session-handlers.ts
|
|
11748
|
-
import * as path20 from "node:path";
|
|
11749
12200
|
import {
|
|
11750
12201
|
DEFAULT_CONTEXT_WINDOW_MODE_ID,
|
|
11751
12202
|
repairToolUseAdjacency,
|
|
11752
12203
|
resolveContextWindowPolicy as resolveContextWindowPolicy3
|
|
11753
12204
|
} from "@wrongstack/core";
|
|
12205
|
+
import { sessionScopedPath as sessionScopedPath2 } from "@wrongstack/core/utils";
|
|
11754
12206
|
function createSessionHandlers(ctx) {
|
|
11755
12207
|
const currentSessionId = () => ctx.getSession().id;
|
|
11756
12208
|
const sessionPayload2 = (payload) => {
|
|
@@ -11776,33 +12228,46 @@ function createSessionHandlers(ctx) {
|
|
|
11776
12228
|
});
|
|
11777
12229
|
return false;
|
|
11778
12230
|
};
|
|
12231
|
+
const finalizeSession = async (writer) => {
|
|
12232
|
+
await writer.append({
|
|
12233
|
+
type: "session_end",
|
|
12234
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12235
|
+
usage: ctx.tokenCounter.total()
|
|
12236
|
+
}).catch(() => void 0);
|
|
12237
|
+
await writer.close().catch(() => void 0);
|
|
12238
|
+
};
|
|
12239
|
+
const activateSession = async (next, messages, usage) => {
|
|
12240
|
+
const current = ctx.getSession();
|
|
12241
|
+
if (current !== next) await finalizeSession(current);
|
|
12242
|
+
ctx.setSession(next);
|
|
12243
|
+
ctx.context.session = next;
|
|
12244
|
+
ctx.context.state.replaceMessages(messages);
|
|
12245
|
+
ctx.context.state.replaceTodos([]);
|
|
12246
|
+
ctx.context.readFiles.clear();
|
|
12247
|
+
ctx.context.fileMtimes.clear();
|
|
12248
|
+
ctx.context.state.setMeta(
|
|
12249
|
+
"plan.path",
|
|
12250
|
+
sessionScopedPath2(ctx.sessionsDir, next.id, ".plan.json")
|
|
12251
|
+
);
|
|
12252
|
+
ctx.context.state.setMeta(
|
|
12253
|
+
"task.path",
|
|
12254
|
+
sessionScopedPath2(ctx.sessionsDir, next.id, ".tasks.json")
|
|
12255
|
+
);
|
|
12256
|
+
ctx.tokenCounter.reset();
|
|
12257
|
+
if (usage) ctx.tokenCounter.account(usage, ctx.config.model);
|
|
12258
|
+
ctx.setSessionStartedAt(Date.now());
|
|
12259
|
+
await ctx.onSessionSwapped(next.id);
|
|
12260
|
+
};
|
|
11779
12261
|
return {
|
|
11780
12262
|
newSession: async (ws, msg) => {
|
|
11781
12263
|
if (!ensureCurrentSession(ws, msg, "session.new")) return;
|
|
11782
|
-
const session = ctx.getSession();
|
|
11783
|
-
try {
|
|
11784
|
-
await session.append({
|
|
11785
|
-
type: "session_end",
|
|
11786
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11787
|
-
usage: ctx.tokenCounter.total()
|
|
11788
|
-
});
|
|
11789
|
-
await session.close();
|
|
11790
|
-
} catch {
|
|
11791
|
-
}
|
|
11792
12264
|
const next = await ctx.getSessionStore().create({
|
|
11793
12265
|
id: "",
|
|
11794
12266
|
title: "",
|
|
11795
12267
|
model: ctx.config.model,
|
|
11796
12268
|
provider: ctx.config.provider
|
|
11797
12269
|
});
|
|
11798
|
-
|
|
11799
|
-
ctx.context.session = next;
|
|
11800
|
-
ctx.context.state.replaceMessages([]);
|
|
11801
|
-
ctx.context.state.replaceTodos([]);
|
|
11802
|
-
ctx.context.readFiles.clear();
|
|
11803
|
-
ctx.context.fileMtimes.clear();
|
|
11804
|
-
ctx.tokenCounter.reset();
|
|
11805
|
-
ctx.setSessionStartedAt(Date.now());
|
|
12270
|
+
await activateSession(next, []);
|
|
11806
12271
|
broadcast(ctx.clients, { type: "session.start", payload: await ctx.sessionStartPayload() });
|
|
11807
12272
|
},
|
|
11808
12273
|
clearContext: async (ws, msg) => {
|
|
@@ -12058,23 +12523,7 @@ function createSessionHandlers(ctx) {
|
|
|
12058
12523
|
return;
|
|
12059
12524
|
}
|
|
12060
12525
|
const resumed = await ctx.getSessionStore().resume(id);
|
|
12061
|
-
|
|
12062
|
-
await current.append({
|
|
12063
|
-
type: "session_end",
|
|
12064
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12065
|
-
usage: ctx.tokenCounter.total()
|
|
12066
|
-
});
|
|
12067
|
-
await current.close();
|
|
12068
|
-
} catch {
|
|
12069
|
-
}
|
|
12070
|
-
ctx.setSession(resumed.writer);
|
|
12071
|
-
ctx.context.session = resumed.writer;
|
|
12072
|
-
ctx.context.state.replaceMessages(resumed.data.messages);
|
|
12073
|
-
ctx.context.readFiles.clear();
|
|
12074
|
-
ctx.context.fileMtimes.clear();
|
|
12075
|
-
ctx.tokenCounter.reset();
|
|
12076
|
-
ctx.tokenCounter.account(resumed.data.usage, ctx.config.model);
|
|
12077
|
-
ctx.setSessionStartedAt(Date.now());
|
|
12526
|
+
await activateSession(resumed.writer, resumed.data.messages, resumed.data.usage);
|
|
12078
12527
|
broadcast(ctx.clients, {
|
|
12079
12528
|
type: "session.start",
|
|
12080
12529
|
payload: {
|
|
@@ -12098,10 +12547,7 @@ function createSessionHandlers(ctx) {
|
|
|
12098
12547
|
try {
|
|
12099
12548
|
const { DefaultSessionRewinder } = await import("@wrongstack/core");
|
|
12100
12549
|
const projectRoot = ctx.getProjectRoot();
|
|
12101
|
-
const rewinder = new DefaultSessionRewinder(
|
|
12102
|
-
path20.join(projectRoot, ".wrongstack", "sessions"),
|
|
12103
|
-
projectRoot
|
|
12104
|
-
);
|
|
12550
|
+
const rewinder = new DefaultSessionRewinder(ctx.sessionsDir, projectRoot);
|
|
12105
12551
|
const checkpoints = await rewinder.listCheckpoints(ctx.getSession().id);
|
|
12106
12552
|
send(ws, { type: "session.checkpoints", payload: sessionPayload2({ checkpoints }) });
|
|
12107
12553
|
} catch {
|
|
@@ -12114,10 +12560,7 @@ function createSessionHandlers(ctx) {
|
|
|
12114
12560
|
try {
|
|
12115
12561
|
const { DefaultSessionRewinder } = await import("@wrongstack/core");
|
|
12116
12562
|
const projectRoot = ctx.getProjectRoot();
|
|
12117
|
-
const rewinder = new DefaultSessionRewinder(
|
|
12118
|
-
path20.join(projectRoot, ".wrongstack", "sessions"),
|
|
12119
|
-
projectRoot
|
|
12120
|
-
);
|
|
12563
|
+
const rewinder = new DefaultSessionRewinder(ctx.sessionsDir, projectRoot);
|
|
12121
12564
|
await rewinder.rewindToCheckpoint(ctx.getSession().id, checkpointIndex);
|
|
12122
12565
|
await ctx.context.session.truncateToCheckpoint(checkpointIndex);
|
|
12123
12566
|
sendResult(ws, true, `Rewound to checkpoint ${checkpointIndex}`);
|
|
@@ -12333,8 +12776,10 @@ function buildRoutes(state, deps2, cb) {
|
|
|
12333
12776
|
getProjectRoot: state.getProjectRoot,
|
|
12334
12777
|
getSession: state.getSession,
|
|
12335
12778
|
getSessionStore: state.getSessionStore,
|
|
12779
|
+
sessionsDir: deps2.wpaths.projectSessions,
|
|
12336
12780
|
setSession: state.setSession,
|
|
12337
12781
|
setSessionStartedAt: state.setSessionStartedAt,
|
|
12782
|
+
onSessionSwapped: cb.onSessionSwapped,
|
|
12338
12783
|
sessionStartPayload: cb.sessionStartPayload
|
|
12339
12784
|
});
|
|
12340
12785
|
const projectRoutes = createProjectHandlers({
|
|
@@ -12429,6 +12874,17 @@ function buildRoutes(state, deps2, cb) {
|
|
|
12429
12874
|
deps2.pipelines.contextWindow.remove("AutoCompaction", { optional: true });
|
|
12430
12875
|
}
|
|
12431
12876
|
}
|
|
12877
|
+
if (typeof payload["breakerEnabled"] === "boolean" || typeof payload["breakerAutoKillResetMs"] === "number") {
|
|
12878
|
+
const { getProcessRegistry } = await import("@wrongstack/tools");
|
|
12879
|
+
getProcessRegistry().setBreakerConfig({
|
|
12880
|
+
...typeof payload["breakerEnabled"] === "boolean" ? { enabled: payload["breakerEnabled"] } : {},
|
|
12881
|
+
...typeof payload["breakerAutoKillResetMs"] === "number" ? { autoKillResetMs: payload["breakerAutoKillResetMs"] } : {}
|
|
12882
|
+
});
|
|
12883
|
+
}
|
|
12884
|
+
if (typeof payload["debugStream"] === "boolean") {
|
|
12885
|
+
const { setDebugStreamEnabled } = await import("@wrongstack/providers");
|
|
12886
|
+
setDebugStreamEnabled(payload["debugStream"]);
|
|
12887
|
+
}
|
|
12432
12888
|
if (typeof payload["logLevel"] === "string") {
|
|
12433
12889
|
const valid = ["debug", "info", "warn", "error"];
|
|
12434
12890
|
if (valid.includes(payload["logLevel"])) {
|
|
@@ -12506,6 +12962,13 @@ function buildRoutes(state, deps2, cb) {
|
|
|
12506
12962
|
{ projectRoot: state.getProjectRoot(), globalRoot: path21.dirname(deps2.globalConfigPath) },
|
|
12507
12963
|
parsed.value
|
|
12508
12964
|
);
|
|
12965
|
+
},
|
|
12966
|
+
compact: (ws, msg) => {
|
|
12967
|
+
return handleMailboxCompact(
|
|
12968
|
+
ws,
|
|
12969
|
+
{ projectRoot: state.getProjectRoot(), globalRoot: path21.dirname(deps2.globalConfigPath) },
|
|
12970
|
+
msg.payload ?? {}
|
|
12971
|
+
);
|
|
12509
12972
|
}
|
|
12510
12973
|
};
|
|
12511
12974
|
const mcpRoutes = {
|
|
@@ -12518,14 +12981,29 @@ function buildRoutes(state, deps2, cb) {
|
|
|
12518
12981
|
sleep: (ws, msg) => handleMcpSleep(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
12519
12982
|
wake: (ws, msg) => handleMcpWake(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
12520
12983
|
restart: (ws, msg) => handleMcpRestart(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
12521
|
-
discover: (ws, msg) => handleMcpDiscover(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry)
|
|
12984
|
+
discover: (ws, msg) => handleMcpDiscover(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
12985
|
+
resources: (ws, msg) => handleMcpResources(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
12986
|
+
prompts: (ws, msg) => handleMcpPrompts(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
12987
|
+
resourceRead: (ws, msg) => handleMcpResourceRead(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
12988
|
+
promptGet: (ws, msg) => handleMcpPromptGet(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry)
|
|
12989
|
+
};
|
|
12990
|
+
const sendBrainStatus = (ws) => {
|
|
12991
|
+
const snapshot = deps2.brainRuntime.getSnapshot();
|
|
12992
|
+
send(ws, {
|
|
12993
|
+
type: "brain.status",
|
|
12994
|
+
payload: {
|
|
12995
|
+
maxAutoRisk: deps2.brainSettings.maxAutoRisk,
|
|
12996
|
+
log: deps2.brainLog,
|
|
12997
|
+
mode: snapshot.mode,
|
|
12998
|
+
poolLabels: snapshot.poolLabels,
|
|
12999
|
+
councilLabels: snapshot.councilLabels,
|
|
13000
|
+
ledgerPath: snapshot.ledger.path
|
|
13001
|
+
}
|
|
13002
|
+
});
|
|
12522
13003
|
};
|
|
12523
13004
|
const brainRoutes = {
|
|
12524
13005
|
status: (ws) => {
|
|
12525
|
-
|
|
12526
|
-
type: "brain.status",
|
|
12527
|
-
payload: { maxAutoRisk: deps2.brainSettings.maxAutoRisk, log: deps2.brainLog }
|
|
12528
|
-
});
|
|
13006
|
+
sendBrainStatus(ws);
|
|
12529
13007
|
},
|
|
12530
13008
|
risk: (ws, msg) => {
|
|
12531
13009
|
const parsed = validateBrainRiskPayload(msg.payload);
|
|
@@ -12535,11 +13013,43 @@ function buildRoutes(state, deps2, cb) {
|
|
|
12535
13013
|
}
|
|
12536
13014
|
const { level } = parsed.value;
|
|
12537
13015
|
deps2.brainSettings.maxAutoRisk = level;
|
|
13016
|
+
sendBrainStatus(ws);
|
|
13017
|
+
},
|
|
13018
|
+
configGet: (ws) => {
|
|
12538
13019
|
send(ws, {
|
|
12539
|
-
type: "brain.
|
|
12540
|
-
payload: {
|
|
13020
|
+
type: "brain.config",
|
|
13021
|
+
payload: { config: deps2.brainRuntime.getSnapshot(), persisted: true }
|
|
12541
13022
|
});
|
|
12542
13023
|
},
|
|
13024
|
+
configSet: async (ws, msg) => {
|
|
13025
|
+
const parsed = validateBrainConfigSetPayload(msg.payload);
|
|
13026
|
+
if (!parsed.ok) {
|
|
13027
|
+
sendResult(ws, false, parsed.message);
|
|
13028
|
+
return;
|
|
13029
|
+
}
|
|
13030
|
+
try {
|
|
13031
|
+
const { persisted } = deps2.brainRuntime.apply(parsed.value.patch);
|
|
13032
|
+
const result = await persisted;
|
|
13033
|
+
send(ws, {
|
|
13034
|
+
type: "brain.config",
|
|
13035
|
+
payload: {
|
|
13036
|
+
config: deps2.brainRuntime.getSnapshot(),
|
|
13037
|
+
persisted: result.ok,
|
|
13038
|
+
...result.ok ? {} : { error: result.error ?? "Persist failed." }
|
|
13039
|
+
}
|
|
13040
|
+
});
|
|
13041
|
+
sendBrainStatus(ws);
|
|
13042
|
+
} catch (err) {
|
|
13043
|
+
send(ws, {
|
|
13044
|
+
type: "brain.config",
|
|
13045
|
+
payload: {
|
|
13046
|
+
config: deps2.brainRuntime.getSnapshot(),
|
|
13047
|
+
persisted: false,
|
|
13048
|
+
error: `Invalid Brain setting: ${errMessage(err)}`
|
|
13049
|
+
}
|
|
13050
|
+
});
|
|
13051
|
+
}
|
|
13052
|
+
},
|
|
12543
13053
|
ask: async (ws, msg) => {
|
|
12544
13054
|
const parsed = validateBrainAskPayload(msg.payload);
|
|
12545
13055
|
if (!parsed.ok) {
|
|
@@ -12647,7 +13157,8 @@ async function startWebUI(opts = {}) {
|
|
|
12647
13157
|
promptsCtx,
|
|
12648
13158
|
modelCapabilitiesRef,
|
|
12649
13159
|
provider,
|
|
12650
|
-
context
|
|
13160
|
+
context,
|
|
13161
|
+
sessionIdentity
|
|
12651
13162
|
} = preContext;
|
|
12652
13163
|
let sessionStore = preContext.sessionStore;
|
|
12653
13164
|
let session = preContext.session;
|
|
@@ -12680,7 +13191,12 @@ async function startWebUI(opts = {}) {
|
|
|
12680
13191
|
modelCapabilitiesRef,
|
|
12681
13192
|
sessionGetter: () => session,
|
|
12682
13193
|
sessionReader,
|
|
12683
|
-
annotationsStore
|
|
13194
|
+
annotationsStore,
|
|
13195
|
+
// Brain settings persist to the GLOBAL config only (config.brain is on
|
|
13196
|
+
// the in-project deny list), serialized behind the shared write lock.
|
|
13197
|
+
persistBrainConfig: (brainConfig) => updateGlobalConfig2((decrypted) => {
|
|
13198
|
+
decrypted["brain"] = brainConfig;
|
|
13199
|
+
}, "brain.config")
|
|
12684
13200
|
});
|
|
12685
13201
|
const {
|
|
12686
13202
|
compactor,
|
|
@@ -12690,6 +13206,7 @@ async function startWebUI(opts = {}) {
|
|
|
12690
13206
|
pipelines,
|
|
12691
13207
|
brain,
|
|
12692
13208
|
brainSettings,
|
|
13209
|
+
brainRuntime,
|
|
12693
13210
|
brainLog,
|
|
12694
13211
|
brainMonitor,
|
|
12695
13212
|
codebaseIndexing,
|
|
@@ -12860,10 +13377,12 @@ async function startWebUI(opts = {}) {
|
|
|
12860
13377
|
terminalHandler,
|
|
12861
13378
|
brain,
|
|
12862
13379
|
brainSettings,
|
|
13380
|
+
brainRuntime,
|
|
12863
13381
|
brainLog
|
|
12864
13382
|
};
|
|
12865
13383
|
const cb = {
|
|
12866
13384
|
sessionStartPayload,
|
|
13385
|
+
onSessionSwapped: (sessionId) => sessionIdentity.activate(sessionId),
|
|
12867
13386
|
updateAutoCompactionMaxContext,
|
|
12868
13387
|
updateGlobalConfig: updateGlobalConfig2,
|
|
12869
13388
|
persistPrefsToConfig: persistPrefsToConfig2,
|
|
@@ -12989,7 +13508,10 @@ async function startWebUI(opts = {}) {
|
|
|
12989
13508
|
onShutdown: async () => {
|
|
12990
13509
|
credentialWatcherClose?.();
|
|
12991
13510
|
brainMonitor.stop();
|
|
13511
|
+
await agentServices.brainLedger?.stop().catch(() => {
|
|
13512
|
+
});
|
|
12992
13513
|
await mcpRegistry.stopAll().catch(() => void 0);
|
|
13514
|
+
await sessionIdentity.stop();
|
|
12993
13515
|
eventArming.getDispose()?.();
|
|
12994
13516
|
if (eternalSubscription) {
|
|
12995
13517
|
eternalSubscription.dispose();
|
|
@@ -13166,7 +13688,11 @@ export {
|
|
|
13166
13688
|
handleMcpDiscover,
|
|
13167
13689
|
handleMcpEnable,
|
|
13168
13690
|
handleMcpList,
|
|
13691
|
+
handleMcpPromptGet,
|
|
13692
|
+
handleMcpPrompts,
|
|
13169
13693
|
handleMcpRemove,
|
|
13694
|
+
handleMcpResourceRead,
|
|
13695
|
+
handleMcpResources,
|
|
13170
13696
|
handleMcpRestart,
|
|
13171
13697
|
handleMcpRoute,
|
|
13172
13698
|
handleMcpSleep,
|