@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/server/entry.js
CHANGED
|
@@ -2992,7 +2992,7 @@ function mapStatus(raw) {
|
|
|
2992
2992
|
return "stopped";
|
|
2993
2993
|
}
|
|
2994
2994
|
}
|
|
2995
|
-
function toView(info) {
|
|
2995
|
+
function toView(info, health) {
|
|
2996
2996
|
const view = {
|
|
2997
2997
|
name: info.name,
|
|
2998
2998
|
transport: info.transport,
|
|
@@ -3004,6 +3004,7 @@ function toView(info) {
|
|
|
3004
3004
|
};
|
|
3005
3005
|
if (info.description !== void 0) view.description = info.description;
|
|
3006
3006
|
if (info.lazy !== void 0) view.lazy = info.lazy;
|
|
3007
|
+
if (health !== void 0) view.health = health;
|
|
3007
3008
|
return view;
|
|
3008
3009
|
}
|
|
3009
3010
|
function deps(ws, globalConfigPath, registry) {
|
|
@@ -3029,7 +3030,13 @@ async function handleMcpList(ws, _msg, globalConfigPath, mcpRegistry) {
|
|
|
3029
3030
|
registry: mcpRegistry,
|
|
3030
3031
|
presets: allServers()
|
|
3031
3032
|
});
|
|
3032
|
-
|
|
3033
|
+
const health = new Map(
|
|
3034
|
+
(typeof mcpRegistry.operationalHealth === "function" ? mcpRegistry.operationalHealth() : []).map((item) => [item.name, item])
|
|
3035
|
+
);
|
|
3036
|
+
send(ws, {
|
|
3037
|
+
type: "mcp.list",
|
|
3038
|
+
payload: { servers: servers.map((server) => toView(server, health.get(server.name))) }
|
|
3039
|
+
});
|
|
3033
3040
|
}
|
|
3034
3041
|
async function handleMcpAdd(ws, msg, globalConfigPath, mcpRegistry) {
|
|
3035
3042
|
const d = deps(ws, globalConfigPath, mcpRegistry);
|
|
@@ -3179,6 +3186,99 @@ async function handleMcpDiscover(ws, msg, globalConfigPath, mcpRegistry) {
|
|
|
3179
3186
|
payload: { success: result.ok, message: result.message }
|
|
3180
3187
|
});
|
|
3181
3188
|
}
|
|
3189
|
+
async function handleMcpResources(ws, msg, _globalConfigPath, mcpRegistry) {
|
|
3190
|
+
if (!mcpRegistry) return sendContentError(ws, "resources", "", "MCP registry is not available.");
|
|
3191
|
+
const payload = payloadRecord(msg);
|
|
3192
|
+
let serverName = "";
|
|
3193
|
+
try {
|
|
3194
|
+
serverName = requiredPayloadString(payload, "name");
|
|
3195
|
+
const refresh = payload["refresh"] === true;
|
|
3196
|
+
const [resources, resourceTemplates] = await Promise.all([
|
|
3197
|
+
mcpRegistry.listResources(serverName, { refresh }),
|
|
3198
|
+
mcpRegistry.listResourceTemplates(serverName, { refresh })
|
|
3199
|
+
]);
|
|
3200
|
+
send(ws, {
|
|
3201
|
+
type: "mcp.resources",
|
|
3202
|
+
payload: { name: serverName, resources, resourceTemplates }
|
|
3203
|
+
});
|
|
3204
|
+
} catch (err) {
|
|
3205
|
+
sendContentError(ws, "resources", serverName, errorMessage(err));
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
async function handleMcpPrompts(ws, msg, _globalConfigPath, mcpRegistry) {
|
|
3209
|
+
if (!mcpRegistry) return sendContentError(ws, "prompts", "", "MCP registry is not available.");
|
|
3210
|
+
const payload = payloadRecord(msg);
|
|
3211
|
+
let serverName = "";
|
|
3212
|
+
try {
|
|
3213
|
+
serverName = requiredPayloadString(payload, "name");
|
|
3214
|
+
const prompts = await mcpRegistry.listPrompts(serverName, {
|
|
3215
|
+
refresh: payload["refresh"] === true
|
|
3216
|
+
});
|
|
3217
|
+
send(ws, { type: "mcp.prompts", payload: { name: serverName, prompts } });
|
|
3218
|
+
} catch (err) {
|
|
3219
|
+
sendContentError(ws, "prompts", serverName, errorMessage(err));
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
3222
|
+
async function handleMcpResourceRead(ws, msg, _globalConfigPath, mcpRegistry) {
|
|
3223
|
+
if (!mcpRegistry)
|
|
3224
|
+
return sendContentError(ws, "resource.read", "", "MCP registry is not available.");
|
|
3225
|
+
const payload = payloadRecord(msg);
|
|
3226
|
+
let serverName = "";
|
|
3227
|
+
try {
|
|
3228
|
+
serverName = requiredPayloadString(payload, "name");
|
|
3229
|
+
const insertion = await mcpRegistry.selectResourceForInsertion(
|
|
3230
|
+
serverName,
|
|
3231
|
+
requiredPayloadString(payload, "uri")
|
|
3232
|
+
);
|
|
3233
|
+
send(ws, { type: "mcp.content.selected", payload: insertion });
|
|
3234
|
+
} catch (err) {
|
|
3235
|
+
sendContentError(ws, "resource.read", serverName, errorMessage(err));
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
async function handleMcpPromptGet(ws, msg, _globalConfigPath, mcpRegistry) {
|
|
3239
|
+
if (!mcpRegistry) return sendContentError(ws, "prompt.get", "", "MCP registry is not available.");
|
|
3240
|
+
const payload = payloadRecord(msg);
|
|
3241
|
+
let serverName = "";
|
|
3242
|
+
try {
|
|
3243
|
+
serverName = requiredPayloadString(payload, "name");
|
|
3244
|
+
const insertion = await mcpRegistry.selectPromptForInsertion(
|
|
3245
|
+
serverName,
|
|
3246
|
+
requiredPayloadString(payload, "prompt"),
|
|
3247
|
+
promptArguments(payload["arguments"])
|
|
3248
|
+
);
|
|
3249
|
+
send(ws, { type: "mcp.content.selected", payload: insertion });
|
|
3250
|
+
} catch (err) {
|
|
3251
|
+
sendContentError(ws, "prompt.get", serverName, errorMessage(err));
|
|
3252
|
+
}
|
|
3253
|
+
}
|
|
3254
|
+
function payloadRecord(msg) {
|
|
3255
|
+
return msg.payload && typeof msg.payload === "object" && !Array.isArray(msg.payload) ? msg.payload : {};
|
|
3256
|
+
}
|
|
3257
|
+
function requiredPayloadString(payload, field) {
|
|
3258
|
+
const value = payload[field];
|
|
3259
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
3260
|
+
throw new Error(`MCP payload field "${field}" must be a non-empty string`);
|
|
3261
|
+
}
|
|
3262
|
+
return value;
|
|
3263
|
+
}
|
|
3264
|
+
function promptArguments(value) {
|
|
3265
|
+
if (value === void 0) return void 0;
|
|
3266
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
3267
|
+
throw new Error("MCP prompt arguments must be an object");
|
|
3268
|
+
}
|
|
3269
|
+
const args = {};
|
|
3270
|
+
for (const [key, item] of Object.entries(value)) {
|
|
3271
|
+
if (typeof item !== "string") throw new Error(`MCP prompt argument "${key}" must be a string`);
|
|
3272
|
+
args[key] = item;
|
|
3273
|
+
}
|
|
3274
|
+
return args;
|
|
3275
|
+
}
|
|
3276
|
+
function sendContentError(ws, action, name2, error) {
|
|
3277
|
+
send(ws, { type: "mcp.content.error", payload: { action, name: name2, error } });
|
|
3278
|
+
}
|
|
3279
|
+
function errorMessage(err) {
|
|
3280
|
+
return err instanceof Error ? err.message : String(err);
|
|
3281
|
+
}
|
|
3182
3282
|
|
|
3183
3283
|
// src/server/memory-handlers.ts
|
|
3184
3284
|
async function handleMemoryList(ws, memoryStore) {
|
|
@@ -4254,6 +4354,16 @@ function validateBrainAskPayload(payload) {
|
|
|
4254
4354
|
}
|
|
4255
4355
|
return { ok: true, value: { question: question.trim() } };
|
|
4256
4356
|
}
|
|
4357
|
+
function validateBrainConfigSetPayload(payload) {
|
|
4358
|
+
if (!isRecord(payload)) {
|
|
4359
|
+
return { ok: false, message: "brain.config.set payload must be an object with a patch object" };
|
|
4360
|
+
}
|
|
4361
|
+
const patch = payload["patch"];
|
|
4362
|
+
if (!isRecord(patch)) {
|
|
4363
|
+
return { ok: false, message: "brain.config.set payload.patch must be an object" };
|
|
4364
|
+
}
|
|
4365
|
+
return { ok: true, value: { patch } };
|
|
4366
|
+
}
|
|
4257
4367
|
function validateAutonomySwitchPayload(payload) {
|
|
4258
4368
|
if (!isRecord(payload)) {
|
|
4259
4369
|
return { ok: false, message: "autonomy.switch payload must be an object with string mode" };
|
|
@@ -4315,7 +4425,9 @@ var BOOLEAN_PREF_KEYS = /* @__PURE__ */ new Set([
|
|
|
4315
4425
|
"hqEnabled",
|
|
4316
4426
|
"hqRawContent",
|
|
4317
4427
|
"fallbackAuto",
|
|
4318
|
-
"favoriteModelsOnly"
|
|
4428
|
+
"favoriteModelsOnly",
|
|
4429
|
+
"breakerEnabled",
|
|
4430
|
+
"debugStream"
|
|
4319
4431
|
]);
|
|
4320
4432
|
var STRING_ARRAY_PREF_KEYS = /* @__PURE__ */ new Set(["fallbackModels", "favoriteModels"]);
|
|
4321
4433
|
var STRING_ARRAY_RECORD_PREF_KEYS = /* @__PURE__ */ new Set(["fallbackProfiles"]);
|
|
@@ -4326,9 +4438,17 @@ var NUMBER_PREF_KEYS = /* @__PURE__ */ new Set([
|
|
|
4326
4438
|
"maxIterations",
|
|
4327
4439
|
"maxConcurrent",
|
|
4328
4440
|
"enhanceDelayMs",
|
|
4329
|
-
"tgLongToolMs"
|
|
4441
|
+
"tgLongToolMs",
|
|
4442
|
+
"breakerAutoKillResetMs"
|
|
4443
|
+
]);
|
|
4444
|
+
var STRING_PREF_KEYS = /* @__PURE__ */ new Set([
|
|
4445
|
+
"hqUrl",
|
|
4446
|
+
"hqToken",
|
|
4447
|
+
"uiLocale",
|
|
4448
|
+
"thinkingWord",
|
|
4449
|
+
"refinerProvider",
|
|
4450
|
+
"refinerModel"
|
|
4330
4451
|
]);
|
|
4331
|
-
var STRING_PREF_KEYS = /* @__PURE__ */ new Set(["hqUrl", "hqToken", "uiLocale"]);
|
|
4332
4452
|
var ENUM_PREF_KEYS = {
|
|
4333
4453
|
autonomy: AUTONOMY_VALUES,
|
|
4334
4454
|
contextStrategy: CONTEXT_STRATEGY_VALUES,
|
|
@@ -4339,7 +4459,10 @@ var ENUM_PREF_KEYS = {
|
|
|
4339
4459
|
auditLevel: AUDIT_LEVEL_VALUES,
|
|
4340
4460
|
reasoningMode: REASONING_MODE_VALUES,
|
|
4341
4461
|
reasoningEffort: REASONING_EFFORT_VALUES,
|
|
4342
|
-
cacheTtl: CACHE_TTL_VALUES
|
|
4462
|
+
cacheTtl: CACHE_TTL_VALUES,
|
|
4463
|
+
statuslineMode: /* @__PURE__ */ new Set(["minimum", "detailed", "no-color"]),
|
|
4464
|
+
animationStyle: /* @__PURE__ */ new Set(["rainbow", "wave", "pulse", "dots", "breathe", "cycle"]),
|
|
4465
|
+
fsAccess: /* @__PURE__ */ new Set(["unrestricted", "project"])
|
|
4343
4466
|
};
|
|
4344
4467
|
function validateModelRuntimeValue(modelRuntime, path22) {
|
|
4345
4468
|
const reasoning = modelRuntime["reasoning"];
|
|
@@ -5662,6 +5785,10 @@ function shouldLogWatcherStats() {
|
|
|
5662
5785
|
function setupEvents(deps2) {
|
|
5663
5786
|
const { events, broadcast: broadcast2, clients, config, context, pendingConfirms, globalConfigPath, sessionBridge, wpaths, watcherMetrics, onFleetBroadcaster } = deps2;
|
|
5664
5787
|
const disposers = [];
|
|
5788
|
+
let disposed = false;
|
|
5789
|
+
const on = (event, listener) => {
|
|
5790
|
+
disposers.push(events.on(event, listener));
|
|
5791
|
+
};
|
|
5665
5792
|
const currentSessionId = () => context.session?.id ?? "";
|
|
5666
5793
|
const sessionPayload2 = (payload) => {
|
|
5667
5794
|
const provided = payload["sessionId"];
|
|
@@ -5677,20 +5804,20 @@ function setupEvents(deps2) {
|
|
|
5677
5804
|
sessionBridge?.append(event).catch(() => {
|
|
5678
5805
|
});
|
|
5679
5806
|
};
|
|
5680
|
-
|
|
5807
|
+
on("iteration.started", (e) => {
|
|
5681
5808
|
const maxIt = typeof context.meta["maxIterations"] === "number" ? context.meta["maxIterations"] : config.tools?.maxIterations ?? 100;
|
|
5682
5809
|
broadcast2(clients, {
|
|
5683
5810
|
type: "iteration.started",
|
|
5684
5811
|
payload: sessionPayload2({ sessionId: e.sessionId, index: e.index, maxIterations: maxIt })
|
|
5685
5812
|
});
|
|
5686
5813
|
});
|
|
5687
|
-
|
|
5814
|
+
on("iteration.completed", (e) => {
|
|
5688
5815
|
broadcast2(clients, {
|
|
5689
5816
|
type: "iteration.completed",
|
|
5690
5817
|
payload: sessionPayload2({ sessionId: e.sessionId, index: e.index, totalIterations: e.index + 1 })
|
|
5691
5818
|
});
|
|
5692
5819
|
});
|
|
5693
|
-
|
|
5820
|
+
on("iteration.limit_reached", (e) => {
|
|
5694
5821
|
broadcast2(clients, {
|
|
5695
5822
|
type: "iteration.limit_reached",
|
|
5696
5823
|
payload: sessionPayload2({
|
|
@@ -5700,19 +5827,19 @@ function setupEvents(deps2) {
|
|
|
5700
5827
|
})
|
|
5701
5828
|
});
|
|
5702
5829
|
});
|
|
5703
|
-
|
|
5830
|
+
on("provider.text_delta", (e) => {
|
|
5704
5831
|
broadcast2(clients, { type: "provider.text_delta", payload: sessionPayload2({ sessionId: e.sessionId, text: e.text, messageId: "current" }) });
|
|
5705
5832
|
});
|
|
5706
|
-
|
|
5833
|
+
on("provider.thinking_delta", (e) => {
|
|
5707
5834
|
broadcast2(clients, { type: "provider.thinking_delta", payload: sessionPayload2({ sessionId: e.sessionId, text: e.text }) });
|
|
5708
5835
|
});
|
|
5709
|
-
|
|
5836
|
+
on("provider.stream_error", (e) => {
|
|
5710
5837
|
broadcast2(clients, {
|
|
5711
5838
|
type: "provider.stream_error",
|
|
5712
5839
|
payload: sessionPayload2({ sessionId: e.sessionId, eventType: e.eventType, message: e.msg })
|
|
5713
5840
|
});
|
|
5714
5841
|
});
|
|
5715
|
-
|
|
5842
|
+
on("tool.started", (e) => {
|
|
5716
5843
|
broadcast2(clients, {
|
|
5717
5844
|
type: "tool.started",
|
|
5718
5845
|
payload: sessionPayload2({ sessionId: e.sessionId, id: e.id, name: e.name, input: e.input, messageId: `tool_${e.id}` })
|
|
@@ -5725,7 +5852,7 @@ function setupEvents(deps2) {
|
|
|
5725
5852
|
input: e.input
|
|
5726
5853
|
});
|
|
5727
5854
|
});
|
|
5728
|
-
|
|
5855
|
+
on("tool.progress", (e) => {
|
|
5729
5856
|
broadcast2(clients, {
|
|
5730
5857
|
type: "tool.progress",
|
|
5731
5858
|
// Nested `event` shape — the client handler reads `payload.event?.text`
|
|
@@ -5746,7 +5873,7 @@ function setupEvents(deps2) {
|
|
|
5746
5873
|
}
|
|
5747
5874
|
});
|
|
5748
5875
|
});
|
|
5749
|
-
|
|
5876
|
+
on("tool.executed", (e) => {
|
|
5750
5877
|
broadcast2(clients, {
|
|
5751
5878
|
type: "tool.executed",
|
|
5752
5879
|
payload: sessionPayload2({ sessionId: e.sessionId, id: e.id, name: e.name, durationMs: e.durationMs, ok: e.ok, input: e.input, output: e.output })
|
|
@@ -5804,7 +5931,7 @@ function setupEvents(deps2) {
|
|
|
5804
5931
|
})();
|
|
5805
5932
|
}
|
|
5806
5933
|
});
|
|
5807
|
-
|
|
5934
|
+
on("tool.loop_detected", (e) => {
|
|
5808
5935
|
broadcast2(clients, {
|
|
5809
5936
|
type: "tool.loop_detected",
|
|
5810
5937
|
payload: sessionPayload2({
|
|
@@ -5816,19 +5943,19 @@ function setupEvents(deps2) {
|
|
|
5816
5943
|
})
|
|
5817
5944
|
});
|
|
5818
5945
|
});
|
|
5819
|
-
|
|
5946
|
+
on("trust.persisted", (e) => {
|
|
5820
5947
|
broadcast2(clients, {
|
|
5821
5948
|
type: "trust.persisted",
|
|
5822
5949
|
payload: sessionPayload2({ sessionId: e.sessionId, tool: e.tool, pattern: e.pattern, decision: e.decision })
|
|
5823
5950
|
});
|
|
5824
5951
|
});
|
|
5825
|
-
|
|
5952
|
+
on("delegate.started", (e) => {
|
|
5826
5953
|
broadcast2(clients, {
|
|
5827
5954
|
type: "delegate.started",
|
|
5828
5955
|
payload: sessionPayload2({ sessionId: e.sessionId, target: e.target, task: e.task })
|
|
5829
5956
|
});
|
|
5830
5957
|
});
|
|
5831
|
-
|
|
5958
|
+
on("delegate.completed", (e) => {
|
|
5832
5959
|
broadcast2(clients, {
|
|
5833
5960
|
type: "delegate.completed",
|
|
5834
5961
|
payload: sessionPayload2({
|
|
@@ -5846,7 +5973,7 @@ function setupEvents(deps2) {
|
|
|
5846
5973
|
})
|
|
5847
5974
|
});
|
|
5848
5975
|
});
|
|
5849
|
-
|
|
5976
|
+
on("provider.response", (e) => {
|
|
5850
5977
|
broadcast2(clients, {
|
|
5851
5978
|
type: "provider.response",
|
|
5852
5979
|
payload: sessionPayload2({
|
|
@@ -5858,7 +5985,7 @@ function setupEvents(deps2) {
|
|
|
5858
5985
|
})
|
|
5859
5986
|
});
|
|
5860
5987
|
});
|
|
5861
|
-
|
|
5988
|
+
on("ctx.pct", (e) => {
|
|
5862
5989
|
broadcast2(clients, {
|
|
5863
5990
|
type: "ctx.pct",
|
|
5864
5991
|
payload: sessionPayload2({ sessionId: e.sessionId, load: e.load, tokens: e.tokens, maxContext: e.maxContext })
|
|
@@ -5875,28 +6002,28 @@ function setupEvents(deps2) {
|
|
|
5875
6002
|
})
|
|
5876
6003
|
});
|
|
5877
6004
|
});
|
|
5878
|
-
|
|
6005
|
+
on("ctx.max_context", (e) => {
|
|
5879
6006
|
broadcast2(clients, {
|
|
5880
6007
|
type: "ctx.max_context",
|
|
5881
6008
|
payload: sessionPayload2({ sessionId: e.sessionId, providerId: e.providerId, modelId: e.modelId, maxContext: e.maxContext })
|
|
5882
6009
|
});
|
|
5883
6010
|
});
|
|
5884
|
-
|
|
6011
|
+
on("token.threshold", (e) => {
|
|
5885
6012
|
broadcast2(clients, {
|
|
5886
6013
|
type: "token.threshold",
|
|
5887
6014
|
payload: sessionPayload2({ sessionId: e.sessionId, used: e.used, limit: e.limit })
|
|
5888
6015
|
});
|
|
5889
6016
|
});
|
|
5890
|
-
|
|
6017
|
+
on("token.cost_estimate_unavailable", (e) => {
|
|
5891
6018
|
broadcast2(clients, {
|
|
5892
6019
|
type: "token.cost_estimate_unavailable",
|
|
5893
6020
|
payload: sessionPayload2({ sessionId: e.sessionId, model: e.model })
|
|
5894
6021
|
});
|
|
5895
6022
|
});
|
|
5896
|
-
|
|
6023
|
+
on("context.repaired", (e) => {
|
|
5897
6024
|
broadcast2(clients, { type: "context.repaired", payload: sessionPayload2({ sessionId: e.sessionId, removedToolUses: e.removedToolUses, removedToolResults: e.removedToolResults, removedMessages: e.removedMessages }) });
|
|
5898
6025
|
});
|
|
5899
|
-
|
|
6026
|
+
on("tool.confirm_needed", (e) => {
|
|
5900
6027
|
const id = e.toolUseId ?? `confirm_${Date.now()}`;
|
|
5901
6028
|
const payload = sessionPayload2({ sessionId: e.sessionId, id, toolName: e.tool?.name ?? "unknown", input: e.input, suggestedPattern: e.suggestedPattern, decisionSource: e.decisionSource, riskTier: e.riskTier });
|
|
5902
6029
|
pendingConfirms.set(id, {
|
|
@@ -5907,7 +6034,7 @@ function setupEvents(deps2) {
|
|
|
5907
6034
|
});
|
|
5908
6035
|
broadcast2(clients, { type: "tool.confirm_needed", payload });
|
|
5909
6036
|
});
|
|
5910
|
-
|
|
6037
|
+
on("error", (e) => {
|
|
5911
6038
|
broadcast2(clients, { type: "error", payload: sessionPayload2({ sessionId: e.sessionId, phase: e.phase, message: e.err instanceof Error ? e.err.message : String(e.err) }) });
|
|
5912
6039
|
appendForCurrentSession(e.sessionId, {
|
|
5913
6040
|
type: "error",
|
|
@@ -5916,13 +6043,13 @@ function setupEvents(deps2) {
|
|
|
5916
6043
|
phase: e.phase
|
|
5917
6044
|
});
|
|
5918
6045
|
});
|
|
5919
|
-
|
|
6046
|
+
on("session.damaged", (e) => {
|
|
5920
6047
|
broadcast2(clients, {
|
|
5921
6048
|
type: "session.damaged",
|
|
5922
6049
|
payload: { sessionId: e.sessionId, detail: e.detail }
|
|
5923
6050
|
});
|
|
5924
6051
|
});
|
|
5925
|
-
|
|
6052
|
+
on("session.rewound", (e) => {
|
|
5926
6053
|
broadcast2(clients, {
|
|
5927
6054
|
type: "session.rewound",
|
|
5928
6055
|
payload: sessionPayload2({
|
|
@@ -5933,7 +6060,7 @@ function setupEvents(deps2) {
|
|
|
5933
6060
|
})
|
|
5934
6061
|
});
|
|
5935
6062
|
});
|
|
5936
|
-
|
|
6063
|
+
on("checkpoint.written", (e) => {
|
|
5937
6064
|
broadcast2(clients, {
|
|
5938
6065
|
type: "checkpoint.written",
|
|
5939
6066
|
payload: sessionPayload2({
|
|
@@ -5945,19 +6072,19 @@ function setupEvents(deps2) {
|
|
|
5945
6072
|
})
|
|
5946
6073
|
});
|
|
5947
6074
|
});
|
|
5948
|
-
|
|
6075
|
+
on("in_flight.started", (e) => {
|
|
5949
6076
|
broadcast2(clients, {
|
|
5950
6077
|
type: "in_flight.started",
|
|
5951
6078
|
payload: sessionPayload2({ sessionId: e.sessionId, context: e.context, ts: e.ts })
|
|
5952
6079
|
});
|
|
5953
6080
|
});
|
|
5954
|
-
|
|
6081
|
+
on("in_flight.ended", (e) => {
|
|
5955
6082
|
broadcast2(clients, {
|
|
5956
6083
|
type: "in_flight.ended",
|
|
5957
6084
|
payload: sessionPayload2({ sessionId: e.sessionId, reason: e.reason, ts: e.ts })
|
|
5958
6085
|
});
|
|
5959
6086
|
});
|
|
5960
|
-
|
|
6087
|
+
on("provider.retry", (e) => {
|
|
5961
6088
|
broadcast2(clients, {
|
|
5962
6089
|
type: "provider.retry",
|
|
5963
6090
|
payload: sessionPayload2({
|
|
@@ -5979,7 +6106,7 @@ function setupEvents(deps2) {
|
|
|
5979
6106
|
description: e.description
|
|
5980
6107
|
});
|
|
5981
6108
|
});
|
|
5982
|
-
|
|
6109
|
+
on("provider.error", (e) => {
|
|
5983
6110
|
broadcast2(clients, {
|
|
5984
6111
|
type: "provider.error",
|
|
5985
6112
|
payload: sessionPayload2({
|
|
@@ -5999,7 +6126,7 @@ function setupEvents(deps2) {
|
|
|
5999
6126
|
retryable: e.retryable
|
|
6000
6127
|
});
|
|
6001
6128
|
});
|
|
6002
|
-
|
|
6129
|
+
on("provider.fallback", (e) => {
|
|
6003
6130
|
broadcast2(clients, {
|
|
6004
6131
|
type: "provider.fallback",
|
|
6005
6132
|
payload: sessionPayload2({
|
|
@@ -6011,7 +6138,7 @@ function setupEvents(deps2) {
|
|
|
6011
6138
|
})
|
|
6012
6139
|
});
|
|
6013
6140
|
});
|
|
6014
|
-
|
|
6141
|
+
on("compaction.fired", (e) => {
|
|
6015
6142
|
broadcast2(clients, {
|
|
6016
6143
|
type: "context.compacted",
|
|
6017
6144
|
payload: sessionPayload2({
|
|
@@ -6023,7 +6150,7 @@ function setupEvents(deps2) {
|
|
|
6023
6150
|
})
|
|
6024
6151
|
});
|
|
6025
6152
|
});
|
|
6026
|
-
|
|
6153
|
+
on("compaction.failed", (e) => {
|
|
6027
6154
|
broadcast2(clients, {
|
|
6028
6155
|
type: "compaction.failed",
|
|
6029
6156
|
payload: sessionPayload2({
|
|
@@ -6038,25 +6165,25 @@ function setupEvents(deps2) {
|
|
|
6038
6165
|
})
|
|
6039
6166
|
});
|
|
6040
6167
|
});
|
|
6041
|
-
|
|
6168
|
+
on("mcp.server.connected", (e) => {
|
|
6042
6169
|
broadcast2(clients, {
|
|
6043
6170
|
type: "mcp.server.connected",
|
|
6044
6171
|
payload: { name: e.name, toolCount: e.toolCount }
|
|
6045
6172
|
});
|
|
6046
6173
|
});
|
|
6047
|
-
|
|
6174
|
+
on("mcp.server.reconnected", (e) => {
|
|
6048
6175
|
broadcast2(clients, {
|
|
6049
6176
|
type: "mcp.server.reconnected",
|
|
6050
6177
|
payload: { name: e.name, toolCount: e.toolCount }
|
|
6051
6178
|
});
|
|
6052
6179
|
});
|
|
6053
|
-
|
|
6180
|
+
on("mcp.server.disconnected", (e) => {
|
|
6054
6181
|
broadcast2(clients, {
|
|
6055
6182
|
type: "mcp.server.disconnected",
|
|
6056
6183
|
payload: { name: e.name, reason: e.reason }
|
|
6057
6184
|
});
|
|
6058
6185
|
});
|
|
6059
|
-
|
|
6186
|
+
on("coordinator.stats", (e) => {
|
|
6060
6187
|
broadcast2(clients, {
|
|
6061
6188
|
type: "coordinator.stats",
|
|
6062
6189
|
payload: sessionPayload2({
|
|
@@ -6084,16 +6211,16 @@ function setupEvents(deps2) {
|
|
|
6084
6211
|
broadcast2(clients, { type: "mailbox.agent_registered", payload });
|
|
6085
6212
|
});
|
|
6086
6213
|
const forwardSubagent = (kind, payload) => broadcast2(clients, { type: "subagent.event", payload: sessionPayload2({ kind, ...payload }) });
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6214
|
+
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 }));
|
|
6215
|
+
on("subagent.task_started", (e) => forwardSubagent("task_started", { sessionId: e.sessionId, subagentId: e.subagentId, taskId: e.taskId, description: e.description }));
|
|
6216
|
+
on("subagent.tool_executed", (e) => forwardSubagent("tool_executed", { sessionId: e.sessionId, subagentId: e.subagentId, toolName: e.name, durationMs: e.durationMs, ok: e.ok }));
|
|
6217
|
+
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 }));
|
|
6218
|
+
on("subagent.budget_warning", (e) => forwardSubagent("budget_warning", { sessionId: e.sessionId, subagentId: e.subagentId, budgetKind: e.kind, used: e.used, limit: e.limit }));
|
|
6219
|
+
on("subagent.budget_extended", (e) => forwardSubagent("budget_extended", { sessionId: e.sessionId, subagentId: e.subagentId, budgetKind: e.kind, newLimit: e.newLimit, totalExtensions: e.totalExtensions }));
|
|
6220
|
+
on("subagent.ctx_pct", (e) => forwardSubagent("ctx_pct", { sessionId: e.sessionId, subagentId: e.subagentId, load: e.load, tokens: e.tokens, maxContext: e.maxContext }));
|
|
6221
|
+
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 }));
|
|
6222
|
+
on("subagent.removed", (e) => forwardSubagent("removed", { sessionId: e.sessionId, subagentId: e.subagentId, reason: e.reason }));
|
|
6223
|
+
on("agent.timeline.message", (e) => {
|
|
6097
6224
|
broadcast2(clients, {
|
|
6098
6225
|
type: "agent.timeline.message",
|
|
6099
6226
|
payload: sessionPayload2({
|
|
@@ -6109,7 +6236,7 @@ function setupEvents(deps2) {
|
|
|
6109
6236
|
})
|
|
6110
6237
|
});
|
|
6111
6238
|
});
|
|
6112
|
-
|
|
6239
|
+
on("agent.status_changed", (e) => {
|
|
6113
6240
|
broadcast2(clients, {
|
|
6114
6241
|
type: "agent.status_changed",
|
|
6115
6242
|
payload: sessionPayload2({
|
|
@@ -6124,7 +6251,7 @@ function setupEvents(deps2) {
|
|
|
6124
6251
|
});
|
|
6125
6252
|
});
|
|
6126
6253
|
let leaderSpawned = false;
|
|
6127
|
-
|
|
6254
|
+
on("iteration.started", (e) => {
|
|
6128
6255
|
if (!leaderSpawned) {
|
|
6129
6256
|
leaderSpawned = true;
|
|
6130
6257
|
const provider = context.provider?.id ?? "unknown";
|
|
@@ -6138,7 +6265,7 @@ function setupEvents(deps2) {
|
|
|
6138
6265
|
});
|
|
6139
6266
|
}
|
|
6140
6267
|
});
|
|
6141
|
-
|
|
6268
|
+
on("tool.executed", (e) => {
|
|
6142
6269
|
forwardSubagent("tool_executed", {
|
|
6143
6270
|
sessionId: e.sessionId,
|
|
6144
6271
|
subagentId: "leader",
|
|
@@ -6147,7 +6274,7 @@ function setupEvents(deps2) {
|
|
|
6147
6274
|
ok: e.ok
|
|
6148
6275
|
});
|
|
6149
6276
|
});
|
|
6150
|
-
|
|
6277
|
+
on("provider.response", (e) => {
|
|
6151
6278
|
if (e.usage?.input != null) {
|
|
6152
6279
|
const maxCtx = context.provider.capabilities.maxContext;
|
|
6153
6280
|
const rawLoad = maxCtx > 0 ? e.usage.input / maxCtx : 0;
|
|
@@ -6164,7 +6291,7 @@ function setupEvents(deps2) {
|
|
|
6164
6291
|
});
|
|
6165
6292
|
}
|
|
6166
6293
|
});
|
|
6167
|
-
|
|
6294
|
+
on("iteration.completed", (e) => {
|
|
6168
6295
|
if (!leaderSpawned) {
|
|
6169
6296
|
leaderSpawned = true;
|
|
6170
6297
|
const provider = context.provider?.id ?? "unknown";
|
|
@@ -6196,7 +6323,7 @@ function setupEvents(deps2) {
|
|
|
6196
6323
|
payload: sessionPayload2({ event: eventName, ...payload })
|
|
6197
6324
|
});
|
|
6198
6325
|
});
|
|
6199
|
-
|
|
6326
|
+
on("client.status", async (e) => {
|
|
6200
6327
|
broadcast2(clients, { type: "client.status_update", payload: e });
|
|
6201
6328
|
if (wpaths?.projectStatus) {
|
|
6202
6329
|
try {
|
|
@@ -6282,6 +6409,7 @@ function setupEvents(deps2) {
|
|
|
6282
6409
|
const startWatcher = async () => {
|
|
6283
6410
|
try {
|
|
6284
6411
|
await fs10.mkdir(projectsDir, { recursive: true });
|
|
6412
|
+
if (disposed) return;
|
|
6285
6413
|
watcher = fsWatch(projectsDir, { persistent: true, recursive: true }, async (eventType, filename) => {
|
|
6286
6414
|
if (eventType !== "change" && eventType !== "rename") return;
|
|
6287
6415
|
if (filename == null) return;
|
|
@@ -6312,7 +6440,7 @@ function setupEvents(deps2) {
|
|
|
6312
6440
|
);
|
|
6313
6441
|
}
|
|
6314
6442
|
};
|
|
6315
|
-
|
|
6443
|
+
on("client.status", (e) => {
|
|
6316
6444
|
if (e.projectHash) {
|
|
6317
6445
|
const hash = String(e.projectHash);
|
|
6318
6446
|
if (!knownProjectHashes.has(hash)) {
|
|
@@ -6352,7 +6480,9 @@ function setupEvents(deps2) {
|
|
|
6352
6480
|
const registry = new SessionRegistry(globalRoot);
|
|
6353
6481
|
const sessions = await registry.list();
|
|
6354
6482
|
const mySlug = sessions.find((s) => s.pid === process.pid)?.projectSlug;
|
|
6355
|
-
const live = sessions.filter(
|
|
6483
|
+
const live = sessions.filter(
|
|
6484
|
+
(s) => s.status === "active" || s.status === "idle"
|
|
6485
|
+
).filter((s) => mySlug ? s.projectSlug === mySlug : true).map((s) => ({
|
|
6356
6486
|
sessionId: s.sessionId,
|
|
6357
6487
|
projectName: s.projectName,
|
|
6358
6488
|
projectSlug: s.projectSlug,
|
|
@@ -6406,6 +6536,8 @@ function setupEvents(deps2) {
|
|
|
6406
6536
|
void broadcastSessions();
|
|
6407
6537
|
}
|
|
6408
6538
|
return () => {
|
|
6539
|
+
if (disposed) return;
|
|
6540
|
+
disposed = true;
|
|
6409
6541
|
for (const dispose of disposers) {
|
|
6410
6542
|
try {
|
|
6411
6543
|
dispose();
|
|
@@ -6632,7 +6764,7 @@ function createWsServers(ports, accessToken) {
|
|
|
6632
6764
|
allowedHostnames: publicHostnames,
|
|
6633
6765
|
allowBrowserUrlToken: Boolean(ports.publicWsUrl)
|
|
6634
6766
|
});
|
|
6635
|
-
const WS_MAX_PAYLOAD =
|
|
6767
|
+
const WS_MAX_PAYLOAD = 20 * 1024 * 1024;
|
|
6636
6768
|
const wssPrimary = new WebSocketServer({
|
|
6637
6769
|
port: ports.wsPort,
|
|
6638
6770
|
host: ports.wsHost,
|
|
@@ -6740,12 +6872,10 @@ function registerShutdown(deps2) {
|
|
|
6740
6872
|
}
|
|
6741
6873
|
|
|
6742
6874
|
// src/server/pre-context-services.ts
|
|
6743
|
-
import * as
|
|
6875
|
+
import * as path15 from "node:path";
|
|
6744
6876
|
import { createRequire as createRequire2 } from "node:module";
|
|
6745
|
-
import { WebSocket as WebSocket2 } from "ws";
|
|
6746
6877
|
import { DefaultTokenCounter } from "@wrongstack/core/infrastructure";
|
|
6747
6878
|
import {
|
|
6748
|
-
AgentStatusTracker,
|
|
6749
6879
|
AnnotationsStore,
|
|
6750
6880
|
DEFAULT_SESSION_PRUNE_DAYS,
|
|
6751
6881
|
DefaultModelsRegistry,
|
|
@@ -6756,7 +6886,6 @@ import {
|
|
|
6756
6886
|
DefaultSkillLoader,
|
|
6757
6887
|
DefaultSystemPromptBuilder,
|
|
6758
6888
|
EventBus,
|
|
6759
|
-
FleetNotifier,
|
|
6760
6889
|
GlobalMailbox,
|
|
6761
6890
|
PromptUsageStore,
|
|
6762
6891
|
ProviderRegistry,
|
|
@@ -6767,8 +6896,8 @@ import {
|
|
|
6767
6896
|
applyToolDescriptionModes,
|
|
6768
6897
|
applyToolResultRenderModes,
|
|
6769
6898
|
configureChildEnvGitIdentity,
|
|
6899
|
+
getSessionRegistry as getSessionRegistry2,
|
|
6770
6900
|
resolveContextWindowPolicy,
|
|
6771
|
-
getSessionRegistry,
|
|
6772
6901
|
makeMailboxTool,
|
|
6773
6902
|
makeMailInboxTool,
|
|
6774
6903
|
makeFleetStatusTool,
|
|
@@ -6777,7 +6906,12 @@ import {
|
|
|
6777
6906
|
import {
|
|
6778
6907
|
createSuperMemoryTools
|
|
6779
6908
|
} from "@wrongstack/super-memory";
|
|
6780
|
-
import {
|
|
6909
|
+
import {
|
|
6910
|
+
MCPAuthorizationManager,
|
|
6911
|
+
MCPRegistry,
|
|
6912
|
+
MCPVaultTokenStore,
|
|
6913
|
+
createVaultBackedMcpAuthorizationProviderFactory
|
|
6914
|
+
} from "@wrongstack/mcp";
|
|
6781
6915
|
import { buildProviderFactoriesFromRegistry } from "@wrongstack/providers";
|
|
6782
6916
|
import { createDefaultContainer } from "@wrongstack/runtime";
|
|
6783
6917
|
import {
|
|
@@ -6789,7 +6923,7 @@ import {
|
|
|
6789
6923
|
rememberTool,
|
|
6790
6924
|
searchMemoryTool
|
|
6791
6925
|
} from "@wrongstack/tools";
|
|
6792
|
-
import { toErrorMessage as toErrorMessage5 } from "@wrongstack/core/utils";
|
|
6926
|
+
import { sessionScopedPath, toErrorMessage as toErrorMessage5 } from "@wrongstack/core/utils";
|
|
6793
6927
|
|
|
6794
6928
|
// src/server/setup-screen.ts
|
|
6795
6929
|
import { expectDefined as expectDefined2 } from "@wrongstack/core";
|
|
@@ -6927,6 +7061,20 @@ function seedContextMeta(config, context) {
|
|
|
6927
7061
|
meta["reasoningPreserve"] = mr.reasoning?.preserve === true;
|
|
6928
7062
|
meta["cacheTtl"] = mr.cache?.ttl ?? "default";
|
|
6929
7063
|
}
|
|
7064
|
+
meta["refinerProvider"] = autonomyCfg["refinerProvider"] ?? "";
|
|
7065
|
+
meta["refinerModel"] = autonomyCfg["refinerModel"] ?? "";
|
|
7066
|
+
meta["thinkingWord"] = autonomyCfg["thinkingWord"] ?? "thinking";
|
|
7067
|
+
meta["statuslineMode"] = autonomyCfg["statuslineMode"] ?? "detailed";
|
|
7068
|
+
meta["animationStyle"] = autonomyCfg["animationStyle"] ?? "rainbow";
|
|
7069
|
+
meta["breakerEnabled"] = config.circuitBreaker?.enabled === true;
|
|
7070
|
+
meta["breakerAutoKillResetMs"] = config.circuitBreaker?.autoKillResetMs ?? 6e4;
|
|
7071
|
+
{
|
|
7072
|
+
const featuresAllow = config.features?.allowOutsideProjectRoot;
|
|
7073
|
+
const toolsRestrict = config.tools?.restrictToProjectRoot;
|
|
7074
|
+
const allow = featuresAllow !== void 0 ? featuresAllow : toolsRestrict !== void 0 ? !toolsRestrict : true;
|
|
7075
|
+
meta["fsAccess"] = allow ? "unrestricted" : "project";
|
|
7076
|
+
}
|
|
7077
|
+
meta["debugStream"] = config.debugStream === true;
|
|
6930
7078
|
const hqConfig = config.hq;
|
|
6931
7079
|
meta["hqEnabled"] = hqConfig?.enabled === true;
|
|
6932
7080
|
meta["hqUrl"] = hqConfig?.url ?? "";
|
|
@@ -7023,10 +7171,188 @@ async function discoverAndMergeWebuiProviders(opts) {
|
|
|
7023
7171
|
}
|
|
7024
7172
|
}
|
|
7025
7173
|
|
|
7174
|
+
// src/server/standalone-session-identity.ts
|
|
7175
|
+
import * as path14 from "node:path";
|
|
7176
|
+
import {
|
|
7177
|
+
AgentStatusTracker,
|
|
7178
|
+
FleetNotifier,
|
|
7179
|
+
getSessionRegistry,
|
|
7180
|
+
RecoveryLock
|
|
7181
|
+
} from "@wrongstack/core";
|
|
7182
|
+
import { WebSocket as WebSocket2 } from "ws";
|
|
7183
|
+
async function createStandaloneSessionIdentityLifecycle(opts) {
|
|
7184
|
+
const { paths, events, logger } = opts;
|
|
7185
|
+
const registry = opts.sessionRegistry ?? getSessionRegistry(paths.globalRoot);
|
|
7186
|
+
const fleetNotifier = new FleetNotifier({
|
|
7187
|
+
baseDir: paths.globalRoot,
|
|
7188
|
+
projectRoot: paths.projectRoot,
|
|
7189
|
+
selfPid: process.pid
|
|
7190
|
+
});
|
|
7191
|
+
let activeSessionId = opts.initialSessionId;
|
|
7192
|
+
let stopped = false;
|
|
7193
|
+
let transition = Promise.resolve();
|
|
7194
|
+
const statusTracker = new AgentStatusTracker({
|
|
7195
|
+
events,
|
|
7196
|
+
registry,
|
|
7197
|
+
sessionId: () => activeSessionId,
|
|
7198
|
+
onUpdate: () => fleetNotifier.notify()
|
|
7199
|
+
});
|
|
7200
|
+
const register = async (sessionId) => {
|
|
7201
|
+
try {
|
|
7202
|
+
await registry.register({
|
|
7203
|
+
sessionId,
|
|
7204
|
+
projectSlug: paths.projectSlug,
|
|
7205
|
+
projectRoot: paths.projectRoot,
|
|
7206
|
+
projectName: path14.basename(paths.projectRoot),
|
|
7207
|
+
workingDir: opts.workingDir,
|
|
7208
|
+
clientType: "webui",
|
|
7209
|
+
pid: process.pid,
|
|
7210
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7211
|
+
agents: statusTracker.getAgents()
|
|
7212
|
+
});
|
|
7213
|
+
fleetNotifier.notify();
|
|
7214
|
+
} catch (err) {
|
|
7215
|
+
logger.debug?.(`WebUI session registry update failed: ${errorMessage2(err)}`);
|
|
7216
|
+
}
|
|
7217
|
+
};
|
|
7218
|
+
await register(activeSessionId);
|
|
7219
|
+
statusTracker.start();
|
|
7220
|
+
const recoveryLock = opts.manageRecoveryLock === false ? void 0 : new RecoveryLock({ dir: paths.projectSessions, sessionStore: opts.sessionStore });
|
|
7221
|
+
let ownsRecoveryLock = false;
|
|
7222
|
+
if (recoveryLock) {
|
|
7223
|
+
try {
|
|
7224
|
+
await recoveryLock.write(activeSessionId);
|
|
7225
|
+
ownsRecoveryLock = true;
|
|
7226
|
+
} catch {
|
|
7227
|
+
}
|
|
7228
|
+
}
|
|
7229
|
+
let stopHqBridges = () => {
|
|
7230
|
+
};
|
|
7231
|
+
let closeHqPublisher = () => {
|
|
7232
|
+
};
|
|
7233
|
+
let restartHqBridges = (_sessionId) => {
|
|
7234
|
+
};
|
|
7235
|
+
if (opts.enableHqTelemetry !== false) {
|
|
7236
|
+
try {
|
|
7237
|
+
const core = await import("@wrongstack/core");
|
|
7238
|
+
const publisher = core.createHqPublisherFromEnv({
|
|
7239
|
+
clientKind: "webui",
|
|
7240
|
+
projectRoot: paths.projectRoot,
|
|
7241
|
+
projectName: path14.basename(paths.projectRoot),
|
|
7242
|
+
appConfig: opts.config,
|
|
7243
|
+
socketFactory: (url) => new WebSocket2(url)
|
|
7244
|
+
});
|
|
7245
|
+
if (publisher) {
|
|
7246
|
+
publisher.connect();
|
|
7247
|
+
closeHqPublisher = () => publisher.close();
|
|
7248
|
+
restartHqBridges = (sessionId) => {
|
|
7249
|
+
stopHqBridges();
|
|
7250
|
+
const stops = [];
|
|
7251
|
+
const add = (start) => {
|
|
7252
|
+
try {
|
|
7253
|
+
stops.push(start());
|
|
7254
|
+
} catch {
|
|
7255
|
+
}
|
|
7256
|
+
};
|
|
7257
|
+
add(
|
|
7258
|
+
() => core.startSessionTelemetryBridge({
|
|
7259
|
+
publisher,
|
|
7260
|
+
events,
|
|
7261
|
+
sessionId,
|
|
7262
|
+
projectRoot: paths.projectRoot,
|
|
7263
|
+
projectName: path14.basename(paths.projectRoot),
|
|
7264
|
+
globalRoot: paths.globalRoot,
|
|
7265
|
+
initialAgents: statusTracker.getAgents(),
|
|
7266
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7267
|
+
})
|
|
7268
|
+
);
|
|
7269
|
+
add(
|
|
7270
|
+
() => core.startFleetTelemetryBridge({
|
|
7271
|
+
events,
|
|
7272
|
+
publisher,
|
|
7273
|
+
runId: sessionId,
|
|
7274
|
+
sessionId
|
|
7275
|
+
})
|
|
7276
|
+
);
|
|
7277
|
+
add(() => core.startBrainTelemetryBridge({ events, publisher, sessionId }));
|
|
7278
|
+
add(() => core.startWorktreeTelemetryBridge({ events, publisher, sessionId }));
|
|
7279
|
+
add(
|
|
7280
|
+
() => core.startToolTelemetryBridge({
|
|
7281
|
+
events,
|
|
7282
|
+
publisher,
|
|
7283
|
+
projectRoot: paths.projectRoot,
|
|
7284
|
+
sessionId
|
|
7285
|
+
})
|
|
7286
|
+
);
|
|
7287
|
+
add(() => core.startCostTelemetryBridge({ events, publisher, sessionId }));
|
|
7288
|
+
stopHqBridges = () => {
|
|
7289
|
+
for (const stop2 of stops.splice(0)) {
|
|
7290
|
+
try {
|
|
7291
|
+
stop2();
|
|
7292
|
+
} catch {
|
|
7293
|
+
}
|
|
7294
|
+
}
|
|
7295
|
+
};
|
|
7296
|
+
};
|
|
7297
|
+
restartHqBridges(activeSessionId);
|
|
7298
|
+
}
|
|
7299
|
+
} catch (err) {
|
|
7300
|
+
logger.debug?.(`WebUI HQ telemetry unavailable: ${errorMessage2(err)}`);
|
|
7301
|
+
}
|
|
7302
|
+
}
|
|
7303
|
+
const repointRecovery = async (sessionId) => {
|
|
7304
|
+
if (!recoveryLock) return;
|
|
7305
|
+
try {
|
|
7306
|
+
if (ownsRecoveryLock) await recoveryLock.clear();
|
|
7307
|
+
await recoveryLock.write(sessionId);
|
|
7308
|
+
ownsRecoveryLock = true;
|
|
7309
|
+
} catch {
|
|
7310
|
+
ownsRecoveryLock = false;
|
|
7311
|
+
}
|
|
7312
|
+
};
|
|
7313
|
+
const activate = async (sessionId) => {
|
|
7314
|
+
if (stopped || sessionId === activeSessionId) return;
|
|
7315
|
+
transition = transition.then(async () => {
|
|
7316
|
+
if (stopped || sessionId === activeSessionId) return;
|
|
7317
|
+
activeSessionId = sessionId;
|
|
7318
|
+
await register(sessionId);
|
|
7319
|
+
await repointRecovery(sessionId);
|
|
7320
|
+
try {
|
|
7321
|
+
restartHqBridges(sessionId);
|
|
7322
|
+
} catch (err) {
|
|
7323
|
+
logger.debug?.(`WebUI HQ session swap failed: ${errorMessage2(err)}`);
|
|
7324
|
+
}
|
|
7325
|
+
});
|
|
7326
|
+
await transition;
|
|
7327
|
+
};
|
|
7328
|
+
const stop = async () => {
|
|
7329
|
+
if (stopped) return;
|
|
7330
|
+
stopped = true;
|
|
7331
|
+
await transition.catch(() => void 0);
|
|
7332
|
+
statusTracker.stop();
|
|
7333
|
+
stopHqBridges();
|
|
7334
|
+
closeHqPublisher();
|
|
7335
|
+
fleetNotifier.dispose();
|
|
7336
|
+
try {
|
|
7337
|
+
await registry.markClosing();
|
|
7338
|
+
await registry.unregister();
|
|
7339
|
+
} catch {
|
|
7340
|
+
}
|
|
7341
|
+
if (recoveryLock && ownsRecoveryLock) {
|
|
7342
|
+
await recoveryLock.clear().catch(() => void 0);
|
|
7343
|
+
ownsRecoveryLock = false;
|
|
7344
|
+
}
|
|
7345
|
+
};
|
|
7346
|
+
return { statusTracker, activate, stop };
|
|
7347
|
+
}
|
|
7348
|
+
function errorMessage2(err) {
|
|
7349
|
+
return err instanceof Error ? err.message : String(err);
|
|
7350
|
+
}
|
|
7351
|
+
|
|
7026
7352
|
// src/server/pre-context-services.ts
|
|
7027
7353
|
var GITHUB_PROVIDERS_OVERLAY_URL = "https://raw.githubusercontent.com/WrongStack/WrongStack/main/packages/cli/data/providers.json";
|
|
7028
7354
|
async function createPreContextServices(input) {
|
|
7029
|
-
const { config, wpaths, logger, opts, projectRoot, workingDir, needsProvider } = input;
|
|
7355
|
+
const { config, wpaths, logger, opts, vault, projectRoot, workingDir, needsProvider } = input;
|
|
7030
7356
|
const modelsRegistry = opts.services?.modelsRegistry ?? new DefaultModelsRegistry({
|
|
7031
7357
|
cacheFile: wpaths.modelsCache,
|
|
7032
7358
|
ttlSeconds: 0,
|
|
@@ -7045,7 +7371,7 @@ async function createPreContextServices(input) {
|
|
|
7045
7371
|
await discoverAndMergeWebuiProviders({
|
|
7046
7372
|
config,
|
|
7047
7373
|
registry: modelsRegistry,
|
|
7048
|
-
cacheDir:
|
|
7374
|
+
cacheDir: path15.dirname(wpaths.modelsCache),
|
|
7049
7375
|
logger
|
|
7050
7376
|
});
|
|
7051
7377
|
} catch (err) {
|
|
@@ -7088,10 +7414,23 @@ async function createPreContextServices(input) {
|
|
|
7088
7414
|
configureDangerBypass(config.tools?.exec?.danger ?? {});
|
|
7089
7415
|
configureChildEnvGitIdentity(config.git?.identity ?? null);
|
|
7090
7416
|
console.log("[WebUI] Tool registry loaded:", toolRegistry.list().length, "tools");
|
|
7091
|
-
const
|
|
7417
|
+
const mcpTokenStore = new MCPVaultTokenStore(
|
|
7418
|
+
path15.join(wpaths.projectDir, "mcp-auth.json"),
|
|
7419
|
+
vault
|
|
7420
|
+
);
|
|
7421
|
+
const mcpAuthorizationManager = new MCPAuthorizationManager({ store: mcpTokenStore });
|
|
7422
|
+
const mcpRegistry = new MCPRegistry({
|
|
7423
|
+
toolRegistry,
|
|
7424
|
+
events,
|
|
7425
|
+
log: logger,
|
|
7426
|
+
cacheDir: wpaths.cacheDir,
|
|
7427
|
+
authorizationProviderFactory: createVaultBackedMcpAuthorizationProviderFactory({
|
|
7428
|
+
store: mcpTokenStore
|
|
7429
|
+
}),
|
|
7430
|
+
authorizationManager: mcpAuthorizationManager
|
|
7431
|
+
});
|
|
7092
7432
|
if (config.features.mcp && config.mcpServers) {
|
|
7093
7433
|
for (const [name2, cfg] of Object.entries(config.mcpServers)) {
|
|
7094
|
-
if (cfg.enabled === false) continue;
|
|
7095
7434
|
void mcpRegistry.start({ ...cfg, name: name2 }).catch((err) => {
|
|
7096
7435
|
logger.warn(`MCP server "${name2}" failed to start at boot`, err);
|
|
7097
7436
|
});
|
|
@@ -7106,7 +7445,7 @@ async function createPreContextServices(input) {
|
|
|
7106
7445
|
// surface via the SessionRegistry.
|
|
7107
7446
|
isSessionInUse: async (sessionId) => {
|
|
7108
7447
|
try {
|
|
7109
|
-
const registry =
|
|
7448
|
+
const registry = getSessionRegistry2(wpaths.globalRoot);
|
|
7110
7449
|
const live = await registry.listByProject(wpaths.projectSlug);
|
|
7111
7450
|
const hit = live.find((e) => e.sessionId === sessionId);
|
|
7112
7451
|
if (hit) {
|
|
@@ -7131,120 +7470,22 @@ async function createPreContextServices(input) {
|
|
|
7131
7470
|
await input.touchProject(projectRoot, workingDir);
|
|
7132
7471
|
} catch {
|
|
7133
7472
|
}
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7473
|
+
const sessionIdentity = await createStandaloneSessionIdentityLifecycle({
|
|
7474
|
+
config,
|
|
7475
|
+
events,
|
|
7476
|
+
logger,
|
|
7477
|
+
paths: {
|
|
7478
|
+
globalRoot: wpaths.globalRoot,
|
|
7140
7479
|
projectRoot,
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
registry,
|
|
7151
|
-
sessionId: () => session.id,
|
|
7152
|
-
onUpdate: () => fleetNotifier.notify()
|
|
7153
|
-
});
|
|
7154
|
-
statusTracker.start();
|
|
7155
|
-
let stopHqSessionBridge;
|
|
7156
|
-
let hqTelemetryPublisher;
|
|
7157
|
-
const stopHqAuxBridges = [];
|
|
7158
|
-
try {
|
|
7159
|
-
const {
|
|
7160
|
-
createHqPublisherFromEnv,
|
|
7161
|
-
startSessionTelemetryBridge,
|
|
7162
|
-
startFleetTelemetryBridge,
|
|
7163
|
-
startBrainTelemetryBridge,
|
|
7164
|
-
startWorktreeTelemetryBridge,
|
|
7165
|
-
startToolTelemetryBridge,
|
|
7166
|
-
startCostTelemetryBridge
|
|
7167
|
-
} = await import("@wrongstack/core");
|
|
7168
|
-
const hqTelemetry = createHqPublisherFromEnv({
|
|
7169
|
-
clientKind: "webui",
|
|
7170
|
-
projectRoot,
|
|
7171
|
-
projectName: path14.basename(projectRoot),
|
|
7172
|
-
appConfig: config,
|
|
7173
|
-
socketFactory: (url) => new WebSocket2(url)
|
|
7174
|
-
});
|
|
7175
|
-
if (hqTelemetry) {
|
|
7176
|
-
hqTelemetry.connect();
|
|
7177
|
-
hqTelemetryPublisher = hqTelemetry;
|
|
7178
|
-
stopHqSessionBridge = startSessionTelemetryBridge({
|
|
7179
|
-
publisher: hqTelemetry,
|
|
7180
|
-
events,
|
|
7181
|
-
sessionId: session.id,
|
|
7182
|
-
projectRoot,
|
|
7183
|
-
projectName: path14.basename(projectRoot),
|
|
7184
|
-
globalRoot: wpaths.globalRoot,
|
|
7185
|
-
initialAgents: statusTracker?.getAgents(),
|
|
7186
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7187
|
-
});
|
|
7188
|
-
try {
|
|
7189
|
-
stopHqAuxBridges.push(
|
|
7190
|
-
startFleetTelemetryBridge({ events, publisher: hqTelemetry, runId: session.id, sessionId: session.id })
|
|
7191
|
-
);
|
|
7192
|
-
} catch {
|
|
7193
|
-
}
|
|
7194
|
-
try {
|
|
7195
|
-
stopHqAuxBridges.push(
|
|
7196
|
-
startBrainTelemetryBridge({ events, publisher: hqTelemetry, sessionId: session.id })
|
|
7197
|
-
);
|
|
7198
|
-
} catch {
|
|
7199
|
-
}
|
|
7200
|
-
try {
|
|
7201
|
-
stopHqAuxBridges.push(
|
|
7202
|
-
startWorktreeTelemetryBridge({ events, publisher: hqTelemetry, sessionId: session.id })
|
|
7203
|
-
);
|
|
7204
|
-
} catch {
|
|
7205
|
-
}
|
|
7206
|
-
try {
|
|
7207
|
-
stopHqAuxBridges.push(
|
|
7208
|
-
startToolTelemetryBridge({ events, publisher: hqTelemetry, projectRoot, sessionId: session.id })
|
|
7209
|
-
);
|
|
7210
|
-
} catch {
|
|
7211
|
-
}
|
|
7212
|
-
try {
|
|
7213
|
-
stopHqAuxBridges.push(
|
|
7214
|
-
startCostTelemetryBridge({ events, publisher: hqTelemetry, sessionId: session.id })
|
|
7215
|
-
);
|
|
7216
|
-
} catch {
|
|
7217
|
-
}
|
|
7218
|
-
}
|
|
7219
|
-
} catch {
|
|
7220
|
-
}
|
|
7221
|
-
const stopTracking = async () => {
|
|
7222
|
-
try {
|
|
7223
|
-
fleetNotifier.dispose();
|
|
7224
|
-
await registry.markClosing();
|
|
7225
|
-
statusTracker?.stop();
|
|
7226
|
-
stopHqSessionBridge?.();
|
|
7227
|
-
for (const stop of stopHqAuxBridges) {
|
|
7228
|
-
try {
|
|
7229
|
-
stop();
|
|
7230
|
-
} catch {
|
|
7231
|
-
}
|
|
7232
|
-
}
|
|
7233
|
-
hqTelemetryPublisher?.close();
|
|
7234
|
-
} catch {
|
|
7235
|
-
}
|
|
7236
|
-
};
|
|
7237
|
-
process.once("beforeExit", () => {
|
|
7238
|
-
void stopTracking();
|
|
7239
|
-
});
|
|
7240
|
-
process.once("SIGINT", () => {
|
|
7241
|
-
void stopTracking();
|
|
7242
|
-
});
|
|
7243
|
-
process.once("SIGTERM", () => {
|
|
7244
|
-
void stopTracking();
|
|
7245
|
-
});
|
|
7246
|
-
} catch {
|
|
7247
|
-
}
|
|
7480
|
+
projectSlug: wpaths.projectSlug,
|
|
7481
|
+
projectSessions: wpaths.projectSessions
|
|
7482
|
+
},
|
|
7483
|
+
workingDir,
|
|
7484
|
+
initialSessionId: session.id,
|
|
7485
|
+
sessionStore,
|
|
7486
|
+
manageRecoveryLock: !opts.services?.session
|
|
7487
|
+
});
|
|
7488
|
+
const statusTracker = sessionIdentity.statusTracker;
|
|
7248
7489
|
let context;
|
|
7249
7490
|
const tokenCounter = new DefaultTokenCounter({
|
|
7250
7491
|
registry: modelsRegistry,
|
|
@@ -7269,7 +7510,7 @@ async function createPreContextServices(input) {
|
|
|
7269
7510
|
const modelCapabilitiesRef = { current: modelCapabilities };
|
|
7270
7511
|
const skillLoader = config.features.skills ? new DefaultSkillLoader({ paths: wpaths }) : void 0;
|
|
7271
7512
|
const skillInstaller = config.features.skills ? new SkillInstaller({
|
|
7272
|
-
manifestPath:
|
|
7513
|
+
manifestPath: path15.join(wpaths.globalRoot, "installed-skills.json"),
|
|
7273
7514
|
projectSkillsDir: wpaths.inProjectSkills,
|
|
7274
7515
|
globalSkillsDir: wpaths.globalSkills,
|
|
7275
7516
|
projectHash: wpaths.projectHash,
|
|
@@ -7279,7 +7520,7 @@ async function createPreContextServices(input) {
|
|
|
7279
7520
|
const bundledPromptsDir = promptsEnabled ? (() => {
|
|
7280
7521
|
try {
|
|
7281
7522
|
const req = createRequire2(import.meta.url);
|
|
7282
|
-
return
|
|
7523
|
+
return path15.join(path15.dirname(req.resolve("@wrongstack/core/package.json")), "data", "prompts");
|
|
7283
7524
|
} catch {
|
|
7284
7525
|
return void 0;
|
|
7285
7526
|
}
|
|
@@ -7331,6 +7572,14 @@ async function createPreContextServices(input) {
|
|
|
7331
7572
|
const initialContextPolicy = resolveContextWindowPolicy(config.context);
|
|
7332
7573
|
context.meta["contextWindowMode"] = initialContextPolicy.id;
|
|
7333
7574
|
context.meta["contextWindowPolicy"] = initialContextPolicy;
|
|
7575
|
+
context.state.setMeta(
|
|
7576
|
+
"plan.path",
|
|
7577
|
+
sessionScopedPath(wpaths.projectSessions, session.id, ".plan.json")
|
|
7578
|
+
);
|
|
7579
|
+
context.state.setMeta(
|
|
7580
|
+
"task.path",
|
|
7581
|
+
sessionScopedPath(wpaths.projectSessions, session.id, ".tasks.json")
|
|
7582
|
+
);
|
|
7334
7583
|
seedContextMeta(config, context);
|
|
7335
7584
|
return {
|
|
7336
7585
|
modelsRegistry,
|
|
@@ -7347,6 +7596,7 @@ async function createPreContextServices(input) {
|
|
|
7347
7596
|
session,
|
|
7348
7597
|
sessionStartedAt,
|
|
7349
7598
|
statusTracker,
|
|
7599
|
+
sessionIdentity,
|
|
7350
7600
|
tokenCounter,
|
|
7351
7601
|
modeStore,
|
|
7352
7602
|
modeId,
|
|
@@ -7396,6 +7646,7 @@ function patchConfig(config, updates) {
|
|
|
7396
7646
|
}
|
|
7397
7647
|
|
|
7398
7648
|
// src/server/backend-services.ts
|
|
7649
|
+
import { join as join12 } from "node:path";
|
|
7399
7650
|
import {
|
|
7400
7651
|
Agent,
|
|
7401
7652
|
AutoCompactionMiddleware as AutoCompactionMiddlewareCtor,
|
|
@@ -7409,9 +7660,10 @@ import {
|
|
|
7409
7660
|
installDesignStudioMiddleware,
|
|
7410
7661
|
ObservableBrainArbiter as ObservableBrainArbiterCtor,
|
|
7411
7662
|
resolveContextWindowPolicy as resolveContextWindowPolicy2,
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7663
|
+
BrainDecisionLedger,
|
|
7664
|
+
createBrainRuntime,
|
|
7665
|
+
resolveBrainConfigDefaults,
|
|
7666
|
+
EscalationRoutingBrainArbiter,
|
|
7415
7667
|
GlobalMailbox as GlobalMailbox2,
|
|
7416
7668
|
mailboxSessionTag,
|
|
7417
7669
|
SessionMemoryConsolidator,
|
|
@@ -7425,12 +7677,13 @@ import { toErrorMessage as toErrorMessage6 } from "@wrongstack/core/utils";
|
|
|
7425
7677
|
var REPLAY_LIMIT = 50;
|
|
7426
7678
|
var PAUSE_TIMEOUT_MS = 6e4;
|
|
7427
7679
|
var CollaborationWebSocketHandler = class {
|
|
7428
|
-
constructor(events, logger, reader, annotations, bus) {
|
|
7680
|
+
constructor(events, logger, reader, annotations, bus, options = {}) {
|
|
7429
7681
|
this.events = events;
|
|
7430
7682
|
this.logger = logger;
|
|
7431
7683
|
this.reader = reader;
|
|
7432
7684
|
this.annotations = annotations;
|
|
7433
7685
|
this.bus = bus;
|
|
7686
|
+
this.options = options;
|
|
7434
7687
|
this.subscribe();
|
|
7435
7688
|
this.bus?.onInjectionConsumed((info) => this.broadcastInjectionConsumed(info));
|
|
7436
7689
|
}
|
|
@@ -7439,6 +7692,7 @@ var CollaborationWebSocketHandler = class {
|
|
|
7439
7692
|
reader;
|
|
7440
7693
|
annotations;
|
|
7441
7694
|
bus;
|
|
7695
|
+
options;
|
|
7442
7696
|
clients = /* @__PURE__ */ new Set();
|
|
7443
7697
|
/** sessionId → participants currently watching it. */
|
|
7444
7698
|
bySession = /* @__PURE__ */ new Map();
|
|
@@ -7466,11 +7720,16 @@ var CollaborationWebSocketHandler = class {
|
|
|
7466
7720
|
handleMessage(ws, msg) {
|
|
7467
7721
|
if (msg.type === "collab.join") {
|
|
7468
7722
|
const payload = msg.payload;
|
|
7469
|
-
if (
|
|
7723
|
+
if (typeof payload?.sessionId !== "string" || payload.sessionId.trim().length === 0) {
|
|
7470
7724
|
this.send(ws, this.errorMessage("collab.join requires sessionId"));
|
|
7471
7725
|
return true;
|
|
7472
7726
|
}
|
|
7473
|
-
|
|
7727
|
+
const role = payload.role ?? "observer";
|
|
7728
|
+
if (role !== "observer" && role !== "annotator" && role !== "controller") {
|
|
7729
|
+
this.send(ws, this.errorMessage(`unknown collaboration role '${String(role)}'`));
|
|
7730
|
+
return true;
|
|
7731
|
+
}
|
|
7732
|
+
this.join(ws, payload.sessionId, role);
|
|
7474
7733
|
return true;
|
|
7475
7734
|
}
|
|
7476
7735
|
if (msg.type === "collab.leave") {
|
|
@@ -7505,6 +7764,20 @@ var CollaborationWebSocketHandler = class {
|
|
|
7505
7764
|
}
|
|
7506
7765
|
// ── Join / leave flow ──────────────────────────────────────────────────
|
|
7507
7766
|
join(ws, sessionId, role) {
|
|
7767
|
+
const activeSessionId = this.options.getActiveSessionId?.();
|
|
7768
|
+
if (activeSessionId !== void 0 && sessionId !== activeSessionId) {
|
|
7769
|
+
this.send(
|
|
7770
|
+
ws,
|
|
7771
|
+
this.errorMessage(
|
|
7772
|
+
`collab.join sessionId mismatch (active: ${activeSessionId})`
|
|
7773
|
+
)
|
|
7774
|
+
);
|
|
7775
|
+
return;
|
|
7776
|
+
}
|
|
7777
|
+
if (this.findParticipant(ws)) {
|
|
7778
|
+
this.send(ws, this.errorMessage("collab.join requires leaving the current session first"));
|
|
7779
|
+
return;
|
|
7780
|
+
}
|
|
7508
7781
|
if (role === "controller" && !this.bus) {
|
|
7509
7782
|
this.send(
|
|
7510
7783
|
ws,
|
|
@@ -7523,6 +7796,13 @@ var CollaborationWebSocketHandler = class {
|
|
|
7523
7796
|
);
|
|
7524
7797
|
return;
|
|
7525
7798
|
}
|
|
7799
|
+
if (role !== "observer" && this.options.authorizeRole?.({ ws, sessionId, requestedRole: role }) !== true) {
|
|
7800
|
+
this.send(
|
|
7801
|
+
ws,
|
|
7802
|
+
this.errorMessage(`role '${role}' requires explicit server authorization`)
|
|
7803
|
+
);
|
|
7804
|
+
return;
|
|
7805
|
+
}
|
|
7526
7806
|
const participant = {
|
|
7527
7807
|
participantId: randomUUID(),
|
|
7528
7808
|
ws,
|
|
@@ -7776,17 +8056,13 @@ var CollaborationWebSocketHandler = class {
|
|
|
7776
8056
|
type: "collab.event",
|
|
7777
8057
|
payload: { kind, payload, at: (/* @__PURE__ */ new Date()).toISOString() }
|
|
7778
8058
|
};
|
|
7779
|
-
const
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
`collab broadcast failed: ${toErrorMessage6(err)}`
|
|
7787
|
-
);
|
|
7788
|
-
}
|
|
7789
|
-
}
|
|
8059
|
+
const activeSessionId = this.options.getActiveSessionId?.();
|
|
8060
|
+
if (activeSessionId !== void 0) {
|
|
8061
|
+
this.broadcast(activeSessionId, msg);
|
|
8062
|
+
return;
|
|
8063
|
+
}
|
|
8064
|
+
for (const sessionId of this.bySession.keys()) {
|
|
8065
|
+
this.broadcast(sessionId, msg);
|
|
7790
8066
|
}
|
|
7791
8067
|
}
|
|
7792
8068
|
/**
|
|
@@ -8110,7 +8386,8 @@ var CollaborationWebSocketHandler = class {
|
|
|
8110
8386
|
* Bus callback: a queued injection was spliced into a real tool call. Re-emit
|
|
8111
8387
|
* `collab.injection.granted` with phase `'consumed'` and the now-known tool
|
|
8112
8388
|
* name. The injection carries no sessionId, so resolve it from the author's
|
|
8113
|
-
* current
|
|
8389
|
+
* current participant. If the author has left, fail closed rather than leak
|
|
8390
|
+
* the payload to unrelated sessions.
|
|
8114
8391
|
*/
|
|
8115
8392
|
broadcastInjectionConsumed(info) {
|
|
8116
8393
|
let sessionId = null;
|
|
@@ -8139,14 +8416,16 @@ var CollaborationWebSocketHandler = class {
|
|
|
8139
8416
|
if (sessionId) {
|
|
8140
8417
|
this.broadcast(sessionId, message(sessionId));
|
|
8141
8418
|
} else {
|
|
8142
|
-
|
|
8419
|
+
this.logger.debug?.(
|
|
8420
|
+
`collab: consumed injection ${info.toolUseId} has no live author; broadcast suppressed`
|
|
8421
|
+
);
|
|
8143
8422
|
}
|
|
8144
8423
|
}
|
|
8145
8424
|
};
|
|
8146
8425
|
|
|
8147
8426
|
// src/server/codebase-indexing.ts
|
|
8148
8427
|
import * as fs12 from "node:fs";
|
|
8149
|
-
import * as
|
|
8428
|
+
import * as path16 from "node:path";
|
|
8150
8429
|
import {
|
|
8151
8430
|
cancelPendingReindexes,
|
|
8152
8431
|
enqueueReindex,
|
|
@@ -8199,7 +8478,7 @@ function setupWebUICodebaseIndexing(deps2) {
|
|
|
8199
8478
|
if (!filename) return;
|
|
8200
8479
|
const rel = filename.toString();
|
|
8201
8480
|
if (isIgnored(rel)) return;
|
|
8202
|
-
const abs =
|
|
8481
|
+
const abs = path16.resolve(deps2.projectRoot, rel);
|
|
8203
8482
|
enqueueFile(abs);
|
|
8204
8483
|
});
|
|
8205
8484
|
watcher.on("error", (err) => deps2.logger.debug(`webui codebase index watcher error: ${err}`));
|
|
@@ -8212,7 +8491,7 @@ function setupWebUICodebaseIndexing(deps2) {
|
|
|
8212
8491
|
}
|
|
8213
8492
|
function enqueueFile(filePath) {
|
|
8214
8493
|
if (!idx.onEdit && !idx.watchExternal) return;
|
|
8215
|
-
const abs =
|
|
8494
|
+
const abs = path16.isAbsolute(filePath) ? path16.normalize(filePath) : path16.resolve(deps2.projectRoot, filePath);
|
|
8216
8495
|
if (!isInside2(deps2.projectRoot, abs) || !isIndexableFile(abs)) return;
|
|
8217
8496
|
enqueueReindex({
|
|
8218
8497
|
projectRoot: deps2.projectRoot,
|
|
@@ -8249,9 +8528,9 @@ function isIgnored(rel) {
|
|
|
8249
8528
|
return rel.split(/[/\\]/).some((seg) => IGNORE_DIRS.has(seg));
|
|
8250
8529
|
}
|
|
8251
8530
|
function isInside2(root, target) {
|
|
8252
|
-
const normalizedRoot =
|
|
8253
|
-
const normalizedTarget =
|
|
8254
|
-
return normalizedTarget === normalizedRoot || normalizedTarget.startsWith(normalizedRoot +
|
|
8531
|
+
const normalizedRoot = path16.resolve(root);
|
|
8532
|
+
const normalizedTarget = path16.resolve(target);
|
|
8533
|
+
return normalizedTarget === normalizedRoot || normalizedTarget.startsWith(normalizedRoot + path16.sep);
|
|
8255
8534
|
}
|
|
8256
8535
|
|
|
8257
8536
|
// src/server/discover-mailbox-bridge.ts
|
|
@@ -8784,21 +9063,59 @@ async function createAgentServices(input) {
|
|
|
8784
9063
|
agent.extensions.register(new SessionMemoryConsolidator({ memoryStore }));
|
|
8785
9064
|
}
|
|
8786
9065
|
console.log("[WebUI] Agent initialized");
|
|
8787
|
-
const
|
|
8788
|
-
|
|
8789
|
-
|
|
8790
|
-
|
|
8791
|
-
|
|
8792
|
-
|
|
8793
|
-
|
|
8794
|
-
|
|
9066
|
+
const brainCfg = resolveBrainConfigDefaults(config.brain, {
|
|
9067
|
+
fallbackModels: config.fallbackModels
|
|
9068
|
+
});
|
|
9069
|
+
const brainLedgerPath = join12(wpaths.projectDir, "brain-ledger.jsonl");
|
|
9070
|
+
let brainLedgerEnabled = brainCfg.ledger?.enabled !== false;
|
|
9071
|
+
let brainLedger;
|
|
9072
|
+
const startBrainLedger = () => {
|
|
9073
|
+
if (brainLedger) return;
|
|
9074
|
+
brainLedger = new BrainDecisionLedger({ events, filePath: brainLedgerPath });
|
|
9075
|
+
void brainLedger.start();
|
|
9076
|
+
};
|
|
9077
|
+
if (brainLedgerEnabled) startBrainLedger();
|
|
9078
|
+
const brainRuntime = createBrainRuntime({
|
|
9079
|
+
initialConfig: brainCfg,
|
|
9080
|
+
defaultProviderId: config.provider,
|
|
9081
|
+
sessionProvider: () => provider,
|
|
9082
|
+
sessionModel: () => context.model,
|
|
9083
|
+
resolveProvider: (providerId) => {
|
|
9084
|
+
const savedCfg = config.providers?.[providerId] ?? {};
|
|
9085
|
+
return providerRegistry.create({
|
|
9086
|
+
...savedCfg,
|
|
9087
|
+
apiKey: savedCfg.apiKey ?? config.apiKey,
|
|
9088
|
+
baseUrl: savedCfg.baseUrl ?? config.baseUrl,
|
|
9089
|
+
type: providerId
|
|
9090
|
+
});
|
|
9091
|
+
},
|
|
9092
|
+
ledger: {
|
|
9093
|
+
getPath: () => brainLedgerEnabled ? brainLedgerPath : void 0,
|
|
9094
|
+
isEnabled: () => brainLedgerEnabled,
|
|
9095
|
+
setEnabled: (on) => {
|
|
9096
|
+
brainLedgerEnabled = on;
|
|
9097
|
+
if (on) {
|
|
9098
|
+
startBrainLedger();
|
|
9099
|
+
} else {
|
|
9100
|
+
void brainLedger?.stop();
|
|
9101
|
+
brainLedger = void 0;
|
|
9102
|
+
}
|
|
9103
|
+
},
|
|
9104
|
+
failureStreakFor: (request) => brainLedger?.failureStreakFor(request) ?? 0,
|
|
9105
|
+
getDecisionDigest: (request) => brainLedger?.digestFor(request)
|
|
9106
|
+
},
|
|
9107
|
+
persist: input.persistBrainConfig
|
|
9108
|
+
});
|
|
9109
|
+
const brainSettings = {
|
|
9110
|
+
get maxAutoRisk() {
|
|
9111
|
+
return brainRuntime.getMaxAutoRisk();
|
|
9112
|
+
},
|
|
9113
|
+
set maxAutoRisk(level) {
|
|
9114
|
+
void brainRuntime.apply({ maxAutoRisk: level }).persisted;
|
|
9115
|
+
}
|
|
8795
9116
|
};
|
|
8796
9117
|
const brain = new ObservableBrainArbiterCtor(
|
|
8797
|
-
|
|
8798
|
-
policy: new DefaultBrainArbiter(),
|
|
8799
|
-
autonomous: autonomousBrain,
|
|
8800
|
-
getMaxAutoRisk: () => brainSettings.maxAutoRisk
|
|
8801
|
-
}),
|
|
9118
|
+
new EscalationRoutingBrainArbiter(brainRuntime.arbiter, void 0, () => "headless"),
|
|
8802
9119
|
events
|
|
8803
9120
|
);
|
|
8804
9121
|
container.bind(TOKENS2.BrainArbiter, () => brain);
|
|
@@ -8838,6 +9155,12 @@ async function createAgentServices(input) {
|
|
|
8838
9155
|
const brainMonitor = new BrainMonitor({
|
|
8839
9156
|
events,
|
|
8840
9157
|
brain,
|
|
9158
|
+
toolFailureStreak: brainCfg.monitor?.toolFailureStreak,
|
|
9159
|
+
errorStormCount: brainCfg.monitor?.errorStormCount,
|
|
9160
|
+
stallMs: brainCfg.monitor?.stallMs,
|
|
9161
|
+
fileChurnThreshold: brainCfg.monitor?.fileChurnThreshold,
|
|
9162
|
+
fileChurnWindowMs: brainCfg.monitor?.fileChurnWindowMs,
|
|
9163
|
+
cooldownMs: brainCfg.monitor?.cooldownMs,
|
|
8841
9164
|
sessionId: () => context.session?.id,
|
|
8842
9165
|
intervene: async ({ subject, body }) => {
|
|
8843
9166
|
const tag = mailboxSessionTag(input.sessionGetter().id);
|
|
@@ -8902,7 +9225,10 @@ async function createAgentServices(input) {
|
|
|
8902
9225
|
logger,
|
|
8903
9226
|
input.sessionReader,
|
|
8904
9227
|
input.annotationsStore,
|
|
8905
|
-
collabBus
|
|
9228
|
+
collabBus,
|
|
9229
|
+
{
|
|
9230
|
+
getActiveSessionId: () => context.session.id
|
|
9231
|
+
}
|
|
8906
9232
|
);
|
|
8907
9233
|
return {
|
|
8908
9234
|
collabBus,
|
|
@@ -8914,8 +9240,13 @@ async function createAgentServices(input) {
|
|
|
8914
9240
|
pipelines,
|
|
8915
9241
|
brain,
|
|
8916
9242
|
brainSettings,
|
|
9243
|
+
brainRuntime,
|
|
8917
9244
|
brainLog,
|
|
8918
9245
|
brainMonitor,
|
|
9246
|
+
// Getter: ledger toggles swap the instance, shutdown must stop the LIVE one.
|
|
9247
|
+
get brainLedger() {
|
|
9248
|
+
return brainLedger;
|
|
9249
|
+
},
|
|
8919
9250
|
codebaseIndexing,
|
|
8920
9251
|
autoPhaseHandler,
|
|
8921
9252
|
specsHandler,
|
|
@@ -9091,7 +9422,18 @@ function createConnectionHandler(opts) {
|
|
|
9091
9422
|
}
|
|
9092
9423
|
|
|
9093
9424
|
// src/server/message-dispatcher.ts
|
|
9094
|
-
import
|
|
9425
|
+
import path17 from "node:path";
|
|
9426
|
+
import {
|
|
9427
|
+
buildUserContentBlocks,
|
|
9428
|
+
IncomingImageError,
|
|
9429
|
+
parseIncomingImages
|
|
9430
|
+
} from "@wrongstack/core/utils";
|
|
9431
|
+
import {
|
|
9432
|
+
createToolVisionAdapters,
|
|
9433
|
+
ImageInputUnsupportedError,
|
|
9434
|
+
routeImagesForModel,
|
|
9435
|
+
VisionUrlBlockedError
|
|
9436
|
+
} from "@wrongstack/runtime/vision";
|
|
9095
9437
|
|
|
9096
9438
|
// src/server/autophase-routes.ts
|
|
9097
9439
|
async function handleAutoPhaseRoute(_ws, msg, handlers) {
|
|
@@ -9112,6 +9454,12 @@ async function handleBrainRoute(ws, msg, handlers) {
|
|
|
9112
9454
|
case "brain.ask":
|
|
9113
9455
|
await handlers.ask(ws, msg);
|
|
9114
9456
|
return true;
|
|
9457
|
+
case "brain.config.get":
|
|
9458
|
+
await handlers.configGet(ws, msg);
|
|
9459
|
+
return true;
|
|
9460
|
+
case "brain.config.set":
|
|
9461
|
+
await handlers.configSet(ws, msg);
|
|
9462
|
+
return true;
|
|
9115
9463
|
default:
|
|
9116
9464
|
return false;
|
|
9117
9465
|
}
|
|
@@ -9842,6 +10190,9 @@ async function handleKanbanRoute(ws, msg, ctx) {
|
|
|
9842
10190
|
"Kanban agent dispatch is only available from the CLI-hosted WebUI runtime."
|
|
9843
10191
|
);
|
|
9844
10192
|
return true;
|
|
10193
|
+
case "kanban.capabilities":
|
|
10194
|
+
ok(ws, type, { dispatchSupported: false });
|
|
10195
|
+
return true;
|
|
9845
10196
|
case "kanban.task.remove": {
|
|
9846
10197
|
const boardId = payload?.boardId;
|
|
9847
10198
|
const taskId = payload?.taskId;
|
|
@@ -9921,6 +10272,9 @@ async function handleMailboxRoute(ws, msg, handlers) {
|
|
|
9921
10272
|
case "mailbox.purge":
|
|
9922
10273
|
await handlers.purge(ws, msg);
|
|
9923
10274
|
return true;
|
|
10275
|
+
case "mailbox.compact":
|
|
10276
|
+
await handlers.compact(ws, msg);
|
|
10277
|
+
return true;
|
|
9924
10278
|
default:
|
|
9925
10279
|
return false;
|
|
9926
10280
|
}
|
|
@@ -9959,6 +10313,18 @@ async function handleMcpRoute(ws, msg, handlers) {
|
|
|
9959
10313
|
case "mcp.discover":
|
|
9960
10314
|
await handlers.discover(ws, msg);
|
|
9961
10315
|
return true;
|
|
10316
|
+
case "mcp.resources":
|
|
10317
|
+
await handlers.resources(ws, msg);
|
|
10318
|
+
return true;
|
|
10319
|
+
case "mcp.prompts":
|
|
10320
|
+
await handlers.prompts(ws, msg);
|
|
10321
|
+
return true;
|
|
10322
|
+
case "mcp.resource.read":
|
|
10323
|
+
await handlers.resourceRead(ws, msg);
|
|
10324
|
+
return true;
|
|
10325
|
+
case "mcp.prompt.get":
|
|
10326
|
+
await handlers.promptGet(ws, msg);
|
|
10327
|
+
return true;
|
|
9962
10328
|
default:
|
|
9963
10329
|
return false;
|
|
9964
10330
|
}
|
|
@@ -10348,7 +10714,7 @@ function createMessageDispatcher(opts) {
|
|
|
10348
10714
|
skillLoader: deps2.skillLoader,
|
|
10349
10715
|
skillInstaller: deps2.skillInstaller,
|
|
10350
10716
|
projectRoot,
|
|
10351
|
-
projectSkillsDir:
|
|
10717
|
+
projectSkillsDir: path17.join(projectRoot, ".wrongstack", "skills"),
|
|
10352
10718
|
globalSkillsDir: deps2.wpaths.globalSkills
|
|
10353
10719
|
};
|
|
10354
10720
|
}
|
|
@@ -10421,7 +10787,8 @@ function createMessageDispatcher(opts) {
|
|
|
10421
10787
|
}
|
|
10422
10788
|
case "user_message": {
|
|
10423
10789
|
if (!ensureCurrentSession(ws, msg, "user_message")) return;
|
|
10424
|
-
const
|
|
10790
|
+
const userPayload = msg.payload;
|
|
10791
|
+
const content = userPayload.content;
|
|
10425
10792
|
if (runLock.get()) {
|
|
10426
10793
|
send(ws, {
|
|
10427
10794
|
type: "error",
|
|
@@ -10435,8 +10802,24 @@ function createMessageDispatcher(opts) {
|
|
|
10435
10802
|
const thisRun = new AbortController();
|
|
10436
10803
|
runLock.set(thisRun);
|
|
10437
10804
|
try {
|
|
10805
|
+
let input = content;
|
|
10806
|
+
const imageBlocks = parseIncomingImages(userPayload.images, userPayload.imageBase64);
|
|
10807
|
+
if (imageBlocks.length > 0) {
|
|
10808
|
+
const routed = await routeImagesForModel(
|
|
10809
|
+
buildUserContentBlocks(content, imageBlocks),
|
|
10810
|
+
{
|
|
10811
|
+
supportsVision: deps2.agent.ctx.provider.capabilities.vision,
|
|
10812
|
+
adapters: () => createToolVisionAdapters(deps2.agent.tools),
|
|
10813
|
+
ctx: deps2.agent.ctx,
|
|
10814
|
+
signal: thisRun.signal,
|
|
10815
|
+
providerId: deps2.agent.ctx.provider.id,
|
|
10816
|
+
model: deps2.agent.ctx.model
|
|
10817
|
+
}
|
|
10818
|
+
);
|
|
10819
|
+
input = routed.blocks;
|
|
10820
|
+
}
|
|
10438
10821
|
const maxIt = typeof deps2.context.meta["maxIterations"] === "number" ? deps2.context.meta["maxIterations"] : void 0;
|
|
10439
|
-
const result = await deps2.agent.run(
|
|
10822
|
+
const result = await deps2.agent.run(input, {
|
|
10440
10823
|
signal: thisRun.signal,
|
|
10441
10824
|
maxIterations: maxIt
|
|
10442
10825
|
});
|
|
@@ -10454,13 +10837,24 @@ function createMessageDispatcher(opts) {
|
|
|
10454
10837
|
})
|
|
10455
10838
|
});
|
|
10456
10839
|
} catch (err) {
|
|
10457
|
-
|
|
10458
|
-
|
|
10459
|
-
|
|
10460
|
-
|
|
10461
|
-
|
|
10462
|
-
|
|
10463
|
-
|
|
10840
|
+
if (err instanceof IncomingImageError || err instanceof ImageInputUnsupportedError || err instanceof VisionUrlBlockedError) {
|
|
10841
|
+
send(ws, {
|
|
10842
|
+
type: "error",
|
|
10843
|
+
payload: sessionPayload2({
|
|
10844
|
+
phase: "user_message",
|
|
10845
|
+
...err instanceof ImageInputUnsupportedError ? { code: "vision_unsupported" } : {},
|
|
10846
|
+
message: err.message
|
|
10847
|
+
})
|
|
10848
|
+
});
|
|
10849
|
+
} else {
|
|
10850
|
+
send(ws, {
|
|
10851
|
+
type: "error",
|
|
10852
|
+
payload: sessionPayload2({
|
|
10853
|
+
phase: "agent.run",
|
|
10854
|
+
message: errMessage(err)
|
|
10855
|
+
})
|
|
10856
|
+
});
|
|
10857
|
+
}
|
|
10464
10858
|
} finally {
|
|
10465
10859
|
if (runLock.get() === thisRun) {
|
|
10466
10860
|
runLock.set(null);
|
|
@@ -10508,7 +10902,7 @@ function createMessageDispatcher(opts) {
|
|
|
10508
10902
|
break;
|
|
10509
10903
|
}
|
|
10510
10904
|
case "tool.disable": {
|
|
10511
|
-
const name2 =
|
|
10905
|
+
const name2 = msg.payload?.name;
|
|
10512
10906
|
if (!name2) {
|
|
10513
10907
|
send(ws, { type: "error", payload: { message: "tool.disable requires a name" } });
|
|
10514
10908
|
break;
|
|
@@ -10523,7 +10917,7 @@ function createMessageDispatcher(opts) {
|
|
|
10523
10917
|
break;
|
|
10524
10918
|
}
|
|
10525
10919
|
case "tool.enable": {
|
|
10526
|
-
const name2 =
|
|
10920
|
+
const name2 = msg.payload?.name;
|
|
10527
10921
|
if (!name2) {
|
|
10528
10922
|
send(ws, { type: "error", payload: { message: "tool.enable requires a name" } });
|
|
10529
10923
|
break;
|
|
@@ -10567,6 +10961,14 @@ function createMessageDispatcher(opts) {
|
|
|
10567
10961
|
throw new Error("handleMcpRoute did not claim mcp.restart \u2014 check chain order");
|
|
10568
10962
|
case "mcp.discover":
|
|
10569
10963
|
throw new Error("handleMcpRoute did not claim mcp.discover \u2014 check chain order");
|
|
10964
|
+
case "mcp.resources":
|
|
10965
|
+
throw new Error("handleMcpRoute did not claim mcp.resources \u2014 check chain order");
|
|
10966
|
+
case "mcp.prompts":
|
|
10967
|
+
throw new Error("handleMcpRoute did not claim mcp.prompts \u2014 check chain order");
|
|
10968
|
+
case "mcp.resource.read":
|
|
10969
|
+
throw new Error("handleMcpRoute did not claim mcp.resource.read \u2014 check chain order");
|
|
10970
|
+
case "mcp.prompt.get":
|
|
10971
|
+
throw new Error("handleMcpRoute did not claim mcp.prompt.get \u2014 check chain order");
|
|
10570
10972
|
// Skills — full request→response cycle lives in skills-handlers.ts.
|
|
10571
10973
|
case "skills.list":
|
|
10572
10974
|
await handleSkillsList(ws, makeSkillsContext());
|
|
@@ -10867,7 +11269,19 @@ var PREF_KEYS = [
|
|
|
10867
11269
|
"favoriteModels",
|
|
10868
11270
|
"favoriteModelsOnly",
|
|
10869
11271
|
"modelMatrix",
|
|
10870
|
-
"fallbackAuto"
|
|
11272
|
+
"fallbackAuto",
|
|
11273
|
+
// Refiner + TUI visual prefs (parity with the CLI's embedded server —
|
|
11274
|
+
// these were browser-editable there but rejected as unknown keys here).
|
|
11275
|
+
"refinerProvider",
|
|
11276
|
+
"refinerModel",
|
|
11277
|
+
"thinkingWord",
|
|
11278
|
+
"statuslineMode",
|
|
11279
|
+
"animationStyle",
|
|
11280
|
+
// Safety / system prefs (parity with /settings breaker, fs-access, debug-stream).
|
|
11281
|
+
"breakerEnabled",
|
|
11282
|
+
"breakerAutoKillResetMs",
|
|
11283
|
+
"fsAccess",
|
|
11284
|
+
"debugStream"
|
|
10871
11285
|
];
|
|
10872
11286
|
function prefSnapshot(contextMeta) {
|
|
10873
11287
|
const snapshot = {};
|
|
@@ -10938,6 +11352,16 @@ async function persistPrefsToConfig(deps2, holder, payload) {
|
|
|
10938
11352
|
setAutonomy("enhanceDelayMs", payload["enhanceDelayMs"]);
|
|
10939
11353
|
if (typeof payload["enhanceLanguage"] === "string")
|
|
10940
11354
|
setAutonomy("enhanceLanguage", payload["enhanceLanguage"]);
|
|
11355
|
+
if (typeof payload["refinerProvider"] === "string")
|
|
11356
|
+
setAutonomy("refinerProvider", payload["refinerProvider"]);
|
|
11357
|
+
if (typeof payload["refinerModel"] === "string")
|
|
11358
|
+
setAutonomy("refinerModel", payload["refinerModel"]);
|
|
11359
|
+
if (typeof payload["thinkingWord"] === "string")
|
|
11360
|
+
setAutonomy("thinkingWord", payload["thinkingWord"]);
|
|
11361
|
+
if (typeof payload["statuslineMode"] === "string")
|
|
11362
|
+
setAutonomy("statuslineMode", payload["statuslineMode"]);
|
|
11363
|
+
if (typeof payload["animationStyle"] === "string")
|
|
11364
|
+
setAutonomy("animationStyle", payload["animationStyle"]);
|
|
10941
11365
|
if (autonomyTouched) decrypted.autonomy = autonomyCfg;
|
|
10942
11366
|
if (typeof payload["nextPrediction"] === "boolean")
|
|
10943
11367
|
decrypted.nextPrediction = payload["nextPrediction"];
|
|
@@ -11055,16 +11479,34 @@ async function persistPrefsToConfig(deps2, holder, payload) {
|
|
|
11055
11479
|
}
|
|
11056
11480
|
decrypted.modelRuntime = mr;
|
|
11057
11481
|
}
|
|
11482
|
+
if (typeof payload["breakerEnabled"] === "boolean" || typeof payload["breakerAutoKillResetMs"] === "number") {
|
|
11483
|
+
const cb = decrypted.circuitBreaker ?? {};
|
|
11484
|
+
if (typeof payload["breakerEnabled"] === "boolean") cb.enabled = payload["breakerEnabled"];
|
|
11485
|
+
if (typeof payload["breakerAutoKillResetMs"] === "number")
|
|
11486
|
+
cb.autoKillResetMs = payload["breakerAutoKillResetMs"];
|
|
11487
|
+
decrypted.circuitBreaker = cb;
|
|
11488
|
+
}
|
|
11489
|
+
if (payload["fsAccess"] === "unrestricted" || payload["fsAccess"] === "project") {
|
|
11490
|
+
const restrict = payload["fsAccess"] === "project";
|
|
11491
|
+
const toolsCfg = decrypted.tools ?? {};
|
|
11492
|
+
toolsCfg.restrictToProjectRoot = restrict;
|
|
11493
|
+
decrypted.tools = toolsCfg;
|
|
11494
|
+
const featsCfg = decrypted.features ?? {};
|
|
11495
|
+
featsCfg.allowOutsideProjectRoot = !restrict;
|
|
11496
|
+
decrypted.features = featsCfg;
|
|
11497
|
+
}
|
|
11498
|
+
if (typeof payload["debugStream"] === "boolean")
|
|
11499
|
+
decrypted.debugStream = payload["debugStream"];
|
|
11058
11500
|
}, "prefs");
|
|
11059
11501
|
}
|
|
11060
11502
|
|
|
11061
11503
|
// src/server/projects-manifest.ts
|
|
11062
11504
|
import * as fs14 from "node:fs/promises";
|
|
11063
|
-
import * as
|
|
11505
|
+
import * as path18 from "node:path";
|
|
11064
11506
|
import { projectSlug } from "@wrongstack/core";
|
|
11065
11507
|
function projectsJsonPath(globalConfigPath) {
|
|
11066
|
-
const base =
|
|
11067
|
-
return
|
|
11508
|
+
const base = path18.dirname(globalConfigPath);
|
|
11509
|
+
return path18.join(base, "projects.json");
|
|
11068
11510
|
}
|
|
11069
11511
|
async function loadManifest(globalConfigPath) {
|
|
11070
11512
|
try {
|
|
@@ -11077,15 +11519,15 @@ async function loadManifest(globalConfigPath) {
|
|
|
11077
11519
|
}
|
|
11078
11520
|
async function saveManifest(manifest, globalConfigPath) {
|
|
11079
11521
|
const file = projectsJsonPath(globalConfigPath);
|
|
11080
|
-
await fs14.mkdir(
|
|
11522
|
+
await fs14.mkdir(path18.dirname(file), { recursive: true });
|
|
11081
11523
|
await fs14.writeFile(file, JSON.stringify(manifest, null, 2), "utf8");
|
|
11082
11524
|
}
|
|
11083
11525
|
function generateProjectSlug(rootPath) {
|
|
11084
11526
|
return projectSlug(rootPath);
|
|
11085
11527
|
}
|
|
11086
11528
|
async function ensureProjectDataDir(slug, globalConfigPath) {
|
|
11087
|
-
const base =
|
|
11088
|
-
const dir =
|
|
11529
|
+
const base = path18.dirname(globalConfigPath);
|
|
11530
|
+
const dir = path18.join(base, "projects", slug);
|
|
11089
11531
|
await fs14.mkdir(dir, { recursive: true });
|
|
11090
11532
|
return dir;
|
|
11091
11533
|
}
|
|
@@ -11502,6 +11944,16 @@ async function handleMailboxPurge(ws, deps2, opts) {
|
|
|
11502
11944
|
send(ws, { type: "mailbox.purged", payload: { error: errMessage(err) } });
|
|
11503
11945
|
}
|
|
11504
11946
|
}
|
|
11947
|
+
async function handleMailboxCompact(ws, deps2, opts) {
|
|
11948
|
+
try {
|
|
11949
|
+
const dir = resolveProjectDir2(deps2.projectRoot, deps2.globalRoot);
|
|
11950
|
+
const mb = new GlobalMailbox3(dir);
|
|
11951
|
+
const result = await mb.autoCompact(opts);
|
|
11952
|
+
send(ws, { type: "mailbox.compacted", payload: result });
|
|
11953
|
+
} catch (err) {
|
|
11954
|
+
send(ws, { type: "mailbox.compacted", payload: { error: errMessage(err) } });
|
|
11955
|
+
}
|
|
11956
|
+
}
|
|
11505
11957
|
|
|
11506
11958
|
// src/server/mode-handlers.ts
|
|
11507
11959
|
import {
|
|
@@ -11590,7 +12042,7 @@ function createModeHandlers(ctx) {
|
|
|
11590
12042
|
}
|
|
11591
12043
|
|
|
11592
12044
|
// src/server/project-handlers.ts
|
|
11593
|
-
import * as
|
|
12045
|
+
import * as path19 from "node:path";
|
|
11594
12046
|
function createProjectHandlers(ctx) {
|
|
11595
12047
|
return {
|
|
11596
12048
|
listProjects: async (ws) => {
|
|
@@ -11616,7 +12068,7 @@ function createProjectHandlers(ctx) {
|
|
|
11616
12068
|
selectProject: async (ws, msg) => {
|
|
11617
12069
|
const payload = msg.payload;
|
|
11618
12070
|
const root = typeof payload?.root === "string" ? payload.root : "";
|
|
11619
|
-
const name2 = typeof payload?.name === "string" ? payload.name : root ?
|
|
12071
|
+
const name2 = typeof payload?.name === "string" ? payload.name : root ? path19.basename(root) : "";
|
|
11620
12072
|
send(ws, {
|
|
11621
12073
|
type: "projects.selected",
|
|
11622
12074
|
payload: {
|
|
@@ -11651,12 +12103,12 @@ function createProjectHandlers(ctx) {
|
|
|
11651
12103
|
}
|
|
11652
12104
|
|
|
11653
12105
|
// src/server/session-handlers.ts
|
|
11654
|
-
import * as path19 from "node:path";
|
|
11655
12106
|
import {
|
|
11656
12107
|
DEFAULT_CONTEXT_WINDOW_MODE_ID,
|
|
11657
12108
|
repairToolUseAdjacency,
|
|
11658
12109
|
resolveContextWindowPolicy as resolveContextWindowPolicy3
|
|
11659
12110
|
} from "@wrongstack/core";
|
|
12111
|
+
import { sessionScopedPath as sessionScopedPath2 } from "@wrongstack/core/utils";
|
|
11660
12112
|
function createSessionHandlers(ctx) {
|
|
11661
12113
|
const currentSessionId = () => ctx.getSession().id;
|
|
11662
12114
|
const sessionPayload2 = (payload) => {
|
|
@@ -11682,33 +12134,46 @@ function createSessionHandlers(ctx) {
|
|
|
11682
12134
|
});
|
|
11683
12135
|
return false;
|
|
11684
12136
|
};
|
|
12137
|
+
const finalizeSession = async (writer) => {
|
|
12138
|
+
await writer.append({
|
|
12139
|
+
type: "session_end",
|
|
12140
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12141
|
+
usage: ctx.tokenCounter.total()
|
|
12142
|
+
}).catch(() => void 0);
|
|
12143
|
+
await writer.close().catch(() => void 0);
|
|
12144
|
+
};
|
|
12145
|
+
const activateSession = async (next, messages, usage) => {
|
|
12146
|
+
const current = ctx.getSession();
|
|
12147
|
+
if (current !== next) await finalizeSession(current);
|
|
12148
|
+
ctx.setSession(next);
|
|
12149
|
+
ctx.context.session = next;
|
|
12150
|
+
ctx.context.state.replaceMessages(messages);
|
|
12151
|
+
ctx.context.state.replaceTodos([]);
|
|
12152
|
+
ctx.context.readFiles.clear();
|
|
12153
|
+
ctx.context.fileMtimes.clear();
|
|
12154
|
+
ctx.context.state.setMeta(
|
|
12155
|
+
"plan.path",
|
|
12156
|
+
sessionScopedPath2(ctx.sessionsDir, next.id, ".plan.json")
|
|
12157
|
+
);
|
|
12158
|
+
ctx.context.state.setMeta(
|
|
12159
|
+
"task.path",
|
|
12160
|
+
sessionScopedPath2(ctx.sessionsDir, next.id, ".tasks.json")
|
|
12161
|
+
);
|
|
12162
|
+
ctx.tokenCounter.reset();
|
|
12163
|
+
if (usage) ctx.tokenCounter.account(usage, ctx.config.model);
|
|
12164
|
+
ctx.setSessionStartedAt(Date.now());
|
|
12165
|
+
await ctx.onSessionSwapped(next.id);
|
|
12166
|
+
};
|
|
11685
12167
|
return {
|
|
11686
12168
|
newSession: async (ws, msg) => {
|
|
11687
12169
|
if (!ensureCurrentSession(ws, msg, "session.new")) return;
|
|
11688
|
-
const session = ctx.getSession();
|
|
11689
|
-
try {
|
|
11690
|
-
await session.append({
|
|
11691
|
-
type: "session_end",
|
|
11692
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11693
|
-
usage: ctx.tokenCounter.total()
|
|
11694
|
-
});
|
|
11695
|
-
await session.close();
|
|
11696
|
-
} catch {
|
|
11697
|
-
}
|
|
11698
12170
|
const next = await ctx.getSessionStore().create({
|
|
11699
12171
|
id: "",
|
|
11700
12172
|
title: "",
|
|
11701
12173
|
model: ctx.config.model,
|
|
11702
12174
|
provider: ctx.config.provider
|
|
11703
12175
|
});
|
|
11704
|
-
|
|
11705
|
-
ctx.context.session = next;
|
|
11706
|
-
ctx.context.state.replaceMessages([]);
|
|
11707
|
-
ctx.context.state.replaceTodos([]);
|
|
11708
|
-
ctx.context.readFiles.clear();
|
|
11709
|
-
ctx.context.fileMtimes.clear();
|
|
11710
|
-
ctx.tokenCounter.reset();
|
|
11711
|
-
ctx.setSessionStartedAt(Date.now());
|
|
12176
|
+
await activateSession(next, []);
|
|
11712
12177
|
broadcast(ctx.clients, { type: "session.start", payload: await ctx.sessionStartPayload() });
|
|
11713
12178
|
},
|
|
11714
12179
|
clearContext: async (ws, msg) => {
|
|
@@ -11964,23 +12429,7 @@ function createSessionHandlers(ctx) {
|
|
|
11964
12429
|
return;
|
|
11965
12430
|
}
|
|
11966
12431
|
const resumed = await ctx.getSessionStore().resume(id);
|
|
11967
|
-
|
|
11968
|
-
await current.append({
|
|
11969
|
-
type: "session_end",
|
|
11970
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11971
|
-
usage: ctx.tokenCounter.total()
|
|
11972
|
-
});
|
|
11973
|
-
await current.close();
|
|
11974
|
-
} catch {
|
|
11975
|
-
}
|
|
11976
|
-
ctx.setSession(resumed.writer);
|
|
11977
|
-
ctx.context.session = resumed.writer;
|
|
11978
|
-
ctx.context.state.replaceMessages(resumed.data.messages);
|
|
11979
|
-
ctx.context.readFiles.clear();
|
|
11980
|
-
ctx.context.fileMtimes.clear();
|
|
11981
|
-
ctx.tokenCounter.reset();
|
|
11982
|
-
ctx.tokenCounter.account(resumed.data.usage, ctx.config.model);
|
|
11983
|
-
ctx.setSessionStartedAt(Date.now());
|
|
12432
|
+
await activateSession(resumed.writer, resumed.data.messages, resumed.data.usage);
|
|
11984
12433
|
broadcast(ctx.clients, {
|
|
11985
12434
|
type: "session.start",
|
|
11986
12435
|
payload: {
|
|
@@ -12004,10 +12453,7 @@ function createSessionHandlers(ctx) {
|
|
|
12004
12453
|
try {
|
|
12005
12454
|
const { DefaultSessionRewinder } = await import("@wrongstack/core");
|
|
12006
12455
|
const projectRoot = ctx.getProjectRoot();
|
|
12007
|
-
const rewinder = new DefaultSessionRewinder(
|
|
12008
|
-
path19.join(projectRoot, ".wrongstack", "sessions"),
|
|
12009
|
-
projectRoot
|
|
12010
|
-
);
|
|
12456
|
+
const rewinder = new DefaultSessionRewinder(ctx.sessionsDir, projectRoot);
|
|
12011
12457
|
const checkpoints = await rewinder.listCheckpoints(ctx.getSession().id);
|
|
12012
12458
|
send(ws, { type: "session.checkpoints", payload: sessionPayload2({ checkpoints }) });
|
|
12013
12459
|
} catch {
|
|
@@ -12020,10 +12466,7 @@ function createSessionHandlers(ctx) {
|
|
|
12020
12466
|
try {
|
|
12021
12467
|
const { DefaultSessionRewinder } = await import("@wrongstack/core");
|
|
12022
12468
|
const projectRoot = ctx.getProjectRoot();
|
|
12023
|
-
const rewinder = new DefaultSessionRewinder(
|
|
12024
|
-
path19.join(projectRoot, ".wrongstack", "sessions"),
|
|
12025
|
-
projectRoot
|
|
12026
|
-
);
|
|
12469
|
+
const rewinder = new DefaultSessionRewinder(ctx.sessionsDir, projectRoot);
|
|
12027
12470
|
await rewinder.rewindToCheckpoint(ctx.getSession().id, checkpointIndex);
|
|
12028
12471
|
await ctx.context.session.truncateToCheckpoint(checkpointIndex);
|
|
12029
12472
|
sendResult(ws, true, `Rewound to checkpoint ${checkpointIndex}`);
|
|
@@ -12239,8 +12682,10 @@ function buildRoutes(state, deps2, cb) {
|
|
|
12239
12682
|
getProjectRoot: state.getProjectRoot,
|
|
12240
12683
|
getSession: state.getSession,
|
|
12241
12684
|
getSessionStore: state.getSessionStore,
|
|
12685
|
+
sessionsDir: deps2.wpaths.projectSessions,
|
|
12242
12686
|
setSession: state.setSession,
|
|
12243
12687
|
setSessionStartedAt: state.setSessionStartedAt,
|
|
12688
|
+
onSessionSwapped: cb.onSessionSwapped,
|
|
12244
12689
|
sessionStartPayload: cb.sessionStartPayload
|
|
12245
12690
|
});
|
|
12246
12691
|
const projectRoutes = createProjectHandlers({
|
|
@@ -12335,6 +12780,17 @@ function buildRoutes(state, deps2, cb) {
|
|
|
12335
12780
|
deps2.pipelines.contextWindow.remove("AutoCompaction", { optional: true });
|
|
12336
12781
|
}
|
|
12337
12782
|
}
|
|
12783
|
+
if (typeof payload["breakerEnabled"] === "boolean" || typeof payload["breakerAutoKillResetMs"] === "number") {
|
|
12784
|
+
const { getProcessRegistry } = await import("@wrongstack/tools");
|
|
12785
|
+
getProcessRegistry().setBreakerConfig({
|
|
12786
|
+
...typeof payload["breakerEnabled"] === "boolean" ? { enabled: payload["breakerEnabled"] } : {},
|
|
12787
|
+
...typeof payload["breakerAutoKillResetMs"] === "number" ? { autoKillResetMs: payload["breakerAutoKillResetMs"] } : {}
|
|
12788
|
+
});
|
|
12789
|
+
}
|
|
12790
|
+
if (typeof payload["debugStream"] === "boolean") {
|
|
12791
|
+
const { setDebugStreamEnabled } = await import("@wrongstack/providers");
|
|
12792
|
+
setDebugStreamEnabled(payload["debugStream"]);
|
|
12793
|
+
}
|
|
12338
12794
|
if (typeof payload["logLevel"] === "string") {
|
|
12339
12795
|
const valid = ["debug", "info", "warn", "error"];
|
|
12340
12796
|
if (valid.includes(payload["logLevel"])) {
|
|
@@ -12412,6 +12868,13 @@ function buildRoutes(state, deps2, cb) {
|
|
|
12412
12868
|
{ projectRoot: state.getProjectRoot(), globalRoot: path20.dirname(deps2.globalConfigPath) },
|
|
12413
12869
|
parsed.value
|
|
12414
12870
|
);
|
|
12871
|
+
},
|
|
12872
|
+
compact: (ws, msg) => {
|
|
12873
|
+
return handleMailboxCompact(
|
|
12874
|
+
ws,
|
|
12875
|
+
{ projectRoot: state.getProjectRoot(), globalRoot: path20.dirname(deps2.globalConfigPath) },
|
|
12876
|
+
msg.payload ?? {}
|
|
12877
|
+
);
|
|
12415
12878
|
}
|
|
12416
12879
|
};
|
|
12417
12880
|
const mcpRoutes = {
|
|
@@ -12424,14 +12887,29 @@ function buildRoutes(state, deps2, cb) {
|
|
|
12424
12887
|
sleep: (ws, msg) => handleMcpSleep(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
12425
12888
|
wake: (ws, msg) => handleMcpWake(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
12426
12889
|
restart: (ws, msg) => handleMcpRestart(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
12427
|
-
discover: (ws, msg) => handleMcpDiscover(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry)
|
|
12890
|
+
discover: (ws, msg) => handleMcpDiscover(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
12891
|
+
resources: (ws, msg) => handleMcpResources(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
12892
|
+
prompts: (ws, msg) => handleMcpPrompts(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
12893
|
+
resourceRead: (ws, msg) => handleMcpResourceRead(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
12894
|
+
promptGet: (ws, msg) => handleMcpPromptGet(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry)
|
|
12895
|
+
};
|
|
12896
|
+
const sendBrainStatus = (ws) => {
|
|
12897
|
+
const snapshot = deps2.brainRuntime.getSnapshot();
|
|
12898
|
+
send(ws, {
|
|
12899
|
+
type: "brain.status",
|
|
12900
|
+
payload: {
|
|
12901
|
+
maxAutoRisk: deps2.brainSettings.maxAutoRisk,
|
|
12902
|
+
log: deps2.brainLog,
|
|
12903
|
+
mode: snapshot.mode,
|
|
12904
|
+
poolLabels: snapshot.poolLabels,
|
|
12905
|
+
councilLabels: snapshot.councilLabels,
|
|
12906
|
+
ledgerPath: snapshot.ledger.path
|
|
12907
|
+
}
|
|
12908
|
+
});
|
|
12428
12909
|
};
|
|
12429
12910
|
const brainRoutes = {
|
|
12430
12911
|
status: (ws) => {
|
|
12431
|
-
|
|
12432
|
-
type: "brain.status",
|
|
12433
|
-
payload: { maxAutoRisk: deps2.brainSettings.maxAutoRisk, log: deps2.brainLog }
|
|
12434
|
-
});
|
|
12912
|
+
sendBrainStatus(ws);
|
|
12435
12913
|
},
|
|
12436
12914
|
risk: (ws, msg) => {
|
|
12437
12915
|
const parsed = validateBrainRiskPayload(msg.payload);
|
|
@@ -12441,11 +12919,43 @@ function buildRoutes(state, deps2, cb) {
|
|
|
12441
12919
|
}
|
|
12442
12920
|
const { level } = parsed.value;
|
|
12443
12921
|
deps2.brainSettings.maxAutoRisk = level;
|
|
12922
|
+
sendBrainStatus(ws);
|
|
12923
|
+
},
|
|
12924
|
+
configGet: (ws) => {
|
|
12444
12925
|
send(ws, {
|
|
12445
|
-
type: "brain.
|
|
12446
|
-
payload: {
|
|
12926
|
+
type: "brain.config",
|
|
12927
|
+
payload: { config: deps2.brainRuntime.getSnapshot(), persisted: true }
|
|
12447
12928
|
});
|
|
12448
12929
|
},
|
|
12930
|
+
configSet: async (ws, msg) => {
|
|
12931
|
+
const parsed = validateBrainConfigSetPayload(msg.payload);
|
|
12932
|
+
if (!parsed.ok) {
|
|
12933
|
+
sendResult(ws, false, parsed.message);
|
|
12934
|
+
return;
|
|
12935
|
+
}
|
|
12936
|
+
try {
|
|
12937
|
+
const { persisted } = deps2.brainRuntime.apply(parsed.value.patch);
|
|
12938
|
+
const result = await persisted;
|
|
12939
|
+
send(ws, {
|
|
12940
|
+
type: "brain.config",
|
|
12941
|
+
payload: {
|
|
12942
|
+
config: deps2.brainRuntime.getSnapshot(),
|
|
12943
|
+
persisted: result.ok,
|
|
12944
|
+
...result.ok ? {} : { error: result.error ?? "Persist failed." }
|
|
12945
|
+
}
|
|
12946
|
+
});
|
|
12947
|
+
sendBrainStatus(ws);
|
|
12948
|
+
} catch (err) {
|
|
12949
|
+
send(ws, {
|
|
12950
|
+
type: "brain.config",
|
|
12951
|
+
payload: {
|
|
12952
|
+
config: deps2.brainRuntime.getSnapshot(),
|
|
12953
|
+
persisted: false,
|
|
12954
|
+
error: `Invalid Brain setting: ${errMessage(err)}`
|
|
12955
|
+
}
|
|
12956
|
+
});
|
|
12957
|
+
}
|
|
12958
|
+
},
|
|
12449
12959
|
ask: async (ws, msg) => {
|
|
12450
12960
|
const parsed = validateBrainAskPayload(msg.payload);
|
|
12451
12961
|
if (!parsed.ok) {
|
|
@@ -12553,7 +13063,8 @@ async function startWebUI(opts = {}) {
|
|
|
12553
13063
|
promptsCtx,
|
|
12554
13064
|
modelCapabilitiesRef,
|
|
12555
13065
|
provider,
|
|
12556
|
-
context
|
|
13066
|
+
context,
|
|
13067
|
+
sessionIdentity
|
|
12557
13068
|
} = preContext;
|
|
12558
13069
|
let sessionStore = preContext.sessionStore;
|
|
12559
13070
|
let session = preContext.session;
|
|
@@ -12586,7 +13097,12 @@ async function startWebUI(opts = {}) {
|
|
|
12586
13097
|
modelCapabilitiesRef,
|
|
12587
13098
|
sessionGetter: () => session,
|
|
12588
13099
|
sessionReader,
|
|
12589
|
-
annotationsStore
|
|
13100
|
+
annotationsStore,
|
|
13101
|
+
// Brain settings persist to the GLOBAL config only (config.brain is on
|
|
13102
|
+
// the in-project deny list), serialized behind the shared write lock.
|
|
13103
|
+
persistBrainConfig: (brainConfig) => updateGlobalConfig2((decrypted) => {
|
|
13104
|
+
decrypted["brain"] = brainConfig;
|
|
13105
|
+
}, "brain.config")
|
|
12590
13106
|
});
|
|
12591
13107
|
const {
|
|
12592
13108
|
compactor,
|
|
@@ -12596,6 +13112,7 @@ async function startWebUI(opts = {}) {
|
|
|
12596
13112
|
pipelines,
|
|
12597
13113
|
brain,
|
|
12598
13114
|
brainSettings,
|
|
13115
|
+
brainRuntime,
|
|
12599
13116
|
brainLog,
|
|
12600
13117
|
brainMonitor,
|
|
12601
13118
|
codebaseIndexing,
|
|
@@ -12766,10 +13283,12 @@ async function startWebUI(opts = {}) {
|
|
|
12766
13283
|
terminalHandler,
|
|
12767
13284
|
brain,
|
|
12768
13285
|
brainSettings,
|
|
13286
|
+
brainRuntime,
|
|
12769
13287
|
brainLog
|
|
12770
13288
|
};
|
|
12771
13289
|
const cb = {
|
|
12772
13290
|
sessionStartPayload,
|
|
13291
|
+
onSessionSwapped: (sessionId) => sessionIdentity.activate(sessionId),
|
|
12773
13292
|
updateAutoCompactionMaxContext,
|
|
12774
13293
|
updateGlobalConfig: updateGlobalConfig2,
|
|
12775
13294
|
persistPrefsToConfig: persistPrefsToConfig2,
|
|
@@ -12895,7 +13414,10 @@ async function startWebUI(opts = {}) {
|
|
|
12895
13414
|
onShutdown: async () => {
|
|
12896
13415
|
credentialWatcherClose?.();
|
|
12897
13416
|
brainMonitor.stop();
|
|
13417
|
+
await agentServices.brainLedger?.stop().catch(() => {
|
|
13418
|
+
});
|
|
12898
13419
|
await mcpRegistry.stopAll().catch(() => void 0);
|
|
13420
|
+
await sessionIdentity.stop();
|
|
12899
13421
|
eventArming.getDispose()?.();
|
|
12900
13422
|
if (eternalSubscription) {
|
|
12901
13423
|
eternalSubscription.dispose();
|