@wrongstack/webui-server 0.289.0 → 0.291.1

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.
Files changed (38) hide show
  1. package/dist/index.js +542 -335
  2. package/dist/index.js.map +3 -3
  3. package/dist/server/backend-services.d.ts +2 -2
  4. package/dist/server/backend-services.d.ts.map +1 -1
  5. package/dist/server/context-meta.d.ts.map +1 -1
  6. package/dist/server/entry.js +540 -335
  7. package/dist/server/entry.js.map +3 -3
  8. package/dist/server/handlers.js.map +2 -2
  9. package/dist/server/http-server.d.ts.map +1 -1
  10. package/dist/server/index.d.ts +3 -1
  11. package/dist/server/index.d.ts.map +1 -1
  12. package/dist/server/kanban-routes.d.ts +15 -0
  13. package/dist/server/kanban-routes.d.ts.map +1 -1
  14. package/dist/server/memory-handlers.d.ts +13 -0
  15. package/dist/server/memory-handlers.d.ts.map +1 -1
  16. package/dist/server/message-dispatcher.d.ts +3 -2
  17. package/dist/server/message-dispatcher.d.ts.map +1 -1
  18. package/dist/server/pending-confirms.d.ts +1 -0
  19. package/dist/server/pending-confirms.d.ts.map +1 -1
  20. package/dist/server/pre-context-services.d.ts +3 -2
  21. package/dist/server/pre-context-services.d.ts.map +1 -1
  22. package/dist/server/pref-helpers.d.ts +22 -8
  23. package/dist/server/pref-helpers.d.ts.map +1 -1
  24. package/dist/server/provider-config-io.d.ts +2 -1
  25. package/dist/server/provider-config-io.d.ts.map +1 -1
  26. package/dist/server/provider-handlers.d.ts +2 -0
  27. package/dist/server/provider-handlers.d.ts.map +1 -1
  28. package/dist/server/routes.d.ts +2 -2
  29. package/dist/server/routes.d.ts.map +1 -1
  30. package/dist/server/server-runtime.d.ts +5 -20
  31. package/dist/server/server-runtime.d.ts.map +1 -1
  32. package/dist/server/setup-events.d.ts.map +1 -1
  33. package/dist/server/start-webui.d.ts.map +1 -1
  34. package/dist/server/terminal-ws-handler.d.ts +2 -1
  35. package/dist/server/terminal-ws-handler.d.ts.map +1 -1
  36. package/dist/server/ws-payload-validation.d.ts +24 -23
  37. package/dist/server/ws-payload-validation.d.ts.map +1 -1
  38. package/package.json +12 -12
@@ -1813,15 +1813,15 @@ async function handleGitChanges(ws, projectRoot) {
1813
1813
  if (!m) continue;
1814
1814
  const added = m[1] === "-" ? 0 : Number(m[1]);
1815
1815
  const deleted = m[2] === "-" ? 0 : Number(m[2]);
1816
- let path23 = m[3] ?? "";
1817
- if (path23 === "") {
1816
+ let path24 = m[3] ?? "";
1817
+ if (path24 === "") {
1818
1818
  i += 1;
1819
- path23 = parts[i + 1] ?? parts[i] ?? "";
1819
+ path24 = parts[i + 1] ?? parts[i] ?? "";
1820
1820
  i += 1;
1821
1821
  }
1822
- if (!path23) continue;
1823
- const prev = counts.get(path23) ?? { added: 0, deleted: 0 };
1824
- counts.set(path23, { added: prev.added + added, deleted: prev.deleted + deleted });
1822
+ if (!path24) continue;
1823
+ const prev = counts.get(path24) ?? { added: 0, deleted: 0 };
1824
+ counts.set(path24, { added: prev.added + added, deleted: prev.deleted + deleted });
1825
1825
  }
1826
1826
  };
1827
1827
  parseNumstat(unstagedNumstat);
@@ -1833,7 +1833,7 @@ async function handleGitChanges(ws, projectRoot) {
1833
1833
  if (!rec || rec.length < 3) continue;
1834
1834
  const x = rec[0] ?? " ";
1835
1835
  const y = rec[1] ?? " ";
1836
- const path23 = rec.slice(3);
1836
+ const path24 = rec.slice(3);
1837
1837
  const isRename = x === "R" || x === "C" || y === "R" || y === "C";
1838
1838
  if (isRename) i += 1;
1839
1839
  let status;
@@ -1845,13 +1845,13 @@ async function handleGitChanges(ws, projectRoot) {
1845
1845
  else if (x === "D" || y === "D") status = "D";
1846
1846
  else status = "M";
1847
1847
  const staged = x !== " " && x !== "?";
1848
- let added = counts.get(path23)?.added ?? 0;
1849
- let deleted = counts.get(path23)?.deleted ?? 0;
1848
+ let added = counts.get(path24)?.added ?? 0;
1849
+ let deleted = counts.get(path24)?.deleted ?? 0;
1850
1850
  if (status === "?") {
1851
1851
  added = 0;
1852
1852
  deleted = 0;
1853
1853
  }
1854
- files.push({ path: path23, status, added, deleted, staged });
1854
+ files.push({ path: path24, status, added, deleted, staged });
1855
1855
  }
1856
1856
  send(ws, { type: "git.changes", payload: { files } });
1857
1857
  } catch (err) {
@@ -1862,10 +1862,10 @@ async function handleGitChanges(ws, projectRoot) {
1862
1862
  }
1863
1863
  }
1864
1864
  var MAX_DIFF_BYTES = 2 * 1024 * 1024;
1865
- async function handleGitDiff(ws, projectRoot, path23) {
1865
+ async function handleGitDiff(ws, projectRoot, path24) {
1866
1866
  const cwd = projectRoot || void 0;
1867
- const reply = (extra) => send(ws, { type: "git.diff", payload: { path: path23, ...extra } });
1868
- if (!path23 || path23.includes("\0") || path23.includes("..") || nodePath.isAbsolute(path23)) {
1867
+ const reply = (extra) => send(ws, { type: "git.diff", payload: { path: path24, ...extra } });
1868
+ if (!path24 || path24.includes("\0") || path24.includes("..") || nodePath.isAbsolute(path24)) {
1869
1869
  reply({ oldText: "", newText: "", error: "invalid path" });
1870
1870
  return;
1871
1871
  }
@@ -1873,10 +1873,10 @@ async function handleGitDiff(ws, projectRoot, path23) {
1873
1873
  const git = makeGit(cwd);
1874
1874
  const { readFile: readFile10 } = await import("node:fs/promises");
1875
1875
  const { join: join14 } = await import("node:path");
1876
- const oldText = await git(["show", `HEAD:${path23}`]);
1876
+ const oldText = await git(["show", `HEAD:${path24}`]);
1877
1877
  let newText = "";
1878
1878
  try {
1879
- const abs = cwd ? join14(cwd, path23) : path23;
1879
+ const abs = cwd ? join14(cwd, path24) : path24;
1880
1880
  const buf = await readFile10(abs);
1881
1881
  if (buf.includes(0)) {
1882
1882
  reply({ oldText: "", newText: "", binary: true });
@@ -1908,6 +1908,7 @@ async function handleGitDiff(ws, projectRoot, path23) {
1908
1908
  import * as fs6 from "node:fs/promises";
1909
1909
  import * as http from "node:http";
1910
1910
  import * as path7 from "node:path";
1911
+ import * as v8 from "node:v8";
1911
1912
 
1912
1913
  // src/server/http-server/api-handlers.ts
1913
1914
  async function handleApiSessions(res, globalRoot) {
@@ -3206,6 +3207,23 @@ function createHttpServer(opts) {
3206
3207
  }
3207
3208
  return;
3208
3209
  }
3210
+ if (url.pathname === "/debug/system" && req.method === "GET") {
3211
+ res.writeHead(200, {
3212
+ "Content-Type": "application/json",
3213
+ "Cache-Control": "no-store"
3214
+ });
3215
+ res.end(
3216
+ JSON.stringify({
3217
+ pid: process.pid,
3218
+ memoryUsage: process.memoryUsage(),
3219
+ heapLimit: v8.getHeapStatistics().heap_size_limit,
3220
+ uptime: process.uptime(),
3221
+ cpuUsage: process.cpuUsage(),
3222
+ timestamp: Date.now()
3223
+ })
3224
+ );
3225
+ return;
3226
+ }
3209
3227
  let filePath;
3210
3228
  if (url.pathname === "/" || url.pathname === "") {
3211
3229
  filePath = path7.join(distDir, "index.html");
@@ -3732,6 +3750,44 @@ async function handleSuperMemoryList(ws, memoryStore) {
3732
3750
  send(ws, { type: "memory.super.list", payload: { error: errMessage(err) } });
3733
3751
  }
3734
3752
  }
3753
+ async function handleSuperMemoryListPage(ws, msg, memoryStore) {
3754
+ if (!isSuperMemoryStore(memoryStore)) {
3755
+ send(ws, { type: "memory.super.listPage", payload: { error: requiresSuperMemory("memory.super.listPage") } });
3756
+ return;
3757
+ }
3758
+ try {
3759
+ const payload = msg.payload ?? {};
3760
+ const options = {
3761
+ statuses: Array.isArray(payload["statuses"]) ? payload["statuses"].filter((s) => typeof s === "string") : void 0,
3762
+ kind: typeof payload["kind"] === "string" ? payload["kind"] : void 0,
3763
+ query: typeof payload["query"] === "string" ? payload["query"] : void 0,
3764
+ limit: typeof payload["limit"] === "number" ? payload["limit"] : void 0,
3765
+ cursor: typeof payload["cursor"] === "string" ? payload["cursor"] : void 0
3766
+ };
3767
+ if (typeof memoryStore.listSuperPage === "function") {
3768
+ const page = await memoryStore.listSuperPage(options);
3769
+ send(ws, { type: "memory.super.listPage", payload: page });
3770
+ return;
3771
+ }
3772
+ const allowed = options.statuses && options.statuses.length > 0 ? new Set(options.statuses) : void 0;
3773
+ const everything = await memoryStore.listSuper();
3774
+ const statusCounts = {};
3775
+ for (const m of everything) statusCounts[m.status] = (statusCounts[m.status] ?? 0) + 1;
3776
+ const kind = options.kind && options.kind !== "all" ? options.kind : void 0;
3777
+ const q = options.query?.trim().toLowerCase();
3778
+ const filtered = everything.filter((m) => {
3779
+ if (allowed) return allowed.has(m.status);
3780
+ return m.status !== "deleted";
3781
+ }).filter((m) => !kind || m.kind === kind).filter((m) => !q || m.text.toLowerCase().includes(q));
3782
+ const limit = Math.max(1, Math.min(500, Math.floor(options.limit ?? 50)));
3783
+ send(ws, {
3784
+ type: "memory.super.listPage",
3785
+ payload: { memories: filtered.slice(0, limit), nextCursor: null, total: filtered.length, statusCounts }
3786
+ });
3787
+ } catch (err) {
3788
+ send(ws, { type: "memory.super.listPage", payload: { error: errMessage(err) } });
3789
+ }
3790
+ }
3735
3791
  async function handleSuperMemoryGet(ws, msg, memoryStore) {
3736
3792
  if (!isSuperMemoryStore(memoryStore)) {
3737
3793
  send(ws, { type: "memory.super.get", payload: { error: requiresSuperMemory("memory.super.get") } });
@@ -4281,7 +4337,7 @@ async function loadSavedProviders(configPath, vault) {
4281
4337
  if (!parsed.providers) return {};
4282
4338
  return decryptConfigSecrets(parsed.providers, vault);
4283
4339
  }
4284
- async function saveProviders(configPath, vault, providers) {
4340
+ async function saveProviders(configPath, vault, providers, profileConfigPath) {
4285
4341
  let raw;
4286
4342
  let fileExists = true;
4287
4343
  try {
@@ -4315,6 +4371,23 @@ async function saveProviders(configPath, vault, providers) {
4315
4371
  parsed.providers = providers;
4316
4372
  const encrypted = encryptConfigSecrets(parsed, vault);
4317
4373
  await atomicWrite4(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
4374
+ if (profileConfigPath && profileConfigPath !== configPath) {
4375
+ let profileRaw;
4376
+ try {
4377
+ profileRaw = await fs8.readFile(profileConfigPath, "utf8");
4378
+ } catch {
4379
+ profileRaw = "{}";
4380
+ }
4381
+ let profileParsed;
4382
+ try {
4383
+ profileParsed = JSON.parse(profileRaw);
4384
+ } catch {
4385
+ return;
4386
+ }
4387
+ profileParsed.providers = providers;
4388
+ const profileEncrypted = encryptConfigSecrets(profileParsed, vault);
4389
+ await atomicWrite4(profileConfigPath, JSON.stringify(profileEncrypted, null, 2), { mode: 384 });
4390
+ }
4318
4391
  }
4319
4392
 
4320
4393
  // src/server/provider-keys.ts
@@ -5164,6 +5237,7 @@ var BOOLEAN_PREF_KEYS = /* @__PURE__ */ new Set([
5164
5237
  "hqRawContent",
5165
5238
  "fallbackAuto",
5166
5239
  "favoriteModelsOnly",
5240
+ "modelAvailabilitySchedule",
5167
5241
  "breakerEnabled",
5168
5242
  "debugStream",
5169
5243
  // Chimera + auto-review master toggles
@@ -5228,34 +5302,34 @@ var ENUM_PREF_KEYS = {
5228
5302
  chimeraAutoFix: /* @__PURE__ */ new Set(["off", "ask", "auto"]),
5229
5303
  autoReviewCascadeOn: /* @__PURE__ */ new Set(["off", "critical", "high"])
5230
5304
  };
5231
- function validateModelRuntimeValue(modelRuntime, path23) {
5305
+ function validateModelRuntimeValue(modelRuntime, path24) {
5232
5306
  const reasoning = modelRuntime["reasoning"];
5233
5307
  if (reasoning !== void 0) {
5234
- if (!isRecord(reasoning)) return `${path23}.reasoning must be an object when provided`;
5308
+ if (!isRecord(reasoning)) return `${path24}.reasoning must be an object when provided`;
5235
5309
  const mode = reasoning["mode"];
5236
5310
  const effort = reasoning["effort"];
5237
5311
  const preserve = reasoning["preserve"];
5238
5312
  if (mode !== void 0 && (typeof mode !== "string" || !REASONING_MODE_VALUES.has(mode))) {
5239
- return `${path23}.reasoning.mode must be one of: ${Array.from(REASONING_MODE_VALUES).join(", ")}`;
5313
+ return `${path24}.reasoning.mode must be one of: ${Array.from(REASONING_MODE_VALUES).join(", ")}`;
5240
5314
  }
5241
5315
  if (effort !== void 0 && (typeof effort !== "string" || !REASONING_EFFORT_VALUES.has(effort))) {
5242
- return `${path23}.reasoning.effort must be one of: ${Array.from(REASONING_EFFORT_VALUES).join(", ")}`;
5316
+ return `${path24}.reasoning.effort must be one of: ${Array.from(REASONING_EFFORT_VALUES).join(", ")}`;
5243
5317
  }
5244
5318
  if (preserve !== void 0 && typeof preserve !== "boolean") {
5245
- return `${path23}.reasoning.preserve must be a boolean when provided`;
5319
+ return `${path24}.reasoning.preserve must be a boolean when provided`;
5246
5320
  }
5247
5321
  }
5248
5322
  const cache = modelRuntime["cache"];
5249
5323
  if (cache !== void 0) {
5250
- if (!isRecord(cache)) return `${path23}.cache must be an object when provided`;
5324
+ if (!isRecord(cache)) return `${path24}.cache must be an object when provided`;
5251
5325
  const ttl = cache["ttl"];
5252
5326
  if (ttl !== void 0 && (typeof ttl !== "string" || !CACHE_TTL_VALUES.has(ttl) || ttl === "default")) {
5253
- return `${path23}.cache.ttl must be one of: 5m, 1h`;
5327
+ return `${path24}.cache.ttl must be one of: 5m, 1h`;
5254
5328
  }
5255
5329
  }
5256
5330
  const parameters = modelRuntime["parameters"];
5257
5331
  if (parameters !== void 0 && !isRecord(parameters)) {
5258
- return `${path23}.parameters must be an object when provided`;
5332
+ return `${path24}.parameters must be an object when provided`;
5259
5333
  }
5260
5334
  return null;
5261
5335
  }
@@ -5308,7 +5382,10 @@ function validatePreferenceValue(key, value) {
5308
5382
  return `prefs.update payload.${key}.modelRuntime must be an object when provided`;
5309
5383
  }
5310
5384
  if (isRecord(modelRuntime)) {
5311
- const runtimeError = validateModelRuntimeValue(modelRuntime, `prefs.update payload.${key}.modelRuntime`);
5385
+ const runtimeError = validateModelRuntimeValue(
5386
+ modelRuntime,
5387
+ `prefs.update payload.${key}.modelRuntime`
5388
+ );
5312
5389
  if (runtimeError) return runtimeError;
5313
5390
  }
5314
5391
  if (model === void 0 && fallbackProfile === void 0 && modelRuntime === void 0) {
@@ -5545,8 +5622,8 @@ function validateShellOpenPayload(payload) {
5545
5622
  if (!isRecord(payload)) {
5546
5623
  return { ok: false, message: "shell.open payload must be an object with string path" };
5547
5624
  }
5548
- const path23 = payload["path"];
5549
- if (typeof path23 !== "string" || path23.trim().length === 0) {
5625
+ const path24 = payload["path"];
5626
+ if (typeof path24 !== "string" || path24.trim().length === 0) {
5550
5627
  return { ok: false, message: "shell.open payload.path must be a non-empty string" };
5551
5628
  }
5552
5629
  const target = payload["target"];
@@ -5559,7 +5636,7 @@ function validateShellOpenPayload(payload) {
5559
5636
  return {
5560
5637
  ok: true,
5561
5638
  value: {
5562
- path: path23,
5639
+ path: path24,
5563
5640
  ...target !== void 0 ? { target } : {}
5564
5641
  }
5565
5642
  };
@@ -5568,14 +5645,14 @@ function validateGitDiffPayload(payload) {
5568
5645
  if (!isRecord(payload)) {
5569
5646
  return { ok: false, message: "git.diff payload must be an object" };
5570
5647
  }
5571
- const path23 = payload["path"];
5572
- if (path23 === void 0 || path23 === null) {
5648
+ const path24 = payload["path"];
5649
+ if (path24 === void 0 || path24 === null) {
5573
5650
  return { ok: true, value: { path: "" } };
5574
5651
  }
5575
- if (typeof path23 !== "string") {
5652
+ if (typeof path24 !== "string") {
5576
5653
  return { ok: false, message: "git.diff payload.path must be a string when provided" };
5577
5654
  }
5578
- return { ok: true, value: { path: path23 } };
5655
+ return { ok: true, value: { path: path24 } };
5579
5656
  }
5580
5657
 
5581
5658
  // src/server/zip.ts
@@ -6544,7 +6621,7 @@ function registerShutdownHandlers(res) {
6544
6621
  import { watch as fsWatch } from "node:fs";
6545
6622
  import * as fs11 from "node:fs/promises";
6546
6623
  import * as path13 from "node:path";
6547
- import { getBoard, getKanbanDir } from "@wrongstack/kanban";
6624
+ import { getBoard, getKanbanDir, recordTaskFileActivity } from "@wrongstack/kanban";
6548
6625
 
6549
6626
  // src/server/codemap-telemetry.ts
6550
6627
  import * as path12 from "node:path";
@@ -6940,6 +7017,18 @@ function setupEvents(deps2) {
6940
7017
  on("file.activity", (e) => {
6941
7018
  broadcast2(clients, { type: "codemap.file_event", payload: e });
6942
7019
  });
7020
+ on("file.event", (e) => {
7021
+ if (e.scope !== "task" || !e.boardId || !e.taskId) return;
7022
+ void recordTaskFileActivity(context.projectRoot, e.boardId, e.taskId, e).then((recorded) => {
7023
+ if (recorded) {
7024
+ broadcast2(clients, {
7025
+ type: "kanban.task.activity.changed",
7026
+ payload: { boardId: e.boardId, taskId: e.taskId }
7027
+ });
7028
+ }
7029
+ }).catch(() => {
7030
+ });
7031
+ });
6943
7032
  on("tool.loop_detected", (e) => {
6944
7033
  broadcast2(clients, {
6945
7034
  type: "tool.loop_detected",
@@ -7064,12 +7153,14 @@ function setupEvents(deps2) {
7064
7153
  input: e.input,
7065
7154
  suggestedPattern: e.suggestedPattern,
7066
7155
  decisionSource: e.decisionSource,
7067
- riskTier: e.riskTier
7156
+ riskTier: e.riskTier,
7157
+ boundaryReason: e.boundaryReason
7068
7158
  });
7069
7159
  pendingConfirms.set(id, {
7070
7160
  resolve: e.resolve,
7071
7161
  decisionSource: e.decisionSource,
7072
7162
  riskTier: e.riskTier,
7163
+ boundaryReason: e.boundaryReason,
7073
7164
  payload
7074
7165
  });
7075
7166
  broadcast2(clients, { type: "tool.confirm_needed", payload });
@@ -7162,7 +7253,8 @@ function setupEvents(deps2) {
7162
7253
  oldState: e.oldState,
7163
7254
  newState: e.newState,
7164
7255
  reason: e.reason,
7165
- timestamp: e.timestamp
7256
+ timestamp: e.timestamp,
7257
+ stateExpiresAt: e.stateExpiresAt
7166
7258
  })
7167
7259
  });
7168
7260
  });
@@ -8269,6 +8361,7 @@ function seedContextMeta(config, context) {
8269
8361
  meta["fallbackProfiles"] = config.fallbackProfiles ?? {};
8270
8362
  meta["favoriteModels"] = config.favoriteModels ?? [];
8271
8363
  meta["favoriteModelsOnly"] = config.favoriteModelsOnly === true;
8364
+ meta["modelAvailabilitySchedule"] = config.modelAvailabilitySchedule ?? [];
8272
8365
  meta["modelMatrix"] = config.modelMatrix ?? {};
8273
8366
  meta["fallbackAuto"] = config.fallbackAuto !== false;
8274
8367
  if (typeof config.uiLocale === "string" && config.uiLocale) meta["uiLocale"] = config.uiLocale;
@@ -8351,9 +8444,7 @@ function seedContextMeta(config, context) {
8351
8444
  } catch {
8352
8445
  resolvedChain = [];
8353
8446
  }
8354
- meta["autoReviewFallbackModels"] = resolvedChain.map(
8355
- (e) => `${e.providerId}/${e.model}`
8356
- );
8447
+ meta["autoReviewFallbackModels"] = resolvedChain.map((e) => `${e.providerId}/${e.model}`);
8357
8448
  }
8358
8449
  }
8359
8450
 
@@ -8891,7 +8982,7 @@ function isSuperMemoryService(memoryStore) {
8891
8982
  }
8892
8983
 
8893
8984
  // src/server/start-webui.ts
8894
- import * as path22 from "node:path";
8985
+ import * as path23 from "node:path";
8895
8986
  import {
8896
8987
  createDefaultPipelines,
8897
8988
  createSessionEventBridge,
@@ -9810,7 +9901,7 @@ function setupWebUICodebaseIndexing(deps2) {
9810
9901
  }
9811
9902
  if (idx) {
9812
9903
  cancelPendingReindexes();
9813
- shutdownCodebaseIndexHost();
9904
+ void shutdownCodebaseIndexHost();
9814
9905
  }
9815
9906
  }
9816
9907
  };
@@ -9977,6 +10068,9 @@ var DEFAULT_COLS = 80;
9977
10068
  var DEFAULT_ROWS = 24;
9978
10069
  var requireFromHere = createRequire4(import.meta.url);
9979
10070
  var cachedNodePty;
10071
+ function resolveTerminalShell(platform = process.platform, env = process.env) {
10072
+ return platform === "win32" ? env.COMSPEC || "cmd.exe" : env.SHELL || "/bin/sh";
10073
+ }
9980
10074
  var TerminalWebSocketHandler = class {
9981
10075
  constructor(getCwd, logger, loadNodePty = defaultLoadNodePty, killProcessTree = defaultKillProcessTree) {
9982
10076
  this.getCwd = getCwd;
@@ -10032,7 +10126,7 @@ var TerminalWebSocketHandler = class {
10032
10126
  });
10033
10127
  return;
10034
10128
  }
10035
- const shell = process.platform === "win32" ? process.env.COMSPEC || "cmd.exe" : process.env.SHELL || "/bin/bash";
10129
+ const shell = resolveTerminalShell();
10036
10130
  const nodePty = this.loadNodePty();
10037
10131
  if (!nodePty) {
10038
10132
  const msg = "Integrated terminal unavailable: optional dependency node-pty is not installed. Install node-pty to enable WebUI terminal sessions.";
@@ -10352,7 +10446,11 @@ async function createAgentServices(input) {
10352
10446
  toolExecutor
10353
10447
  });
10354
10448
  if (config.features.memory && config.features.memoryConsolidation !== false) {
10355
- agent.extensions.register(new SessionMemoryConsolidator({ memoryStore }));
10449
+ const consSuperMemory = typeof memoryStore["rememberSuper"] === "function" ? memoryStore : void 0;
10450
+ agent.extensions.register(new SessionMemoryConsolidator({
10451
+ memoryStore,
10452
+ ...consSuperMemory ? { superMemory: consSuperMemory } : {}
10453
+ }));
10356
10454
  }
10357
10455
  console.log("[WebUI] Agent initialized");
10358
10456
  const brainCfg = resolveBrainConfigDefaults(config.brain, {
@@ -10558,6 +10656,7 @@ function isSuperMemoryRetriever(memoryStore) {
10558
10656
  // src/server/pending-confirms.ts
10559
10657
  function resolveYoloEligiblePendingConfirms(pendingConfirms) {
10560
10658
  for (const [id, confirm] of pendingConfirms) {
10659
+ if (confirm.boundaryReason) continue;
10561
10660
  pendingConfirms.delete(id);
10562
10661
  confirm.resolve("yes");
10563
10662
  }
@@ -11037,7 +11136,7 @@ import {
11037
11136
  createBoard,
11038
11137
  duplicateBoard,
11039
11138
  exportBoardToTaskGraph,
11040
- generateBoardFromDescription,
11139
+ createBoardFromText,
11041
11140
  getBoard as getBoard2,
11042
11141
  getKanbanOrchestrationSnapshot,
11043
11142
  getKanbanQueueHealth,
@@ -11068,6 +11167,32 @@ import {
11068
11167
  updateTask
11069
11168
  } from "@wrongstack/kanban";
11070
11169
  import { applySessionKanbanTaskToSource } from "@wrongstack/tools/session-kanban";
11170
+ function paginateKanbanBoards(boards, input) {
11171
+ const pageSize = Math.min(100, Math.max(1, Math.floor(input.pageSize)));
11172
+ const activeSessionIds = new Set(input.activeSessionIds ?? []);
11173
+ const isActive = (board) => board.presence?.some((entry) => entry.active) === true || board.tags?.some(
11174
+ (tag) => tag.startsWith("session:") && activeSessionIds.has(tag.slice(8))
11175
+ ) === true;
11176
+ const sorted = [...boards].sort((left, right) => {
11177
+ const activityOrder = Number(isActive(right)) - Number(isActive(left));
11178
+ return activityOrder || right.updatedAt.localeCompare(left.updatedAt);
11179
+ });
11180
+ const activeTotal = sorted.filter(isActive).length;
11181
+ const total = sorted.length;
11182
+ const totalPages = Math.max(1, Math.ceil(total / pageSize));
11183
+ const requestedPage = Number.isFinite(input.page) ? Math.floor(input.page) : 1;
11184
+ const page = Math.min(totalPages, Math.max(1, requestedPage));
11185
+ const start = (page - 1) * pageSize;
11186
+ return {
11187
+ items: sorted.slice(start, start + pageSize),
11188
+ total,
11189
+ page,
11190
+ pageSize,
11191
+ totalPages,
11192
+ activeTotal,
11193
+ orphanedTotal: total - activeTotal
11194
+ };
11195
+ }
11071
11196
  async function syncSessionSource(ctx, task, remove = false) {
11072
11197
  if (!ctx.context) return;
11073
11198
  const update = await applySessionKanbanTaskToSource(ctx.context, task, { remove });
@@ -11117,9 +11242,26 @@ async function handleKanbanRoute(ws, msg, ctx) {
11117
11242
  const type = msg.type;
11118
11243
  try {
11119
11244
  switch (type) {
11120
- case "kanban.list":
11121
- ok(ws, type, await listBoards(ctx.projectRoot));
11245
+ case "kanban.list": {
11246
+ const boards = await listBoards(ctx.projectRoot);
11247
+ const requestedPage = Number(payload?.page);
11248
+ const requestedPageSize = Number(payload?.pageSize);
11249
+ if (!Number.isFinite(requestedPage) || !Number.isFinite(requestedPageSize)) {
11250
+ ok(ws, type, boards);
11251
+ return true;
11252
+ }
11253
+ const activeSessionIds = Array.isArray(payload?.activeSessionIds) ? payload.activeSessionIds.filter((id) => typeof id === "string") : [];
11254
+ ok(
11255
+ ws,
11256
+ type,
11257
+ paginateKanbanBoards(boards, {
11258
+ page: requestedPage,
11259
+ pageSize: requestedPageSize,
11260
+ activeSessionIds
11261
+ })
11262
+ );
11122
11263
  return true;
11264
+ }
11123
11265
  case "kanban.get": {
11124
11266
  const boardId = payload?.boardId;
11125
11267
  if (!boardId) {
@@ -11185,7 +11327,8 @@ async function handleKanbanRoute(ws, msg, ctx) {
11185
11327
  ...payload?.description ? { description: payload.description } : {},
11186
11328
  ...payload?.tags ? { tags: payload.tags } : {},
11187
11329
  ...payload?.columns ? { columns: payload.columns } : {},
11188
- ...has(payload, "lifecycle") ? { lifecycle: payload?.lifecycle } : {}
11330
+ ...has(payload, "lifecycle") ? { lifecycle: payload?.lifecycle } : {},
11331
+ ...has(payload, "boundary") ? { boundary: payload?.boundary } : {}
11189
11332
  })
11190
11333
  );
11191
11334
  return true;
@@ -11206,6 +11349,9 @@ async function handleKanbanRoute(ws, msg, ctx) {
11206
11349
  } : {},
11207
11350
  ...has(payload, "supervisor") ? {
11208
11351
  supervisor: payload?.supervisor ?? null
11352
+ } : {},
11353
+ ...has(payload, "boundary") ? {
11354
+ boundary: payload?.boundary ?? null
11209
11355
  } : {}
11210
11356
  });
11211
11357
  board ? ok(ws, type, board) : fail(ws, type, `Board not found: ${boardId}`);
@@ -11252,7 +11398,7 @@ async function handleKanbanRoute(ws, msg, ctx) {
11252
11398
  }
11253
11399
  const board = await createBoard(
11254
11400
  ctx.projectRoot,
11255
- generateBoardFromDescription({
11401
+ createBoardFromText({
11256
11402
  description,
11257
11403
  ...payload?.title ? { title: payload.title } : {},
11258
11404
  ...payload?.context ? { context: payload.context } : {}
@@ -11364,7 +11510,8 @@ async function handleKanbanRoute(ws, msg, ctx) {
11364
11510
  ...payload?.dueDate ? { dueDate: payload.dueDate } : {},
11365
11511
  ...payload?.priority ? { priority: payload.priority } : {},
11366
11512
  ...payload?.assignedAgent ? { assignedAgent: payload.assignedAgent } : {},
11367
- ...payload?.labels ? { labels: payload.labels } : {}
11513
+ ...payload?.labels ? { labels: payload.labels } : {},
11514
+ ...has(payload, "boundary") ? { boundary: payload?.boundary } : {}
11368
11515
  },
11369
11516
  activityContext(ctx, "webui", payload?.activityNote)
11370
11517
  );
@@ -11445,6 +11592,9 @@ async function handleKanbanRoute(ws, msg, ctx) {
11445
11592
  } : {},
11446
11593
  ...has(payload, "costCeilingUsd") ? {
11447
11594
  costCeilingUsd: payload?.costCeilingUsd === null || payload?.costCeilingUsd === "" ? null : Number(payload?.costCeilingUsd)
11595
+ } : {},
11596
+ ...has(payload, "boundary") ? {
11597
+ boundary: payload?.boundary ?? null
11448
11598
  } : {}
11449
11599
  },
11450
11600
  activityContext(ctx, "webui", payload?.activityNote)
@@ -12337,7 +12487,7 @@ var chronicleCache = /* @__PURE__ */ new Map();
12337
12487
  async function chronicleEngine(projectRoot) {
12338
12488
  const now = Date.now();
12339
12489
  const cached = chronicleCache.get(projectRoot);
12340
- if (cached && now - cached.loadedAt < 1e3) return cached.engine;
12490
+ if (cached && now - cached.loadedAt < 6e4) return cached.engine;
12341
12491
  const paths = resolveWstackPaths2({ projectRoot, userHome: os2.homedir() });
12342
12492
  const engine = await ChronicleQueryEngine.fromDirectory(
12343
12493
  path19.join(paths.projectDir, "chronicle")
@@ -12600,6 +12750,8 @@ function createMessageDispatcher(opts) {
12600
12750
  // ── SuperMemory operations ──
12601
12751
  case "memory.super.list":
12602
12752
  return handleSuperMemoryList(ws, deps2.memoryStore);
12753
+ case "memory.super.listPage":
12754
+ return handleSuperMemoryListPage(ws, msg, deps2.memoryStore);
12603
12755
  case "memory.super.get":
12604
12756
  return handleSuperMemoryGet(ws, msg, deps2.memoryStore);
12605
12757
  case "memory.super.update":
@@ -12854,7 +13006,7 @@ function createMessageDispatcher(opts) {
12854
13006
  case "chronicle.query": {
12855
13007
  const payload = msg.payload ?? {};
12856
13008
  const engine = await chronicleEngine(state.getProjectRoot());
12857
- send(ws, { type: "chronicle.query_result", payload: engine.query(payload.query ?? {}) });
13009
+ send(ws, { type: "chronicle.query_result", payload: await engine.query(payload.query ?? {}) });
12858
13010
  break;
12859
13011
  }
12860
13012
  case "chronicle.facet": {
@@ -12884,7 +13036,7 @@ function createMessageDispatcher(opts) {
12884
13036
  type: "chronicle.facet_result",
12885
13037
  payload: {
12886
13038
  field: payload.field,
12887
- values: engine.facet(payload.field, payload.query ?? {}, payload.limit),
13039
+ values: await engine.facet(payload.field, payload.query ?? {}, payload.limit),
12888
13040
  diagnostics: engine.diagnostics
12889
13041
  }
12890
13042
  });
@@ -12895,7 +13047,7 @@ function createMessageDispatcher(opts) {
12895
13047
  const engine = await chronicleEngine(state.getProjectRoot());
12896
13048
  send(ws, {
12897
13049
  type: "chronicle.graph_result",
12898
- payload: engine.graph(payload.seed ?? {}, payload.hops, payload.maxNodes)
13050
+ payload: await engine.graph(payload.seed ?? {}, payload.hops, payload.maxNodes)
12899
13051
  });
12900
13052
  break;
12901
13053
  }
@@ -12950,9 +13102,10 @@ function createMessageDispatcher(opts) {
12950
13102
  }
12951
13103
 
12952
13104
  // src/server/pref-helpers.ts
12953
- import { atomicWrite as atomicWrite6, FORBIDDEN_PROTO_KEYS as FORBIDDEN_PROTO_KEYS2 } from "@wrongstack/core/utils";
12954
- import { decryptConfigSecrets as decryptConfigSecrets2, encryptConfigSecrets as encryptConfigSecrets2 } from "@wrongstack/core/security";
12955
13105
  import * as fs14 from "node:fs/promises";
13106
+ import * as path20 from "node:path";
13107
+ import { decryptConfigSecrets as decryptConfigSecrets2, encryptConfigSecrets as encryptConfigSecrets2 } from "@wrongstack/core/security";
13108
+ import { atomicWrite as atomicWrite6, backupConfigFile, FORBIDDEN_PROTO_KEYS as FORBIDDEN_PROTO_KEYS2 } from "@wrongstack/core/utils";
12956
13109
  var PREF_KEYS = [
12957
13110
  "autonomy",
12958
13111
  "autonomyDelayMs",
@@ -12997,6 +13150,7 @@ var PREF_KEYS = [
12997
13150
  "fallbackProfiles",
12998
13151
  "favoriteModels",
12999
13152
  "favoriteModelsOnly",
13153
+ "modelAvailabilitySchedule",
13000
13154
  "modelMatrix",
13001
13155
  "fallbackAuto",
13002
13156
  // Refiner + TUI visual prefs (parity with the CLI's embedded server —
@@ -13040,26 +13194,34 @@ function prefSnapshot(contextMeta) {
13040
13194
  }
13041
13195
  return snapshot;
13042
13196
  }
13197
+ async function writeGlobalConfigFile(filePath, vault, mutate, logger, errorLabel) {
13198
+ const globalRoot = path20.dirname(filePath);
13199
+ await backupConfigFile(filePath, { globalRoot });
13200
+ let raw;
13201
+ try {
13202
+ raw = await fs14.readFile(filePath, "utf8");
13203
+ } catch {
13204
+ raw = "{}";
13205
+ }
13206
+ let parsed;
13207
+ try {
13208
+ parsed = JSON.parse(raw);
13209
+ } catch {
13210
+ logger.warn(`${errorLabel}: refusing to overwrite corrupt config at ${filePath}`);
13211
+ return;
13212
+ }
13213
+ const decrypted = decryptConfigSecrets2(parsed, vault);
13214
+ mutate(decrypted);
13215
+ const encrypted = encryptConfigSecrets2(decrypted, vault);
13216
+ await atomicWrite6(filePath, JSON.stringify(encrypted, null, 2), { mode: 384 });
13217
+ }
13043
13218
  async function updateGlobalConfig(deps2, holder, mutate, errorLabel) {
13044
- const { globalConfigPath, vault, logger } = deps2;
13219
+ const { globalConfigPath, profileConfigPath, vault, logger } = deps2;
13045
13220
  const write = async () => {
13046
- let raw;
13047
- try {
13048
- raw = await fs14.readFile(globalConfigPath, "utf8");
13049
- } catch {
13050
- raw = "{}";
13221
+ await writeGlobalConfigFile(globalConfigPath, vault, mutate, logger, errorLabel);
13222
+ if (profileConfigPath && profileConfigPath !== globalConfigPath) {
13223
+ await writeGlobalConfigFile(profileConfigPath, vault, mutate, logger, errorLabel);
13051
13224
  }
13052
- let parsed;
13053
- try {
13054
- parsed = JSON.parse(raw);
13055
- } catch {
13056
- logger.warn(`${errorLabel}: refusing to overwrite corrupt config at ${globalConfigPath}`);
13057
- return;
13058
- }
13059
- const decrypted = decryptConfigSecrets2(parsed, vault);
13060
- mutate(decrypted);
13061
- const encrypted = encryptConfigSecrets2(decrypted, vault);
13062
- await atomicWrite6(globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
13063
13225
  };
13064
13226
  const next = holder.lock.then(write);
13065
13227
  holder.lock = next.then(
@@ -13069,256 +13231,264 @@ async function updateGlobalConfig(deps2, holder, mutate, errorLabel) {
13069
13231
  try {
13070
13232
  await next;
13071
13233
  } catch (err) {
13072
- logger.warn(`${errorLabel}: failed to persist to config: ${err instanceof Error ? err.message : String(err)}`);
13234
+ logger.warn(
13235
+ `${errorLabel}: failed to persist to config: ${err instanceof Error ? err.message : String(err)}`
13236
+ );
13073
13237
  }
13074
13238
  }
13075
13239
  async function persistPrefsToConfig(deps2, holder, payload) {
13076
- return updateGlobalConfig(deps2, holder, (decrypted) => {
13077
- const autonomyCfg = decrypted.autonomy ?? {};
13078
- let autonomyTouched = false;
13079
- const setAutonomy = (key, val) => {
13080
- autonomyCfg[key] = val;
13081
- autonomyTouched = true;
13082
- };
13083
- if (typeof payload["autonomy"] === "string" && ["off", "suggest", "auto"].includes(payload["autonomy"])) {
13084
- setAutonomy("defaultMode", payload["autonomy"]);
13085
- }
13086
- if (typeof payload["autonomyDelayMs"] === "number")
13087
- setAutonomy("autoProceedDelayMs", payload["autonomyDelayMs"]);
13088
- if (typeof payload["autoProceedMaxIterations"] === "number")
13089
- setAutonomy("autoProceedMaxIterations", payload["autoProceedMaxIterations"]);
13090
- if (typeof payload["yolo"] === "boolean") {
13091
- setAutonomy("yolo", payload["yolo"]);
13092
- decrypted.yolo = payload["yolo"];
13093
- }
13094
- if (typeof payload["chime"] === "boolean") setAutonomy("chime", payload["chime"]);
13095
- if (typeof payload["confirmExit"] === "boolean")
13096
- setAutonomy("confirmExit", payload["confirmExit"]);
13097
- if (typeof payload["streamFleet"] === "boolean")
13098
- setAutonomy("streamFleet", payload["streamFleet"]);
13099
- if (typeof payload["enhanceEnabled"] === "boolean")
13100
- setAutonomy("enhance", payload["enhanceEnabled"]);
13101
- if (typeof payload["enhanceDelayMs"] === "number")
13102
- setAutonomy("enhanceDelayMs", payload["enhanceDelayMs"]);
13103
- if (typeof payload["enhanceLanguage"] === "string")
13104
- setAutonomy("enhanceLanguage", payload["enhanceLanguage"]);
13105
- if (typeof payload["refinerProvider"] === "string")
13106
- setAutonomy("refinerProvider", payload["refinerProvider"]);
13107
- if (typeof payload["refinerModel"] === "string")
13108
- setAutonomy("refinerModel", payload["refinerModel"]);
13109
- if (typeof payload["refinerFallbackProfile"] === "string")
13110
- setAutonomy("refinerFallbackProfile", payload["refinerFallbackProfile"]);
13111
- if (typeof payload["thinkingWord"] === "string")
13112
- setAutonomy("thinkingWord", payload["thinkingWord"]);
13113
- if (typeof payload["statuslineMode"] === "string")
13114
- setAutonomy("statuslineMode", payload["statuslineMode"]);
13115
- if (typeof payload["animationStyle"] === "string")
13116
- setAutonomy("animationStyle", payload["animationStyle"]);
13117
- if (typeof payload["showModelReasoning"] === "boolean")
13118
- setAutonomy("showModelReasoning", payload["showModelReasoning"]);
13119
- if (autonomyTouched) decrypted.autonomy = autonomyCfg;
13120
- if (typeof payload["nextPrediction"] === "boolean")
13121
- decrypted.nextPrediction = payload["nextPrediction"];
13122
- if (typeof payload["uiLocale"] === "string") decrypted.uiLocale = payload["uiLocale"];
13123
- if (Array.isArray(payload["fallbackModels"]))
13124
- decrypted.fallbackModels = payload["fallbackModels"];
13125
- if (payload["fallbackProfiles"] && typeof payload["fallbackProfiles"] === "object" && !Array.isArray(payload["fallbackProfiles"])) {
13126
- decrypted.fallbackProfiles = payload["fallbackProfiles"];
13127
- }
13128
- if (Array.isArray(payload["favoriteModels"]))
13129
- decrypted.favoriteModels = payload["favoriteModels"];
13130
- if (typeof payload["favoriteModelsOnly"] === "boolean")
13131
- decrypted.favoriteModelsOnly = payload["favoriteModelsOnly"];
13132
- if (payload["modelMatrix"] && typeof payload["modelMatrix"] === "object" && !Array.isArray(payload["modelMatrix"])) {
13133
- decrypted.modelMatrix = payload["modelMatrix"];
13134
- }
13135
- if (typeof payload["fallbackAuto"] === "boolean")
13136
- decrypted.fallbackAuto = payload["fallbackAuto"];
13137
- const FEATURE_MAP = {
13138
- featureMcp: "mcp",
13139
- featurePlugins: "plugins",
13140
- featureMemory: "memory",
13141
- featureSkills: "skills",
13142
- featureModelsRegistry: "modelsRegistry"
13143
- };
13144
- for (const [prefKey, cfgKey] of Object.entries(FEATURE_MAP)) {
13145
- if (typeof payload[prefKey] === "boolean") {
13146
- const feats = decrypted.features ?? {};
13147
- feats[cfgKey] = payload[prefKey];
13148
- decrypted.features = feats;
13149
- }
13150
- }
13151
- if (typeof payload["contextAutoCompact"] === "boolean" || typeof payload["contextStrategy"] === "string" || typeof payload["contextMode"] === "string") {
13152
- const ctxCfg = decrypted.context ?? {};
13153
- if (typeof payload["contextAutoCompact"] === "boolean")
13154
- ctxCfg.autoCompact = payload["contextAutoCompact"];
13155
- if (typeof payload["contextStrategy"] === "string")
13156
- ctxCfg.strategy = payload["contextStrategy"];
13157
- if (typeof payload["contextMode"] === "string") ctxCfg.mode = payload["contextMode"];
13158
- decrypted.context = ctxCfg;
13159
- }
13160
- if (typeof payload["tokenSavingTier"] === "string") {
13161
- const featsCfg = decrypted.features ?? {};
13162
- featsCfg.tokenSavingMode = payload["tokenSavingTier"];
13163
- decrypted.features = featsCfg;
13164
- }
13165
- if (typeof payload["maxConcurrent"] === "number") {
13166
- decrypted.maxConcurrent = payload["maxConcurrent"];
13167
- }
13168
- if (typeof payload["titleAnimation"] === "boolean") {
13169
- const autoCfg = decrypted.autonomy ?? {};
13170
- autoCfg.terminalTitleAnimation = payload["titleAnimation"];
13171
- decrypted.autonomy = autoCfg;
13172
- }
13173
- if (typeof payload["logLevel"] === "string") {
13174
- const logCfg = decrypted.log ?? {};
13175
- logCfg.level = payload["logLevel"];
13176
- decrypted.log = logCfg;
13177
- }
13178
- if (typeof payload["auditLevel"] === "string") {
13179
- const sessionCfg = decrypted.session ?? {};
13180
- sessionCfg.auditLevel = payload["auditLevel"];
13181
- decrypted.session = sessionCfg;
13182
- }
13183
- if (typeof payload["indexOnStart"] === "boolean") {
13184
- const indexingCfg = decrypted.indexing ?? {};
13185
- indexingCfg.onSessionStart = payload["indexOnStart"];
13186
- decrypted.indexing = indexingCfg;
13187
- }
13188
- if (typeof payload["maxIterations"] === "number") {
13189
- const toolsCfg = decrypted.tools ?? {};
13190
- toolsCfg.maxIterations = payload["maxIterations"];
13191
- decrypted.tools = toolsCfg;
13192
- }
13193
- const hqTouched = typeof payload["hqEnabled"] === "boolean" || typeof payload["hqUrl"] === "string" || typeof payload["hqToken"] === "string" || typeof payload["hqRawContent"] === "boolean";
13194
- if (hqTouched) {
13195
- const hqCfg = decrypted.hq ?? {};
13196
- if (typeof payload["hqEnabled"] === "boolean") hqCfg.enabled = payload["hqEnabled"];
13197
- if (typeof payload["hqUrl"] === "string") hqCfg.url = payload["hqUrl"];
13198
- if (typeof payload["hqToken"] === "string") hqCfg.token = payload["hqToken"];
13199
- if (typeof payload["hqRawContent"] === "boolean")
13200
- hqCfg.rawContent = payload["hqRawContent"];
13201
- decrypted.hq = hqCfg;
13202
- }
13203
- const tgTouched = typeof payload["tgSessionEnd"] === "boolean" || typeof payload["tgDelegate"] === "boolean" || typeof payload["tgLongToolMs"] === "number";
13204
- if (tgTouched) {
13205
- const ext = decrypted.extensions ?? {};
13206
- const tg = ext["telegram"] ?? {};
13207
- if (typeof payload["tgSessionEnd"] === "boolean") {
13208
- tg["notifyOnSessionEnd"] = payload["tgSessionEnd"];
13209
- }
13210
- if (typeof payload["tgDelegate"] === "boolean") {
13211
- tg["notifyOnDelegate"] = payload["tgDelegate"];
13212
- }
13213
- if (typeof payload["tgLongToolMs"] === "number") {
13214
- tg["longToolThresholdMs"] = payload["tgLongToolMs"];
13215
- }
13216
- ext["telegram"] = tg;
13217
- decrypted.extensions = ext;
13218
- }
13219
- const modelRuntimeTouched = typeof payload["reasoningMode"] === "string" || typeof payload["reasoningEffort"] === "string" || typeof payload["reasoningPreserve"] === "boolean" || typeof payload["cacheTtl"] === "string";
13220
- if (modelRuntimeTouched) {
13221
- const mr = decrypted.modelRuntime ?? {};
13222
- const reasoning = mr.reasoning ?? {};
13223
- if (typeof payload["reasoningMode"] === "string") reasoning.mode = payload["reasoningMode"];
13224
- if (typeof payload["reasoningEffort"] === "string")
13225
- reasoning.effort = payload["reasoningEffort"];
13226
- if (typeof payload["reasoningPreserve"] === "boolean")
13227
- reasoning.preserve = payload["reasoningPreserve"];
13228
- mr.reasoning = reasoning;
13229
- if (typeof payload["cacheTtl"] === "string" && payload["cacheTtl"] !== "default") {
13230
- mr.cache = { ttl: payload["cacheTtl"] };
13231
- } else if (payload["cacheTtl"] === "default") {
13232
- delete mr.cache;
13233
- }
13234
- decrypted.modelRuntime = mr;
13235
- }
13236
- if (typeof payload["breakerEnabled"] === "boolean" || typeof payload["breakerAutoKillResetMs"] === "number") {
13237
- const cb = decrypted.circuitBreaker ?? {};
13238
- if (typeof payload["breakerEnabled"] === "boolean") cb.enabled = payload["breakerEnabled"];
13239
- if (typeof payload["breakerAutoKillResetMs"] === "number")
13240
- cb.autoKillResetMs = payload["breakerAutoKillResetMs"];
13241
- decrypted.circuitBreaker = cb;
13242
- }
13243
- if (payload["fsAccess"] === "unrestricted" || payload["fsAccess"] === "project") {
13244
- const restrict = payload["fsAccess"] === "project";
13245
- const toolsCfg = decrypted.tools ?? {};
13246
- toolsCfg.restrictToProjectRoot = restrict;
13247
- decrypted.tools = toolsCfg;
13248
- const featsCfg = decrypted.features ?? {};
13249
- featsCfg.allowOutsideProjectRoot = !restrict;
13250
- decrypted.features = featsCfg;
13251
- }
13252
- if (typeof payload["debugStream"] === "boolean")
13253
- decrypted.debugStream = payload["debugStream"];
13254
- if (typeof payload["pluginsEnabled"] === "object" && payload["pluginsEnabled"] !== null) {
13255
- const ext = decrypted.extensions ?? {};
13256
- for (const [pluginName, enabled] of Object.entries(
13257
- payload["pluginsEnabled"]
13258
- )) {
13259
- if (FORBIDDEN_PROTO_KEYS2.has(pluginName)) continue;
13260
- const pExt = ext[pluginName] ?? {};
13261
- pExt["enabled"] = enabled;
13262
- ext[pluginName] = pExt;
13263
- }
13264
- decrypted.extensions = ext;
13265
- }
13266
- const chimeraTouched = typeof payload["chimeraEnabled"] === "boolean" || typeof payload["chimeraProvider"] === "string" || typeof payload["chimeraModel"] === "string" || typeof payload["chimeraMaxFiles"] === "number" || typeof payload["chimeraAutoFix"] === "string";
13267
- if (chimeraTouched) {
13268
- const ext = decrypted.extensions ?? {};
13269
- const chimera = ext["wstack-chimera"] ?? {};
13270
- if (typeof payload["chimeraEnabled"] === "boolean")
13271
- chimera["enabled"] = payload["chimeraEnabled"];
13272
- if (typeof payload["chimeraProvider"] === "string")
13273
- chimera["provider"] = payload["chimeraProvider"];
13274
- if (typeof payload["chimeraModel"] === "string")
13275
- chimera["model"] = payload["chimeraModel"];
13276
- if (typeof payload["chimeraMaxFiles"] === "number" && payload["chimeraMaxFiles"] >= 1) {
13277
- chimera["maxFiles"] = payload["chimeraMaxFiles"];
13278
- }
13279
- if (typeof payload["chimeraAutoFix"] === "string") {
13280
- if (payload["chimeraAutoFix"] === "off" || payload["chimeraAutoFix"] === "ask" || payload["chimeraAutoFix"] === "auto") {
13281
- chimera["autoFix"] = payload["chimeraAutoFix"];
13282
- }
13283
- }
13284
- ext["wstack-chimera"] = chimera;
13285
- decrypted.extensions = ext;
13286
- }
13287
- const autoReviewTouched = typeof payload["autoReviewEnabled"] === "boolean" || typeof payload["autoReviewProvider"] === "string" || typeof payload["autoReviewModel"] === "string" || typeof payload["autoReviewFallbackProfile"] === "string" || Array.isArray(payload["autoReviewFallbackModels"]) || typeof payload["autoReviewDebounceMs"] === "number" || typeof payload["autoReviewMaxFilesPerBatch"] === "number" || typeof payload["autoReviewMaxConcurrentReviews"] === "number" || typeof payload["autoReviewCascadeOn"] === "string";
13288
- if (autoReviewTouched) {
13289
- const ext = decrypted.extensions ?? {};
13290
- const ar = ext["wstack-auto-review"] ?? {};
13291
- if (typeof payload["autoReviewEnabled"] === "boolean")
13292
- ar["enabled"] = payload["autoReviewEnabled"];
13293
- if (typeof payload["autoReviewProvider"] === "string")
13294
- ar["provider"] = payload["autoReviewProvider"];
13295
- if (typeof payload["autoReviewModel"] === "string")
13296
- ar["model"] = payload["autoReviewModel"];
13297
- if (typeof payload["autoReviewFallbackProfile"] === "string") {
13298
- if (payload["autoReviewFallbackProfile"] === "") {
13299
- delete ar["fallbackProfile"];
13300
- } else {
13301
- ar["fallbackProfile"] = payload["autoReviewFallbackProfile"];
13302
- }
13240
+ return updateGlobalConfig(
13241
+ deps2,
13242
+ holder,
13243
+ (decrypted) => {
13244
+ const autonomyCfg = decrypted.autonomy ?? {};
13245
+ let autonomyTouched = false;
13246
+ const setAutonomy = (key, val) => {
13247
+ autonomyCfg[key] = val;
13248
+ autonomyTouched = true;
13249
+ };
13250
+ if (typeof payload["autonomy"] === "string" && ["off", "suggest", "auto"].includes(payload["autonomy"])) {
13251
+ setAutonomy("defaultMode", payload["autonomy"]);
13303
13252
  }
13304
- if (typeof payload["autoReviewDebounceMs"] === "number" && payload["autoReviewDebounceMs"] >= 0) {
13305
- ar["debounceMs"] = payload["autoReviewDebounceMs"];
13253
+ if (typeof payload["autonomyDelayMs"] === "number")
13254
+ setAutonomy("autoProceedDelayMs", payload["autonomyDelayMs"]);
13255
+ if (typeof payload["autoProceedMaxIterations"] === "number")
13256
+ setAutonomy("autoProceedMaxIterations", payload["autoProceedMaxIterations"]);
13257
+ if (typeof payload["yolo"] === "boolean") {
13258
+ setAutonomy("yolo", payload["yolo"]);
13259
+ decrypted.yolo = payload["yolo"];
13260
+ }
13261
+ if (typeof payload["chime"] === "boolean") setAutonomy("chime", payload["chime"]);
13262
+ if (typeof payload["confirmExit"] === "boolean")
13263
+ setAutonomy("confirmExit", payload["confirmExit"]);
13264
+ if (typeof payload["streamFleet"] === "boolean")
13265
+ setAutonomy("streamFleet", payload["streamFleet"]);
13266
+ if (typeof payload["enhanceEnabled"] === "boolean")
13267
+ setAutonomy("enhance", payload["enhanceEnabled"]);
13268
+ if (typeof payload["enhanceDelayMs"] === "number")
13269
+ setAutonomy("enhanceDelayMs", payload["enhanceDelayMs"]);
13270
+ if (typeof payload["enhanceLanguage"] === "string")
13271
+ setAutonomy("enhanceLanguage", payload["enhanceLanguage"]);
13272
+ if (typeof payload["refinerProvider"] === "string")
13273
+ setAutonomy("refinerProvider", payload["refinerProvider"]);
13274
+ if (typeof payload["refinerModel"] === "string")
13275
+ setAutonomy("refinerModel", payload["refinerModel"]);
13276
+ if (typeof payload["refinerFallbackProfile"] === "string")
13277
+ setAutonomy("refinerFallbackProfile", payload["refinerFallbackProfile"]);
13278
+ if (typeof payload["thinkingWord"] === "string")
13279
+ setAutonomy("thinkingWord", payload["thinkingWord"]);
13280
+ if (typeof payload["statuslineMode"] === "string")
13281
+ setAutonomy("statuslineMode", payload["statuslineMode"]);
13282
+ if (typeof payload["animationStyle"] === "string")
13283
+ setAutonomy("animationStyle", payload["animationStyle"]);
13284
+ if (typeof payload["showModelReasoning"] === "boolean")
13285
+ setAutonomy("showModelReasoning", payload["showModelReasoning"]);
13286
+ if (autonomyTouched) decrypted.autonomy = autonomyCfg;
13287
+ if (typeof payload["nextPrediction"] === "boolean")
13288
+ decrypted.nextPrediction = payload["nextPrediction"];
13289
+ if (typeof payload["uiLocale"] === "string") decrypted.uiLocale = payload["uiLocale"];
13290
+ if (Array.isArray(payload["fallbackModels"]))
13291
+ decrypted.fallbackModels = payload["fallbackModels"];
13292
+ if (payload["fallbackProfiles"] && typeof payload["fallbackProfiles"] === "object" && !Array.isArray(payload["fallbackProfiles"])) {
13293
+ decrypted.fallbackProfiles = payload["fallbackProfiles"];
13306
13294
  }
13307
- if (typeof payload["autoReviewMaxFilesPerBatch"] === "number" && payload["autoReviewMaxFilesPerBatch"] >= 1) {
13308
- ar["maxFilesPerBatch"] = payload["autoReviewMaxFilesPerBatch"];
13295
+ if (Array.isArray(payload["favoriteModels"]))
13296
+ decrypted.favoriteModels = payload["favoriteModels"];
13297
+ if (typeof payload["favoriteModelsOnly"] === "boolean")
13298
+ decrypted.favoriteModelsOnly = payload["favoriteModelsOnly"];
13299
+ if (Array.isArray(payload["modelAvailabilitySchedule"]))
13300
+ decrypted.modelAvailabilitySchedule = payload["modelAvailabilitySchedule"];
13301
+ if (payload["modelMatrix"] && typeof payload["modelMatrix"] === "object" && !Array.isArray(payload["modelMatrix"])) {
13302
+ decrypted.modelMatrix = payload["modelMatrix"];
13303
+ }
13304
+ if (typeof payload["fallbackAuto"] === "boolean")
13305
+ decrypted.fallbackAuto = payload["fallbackAuto"];
13306
+ const FEATURE_MAP = {
13307
+ featureMcp: "mcp",
13308
+ featurePlugins: "plugins",
13309
+ featureMemory: "memory",
13310
+ featureSkills: "skills",
13311
+ featureModelsRegistry: "modelsRegistry"
13312
+ };
13313
+ for (const [prefKey, cfgKey] of Object.entries(FEATURE_MAP)) {
13314
+ if (typeof payload[prefKey] === "boolean") {
13315
+ const feats = decrypted.features ?? {};
13316
+ feats[cfgKey] = payload[prefKey];
13317
+ decrypted.features = feats;
13318
+ }
13319
+ }
13320
+ if (typeof payload["contextAutoCompact"] === "boolean" || typeof payload["contextStrategy"] === "string" || typeof payload["contextMode"] === "string") {
13321
+ const ctxCfg = decrypted.context ?? {};
13322
+ if (typeof payload["contextAutoCompact"] === "boolean")
13323
+ ctxCfg.autoCompact = payload["contextAutoCompact"];
13324
+ if (typeof payload["contextStrategy"] === "string")
13325
+ ctxCfg.strategy = payload["contextStrategy"];
13326
+ if (typeof payload["contextMode"] === "string") ctxCfg.mode = payload["contextMode"];
13327
+ decrypted.context = ctxCfg;
13328
+ }
13329
+ if (typeof payload["tokenSavingTier"] === "string") {
13330
+ const featsCfg = decrypted.features ?? {};
13331
+ featsCfg.tokenSavingMode = payload["tokenSavingTier"];
13332
+ decrypted.features = featsCfg;
13333
+ }
13334
+ if (typeof payload["maxConcurrent"] === "number") {
13335
+ decrypted.maxConcurrent = payload["maxConcurrent"];
13336
+ }
13337
+ if (typeof payload["titleAnimation"] === "boolean") {
13338
+ const autoCfg = decrypted.autonomy ?? {};
13339
+ autoCfg.terminalTitleAnimation = payload["titleAnimation"];
13340
+ decrypted.autonomy = autoCfg;
13309
13341
  }
13310
- if (typeof payload["autoReviewMaxConcurrentReviews"] === "number" && payload["autoReviewMaxConcurrentReviews"] >= 1) {
13311
- ar["maxConcurrentReviews"] = payload["autoReviewMaxConcurrentReviews"];
13342
+ if (typeof payload["logLevel"] === "string") {
13343
+ const logCfg = decrypted.log ?? {};
13344
+ logCfg.level = payload["logLevel"];
13345
+ decrypted.log = logCfg;
13346
+ }
13347
+ if (typeof payload["auditLevel"] === "string") {
13348
+ const sessionCfg = decrypted.session ?? {};
13349
+ sessionCfg.auditLevel = payload["auditLevel"];
13350
+ decrypted.session = sessionCfg;
13351
+ }
13352
+ if (typeof payload["indexOnStart"] === "boolean") {
13353
+ const indexingCfg = decrypted.indexing ?? {};
13354
+ indexingCfg.onSessionStart = payload["indexOnStart"];
13355
+ decrypted.indexing = indexingCfg;
13356
+ }
13357
+ if (typeof payload["maxIterations"] === "number") {
13358
+ const toolsCfg = decrypted.tools ?? {};
13359
+ toolsCfg.maxIterations = payload["maxIterations"];
13360
+ decrypted.tools = toolsCfg;
13361
+ }
13362
+ const hqTouched = typeof payload["hqEnabled"] === "boolean" || typeof payload["hqUrl"] === "string" || typeof payload["hqToken"] === "string" || typeof payload["hqRawContent"] === "boolean";
13363
+ if (hqTouched) {
13364
+ const hqCfg = decrypted.hq ?? {};
13365
+ if (typeof payload["hqEnabled"] === "boolean") hqCfg.enabled = payload["hqEnabled"];
13366
+ if (typeof payload["hqUrl"] === "string") hqCfg.url = payload["hqUrl"];
13367
+ if (typeof payload["hqToken"] === "string") hqCfg.token = payload["hqToken"];
13368
+ if (typeof payload["hqRawContent"] === "boolean")
13369
+ hqCfg.rawContent = payload["hqRawContent"];
13370
+ decrypted.hq = hqCfg;
13371
+ }
13372
+ const tgTouched = typeof payload["tgSessionEnd"] === "boolean" || typeof payload["tgDelegate"] === "boolean" || typeof payload["tgLongToolMs"] === "number";
13373
+ if (tgTouched) {
13374
+ const ext = decrypted.extensions ?? {};
13375
+ const tg = ext["telegram"] ?? {};
13376
+ if (typeof payload["tgSessionEnd"] === "boolean") {
13377
+ tg["notifyOnSessionEnd"] = payload["tgSessionEnd"];
13378
+ }
13379
+ if (typeof payload["tgDelegate"] === "boolean") {
13380
+ tg["notifyOnDelegate"] = payload["tgDelegate"];
13381
+ }
13382
+ if (typeof payload["tgLongToolMs"] === "number") {
13383
+ tg["longToolThresholdMs"] = payload["tgLongToolMs"];
13384
+ }
13385
+ ext["telegram"] = tg;
13386
+ decrypted.extensions = ext;
13387
+ }
13388
+ const modelRuntimeTouched = typeof payload["reasoningMode"] === "string" || typeof payload["reasoningEffort"] === "string" || typeof payload["reasoningPreserve"] === "boolean" || typeof payload["cacheTtl"] === "string";
13389
+ if (modelRuntimeTouched) {
13390
+ const mr = decrypted.modelRuntime ?? {};
13391
+ const reasoning = mr.reasoning ?? {};
13392
+ if (typeof payload["reasoningMode"] === "string") reasoning.mode = payload["reasoningMode"];
13393
+ if (typeof payload["reasoningEffort"] === "string")
13394
+ reasoning.effort = payload["reasoningEffort"];
13395
+ if (typeof payload["reasoningPreserve"] === "boolean")
13396
+ reasoning.preserve = payload["reasoningPreserve"];
13397
+ mr.reasoning = reasoning;
13398
+ if (typeof payload["cacheTtl"] === "string" && payload["cacheTtl"] !== "default") {
13399
+ mr.cache = { ttl: payload["cacheTtl"] };
13400
+ } else if (payload["cacheTtl"] === "default") {
13401
+ delete mr.cache;
13402
+ }
13403
+ decrypted.modelRuntime = mr;
13312
13404
  }
13313
- if (typeof payload["autoReviewCascadeOn"] === "string") {
13314
- if (payload["autoReviewCascadeOn"] === "off" || payload["autoReviewCascadeOn"] === "critical" || payload["autoReviewCascadeOn"] === "high") {
13315
- ar["cascadeOn"] = payload["autoReviewCascadeOn"];
13405
+ if (typeof payload["breakerEnabled"] === "boolean" || typeof payload["breakerAutoKillResetMs"] === "number") {
13406
+ const cb = decrypted.circuitBreaker ?? {};
13407
+ if (typeof payload["breakerEnabled"] === "boolean") cb.enabled = payload["breakerEnabled"];
13408
+ if (typeof payload["breakerAutoKillResetMs"] === "number")
13409
+ cb.autoKillResetMs = payload["breakerAutoKillResetMs"];
13410
+ decrypted.circuitBreaker = cb;
13411
+ }
13412
+ if (payload["fsAccess"] === "unrestricted" || payload["fsAccess"] === "project") {
13413
+ const restrict = payload["fsAccess"] === "project";
13414
+ const toolsCfg = decrypted.tools ?? {};
13415
+ toolsCfg.restrictToProjectRoot = restrict;
13416
+ decrypted.tools = toolsCfg;
13417
+ const featsCfg = decrypted.features ?? {};
13418
+ featsCfg.allowOutsideProjectRoot = !restrict;
13419
+ decrypted.features = featsCfg;
13420
+ }
13421
+ if (typeof payload["debugStream"] === "boolean")
13422
+ decrypted.debugStream = payload["debugStream"];
13423
+ if (typeof payload["pluginsEnabled"] === "object" && payload["pluginsEnabled"] !== null) {
13424
+ const ext = decrypted.extensions ?? {};
13425
+ for (const [pluginName, enabled] of Object.entries(
13426
+ payload["pluginsEnabled"]
13427
+ )) {
13428
+ if (FORBIDDEN_PROTO_KEYS2.has(pluginName)) continue;
13429
+ const pExt = ext[pluginName] ?? {};
13430
+ pExt["enabled"] = enabled;
13431
+ ext[pluginName] = pExt;
13432
+ }
13433
+ decrypted.extensions = ext;
13434
+ }
13435
+ const chimeraTouched = typeof payload["chimeraEnabled"] === "boolean" || typeof payload["chimeraProvider"] === "string" || typeof payload["chimeraModel"] === "string" || typeof payload["chimeraMaxFiles"] === "number" || typeof payload["chimeraAutoFix"] === "string";
13436
+ if (chimeraTouched) {
13437
+ const ext = decrypted.extensions ?? {};
13438
+ const chimera = ext["wstack-chimera"] ?? {};
13439
+ if (typeof payload["chimeraEnabled"] === "boolean")
13440
+ chimera["enabled"] = payload["chimeraEnabled"];
13441
+ if (typeof payload["chimeraProvider"] === "string")
13442
+ chimera["provider"] = payload["chimeraProvider"];
13443
+ if (typeof payload["chimeraModel"] === "string") chimera["model"] = payload["chimeraModel"];
13444
+ if (typeof payload["chimeraMaxFiles"] === "number" && payload["chimeraMaxFiles"] >= 1) {
13445
+ chimera["maxFiles"] = payload["chimeraMaxFiles"];
13446
+ }
13447
+ if (typeof payload["chimeraAutoFix"] === "string") {
13448
+ if (payload["chimeraAutoFix"] === "off" || payload["chimeraAutoFix"] === "ask" || payload["chimeraAutoFix"] === "auto") {
13449
+ chimera["autoFix"] = payload["chimeraAutoFix"];
13450
+ }
13451
+ }
13452
+ ext["wstack-chimera"] = chimera;
13453
+ decrypted.extensions = ext;
13454
+ }
13455
+ const autoReviewTouched = typeof payload["autoReviewEnabled"] === "boolean" || typeof payload["autoReviewProvider"] === "string" || typeof payload["autoReviewModel"] === "string" || typeof payload["autoReviewFallbackProfile"] === "string" || Array.isArray(payload["autoReviewFallbackModels"]) || typeof payload["autoReviewDebounceMs"] === "number" || typeof payload["autoReviewMaxFilesPerBatch"] === "number" || typeof payload["autoReviewMaxConcurrentReviews"] === "number" || typeof payload["autoReviewCascadeOn"] === "string";
13456
+ if (autoReviewTouched) {
13457
+ const ext = decrypted.extensions ?? {};
13458
+ const ar = ext["wstack-auto-review"] ?? {};
13459
+ if (typeof payload["autoReviewEnabled"] === "boolean")
13460
+ ar["enabled"] = payload["autoReviewEnabled"];
13461
+ if (typeof payload["autoReviewProvider"] === "string")
13462
+ ar["provider"] = payload["autoReviewProvider"];
13463
+ if (typeof payload["autoReviewModel"] === "string")
13464
+ ar["model"] = payload["autoReviewModel"];
13465
+ if (typeof payload["autoReviewFallbackProfile"] === "string") {
13466
+ if (payload["autoReviewFallbackProfile"] === "") {
13467
+ delete ar["fallbackProfile"];
13468
+ } else {
13469
+ ar["fallbackProfile"] = payload["autoReviewFallbackProfile"];
13470
+ }
13471
+ }
13472
+ if (typeof payload["autoReviewDebounceMs"] === "number" && payload["autoReviewDebounceMs"] >= 0) {
13473
+ ar["debounceMs"] = payload["autoReviewDebounceMs"];
13474
+ }
13475
+ if (typeof payload["autoReviewMaxFilesPerBatch"] === "number" && payload["autoReviewMaxFilesPerBatch"] >= 1) {
13476
+ ar["maxFilesPerBatch"] = payload["autoReviewMaxFilesPerBatch"];
13477
+ }
13478
+ if (typeof payload["autoReviewMaxConcurrentReviews"] === "number" && payload["autoReviewMaxConcurrentReviews"] >= 1) {
13479
+ ar["maxConcurrentReviews"] = payload["autoReviewMaxConcurrentReviews"];
13480
+ }
13481
+ if (typeof payload["autoReviewCascadeOn"] === "string") {
13482
+ if (payload["autoReviewCascadeOn"] === "off" || payload["autoReviewCascadeOn"] === "critical" || payload["autoReviewCascadeOn"] === "high") {
13483
+ ar["cascadeOn"] = payload["autoReviewCascadeOn"];
13484
+ }
13316
13485
  }
13486
+ ext["wstack-auto-review"] = ar;
13487
+ decrypted.extensions = ext;
13317
13488
  }
13318
- ext["wstack-auto-review"] = ar;
13319
- decrypted.extensions = ext;
13320
- }
13321
- }, "prefs");
13489
+ },
13490
+ "prefs"
13491
+ );
13322
13492
  }
13323
13493
 
13324
13494
  // src/server/provider-handlers.ts
@@ -13368,13 +13538,13 @@ async function probeModelDescriptors(cfg) {
13368
13538
  }
13369
13539
  }
13370
13540
  function createProviderHandlers(deps2) {
13371
- const { globalConfigPath, vault, broadcast: broadcast2, clients } = deps2;
13541
+ const { globalConfigPath, profileConfigPath, vault, broadcast: broadcast2, clients } = deps2;
13372
13542
  let configWriteLock = deps2.getConfigWriteLock();
13373
13543
  async function loadConfigProviders() {
13374
13544
  return loadSavedProviders(globalConfigPath, vault);
13375
13545
  }
13376
13546
  async function saveConfigProviders(providers) {
13377
- const next = configWriteLock.then(() => saveProviders(globalConfigPath, vault, providers)).catch((err) => {
13547
+ const next = configWriteLock.then(() => saveProviders(globalConfigPath, vault, providers, profileConfigPath)).catch((err) => {
13378
13548
  const msg = toErrorMessage9(err);
13379
13549
  console.error(JSON.stringify({
13380
13550
  level: "error",
@@ -13649,13 +13819,14 @@ function createProviderHandlers(deps2) {
13649
13819
  }
13650
13820
 
13651
13821
  // src/server/routes.ts
13652
- import path21 from "node:path";
13822
+ import path22 from "node:path";
13653
13823
  import {
13654
13824
  buildRefinerContextSections,
13655
13825
  enhanceUserPrompt,
13656
13826
  gatedEnhancerReasoning,
13657
13827
  nextEnhanceTimeout,
13658
13828
  recentTextTurns,
13829
+ resolveConfiguredRefinerRef,
13659
13830
  resolveEnhanceFallbackRef,
13660
13831
  resolveProviderModelList
13661
13832
  } from "@wrongstack/core";
@@ -13859,7 +14030,7 @@ function createModeHandlers(ctx) {
13859
14030
  }
13860
14031
 
13861
14032
  // src/server/project-handlers.ts
13862
- import * as path20 from "node:path";
14033
+ import * as path21 from "node:path";
13863
14034
  function createProjectHandlers(ctx) {
13864
14035
  return {
13865
14036
  listProjects: async (ws) => {
@@ -13885,7 +14056,7 @@ function createProjectHandlers(ctx) {
13885
14056
  selectProject: async (ws, msg) => {
13886
14057
  const payload = msg.payload;
13887
14058
  const root = typeof payload?.root === "string" ? payload.root : "";
13888
- const name2 = typeof payload?.name === "string" ? payload.name : root ? path20.basename(root) : "";
14059
+ const name2 = typeof payload?.name === "string" ? payload.name : root ? path21.basename(root) : "";
13889
14060
  send(ws, {
13890
14061
  type: "projects.selected",
13891
14062
  payload: {
@@ -14339,7 +14510,9 @@ async function enrichProviderModelDescriptors(modelsRegistry, providerId, cfg, m
14339
14510
  if (resolved.capabilities.vision) capabilities.add("vision");
14340
14511
  return {
14341
14512
  ...model,
14342
- contextWindow: model.contextWindow || resolved.capabilities.maxContext || void 0,
14513
+ contextWindow: model.contextWindow ?? resolved.capabilities.maxContext ?? void 0,
14514
+ inputCost: model.inputCost ?? resolved.cost?.input,
14515
+ outputCost: model.outputCost ?? resolved.cost?.output,
14343
14516
  capabilities: [...capabilities]
14344
14517
  };
14345
14518
  })
@@ -14348,6 +14521,7 @@ async function enrichProviderModelDescriptors(modelsRegistry, providerId, cfg, m
14348
14521
  function buildRoutes(state, deps2, cb) {
14349
14522
  const providerHandlers = createProviderHandlers({
14350
14523
  globalConfigPath: deps2.globalConfigPath,
14524
+ profileConfigPath: deps2.profileConfigPath,
14351
14525
  vault: deps2.vault,
14352
14526
  getConfigWriteLock: state.getConfigWriteLock,
14353
14527
  setConfigWriteLock: state.setConfigWriteLock,
@@ -14518,6 +14692,26 @@ function buildRoutes(state, deps2, cb) {
14518
14692
  });
14519
14693
  return;
14520
14694
  }
14695
+ } else {
14696
+ const configuredRef = resolveConfiguredRefinerRef({
14697
+ ...cfg,
14698
+ provider: providerId,
14699
+ model
14700
+ });
14701
+ if (configuredRef) {
14702
+ const slash = configuredRef.indexOf("/");
14703
+ const configuredProvider = slash > 0 ? configuredRef.slice(0, slash) : providerId;
14704
+ const configuredModel = slash > 0 ? configuredRef.slice(slash + 1) : configuredRef;
14705
+ try {
14706
+ const providerCfg = cfg.providers?.[configuredProvider] ?? {
14707
+ type: configuredProvider
14708
+ };
14709
+ provider = deps2.providerRegistry.has(configuredProvider) ? deps2.providerRegistry.create({ ...providerCfg, type: configuredProvider }) : makeProviderFromConfig2(configuredProvider, providerCfg);
14710
+ providerId = configuredProvider;
14711
+ model = configuredModel;
14712
+ } catch {
14713
+ }
14714
+ }
14521
14715
  }
14522
14716
  const baseTimeout = 9e4;
14523
14717
  const timeoutMs = typeof payload.timeoutMs === "number" && payload.timeoutMs > 0 ? payload.timeoutMs : baseTimeout;
@@ -14704,19 +14898,27 @@ function buildRoutes(state, deps2, cb) {
14704
14898
  cfg.favoriteModels = payload["favoriteModels"];
14705
14899
  if (typeof payload["favoriteModelsOnly"] === "boolean")
14706
14900
  cfg.favoriteModelsOnly = payload["favoriteModelsOnly"];
14901
+ if (Array.isArray(payload["modelAvailabilitySchedule"]))
14902
+ cfg.modelAvailabilitySchedule = payload["modelAvailabilitySchedule"];
14707
14903
  if (payload["modelMatrix"] && typeof payload["modelMatrix"] === "object" && !Array.isArray(payload["modelMatrix"])) {
14708
14904
  cfg.modelMatrix = payload["modelMatrix"];
14709
14905
  }
14710
14906
  if (typeof payload["fallbackAuto"] === "boolean") cfg.fallbackAuto = payload["fallbackAuto"];
14711
14907
  const routingPatch = {};
14712
- if (Array.isArray(payload["fallbackModels"])) routingPatch.fallbackModels = payload["fallbackModels"];
14908
+ if (Array.isArray(payload["fallbackModels"]))
14909
+ routingPatch.fallbackModels = payload["fallbackModels"];
14713
14910
  if (payload["fallbackProfiles"] && typeof payload["fallbackProfiles"] === "object" && !Array.isArray(payload["fallbackProfiles"]))
14714
14911
  routingPatch.fallbackProfiles = payload["fallbackProfiles"];
14715
- if (Array.isArray(payload["favoriteModels"])) routingPatch.favoriteModels = payload["favoriteModels"];
14716
- if (typeof payload["favoriteModelsOnly"] === "boolean") routingPatch.favoriteModelsOnly = payload["favoriteModelsOnly"];
14912
+ if (Array.isArray(payload["favoriteModels"]))
14913
+ routingPatch.favoriteModels = payload["favoriteModels"];
14914
+ if (typeof payload["favoriteModelsOnly"] === "boolean")
14915
+ routingPatch.favoriteModelsOnly = payload["favoriteModelsOnly"];
14916
+ if (Array.isArray(payload["modelAvailabilitySchedule"]))
14917
+ routingPatch.modelAvailabilitySchedule = payload["modelAvailabilitySchedule"];
14717
14918
  if (payload["modelMatrix"] && typeof payload["modelMatrix"] === "object" && !Array.isArray(payload["modelMatrix"]))
14718
14919
  routingPatch.modelMatrix = payload["modelMatrix"];
14719
- if (typeof payload["fallbackAuto"] === "boolean") routingPatch.fallbackAuto = payload["fallbackAuto"];
14920
+ if (typeof payload["fallbackAuto"] === "boolean")
14921
+ routingPatch.fallbackAuto = payload["fallbackAuto"];
14720
14922
  if (Object.keys(routingPatch).length > 0)
14721
14923
  deps2.configStore.update(routingPatch);
14722
14924
  if (typeof payload["contextAutoCompact"] === "boolean") {
@@ -14787,7 +14989,7 @@ function buildRoutes(state, deps2, cb) {
14787
14989
  }
14788
14990
  return handleMailboxMessages(
14789
14991
  ws,
14790
- { projectRoot: state.getProjectRoot(), globalRoot: path21.dirname(deps2.globalConfigPath) },
14992
+ { projectRoot: state.getProjectRoot(), globalRoot: path22.dirname(deps2.globalConfigPath) },
14791
14993
  parsed.value
14792
14994
  );
14793
14995
  },
@@ -14799,13 +15001,13 @@ function buildRoutes(state, deps2, cb) {
14799
15001
  }
14800
15002
  return handleMailboxAgents(
14801
15003
  ws,
14802
- { projectRoot: state.getProjectRoot(), globalRoot: path21.dirname(deps2.globalConfigPath) },
15004
+ { projectRoot: state.getProjectRoot(), globalRoot: path22.dirname(deps2.globalConfigPath) },
14803
15005
  parsed.value
14804
15006
  );
14805
15007
  },
14806
15008
  clear: (ws) => handleMailboxClear(ws, {
14807
15009
  projectRoot: state.getProjectRoot(),
14808
- globalRoot: path21.dirname(deps2.globalConfigPath)
15010
+ globalRoot: path22.dirname(deps2.globalConfigPath)
14809
15011
  }),
14810
15012
  purge: (ws, msg) => {
14811
15013
  const parsed = validateMailboxPurgePayload(msg.payload);
@@ -14815,14 +15017,14 @@ function buildRoutes(state, deps2, cb) {
14815
15017
  }
14816
15018
  return handleMailboxPurge(
14817
15019
  ws,
14818
- { projectRoot: state.getProjectRoot(), globalRoot: path21.dirname(deps2.globalConfigPath) },
15020
+ { projectRoot: state.getProjectRoot(), globalRoot: path22.dirname(deps2.globalConfigPath) },
14819
15021
  parsed.value
14820
15022
  );
14821
15023
  },
14822
15024
  compact: (ws, msg) => {
14823
15025
  return handleMailboxCompact(
14824
15026
  ws,
14825
- { projectRoot: state.getProjectRoot(), globalRoot: path21.dirname(deps2.globalConfigPath) },
15027
+ { projectRoot: state.getProjectRoot(), globalRoot: path22.dirname(deps2.globalConfigPath) },
14826
15028
  msg.payload ?? {}
14827
15029
  );
14828
15030
  }
@@ -14973,7 +15175,9 @@ async function startWebUI(opts = {}) {
14973
15175
  let projectRoot = boot.projectRoot;
14974
15176
  let workingDir = projectRoot;
14975
15177
  const configWriteLock = { lock: Promise.resolve() };
14976
- const prefHelperDeps = { globalConfigPath, vault, logger };
15178
+ const activeProfile = config.activeProfile ?? "default";
15179
+ const profileConfigPath = wpaths.profileConfig(activeProfile);
15180
+ const prefHelperDeps = { globalConfigPath, profileConfigPath, vault, logger };
14977
15181
  const updateGlobalConfig2 = async (mutate, errorLabel) => updateGlobalConfig(prefHelperDeps, configWriteLock, mutate, errorLabel);
14978
15182
  console.log("[WebUI] Config loaded:", config.provider ?? "(none)", "/", config.model ?? "(none)");
14979
15183
  if (!config.provider && config.providers && typeof config.providers === "object" && config.providers !== null && !Array.isArray(config.providers) && Object.keys(config.providers).length > 0) {
@@ -15135,21 +15339,21 @@ async function startWebUI(opts = {}) {
15135
15339
  wpaths
15136
15340
  }, watcherMetricsRef);
15137
15341
  async function touchProjectEntry(root, workDir) {
15138
- const resolved = path22.resolve(root);
15342
+ const resolved = path23.resolve(root);
15139
15343
  const manifest = await loadManifest(globalConfigPath);
15140
15344
  const now = (/* @__PURE__ */ new Date()).toISOString();
15141
- const existing = manifest.projects.find((p) => path22.resolve(p.root) === resolved);
15345
+ const existing = manifest.projects.find((p) => path23.resolve(p.root) === resolved);
15142
15346
  if (existing) {
15143
15347
  existing.lastSeen = now;
15144
- if (workDir) existing.lastWorkingDir = path22.resolve(workDir);
15348
+ if (workDir) existing.lastWorkingDir = path23.resolve(workDir);
15145
15349
  } else {
15146
15350
  manifest.projects.push({
15147
- name: path22.basename(resolved),
15351
+ name: path23.basename(resolved),
15148
15352
  root: resolved,
15149
15353
  slug: generateProjectSlug(resolved),
15150
15354
  createdAt: now,
15151
15355
  lastSeen: now,
15152
- lastWorkingDir: workDir ? path22.resolve(workDir) : void 0
15356
+ lastWorkingDir: workDir ? path23.resolve(workDir) : void 0
15153
15357
  });
15154
15358
  }
15155
15359
  await saveManifest(manifest, globalConfigPath);
@@ -15256,6 +15460,7 @@ async function startWebUI(opts = {}) {
15256
15460
  persistPrefsToConfig: persistPrefsToConfig2,
15257
15461
  prefSnapshot: prefSnapshot2
15258
15462
  };
15463
+ const watchConfigPath = profileConfigPath ?? globalConfigPath;
15259
15464
  let credentialWatcherClose;
15260
15465
  if (process.env["WRONGSTACK_DISABLE_CONFIG_WATCH"] !== "1") {
15261
15466
  let lastActiveCfg = JSON.stringify(
@@ -15263,7 +15468,7 @@ async function startWebUI(opts = {}) {
15263
15468
  );
15264
15469
  let lastUiLocale = state.getConfig().uiLocale;
15265
15470
  const credentialWatcher = watchProviderConfig(
15266
- globalConfigPath,
15471
+ watchConfigPath,
15267
15472
  vault,
15268
15473
  (snapshot) => {
15269
15474
  state.setConfig(
@@ -15404,7 +15609,7 @@ async function startWebUI(opts = {}) {
15404
15609
  archiveLowConfidenceAfterDays: config.superMemory?.hygiene?.archiveLowConfidenceAfterDays
15405
15610
  }).catch((err) => logger.warn(`super-memory session hygiene failed: ${toErrorMessage10(err)}`));
15406
15611
  }
15407
- await unregisterInstance(process.pid, path22.dirname(globalConfigPath));
15612
+ await unregisterInstance(process.pid, path23.dirname(globalConfigPath));
15408
15613
  }
15409
15614
  });
15410
15615
  }