@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
package/dist/index.js CHANGED
@@ -1809,15 +1809,15 @@ async function handleGitChanges(ws, projectRoot) {
1809
1809
  if (!m) continue;
1810
1810
  const added = m[1] === "-" ? 0 : Number(m[1]);
1811
1811
  const deleted = m[2] === "-" ? 0 : Number(m[2]);
1812
- let path24 = m[3] ?? "";
1813
- if (path24 === "") {
1812
+ let path25 = m[3] ?? "";
1813
+ if (path25 === "") {
1814
1814
  i += 1;
1815
- path24 = parts[i + 1] ?? parts[i] ?? "";
1815
+ path25 = parts[i + 1] ?? parts[i] ?? "";
1816
1816
  i += 1;
1817
1817
  }
1818
- if (!path24) continue;
1819
- const prev = counts.get(path24) ?? { added: 0, deleted: 0 };
1820
- counts.set(path24, { added: prev.added + added, deleted: prev.deleted + deleted });
1818
+ if (!path25) continue;
1819
+ const prev = counts.get(path25) ?? { added: 0, deleted: 0 };
1820
+ counts.set(path25, { added: prev.added + added, deleted: prev.deleted + deleted });
1821
1821
  }
1822
1822
  };
1823
1823
  parseNumstat(unstagedNumstat);
@@ -1829,7 +1829,7 @@ async function handleGitChanges(ws, projectRoot) {
1829
1829
  if (!rec || rec.length < 3) continue;
1830
1830
  const x = rec[0] ?? " ";
1831
1831
  const y = rec[1] ?? " ";
1832
- const path24 = rec.slice(3);
1832
+ const path25 = rec.slice(3);
1833
1833
  const isRename = x === "R" || x === "C" || y === "R" || y === "C";
1834
1834
  if (isRename) i += 1;
1835
1835
  let status;
@@ -1841,13 +1841,13 @@ async function handleGitChanges(ws, projectRoot) {
1841
1841
  else if (x === "D" || y === "D") status = "D";
1842
1842
  else status = "M";
1843
1843
  const staged = x !== " " && x !== "?";
1844
- let added = counts.get(path24)?.added ?? 0;
1845
- let deleted = counts.get(path24)?.deleted ?? 0;
1844
+ let added = counts.get(path25)?.added ?? 0;
1845
+ let deleted = counts.get(path25)?.deleted ?? 0;
1846
1846
  if (status === "?") {
1847
1847
  added = 0;
1848
1848
  deleted = 0;
1849
1849
  }
1850
- files.push({ path: path24, status, added, deleted, staged });
1850
+ files.push({ path: path25, status, added, deleted, staged });
1851
1851
  }
1852
1852
  send(ws, { type: "git.changes", payload: { files } });
1853
1853
  } catch (err) {
@@ -1858,10 +1858,10 @@ async function handleGitChanges(ws, projectRoot) {
1858
1858
  }
1859
1859
  }
1860
1860
  var MAX_DIFF_BYTES = 2 * 1024 * 1024;
1861
- async function handleGitDiff(ws, projectRoot, path24) {
1861
+ async function handleGitDiff(ws, projectRoot, path25) {
1862
1862
  const cwd = projectRoot || void 0;
1863
- const reply = (extra) => send(ws, { type: "git.diff", payload: { path: path24, ...extra } });
1864
- if (!path24 || path24.includes("\0") || path24.includes("..") || nodePath.isAbsolute(path24)) {
1863
+ const reply = (extra) => send(ws, { type: "git.diff", payload: { path: path25, ...extra } });
1864
+ if (!path25 || path25.includes("\0") || path25.includes("..") || nodePath.isAbsolute(path25)) {
1865
1865
  reply({ oldText: "", newText: "", error: "invalid path" });
1866
1866
  return;
1867
1867
  }
@@ -1869,10 +1869,10 @@ async function handleGitDiff(ws, projectRoot, path24) {
1869
1869
  const git = makeGit(cwd);
1870
1870
  const { readFile: readFile11 } = await import("node:fs/promises");
1871
1871
  const { join: join15 } = await import("node:path");
1872
- const oldText = await git(["show", `HEAD:${path24}`]);
1872
+ const oldText = await git(["show", `HEAD:${path25}`]);
1873
1873
  let newText = "";
1874
1874
  try {
1875
- const abs = cwd ? join15(cwd, path24) : path24;
1875
+ const abs = cwd ? join15(cwd, path25) : path25;
1876
1876
  const buf = await readFile11(abs);
1877
1877
  if (buf.includes(0)) {
1878
1878
  reply({ oldText: "", newText: "", binary: true });
@@ -1904,6 +1904,7 @@ async function handleGitDiff(ws, projectRoot, path24) {
1904
1904
  import * as fs6 from "node:fs/promises";
1905
1905
  import * as http from "node:http";
1906
1906
  import * as path7 from "node:path";
1907
+ import * as v8 from "node:v8";
1907
1908
 
1908
1909
  // src/server/http-server/api-handlers.ts
1909
1910
  async function handleApiSessions(res, globalRoot) {
@@ -3210,6 +3211,23 @@ function createHttpServer(opts) {
3210
3211
  }
3211
3212
  return;
3212
3213
  }
3214
+ if (url.pathname === "/debug/system" && req.method === "GET") {
3215
+ res.writeHead(200, {
3216
+ "Content-Type": "application/json",
3217
+ "Cache-Control": "no-store"
3218
+ });
3219
+ res.end(
3220
+ JSON.stringify({
3221
+ pid: process.pid,
3222
+ memoryUsage: process.memoryUsage(),
3223
+ heapLimit: v8.getHeapStatistics().heap_size_limit,
3224
+ uptime: process.uptime(),
3225
+ cpuUsage: process.cpuUsage(),
3226
+ timestamp: Date.now()
3227
+ })
3228
+ );
3229
+ return;
3230
+ }
3213
3231
  let filePath;
3214
3232
  if (url.pathname === "/" || url.pathname === "") {
3215
3233
  filePath = path7.join(distDir, "index.html");
@@ -3736,6 +3754,44 @@ async function handleSuperMemoryList(ws, memoryStore) {
3736
3754
  send(ws, { type: "memory.super.list", payload: { error: errMessage(err) } });
3737
3755
  }
3738
3756
  }
3757
+ async function handleSuperMemoryListPage(ws, msg, memoryStore) {
3758
+ if (!isSuperMemoryStore(memoryStore)) {
3759
+ send(ws, { type: "memory.super.listPage", payload: { error: requiresSuperMemory("memory.super.listPage") } });
3760
+ return;
3761
+ }
3762
+ try {
3763
+ const payload = msg.payload ?? {};
3764
+ const options = {
3765
+ statuses: Array.isArray(payload["statuses"]) ? payload["statuses"].filter((s) => typeof s === "string") : void 0,
3766
+ kind: typeof payload["kind"] === "string" ? payload["kind"] : void 0,
3767
+ query: typeof payload["query"] === "string" ? payload["query"] : void 0,
3768
+ limit: typeof payload["limit"] === "number" ? payload["limit"] : void 0,
3769
+ cursor: typeof payload["cursor"] === "string" ? payload["cursor"] : void 0
3770
+ };
3771
+ if (typeof memoryStore.listSuperPage === "function") {
3772
+ const page = await memoryStore.listSuperPage(options);
3773
+ send(ws, { type: "memory.super.listPage", payload: page });
3774
+ return;
3775
+ }
3776
+ const allowed = options.statuses && options.statuses.length > 0 ? new Set(options.statuses) : void 0;
3777
+ const everything = await memoryStore.listSuper();
3778
+ const statusCounts = {};
3779
+ for (const m of everything) statusCounts[m.status] = (statusCounts[m.status] ?? 0) + 1;
3780
+ const kind = options.kind && options.kind !== "all" ? options.kind : void 0;
3781
+ const q = options.query?.trim().toLowerCase();
3782
+ const filtered = everything.filter((m) => {
3783
+ if (allowed) return allowed.has(m.status);
3784
+ return m.status !== "deleted";
3785
+ }).filter((m) => !kind || m.kind === kind).filter((m) => !q || m.text.toLowerCase().includes(q));
3786
+ const limit = Math.max(1, Math.min(500, Math.floor(options.limit ?? 50)));
3787
+ send(ws, {
3788
+ type: "memory.super.listPage",
3789
+ payload: { memories: filtered.slice(0, limit), nextCursor: null, total: filtered.length, statusCounts }
3790
+ });
3791
+ } catch (err) {
3792
+ send(ws, { type: "memory.super.listPage", payload: { error: errMessage(err) } });
3793
+ }
3794
+ }
3739
3795
  async function handleSuperMemoryGet(ws, msg, memoryStore) {
3740
3796
  if (!isSuperMemoryStore(memoryStore)) {
3741
3797
  send(ws, { type: "memory.super.get", payload: { error: requiresSuperMemory("memory.super.get") } });
@@ -4299,7 +4355,7 @@ async function loadSavedProviders(configPath, vault) {
4299
4355
  if (!parsed.providers) return {};
4300
4356
  return decryptConfigSecrets(parsed.providers, vault);
4301
4357
  }
4302
- async function saveProviders(configPath, vault, providers) {
4358
+ async function saveProviders(configPath, vault, providers, profileConfigPath) {
4303
4359
  let raw;
4304
4360
  let fileExists = true;
4305
4361
  try {
@@ -4333,6 +4389,23 @@ async function saveProviders(configPath, vault, providers) {
4333
4389
  parsed.providers = providers;
4334
4390
  const encrypted = encryptConfigSecrets(parsed, vault);
4335
4391
  await atomicWrite4(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
4392
+ if (profileConfigPath && profileConfigPath !== configPath) {
4393
+ let profileRaw;
4394
+ try {
4395
+ profileRaw = await fs8.readFile(profileConfigPath, "utf8");
4396
+ } catch {
4397
+ profileRaw = "{}";
4398
+ }
4399
+ let profileParsed;
4400
+ try {
4401
+ profileParsed = JSON.parse(profileRaw);
4402
+ } catch {
4403
+ return;
4404
+ }
4405
+ profileParsed.providers = providers;
4406
+ const profileEncrypted = encryptConfigSecrets(profileParsed, vault);
4407
+ await atomicWrite4(profileConfigPath, JSON.stringify(profileEncrypted, null, 2), { mode: 384 });
4408
+ }
4336
4409
  }
4337
4410
 
4338
4411
  // src/server/provider-config-standalone.ts
@@ -5192,6 +5265,7 @@ var BOOLEAN_PREF_KEYS = /* @__PURE__ */ new Set([
5192
5265
  "hqRawContent",
5193
5266
  "fallbackAuto",
5194
5267
  "favoriteModelsOnly",
5268
+ "modelAvailabilitySchedule",
5195
5269
  "breakerEnabled",
5196
5270
  "debugStream",
5197
5271
  // Chimera + auto-review master toggles
@@ -5256,34 +5330,34 @@ var ENUM_PREF_KEYS = {
5256
5330
  chimeraAutoFix: /* @__PURE__ */ new Set(["off", "ask", "auto"]),
5257
5331
  autoReviewCascadeOn: /* @__PURE__ */ new Set(["off", "critical", "high"])
5258
5332
  };
5259
- function validateModelRuntimeValue(modelRuntime, path24) {
5333
+ function validateModelRuntimeValue(modelRuntime, path25) {
5260
5334
  const reasoning = modelRuntime["reasoning"];
5261
5335
  if (reasoning !== void 0) {
5262
- if (!isRecord(reasoning)) return `${path24}.reasoning must be an object when provided`;
5336
+ if (!isRecord(reasoning)) return `${path25}.reasoning must be an object when provided`;
5263
5337
  const mode = reasoning["mode"];
5264
5338
  const effort = reasoning["effort"];
5265
5339
  const preserve = reasoning["preserve"];
5266
5340
  if (mode !== void 0 && (typeof mode !== "string" || !REASONING_MODE_VALUES.has(mode))) {
5267
- return `${path24}.reasoning.mode must be one of: ${Array.from(REASONING_MODE_VALUES).join(", ")}`;
5341
+ return `${path25}.reasoning.mode must be one of: ${Array.from(REASONING_MODE_VALUES).join(", ")}`;
5268
5342
  }
5269
5343
  if (effort !== void 0 && (typeof effort !== "string" || !REASONING_EFFORT_VALUES.has(effort))) {
5270
- return `${path24}.reasoning.effort must be one of: ${Array.from(REASONING_EFFORT_VALUES).join(", ")}`;
5344
+ return `${path25}.reasoning.effort must be one of: ${Array.from(REASONING_EFFORT_VALUES).join(", ")}`;
5271
5345
  }
5272
5346
  if (preserve !== void 0 && typeof preserve !== "boolean") {
5273
- return `${path24}.reasoning.preserve must be a boolean when provided`;
5347
+ return `${path25}.reasoning.preserve must be a boolean when provided`;
5274
5348
  }
5275
5349
  }
5276
5350
  const cache = modelRuntime["cache"];
5277
5351
  if (cache !== void 0) {
5278
- if (!isRecord(cache)) return `${path24}.cache must be an object when provided`;
5352
+ if (!isRecord(cache)) return `${path25}.cache must be an object when provided`;
5279
5353
  const ttl = cache["ttl"];
5280
5354
  if (ttl !== void 0 && (typeof ttl !== "string" || !CACHE_TTL_VALUES.has(ttl) || ttl === "default")) {
5281
- return `${path24}.cache.ttl must be one of: 5m, 1h`;
5355
+ return `${path25}.cache.ttl must be one of: 5m, 1h`;
5282
5356
  }
5283
5357
  }
5284
5358
  const parameters = modelRuntime["parameters"];
5285
5359
  if (parameters !== void 0 && !isRecord(parameters)) {
5286
- return `${path24}.parameters must be an object when provided`;
5360
+ return `${path25}.parameters must be an object when provided`;
5287
5361
  }
5288
5362
  return null;
5289
5363
  }
@@ -5336,7 +5410,10 @@ function validatePreferenceValue(key, value) {
5336
5410
  return `prefs.update payload.${key}.modelRuntime must be an object when provided`;
5337
5411
  }
5338
5412
  if (isRecord(modelRuntime)) {
5339
- const runtimeError = validateModelRuntimeValue(modelRuntime, `prefs.update payload.${key}.modelRuntime`);
5413
+ const runtimeError = validateModelRuntimeValue(
5414
+ modelRuntime,
5415
+ `prefs.update payload.${key}.modelRuntime`
5416
+ );
5340
5417
  if (runtimeError) return runtimeError;
5341
5418
  }
5342
5419
  if (model === void 0 && fallbackProfile === void 0 && modelRuntime === void 0) {
@@ -5573,8 +5650,8 @@ function validateShellOpenPayload(payload) {
5573
5650
  if (!isRecord(payload)) {
5574
5651
  return { ok: false, message: "shell.open payload must be an object with string path" };
5575
5652
  }
5576
- const path24 = payload["path"];
5577
- if (typeof path24 !== "string" || path24.trim().length === 0) {
5653
+ const path25 = payload["path"];
5654
+ if (typeof path25 !== "string" || path25.trim().length === 0) {
5578
5655
  return { ok: false, message: "shell.open payload.path must be a non-empty string" };
5579
5656
  }
5580
5657
  const target = payload["target"];
@@ -5587,7 +5664,7 @@ function validateShellOpenPayload(payload) {
5587
5664
  return {
5588
5665
  ok: true,
5589
5666
  value: {
5590
- path: path24,
5667
+ path: path25,
5591
5668
  ...target !== void 0 ? { target } : {}
5592
5669
  }
5593
5670
  };
@@ -5596,14 +5673,14 @@ function validateGitDiffPayload(payload) {
5596
5673
  if (!isRecord(payload)) {
5597
5674
  return { ok: false, message: "git.diff payload must be an object" };
5598
5675
  }
5599
- const path24 = payload["path"];
5600
- if (path24 === void 0 || path24 === null) {
5676
+ const path25 = payload["path"];
5677
+ if (path25 === void 0 || path25 === null) {
5601
5678
  return { ok: true, value: { path: "" } };
5602
5679
  }
5603
- if (typeof path24 !== "string") {
5680
+ if (typeof path25 !== "string") {
5604
5681
  return { ok: false, message: "git.diff payload.path must be a string when provided" };
5605
5682
  }
5606
- return { ok: true, value: { path: path24 } };
5683
+ return { ok: true, value: { path: path25 } };
5607
5684
  }
5608
5685
  function validateProjectsAddPayload(payload) {
5609
5686
  if (!isRecord(payload)) {
@@ -6648,7 +6725,7 @@ function registerShutdownHandlers(res) {
6648
6725
  import { watch as fsWatch } from "node:fs";
6649
6726
  import * as fs11 from "node:fs/promises";
6650
6727
  import * as path14 from "node:path";
6651
- import { getBoard, getKanbanDir } from "@wrongstack/kanban";
6728
+ import { getBoard, getKanbanDir, recordTaskFileActivity } from "@wrongstack/kanban";
6652
6729
 
6653
6730
  // src/server/codemap-telemetry.ts
6654
6731
  import * as path13 from "node:path";
@@ -7044,6 +7121,18 @@ function setupEvents(deps2) {
7044
7121
  on("file.activity", (e) => {
7045
7122
  broadcast2(clients, { type: "codemap.file_event", payload: e });
7046
7123
  });
7124
+ on("file.event", (e) => {
7125
+ if (e.scope !== "task" || !e.boardId || !e.taskId) return;
7126
+ void recordTaskFileActivity(context.projectRoot, e.boardId, e.taskId, e).then((recorded) => {
7127
+ if (recorded) {
7128
+ broadcast2(clients, {
7129
+ type: "kanban.task.activity.changed",
7130
+ payload: { boardId: e.boardId, taskId: e.taskId }
7131
+ });
7132
+ }
7133
+ }).catch(() => {
7134
+ });
7135
+ });
7047
7136
  on("tool.loop_detected", (e) => {
7048
7137
  broadcast2(clients, {
7049
7138
  type: "tool.loop_detected",
@@ -7168,12 +7257,14 @@ function setupEvents(deps2) {
7168
7257
  input: e.input,
7169
7258
  suggestedPattern: e.suggestedPattern,
7170
7259
  decisionSource: e.decisionSource,
7171
- riskTier: e.riskTier
7260
+ riskTier: e.riskTier,
7261
+ boundaryReason: e.boundaryReason
7172
7262
  });
7173
7263
  pendingConfirms.set(id, {
7174
7264
  resolve: e.resolve,
7175
7265
  decisionSource: e.decisionSource,
7176
7266
  riskTier: e.riskTier,
7267
+ boundaryReason: e.boundaryReason,
7177
7268
  payload
7178
7269
  });
7179
7270
  broadcast2(clients, { type: "tool.confirm_needed", payload });
@@ -7266,7 +7357,8 @@ function setupEvents(deps2) {
7266
7357
  oldState: e.oldState,
7267
7358
  newState: e.newState,
7268
7359
  reason: e.reason,
7269
- timestamp: e.timestamp
7360
+ timestamp: e.timestamp,
7361
+ stateExpiresAt: e.stateExpiresAt
7270
7362
  })
7271
7363
  });
7272
7364
  });
@@ -8373,6 +8465,7 @@ function seedContextMeta(config, context) {
8373
8465
  meta["fallbackProfiles"] = config.fallbackProfiles ?? {};
8374
8466
  meta["favoriteModels"] = config.favoriteModels ?? [];
8375
8467
  meta["favoriteModelsOnly"] = config.favoriteModelsOnly === true;
8468
+ meta["modelAvailabilitySchedule"] = config.modelAvailabilitySchedule ?? [];
8376
8469
  meta["modelMatrix"] = config.modelMatrix ?? {};
8377
8470
  meta["fallbackAuto"] = config.fallbackAuto !== false;
8378
8471
  if (typeof config.uiLocale === "string" && config.uiLocale) meta["uiLocale"] = config.uiLocale;
@@ -8455,9 +8548,7 @@ function seedContextMeta(config, context) {
8455
8548
  } catch {
8456
8549
  resolvedChain = [];
8457
8550
  }
8458
- meta["autoReviewFallbackModels"] = resolvedChain.map(
8459
- (e) => `${e.providerId}/${e.model}`
8460
- );
8551
+ meta["autoReviewFallbackModels"] = resolvedChain.map((e) => `${e.providerId}/${e.model}`);
8461
8552
  }
8462
8553
  }
8463
8554
 
@@ -8995,7 +9086,7 @@ function isSuperMemoryService(memoryStore) {
8995
9086
  }
8996
9087
 
8997
9088
  // src/server/start-webui.ts
8998
- import * as path23 from "node:path";
9089
+ import * as path24 from "node:path";
8999
9090
  import {
9000
9091
  createDefaultPipelines,
9001
9092
  createSessionEventBridge,
@@ -9914,7 +10005,7 @@ function setupWebUICodebaseIndexing(deps2) {
9914
10005
  }
9915
10006
  if (idx) {
9916
10007
  cancelPendingReindexes();
9917
- shutdownCodebaseIndexHost();
10008
+ void shutdownCodebaseIndexHost();
9918
10009
  }
9919
10010
  }
9920
10011
  };
@@ -10081,6 +10172,9 @@ var DEFAULT_COLS = 80;
10081
10172
  var DEFAULT_ROWS = 24;
10082
10173
  var requireFromHere = createRequire4(import.meta.url);
10083
10174
  var cachedNodePty;
10175
+ function resolveTerminalShell(platform = process.platform, env = process.env) {
10176
+ return platform === "win32" ? env.COMSPEC || "cmd.exe" : env.SHELL || "/bin/sh";
10177
+ }
10084
10178
  var TerminalWebSocketHandler = class {
10085
10179
  constructor(getCwd, logger, loadNodePty = defaultLoadNodePty, killProcessTree = defaultKillProcessTree) {
10086
10180
  this.getCwd = getCwd;
@@ -10136,7 +10230,7 @@ var TerminalWebSocketHandler = class {
10136
10230
  });
10137
10231
  return;
10138
10232
  }
10139
- const shell = process.platform === "win32" ? process.env.COMSPEC || "cmd.exe" : process.env.SHELL || "/bin/bash";
10233
+ const shell = resolveTerminalShell();
10140
10234
  const nodePty = this.loadNodePty();
10141
10235
  if (!nodePty) {
10142
10236
  const msg = "Integrated terminal unavailable: optional dependency node-pty is not installed. Install node-pty to enable WebUI terminal sessions.";
@@ -10456,7 +10550,11 @@ async function createAgentServices(input) {
10456
10550
  toolExecutor
10457
10551
  });
10458
10552
  if (config.features.memory && config.features.memoryConsolidation !== false) {
10459
- agent.extensions.register(new SessionMemoryConsolidator({ memoryStore }));
10553
+ const consSuperMemory = typeof memoryStore["rememberSuper"] === "function" ? memoryStore : void 0;
10554
+ agent.extensions.register(new SessionMemoryConsolidator({
10555
+ memoryStore,
10556
+ ...consSuperMemory ? { superMemory: consSuperMemory } : {}
10557
+ }));
10460
10558
  }
10461
10559
  console.log("[WebUI] Agent initialized");
10462
10560
  const brainCfg = resolveBrainConfigDefaults(config.brain, {
@@ -10662,6 +10760,7 @@ function isSuperMemoryRetriever(memoryStore) {
10662
10760
  // src/server/pending-confirms.ts
10663
10761
  function resolveYoloEligiblePendingConfirms(pendingConfirms) {
10664
10762
  for (const [id, confirm] of pendingConfirms) {
10763
+ if (confirm.boundaryReason) continue;
10665
10764
  pendingConfirms.delete(id);
10666
10765
  confirm.resolve("yes");
10667
10766
  }
@@ -11141,7 +11240,7 @@ import {
11141
11240
  createBoard,
11142
11241
  duplicateBoard,
11143
11242
  exportBoardToTaskGraph,
11144
- generateBoardFromDescription,
11243
+ createBoardFromText,
11145
11244
  getBoard as getBoard2,
11146
11245
  getKanbanOrchestrationSnapshot,
11147
11246
  getKanbanQueueHealth,
@@ -11172,6 +11271,32 @@ import {
11172
11271
  updateTask
11173
11272
  } from "@wrongstack/kanban";
11174
11273
  import { applySessionKanbanTaskToSource } from "@wrongstack/tools/session-kanban";
11274
+ function paginateKanbanBoards(boards, input) {
11275
+ const pageSize = Math.min(100, Math.max(1, Math.floor(input.pageSize)));
11276
+ const activeSessionIds = new Set(input.activeSessionIds ?? []);
11277
+ const isActive = (board) => board.presence?.some((entry) => entry.active) === true || board.tags?.some(
11278
+ (tag) => tag.startsWith("session:") && activeSessionIds.has(tag.slice(8))
11279
+ ) === true;
11280
+ const sorted = [...boards].sort((left, right) => {
11281
+ const activityOrder = Number(isActive(right)) - Number(isActive(left));
11282
+ return activityOrder || right.updatedAt.localeCompare(left.updatedAt);
11283
+ });
11284
+ const activeTotal = sorted.filter(isActive).length;
11285
+ const total = sorted.length;
11286
+ const totalPages = Math.max(1, Math.ceil(total / pageSize));
11287
+ const requestedPage = Number.isFinite(input.page) ? Math.floor(input.page) : 1;
11288
+ const page = Math.min(totalPages, Math.max(1, requestedPage));
11289
+ const start = (page - 1) * pageSize;
11290
+ return {
11291
+ items: sorted.slice(start, start + pageSize),
11292
+ total,
11293
+ page,
11294
+ pageSize,
11295
+ totalPages,
11296
+ activeTotal,
11297
+ orphanedTotal: total - activeTotal
11298
+ };
11299
+ }
11175
11300
  async function syncSessionSource(ctx, task, remove = false) {
11176
11301
  if (!ctx.context) return;
11177
11302
  const update = await applySessionKanbanTaskToSource(ctx.context, task, { remove });
@@ -11221,9 +11346,26 @@ async function handleKanbanRoute(ws, msg, ctx) {
11221
11346
  const type = msg.type;
11222
11347
  try {
11223
11348
  switch (type) {
11224
- case "kanban.list":
11225
- ok(ws, type, await listBoards(ctx.projectRoot));
11349
+ case "kanban.list": {
11350
+ const boards = await listBoards(ctx.projectRoot);
11351
+ const requestedPage = Number(payload?.page);
11352
+ const requestedPageSize = Number(payload?.pageSize);
11353
+ if (!Number.isFinite(requestedPage) || !Number.isFinite(requestedPageSize)) {
11354
+ ok(ws, type, boards);
11355
+ return true;
11356
+ }
11357
+ const activeSessionIds = Array.isArray(payload?.activeSessionIds) ? payload.activeSessionIds.filter((id) => typeof id === "string") : [];
11358
+ ok(
11359
+ ws,
11360
+ type,
11361
+ paginateKanbanBoards(boards, {
11362
+ page: requestedPage,
11363
+ pageSize: requestedPageSize,
11364
+ activeSessionIds
11365
+ })
11366
+ );
11226
11367
  return true;
11368
+ }
11227
11369
  case "kanban.get": {
11228
11370
  const boardId = payload?.boardId;
11229
11371
  if (!boardId) {
@@ -11289,7 +11431,8 @@ async function handleKanbanRoute(ws, msg, ctx) {
11289
11431
  ...payload?.description ? { description: payload.description } : {},
11290
11432
  ...payload?.tags ? { tags: payload.tags } : {},
11291
11433
  ...payload?.columns ? { columns: payload.columns } : {},
11292
- ...has(payload, "lifecycle") ? { lifecycle: payload?.lifecycle } : {}
11434
+ ...has(payload, "lifecycle") ? { lifecycle: payload?.lifecycle } : {},
11435
+ ...has(payload, "boundary") ? { boundary: payload?.boundary } : {}
11293
11436
  })
11294
11437
  );
11295
11438
  return true;
@@ -11310,6 +11453,9 @@ async function handleKanbanRoute(ws, msg, ctx) {
11310
11453
  } : {},
11311
11454
  ...has(payload, "supervisor") ? {
11312
11455
  supervisor: payload?.supervisor ?? null
11456
+ } : {},
11457
+ ...has(payload, "boundary") ? {
11458
+ boundary: payload?.boundary ?? null
11313
11459
  } : {}
11314
11460
  });
11315
11461
  board ? ok(ws, type, board) : fail(ws, type, `Board not found: ${boardId}`);
@@ -11356,7 +11502,7 @@ async function handleKanbanRoute(ws, msg, ctx) {
11356
11502
  }
11357
11503
  const board = await createBoard(
11358
11504
  ctx.projectRoot,
11359
- generateBoardFromDescription({
11505
+ createBoardFromText({
11360
11506
  description,
11361
11507
  ...payload?.title ? { title: payload.title } : {},
11362
11508
  ...payload?.context ? { context: payload.context } : {}
@@ -11468,7 +11614,8 @@ async function handleKanbanRoute(ws, msg, ctx) {
11468
11614
  ...payload?.dueDate ? { dueDate: payload.dueDate } : {},
11469
11615
  ...payload?.priority ? { priority: payload.priority } : {},
11470
11616
  ...payload?.assignedAgent ? { assignedAgent: payload.assignedAgent } : {},
11471
- ...payload?.labels ? { labels: payload.labels } : {}
11617
+ ...payload?.labels ? { labels: payload.labels } : {},
11618
+ ...has(payload, "boundary") ? { boundary: payload?.boundary } : {}
11472
11619
  },
11473
11620
  activityContext(ctx, "webui", payload?.activityNote)
11474
11621
  );
@@ -11549,6 +11696,9 @@ async function handleKanbanRoute(ws, msg, ctx) {
11549
11696
  } : {},
11550
11697
  ...has(payload, "costCeilingUsd") ? {
11551
11698
  costCeilingUsd: payload?.costCeilingUsd === null || payload?.costCeilingUsd === "" ? null : Number(payload?.costCeilingUsd)
11699
+ } : {},
11700
+ ...has(payload, "boundary") ? {
11701
+ boundary: payload?.boundary ?? null
11552
11702
  } : {}
11553
11703
  },
11554
11704
  activityContext(ctx, "webui", payload?.activityNote)
@@ -12441,7 +12591,7 @@ var chronicleCache = /* @__PURE__ */ new Map();
12441
12591
  async function chronicleEngine(projectRoot) {
12442
12592
  const now = Date.now();
12443
12593
  const cached = chronicleCache.get(projectRoot);
12444
- if (cached && now - cached.loadedAt < 1e3) return cached.engine;
12594
+ if (cached && now - cached.loadedAt < 6e4) return cached.engine;
12445
12595
  const paths = resolveWstackPaths2({ projectRoot, userHome: os2.homedir() });
12446
12596
  const engine = await ChronicleQueryEngine.fromDirectory(
12447
12597
  path20.join(paths.projectDir, "chronicle")
@@ -12704,6 +12854,8 @@ function createMessageDispatcher(opts) {
12704
12854
  // ── SuperMemory operations ──
12705
12855
  case "memory.super.list":
12706
12856
  return handleSuperMemoryList(ws, deps2.memoryStore);
12857
+ case "memory.super.listPage":
12858
+ return handleSuperMemoryListPage(ws, msg, deps2.memoryStore);
12707
12859
  case "memory.super.get":
12708
12860
  return handleSuperMemoryGet(ws, msg, deps2.memoryStore);
12709
12861
  case "memory.super.update":
@@ -12958,7 +13110,7 @@ function createMessageDispatcher(opts) {
12958
13110
  case "chronicle.query": {
12959
13111
  const payload = msg.payload ?? {};
12960
13112
  const engine = await chronicleEngine(state.getProjectRoot());
12961
- send(ws, { type: "chronicle.query_result", payload: engine.query(payload.query ?? {}) });
13113
+ send(ws, { type: "chronicle.query_result", payload: await engine.query(payload.query ?? {}) });
12962
13114
  break;
12963
13115
  }
12964
13116
  case "chronicle.facet": {
@@ -12988,7 +13140,7 @@ function createMessageDispatcher(opts) {
12988
13140
  type: "chronicle.facet_result",
12989
13141
  payload: {
12990
13142
  field: payload.field,
12991
- values: engine.facet(payload.field, payload.query ?? {}, payload.limit),
13143
+ values: await engine.facet(payload.field, payload.query ?? {}, payload.limit),
12992
13144
  diagnostics: engine.diagnostics
12993
13145
  }
12994
13146
  });
@@ -12999,7 +13151,7 @@ function createMessageDispatcher(opts) {
12999
13151
  const engine = await chronicleEngine(state.getProjectRoot());
13000
13152
  send(ws, {
13001
13153
  type: "chronicle.graph_result",
13002
- payload: engine.graph(payload.seed ?? {}, payload.hops, payload.maxNodes)
13154
+ payload: await engine.graph(payload.seed ?? {}, payload.hops, payload.maxNodes)
13003
13155
  });
13004
13156
  break;
13005
13157
  }
@@ -13054,9 +13206,10 @@ function createMessageDispatcher(opts) {
13054
13206
  }
13055
13207
 
13056
13208
  // src/server/pref-helpers.ts
13057
- import { atomicWrite as atomicWrite6, FORBIDDEN_PROTO_KEYS as FORBIDDEN_PROTO_KEYS2 } from "@wrongstack/core/utils";
13058
- import { decryptConfigSecrets as decryptConfigSecrets2, encryptConfigSecrets as encryptConfigSecrets2 } from "@wrongstack/core/security";
13059
13209
  import * as fs14 from "node:fs/promises";
13210
+ import * as path21 from "node:path";
13211
+ import { decryptConfigSecrets as decryptConfigSecrets2, encryptConfigSecrets as encryptConfigSecrets2 } from "@wrongstack/core/security";
13212
+ import { atomicWrite as atomicWrite6, backupConfigFile, FORBIDDEN_PROTO_KEYS as FORBIDDEN_PROTO_KEYS2 } from "@wrongstack/core/utils";
13060
13213
  var PREF_KEYS = [
13061
13214
  "autonomy",
13062
13215
  "autonomyDelayMs",
@@ -13101,6 +13254,7 @@ var PREF_KEYS = [
13101
13254
  "fallbackProfiles",
13102
13255
  "favoriteModels",
13103
13256
  "favoriteModelsOnly",
13257
+ "modelAvailabilitySchedule",
13104
13258
  "modelMatrix",
13105
13259
  "fallbackAuto",
13106
13260
  // Refiner + TUI visual prefs (parity with the CLI's embedded server —
@@ -13144,26 +13298,34 @@ function prefSnapshot(contextMeta) {
13144
13298
  }
13145
13299
  return snapshot;
13146
13300
  }
13301
+ async function writeGlobalConfigFile(filePath, vault, mutate, logger, errorLabel) {
13302
+ const globalRoot = path21.dirname(filePath);
13303
+ await backupConfigFile(filePath, { globalRoot });
13304
+ let raw;
13305
+ try {
13306
+ raw = await fs14.readFile(filePath, "utf8");
13307
+ } catch {
13308
+ raw = "{}";
13309
+ }
13310
+ let parsed;
13311
+ try {
13312
+ parsed = JSON.parse(raw);
13313
+ } catch {
13314
+ logger.warn(`${errorLabel}: refusing to overwrite corrupt config at ${filePath}`);
13315
+ return;
13316
+ }
13317
+ const decrypted = decryptConfigSecrets2(parsed, vault);
13318
+ mutate(decrypted);
13319
+ const encrypted = encryptConfigSecrets2(decrypted, vault);
13320
+ await atomicWrite6(filePath, JSON.stringify(encrypted, null, 2), { mode: 384 });
13321
+ }
13147
13322
  async function updateGlobalConfig(deps2, holder, mutate, errorLabel) {
13148
- const { globalConfigPath, vault, logger } = deps2;
13323
+ const { globalConfigPath, profileConfigPath, vault, logger } = deps2;
13149
13324
  const write = async () => {
13150
- let raw;
13151
- try {
13152
- raw = await fs14.readFile(globalConfigPath, "utf8");
13153
- } catch {
13154
- raw = "{}";
13325
+ await writeGlobalConfigFile(globalConfigPath, vault, mutate, logger, errorLabel);
13326
+ if (profileConfigPath && profileConfigPath !== globalConfigPath) {
13327
+ await writeGlobalConfigFile(profileConfigPath, vault, mutate, logger, errorLabel);
13155
13328
  }
13156
- let parsed;
13157
- try {
13158
- parsed = JSON.parse(raw);
13159
- } catch {
13160
- logger.warn(`${errorLabel}: refusing to overwrite corrupt config at ${globalConfigPath}`);
13161
- return;
13162
- }
13163
- const decrypted = decryptConfigSecrets2(parsed, vault);
13164
- mutate(decrypted);
13165
- const encrypted = encryptConfigSecrets2(decrypted, vault);
13166
- await atomicWrite6(globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
13167
13329
  };
13168
13330
  const next = holder.lock.then(write);
13169
13331
  holder.lock = next.then(
@@ -13173,256 +13335,264 @@ async function updateGlobalConfig(deps2, holder, mutate, errorLabel) {
13173
13335
  try {
13174
13336
  await next;
13175
13337
  } catch (err) {
13176
- logger.warn(`${errorLabel}: failed to persist to config: ${err instanceof Error ? err.message : String(err)}`);
13338
+ logger.warn(
13339
+ `${errorLabel}: failed to persist to config: ${err instanceof Error ? err.message : String(err)}`
13340
+ );
13177
13341
  }
13178
13342
  }
13179
13343
  async function persistPrefsToConfig(deps2, holder, payload) {
13180
- return updateGlobalConfig(deps2, holder, (decrypted) => {
13181
- const autonomyCfg = decrypted.autonomy ?? {};
13182
- let autonomyTouched = false;
13183
- const setAutonomy = (key, val) => {
13184
- autonomyCfg[key] = val;
13185
- autonomyTouched = true;
13186
- };
13187
- if (typeof payload["autonomy"] === "string" && ["off", "suggest", "auto"].includes(payload["autonomy"])) {
13188
- setAutonomy("defaultMode", payload["autonomy"]);
13189
- }
13190
- if (typeof payload["autonomyDelayMs"] === "number")
13191
- setAutonomy("autoProceedDelayMs", payload["autonomyDelayMs"]);
13192
- if (typeof payload["autoProceedMaxIterations"] === "number")
13193
- setAutonomy("autoProceedMaxIterations", payload["autoProceedMaxIterations"]);
13194
- if (typeof payload["yolo"] === "boolean") {
13195
- setAutonomy("yolo", payload["yolo"]);
13196
- decrypted.yolo = payload["yolo"];
13197
- }
13198
- if (typeof payload["chime"] === "boolean") setAutonomy("chime", payload["chime"]);
13199
- if (typeof payload["confirmExit"] === "boolean")
13200
- setAutonomy("confirmExit", payload["confirmExit"]);
13201
- if (typeof payload["streamFleet"] === "boolean")
13202
- setAutonomy("streamFleet", payload["streamFleet"]);
13203
- if (typeof payload["enhanceEnabled"] === "boolean")
13204
- setAutonomy("enhance", payload["enhanceEnabled"]);
13205
- if (typeof payload["enhanceDelayMs"] === "number")
13206
- setAutonomy("enhanceDelayMs", payload["enhanceDelayMs"]);
13207
- if (typeof payload["enhanceLanguage"] === "string")
13208
- setAutonomy("enhanceLanguage", payload["enhanceLanguage"]);
13209
- if (typeof payload["refinerProvider"] === "string")
13210
- setAutonomy("refinerProvider", payload["refinerProvider"]);
13211
- if (typeof payload["refinerModel"] === "string")
13212
- setAutonomy("refinerModel", payload["refinerModel"]);
13213
- if (typeof payload["refinerFallbackProfile"] === "string")
13214
- setAutonomy("refinerFallbackProfile", payload["refinerFallbackProfile"]);
13215
- if (typeof payload["thinkingWord"] === "string")
13216
- setAutonomy("thinkingWord", payload["thinkingWord"]);
13217
- if (typeof payload["statuslineMode"] === "string")
13218
- setAutonomy("statuslineMode", payload["statuslineMode"]);
13219
- if (typeof payload["animationStyle"] === "string")
13220
- setAutonomy("animationStyle", payload["animationStyle"]);
13221
- if (typeof payload["showModelReasoning"] === "boolean")
13222
- setAutonomy("showModelReasoning", payload["showModelReasoning"]);
13223
- if (autonomyTouched) decrypted.autonomy = autonomyCfg;
13224
- if (typeof payload["nextPrediction"] === "boolean")
13225
- decrypted.nextPrediction = payload["nextPrediction"];
13226
- if (typeof payload["uiLocale"] === "string") decrypted.uiLocale = payload["uiLocale"];
13227
- if (Array.isArray(payload["fallbackModels"]))
13228
- decrypted.fallbackModels = payload["fallbackModels"];
13229
- if (payload["fallbackProfiles"] && typeof payload["fallbackProfiles"] === "object" && !Array.isArray(payload["fallbackProfiles"])) {
13230
- decrypted.fallbackProfiles = payload["fallbackProfiles"];
13231
- }
13232
- if (Array.isArray(payload["favoriteModels"]))
13233
- decrypted.favoriteModels = payload["favoriteModels"];
13234
- if (typeof payload["favoriteModelsOnly"] === "boolean")
13235
- decrypted.favoriteModelsOnly = payload["favoriteModelsOnly"];
13236
- if (payload["modelMatrix"] && typeof payload["modelMatrix"] === "object" && !Array.isArray(payload["modelMatrix"])) {
13237
- decrypted.modelMatrix = payload["modelMatrix"];
13238
- }
13239
- if (typeof payload["fallbackAuto"] === "boolean")
13240
- decrypted.fallbackAuto = payload["fallbackAuto"];
13241
- const FEATURE_MAP = {
13242
- featureMcp: "mcp",
13243
- featurePlugins: "plugins",
13244
- featureMemory: "memory",
13245
- featureSkills: "skills",
13246
- featureModelsRegistry: "modelsRegistry"
13247
- };
13248
- for (const [prefKey, cfgKey] of Object.entries(FEATURE_MAP)) {
13249
- if (typeof payload[prefKey] === "boolean") {
13250
- const feats = decrypted.features ?? {};
13251
- feats[cfgKey] = payload[prefKey];
13252
- decrypted.features = feats;
13253
- }
13254
- }
13255
- if (typeof payload["contextAutoCompact"] === "boolean" || typeof payload["contextStrategy"] === "string" || typeof payload["contextMode"] === "string") {
13256
- const ctxCfg = decrypted.context ?? {};
13257
- if (typeof payload["contextAutoCompact"] === "boolean")
13258
- ctxCfg.autoCompact = payload["contextAutoCompact"];
13259
- if (typeof payload["contextStrategy"] === "string")
13260
- ctxCfg.strategy = payload["contextStrategy"];
13261
- if (typeof payload["contextMode"] === "string") ctxCfg.mode = payload["contextMode"];
13262
- decrypted.context = ctxCfg;
13263
- }
13264
- if (typeof payload["tokenSavingTier"] === "string") {
13265
- const featsCfg = decrypted.features ?? {};
13266
- featsCfg.tokenSavingMode = payload["tokenSavingTier"];
13267
- decrypted.features = featsCfg;
13268
- }
13269
- if (typeof payload["maxConcurrent"] === "number") {
13270
- decrypted.maxConcurrent = payload["maxConcurrent"];
13271
- }
13272
- if (typeof payload["titleAnimation"] === "boolean") {
13273
- const autoCfg = decrypted.autonomy ?? {};
13274
- autoCfg.terminalTitleAnimation = payload["titleAnimation"];
13275
- decrypted.autonomy = autoCfg;
13276
- }
13277
- if (typeof payload["logLevel"] === "string") {
13278
- const logCfg = decrypted.log ?? {};
13279
- logCfg.level = payload["logLevel"];
13280
- decrypted.log = logCfg;
13281
- }
13282
- if (typeof payload["auditLevel"] === "string") {
13283
- const sessionCfg = decrypted.session ?? {};
13284
- sessionCfg.auditLevel = payload["auditLevel"];
13285
- decrypted.session = sessionCfg;
13286
- }
13287
- if (typeof payload["indexOnStart"] === "boolean") {
13288
- const indexingCfg = decrypted.indexing ?? {};
13289
- indexingCfg.onSessionStart = payload["indexOnStart"];
13290
- decrypted.indexing = indexingCfg;
13291
- }
13292
- if (typeof payload["maxIterations"] === "number") {
13293
- const toolsCfg = decrypted.tools ?? {};
13294
- toolsCfg.maxIterations = payload["maxIterations"];
13295
- decrypted.tools = toolsCfg;
13296
- }
13297
- const hqTouched = typeof payload["hqEnabled"] === "boolean" || typeof payload["hqUrl"] === "string" || typeof payload["hqToken"] === "string" || typeof payload["hqRawContent"] === "boolean";
13298
- if (hqTouched) {
13299
- const hqCfg = decrypted.hq ?? {};
13300
- if (typeof payload["hqEnabled"] === "boolean") hqCfg.enabled = payload["hqEnabled"];
13301
- if (typeof payload["hqUrl"] === "string") hqCfg.url = payload["hqUrl"];
13302
- if (typeof payload["hqToken"] === "string") hqCfg.token = payload["hqToken"];
13303
- if (typeof payload["hqRawContent"] === "boolean")
13304
- hqCfg.rawContent = payload["hqRawContent"];
13305
- decrypted.hq = hqCfg;
13306
- }
13307
- const tgTouched = typeof payload["tgSessionEnd"] === "boolean" || typeof payload["tgDelegate"] === "boolean" || typeof payload["tgLongToolMs"] === "number";
13308
- if (tgTouched) {
13309
- const ext = decrypted.extensions ?? {};
13310
- const tg = ext["telegram"] ?? {};
13311
- if (typeof payload["tgSessionEnd"] === "boolean") {
13312
- tg["notifyOnSessionEnd"] = payload["tgSessionEnd"];
13313
- }
13314
- if (typeof payload["tgDelegate"] === "boolean") {
13315
- tg["notifyOnDelegate"] = payload["tgDelegate"];
13316
- }
13317
- if (typeof payload["tgLongToolMs"] === "number") {
13318
- tg["longToolThresholdMs"] = payload["tgLongToolMs"];
13319
- }
13320
- ext["telegram"] = tg;
13321
- decrypted.extensions = ext;
13322
- }
13323
- const modelRuntimeTouched = typeof payload["reasoningMode"] === "string" || typeof payload["reasoningEffort"] === "string" || typeof payload["reasoningPreserve"] === "boolean" || typeof payload["cacheTtl"] === "string";
13324
- if (modelRuntimeTouched) {
13325
- const mr = decrypted.modelRuntime ?? {};
13326
- const reasoning = mr.reasoning ?? {};
13327
- if (typeof payload["reasoningMode"] === "string") reasoning.mode = payload["reasoningMode"];
13328
- if (typeof payload["reasoningEffort"] === "string")
13329
- reasoning.effort = payload["reasoningEffort"];
13330
- if (typeof payload["reasoningPreserve"] === "boolean")
13331
- reasoning.preserve = payload["reasoningPreserve"];
13332
- mr.reasoning = reasoning;
13333
- if (typeof payload["cacheTtl"] === "string" && payload["cacheTtl"] !== "default") {
13334
- mr.cache = { ttl: payload["cacheTtl"] };
13335
- } else if (payload["cacheTtl"] === "default") {
13336
- delete mr.cache;
13337
- }
13338
- decrypted.modelRuntime = mr;
13339
- }
13340
- if (typeof payload["breakerEnabled"] === "boolean" || typeof payload["breakerAutoKillResetMs"] === "number") {
13341
- const cb = decrypted.circuitBreaker ?? {};
13342
- if (typeof payload["breakerEnabled"] === "boolean") cb.enabled = payload["breakerEnabled"];
13343
- if (typeof payload["breakerAutoKillResetMs"] === "number")
13344
- cb.autoKillResetMs = payload["breakerAutoKillResetMs"];
13345
- decrypted.circuitBreaker = cb;
13346
- }
13347
- if (payload["fsAccess"] === "unrestricted" || payload["fsAccess"] === "project") {
13348
- const restrict = payload["fsAccess"] === "project";
13349
- const toolsCfg = decrypted.tools ?? {};
13350
- toolsCfg.restrictToProjectRoot = restrict;
13351
- decrypted.tools = toolsCfg;
13352
- const featsCfg = decrypted.features ?? {};
13353
- featsCfg.allowOutsideProjectRoot = !restrict;
13354
- decrypted.features = featsCfg;
13355
- }
13356
- if (typeof payload["debugStream"] === "boolean")
13357
- decrypted.debugStream = payload["debugStream"];
13358
- if (typeof payload["pluginsEnabled"] === "object" && payload["pluginsEnabled"] !== null) {
13359
- const ext = decrypted.extensions ?? {};
13360
- for (const [pluginName, enabled] of Object.entries(
13361
- payload["pluginsEnabled"]
13362
- )) {
13363
- if (FORBIDDEN_PROTO_KEYS2.has(pluginName)) continue;
13364
- const pExt = ext[pluginName] ?? {};
13365
- pExt["enabled"] = enabled;
13366
- ext[pluginName] = pExt;
13367
- }
13368
- decrypted.extensions = ext;
13369
- }
13370
- const chimeraTouched = typeof payload["chimeraEnabled"] === "boolean" || typeof payload["chimeraProvider"] === "string" || typeof payload["chimeraModel"] === "string" || typeof payload["chimeraMaxFiles"] === "number" || typeof payload["chimeraAutoFix"] === "string";
13371
- if (chimeraTouched) {
13372
- const ext = decrypted.extensions ?? {};
13373
- const chimera = ext["wstack-chimera"] ?? {};
13374
- if (typeof payload["chimeraEnabled"] === "boolean")
13375
- chimera["enabled"] = payload["chimeraEnabled"];
13376
- if (typeof payload["chimeraProvider"] === "string")
13377
- chimera["provider"] = payload["chimeraProvider"];
13378
- if (typeof payload["chimeraModel"] === "string")
13379
- chimera["model"] = payload["chimeraModel"];
13380
- if (typeof payload["chimeraMaxFiles"] === "number" && payload["chimeraMaxFiles"] >= 1) {
13381
- chimera["maxFiles"] = payload["chimeraMaxFiles"];
13382
- }
13383
- if (typeof payload["chimeraAutoFix"] === "string") {
13384
- if (payload["chimeraAutoFix"] === "off" || payload["chimeraAutoFix"] === "ask" || payload["chimeraAutoFix"] === "auto") {
13385
- chimera["autoFix"] = payload["chimeraAutoFix"];
13386
- }
13387
- }
13388
- ext["wstack-chimera"] = chimera;
13389
- decrypted.extensions = ext;
13390
- }
13391
- 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";
13392
- if (autoReviewTouched) {
13393
- const ext = decrypted.extensions ?? {};
13394
- const ar = ext["wstack-auto-review"] ?? {};
13395
- if (typeof payload["autoReviewEnabled"] === "boolean")
13396
- ar["enabled"] = payload["autoReviewEnabled"];
13397
- if (typeof payload["autoReviewProvider"] === "string")
13398
- ar["provider"] = payload["autoReviewProvider"];
13399
- if (typeof payload["autoReviewModel"] === "string")
13400
- ar["model"] = payload["autoReviewModel"];
13401
- if (typeof payload["autoReviewFallbackProfile"] === "string") {
13402
- if (payload["autoReviewFallbackProfile"] === "") {
13403
- delete ar["fallbackProfile"];
13404
- } else {
13405
- ar["fallbackProfile"] = payload["autoReviewFallbackProfile"];
13406
- }
13344
+ return updateGlobalConfig(
13345
+ deps2,
13346
+ holder,
13347
+ (decrypted) => {
13348
+ const autonomyCfg = decrypted.autonomy ?? {};
13349
+ let autonomyTouched = false;
13350
+ const setAutonomy = (key, val) => {
13351
+ autonomyCfg[key] = val;
13352
+ autonomyTouched = true;
13353
+ };
13354
+ if (typeof payload["autonomy"] === "string" && ["off", "suggest", "auto"].includes(payload["autonomy"])) {
13355
+ setAutonomy("defaultMode", payload["autonomy"]);
13407
13356
  }
13408
- if (typeof payload["autoReviewDebounceMs"] === "number" && payload["autoReviewDebounceMs"] >= 0) {
13409
- ar["debounceMs"] = payload["autoReviewDebounceMs"];
13357
+ if (typeof payload["autonomyDelayMs"] === "number")
13358
+ setAutonomy("autoProceedDelayMs", payload["autonomyDelayMs"]);
13359
+ if (typeof payload["autoProceedMaxIterations"] === "number")
13360
+ setAutonomy("autoProceedMaxIterations", payload["autoProceedMaxIterations"]);
13361
+ if (typeof payload["yolo"] === "boolean") {
13362
+ setAutonomy("yolo", payload["yolo"]);
13363
+ decrypted.yolo = payload["yolo"];
13364
+ }
13365
+ if (typeof payload["chime"] === "boolean") setAutonomy("chime", payload["chime"]);
13366
+ if (typeof payload["confirmExit"] === "boolean")
13367
+ setAutonomy("confirmExit", payload["confirmExit"]);
13368
+ if (typeof payload["streamFleet"] === "boolean")
13369
+ setAutonomy("streamFleet", payload["streamFleet"]);
13370
+ if (typeof payload["enhanceEnabled"] === "boolean")
13371
+ setAutonomy("enhance", payload["enhanceEnabled"]);
13372
+ if (typeof payload["enhanceDelayMs"] === "number")
13373
+ setAutonomy("enhanceDelayMs", payload["enhanceDelayMs"]);
13374
+ if (typeof payload["enhanceLanguage"] === "string")
13375
+ setAutonomy("enhanceLanguage", payload["enhanceLanguage"]);
13376
+ if (typeof payload["refinerProvider"] === "string")
13377
+ setAutonomy("refinerProvider", payload["refinerProvider"]);
13378
+ if (typeof payload["refinerModel"] === "string")
13379
+ setAutonomy("refinerModel", payload["refinerModel"]);
13380
+ if (typeof payload["refinerFallbackProfile"] === "string")
13381
+ setAutonomy("refinerFallbackProfile", payload["refinerFallbackProfile"]);
13382
+ if (typeof payload["thinkingWord"] === "string")
13383
+ setAutonomy("thinkingWord", payload["thinkingWord"]);
13384
+ if (typeof payload["statuslineMode"] === "string")
13385
+ setAutonomy("statuslineMode", payload["statuslineMode"]);
13386
+ if (typeof payload["animationStyle"] === "string")
13387
+ setAutonomy("animationStyle", payload["animationStyle"]);
13388
+ if (typeof payload["showModelReasoning"] === "boolean")
13389
+ setAutonomy("showModelReasoning", payload["showModelReasoning"]);
13390
+ if (autonomyTouched) decrypted.autonomy = autonomyCfg;
13391
+ if (typeof payload["nextPrediction"] === "boolean")
13392
+ decrypted.nextPrediction = payload["nextPrediction"];
13393
+ if (typeof payload["uiLocale"] === "string") decrypted.uiLocale = payload["uiLocale"];
13394
+ if (Array.isArray(payload["fallbackModels"]))
13395
+ decrypted.fallbackModels = payload["fallbackModels"];
13396
+ if (payload["fallbackProfiles"] && typeof payload["fallbackProfiles"] === "object" && !Array.isArray(payload["fallbackProfiles"])) {
13397
+ decrypted.fallbackProfiles = payload["fallbackProfiles"];
13410
13398
  }
13411
- if (typeof payload["autoReviewMaxFilesPerBatch"] === "number" && payload["autoReviewMaxFilesPerBatch"] >= 1) {
13412
- ar["maxFilesPerBatch"] = payload["autoReviewMaxFilesPerBatch"];
13399
+ if (Array.isArray(payload["favoriteModels"]))
13400
+ decrypted.favoriteModels = payload["favoriteModels"];
13401
+ if (typeof payload["favoriteModelsOnly"] === "boolean")
13402
+ decrypted.favoriteModelsOnly = payload["favoriteModelsOnly"];
13403
+ if (Array.isArray(payload["modelAvailabilitySchedule"]))
13404
+ decrypted.modelAvailabilitySchedule = payload["modelAvailabilitySchedule"];
13405
+ if (payload["modelMatrix"] && typeof payload["modelMatrix"] === "object" && !Array.isArray(payload["modelMatrix"])) {
13406
+ decrypted.modelMatrix = payload["modelMatrix"];
13407
+ }
13408
+ if (typeof payload["fallbackAuto"] === "boolean")
13409
+ decrypted.fallbackAuto = payload["fallbackAuto"];
13410
+ const FEATURE_MAP = {
13411
+ featureMcp: "mcp",
13412
+ featurePlugins: "plugins",
13413
+ featureMemory: "memory",
13414
+ featureSkills: "skills",
13415
+ featureModelsRegistry: "modelsRegistry"
13416
+ };
13417
+ for (const [prefKey, cfgKey] of Object.entries(FEATURE_MAP)) {
13418
+ if (typeof payload[prefKey] === "boolean") {
13419
+ const feats = decrypted.features ?? {};
13420
+ feats[cfgKey] = payload[prefKey];
13421
+ decrypted.features = feats;
13422
+ }
13423
+ }
13424
+ if (typeof payload["contextAutoCompact"] === "boolean" || typeof payload["contextStrategy"] === "string" || typeof payload["contextMode"] === "string") {
13425
+ const ctxCfg = decrypted.context ?? {};
13426
+ if (typeof payload["contextAutoCompact"] === "boolean")
13427
+ ctxCfg.autoCompact = payload["contextAutoCompact"];
13428
+ if (typeof payload["contextStrategy"] === "string")
13429
+ ctxCfg.strategy = payload["contextStrategy"];
13430
+ if (typeof payload["contextMode"] === "string") ctxCfg.mode = payload["contextMode"];
13431
+ decrypted.context = ctxCfg;
13432
+ }
13433
+ if (typeof payload["tokenSavingTier"] === "string") {
13434
+ const featsCfg = decrypted.features ?? {};
13435
+ featsCfg.tokenSavingMode = payload["tokenSavingTier"];
13436
+ decrypted.features = featsCfg;
13437
+ }
13438
+ if (typeof payload["maxConcurrent"] === "number") {
13439
+ decrypted.maxConcurrent = payload["maxConcurrent"];
13440
+ }
13441
+ if (typeof payload["titleAnimation"] === "boolean") {
13442
+ const autoCfg = decrypted.autonomy ?? {};
13443
+ autoCfg.terminalTitleAnimation = payload["titleAnimation"];
13444
+ decrypted.autonomy = autoCfg;
13413
13445
  }
13414
- if (typeof payload["autoReviewMaxConcurrentReviews"] === "number" && payload["autoReviewMaxConcurrentReviews"] >= 1) {
13415
- ar["maxConcurrentReviews"] = payload["autoReviewMaxConcurrentReviews"];
13446
+ if (typeof payload["logLevel"] === "string") {
13447
+ const logCfg = decrypted.log ?? {};
13448
+ logCfg.level = payload["logLevel"];
13449
+ decrypted.log = logCfg;
13450
+ }
13451
+ if (typeof payload["auditLevel"] === "string") {
13452
+ const sessionCfg = decrypted.session ?? {};
13453
+ sessionCfg.auditLevel = payload["auditLevel"];
13454
+ decrypted.session = sessionCfg;
13455
+ }
13456
+ if (typeof payload["indexOnStart"] === "boolean") {
13457
+ const indexingCfg = decrypted.indexing ?? {};
13458
+ indexingCfg.onSessionStart = payload["indexOnStart"];
13459
+ decrypted.indexing = indexingCfg;
13460
+ }
13461
+ if (typeof payload["maxIterations"] === "number") {
13462
+ const toolsCfg = decrypted.tools ?? {};
13463
+ toolsCfg.maxIterations = payload["maxIterations"];
13464
+ decrypted.tools = toolsCfg;
13465
+ }
13466
+ const hqTouched = typeof payload["hqEnabled"] === "boolean" || typeof payload["hqUrl"] === "string" || typeof payload["hqToken"] === "string" || typeof payload["hqRawContent"] === "boolean";
13467
+ if (hqTouched) {
13468
+ const hqCfg = decrypted.hq ?? {};
13469
+ if (typeof payload["hqEnabled"] === "boolean") hqCfg.enabled = payload["hqEnabled"];
13470
+ if (typeof payload["hqUrl"] === "string") hqCfg.url = payload["hqUrl"];
13471
+ if (typeof payload["hqToken"] === "string") hqCfg.token = payload["hqToken"];
13472
+ if (typeof payload["hqRawContent"] === "boolean")
13473
+ hqCfg.rawContent = payload["hqRawContent"];
13474
+ decrypted.hq = hqCfg;
13475
+ }
13476
+ const tgTouched = typeof payload["tgSessionEnd"] === "boolean" || typeof payload["tgDelegate"] === "boolean" || typeof payload["tgLongToolMs"] === "number";
13477
+ if (tgTouched) {
13478
+ const ext = decrypted.extensions ?? {};
13479
+ const tg = ext["telegram"] ?? {};
13480
+ if (typeof payload["tgSessionEnd"] === "boolean") {
13481
+ tg["notifyOnSessionEnd"] = payload["tgSessionEnd"];
13482
+ }
13483
+ if (typeof payload["tgDelegate"] === "boolean") {
13484
+ tg["notifyOnDelegate"] = payload["tgDelegate"];
13485
+ }
13486
+ if (typeof payload["tgLongToolMs"] === "number") {
13487
+ tg["longToolThresholdMs"] = payload["tgLongToolMs"];
13488
+ }
13489
+ ext["telegram"] = tg;
13490
+ decrypted.extensions = ext;
13491
+ }
13492
+ const modelRuntimeTouched = typeof payload["reasoningMode"] === "string" || typeof payload["reasoningEffort"] === "string" || typeof payload["reasoningPreserve"] === "boolean" || typeof payload["cacheTtl"] === "string";
13493
+ if (modelRuntimeTouched) {
13494
+ const mr = decrypted.modelRuntime ?? {};
13495
+ const reasoning = mr.reasoning ?? {};
13496
+ if (typeof payload["reasoningMode"] === "string") reasoning.mode = payload["reasoningMode"];
13497
+ if (typeof payload["reasoningEffort"] === "string")
13498
+ reasoning.effort = payload["reasoningEffort"];
13499
+ if (typeof payload["reasoningPreserve"] === "boolean")
13500
+ reasoning.preserve = payload["reasoningPreserve"];
13501
+ mr.reasoning = reasoning;
13502
+ if (typeof payload["cacheTtl"] === "string" && payload["cacheTtl"] !== "default") {
13503
+ mr.cache = { ttl: payload["cacheTtl"] };
13504
+ } else if (payload["cacheTtl"] === "default") {
13505
+ delete mr.cache;
13506
+ }
13507
+ decrypted.modelRuntime = mr;
13416
13508
  }
13417
- if (typeof payload["autoReviewCascadeOn"] === "string") {
13418
- if (payload["autoReviewCascadeOn"] === "off" || payload["autoReviewCascadeOn"] === "critical" || payload["autoReviewCascadeOn"] === "high") {
13419
- ar["cascadeOn"] = payload["autoReviewCascadeOn"];
13509
+ if (typeof payload["breakerEnabled"] === "boolean" || typeof payload["breakerAutoKillResetMs"] === "number") {
13510
+ const cb = decrypted.circuitBreaker ?? {};
13511
+ if (typeof payload["breakerEnabled"] === "boolean") cb.enabled = payload["breakerEnabled"];
13512
+ if (typeof payload["breakerAutoKillResetMs"] === "number")
13513
+ cb.autoKillResetMs = payload["breakerAutoKillResetMs"];
13514
+ decrypted.circuitBreaker = cb;
13515
+ }
13516
+ if (payload["fsAccess"] === "unrestricted" || payload["fsAccess"] === "project") {
13517
+ const restrict = payload["fsAccess"] === "project";
13518
+ const toolsCfg = decrypted.tools ?? {};
13519
+ toolsCfg.restrictToProjectRoot = restrict;
13520
+ decrypted.tools = toolsCfg;
13521
+ const featsCfg = decrypted.features ?? {};
13522
+ featsCfg.allowOutsideProjectRoot = !restrict;
13523
+ decrypted.features = featsCfg;
13524
+ }
13525
+ if (typeof payload["debugStream"] === "boolean")
13526
+ decrypted.debugStream = payload["debugStream"];
13527
+ if (typeof payload["pluginsEnabled"] === "object" && payload["pluginsEnabled"] !== null) {
13528
+ const ext = decrypted.extensions ?? {};
13529
+ for (const [pluginName, enabled] of Object.entries(
13530
+ payload["pluginsEnabled"]
13531
+ )) {
13532
+ if (FORBIDDEN_PROTO_KEYS2.has(pluginName)) continue;
13533
+ const pExt = ext[pluginName] ?? {};
13534
+ pExt["enabled"] = enabled;
13535
+ ext[pluginName] = pExt;
13536
+ }
13537
+ decrypted.extensions = ext;
13538
+ }
13539
+ const chimeraTouched = typeof payload["chimeraEnabled"] === "boolean" || typeof payload["chimeraProvider"] === "string" || typeof payload["chimeraModel"] === "string" || typeof payload["chimeraMaxFiles"] === "number" || typeof payload["chimeraAutoFix"] === "string";
13540
+ if (chimeraTouched) {
13541
+ const ext = decrypted.extensions ?? {};
13542
+ const chimera = ext["wstack-chimera"] ?? {};
13543
+ if (typeof payload["chimeraEnabled"] === "boolean")
13544
+ chimera["enabled"] = payload["chimeraEnabled"];
13545
+ if (typeof payload["chimeraProvider"] === "string")
13546
+ chimera["provider"] = payload["chimeraProvider"];
13547
+ if (typeof payload["chimeraModel"] === "string") chimera["model"] = payload["chimeraModel"];
13548
+ if (typeof payload["chimeraMaxFiles"] === "number" && payload["chimeraMaxFiles"] >= 1) {
13549
+ chimera["maxFiles"] = payload["chimeraMaxFiles"];
13550
+ }
13551
+ if (typeof payload["chimeraAutoFix"] === "string") {
13552
+ if (payload["chimeraAutoFix"] === "off" || payload["chimeraAutoFix"] === "ask" || payload["chimeraAutoFix"] === "auto") {
13553
+ chimera["autoFix"] = payload["chimeraAutoFix"];
13554
+ }
13555
+ }
13556
+ ext["wstack-chimera"] = chimera;
13557
+ decrypted.extensions = ext;
13558
+ }
13559
+ 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";
13560
+ if (autoReviewTouched) {
13561
+ const ext = decrypted.extensions ?? {};
13562
+ const ar = ext["wstack-auto-review"] ?? {};
13563
+ if (typeof payload["autoReviewEnabled"] === "boolean")
13564
+ ar["enabled"] = payload["autoReviewEnabled"];
13565
+ if (typeof payload["autoReviewProvider"] === "string")
13566
+ ar["provider"] = payload["autoReviewProvider"];
13567
+ if (typeof payload["autoReviewModel"] === "string")
13568
+ ar["model"] = payload["autoReviewModel"];
13569
+ if (typeof payload["autoReviewFallbackProfile"] === "string") {
13570
+ if (payload["autoReviewFallbackProfile"] === "") {
13571
+ delete ar["fallbackProfile"];
13572
+ } else {
13573
+ ar["fallbackProfile"] = payload["autoReviewFallbackProfile"];
13574
+ }
13575
+ }
13576
+ if (typeof payload["autoReviewDebounceMs"] === "number" && payload["autoReviewDebounceMs"] >= 0) {
13577
+ ar["debounceMs"] = payload["autoReviewDebounceMs"];
13578
+ }
13579
+ if (typeof payload["autoReviewMaxFilesPerBatch"] === "number" && payload["autoReviewMaxFilesPerBatch"] >= 1) {
13580
+ ar["maxFilesPerBatch"] = payload["autoReviewMaxFilesPerBatch"];
13581
+ }
13582
+ if (typeof payload["autoReviewMaxConcurrentReviews"] === "number" && payload["autoReviewMaxConcurrentReviews"] >= 1) {
13583
+ ar["maxConcurrentReviews"] = payload["autoReviewMaxConcurrentReviews"];
13584
+ }
13585
+ if (typeof payload["autoReviewCascadeOn"] === "string") {
13586
+ if (payload["autoReviewCascadeOn"] === "off" || payload["autoReviewCascadeOn"] === "critical" || payload["autoReviewCascadeOn"] === "high") {
13587
+ ar["cascadeOn"] = payload["autoReviewCascadeOn"];
13588
+ }
13420
13589
  }
13590
+ ext["wstack-auto-review"] = ar;
13591
+ decrypted.extensions = ext;
13421
13592
  }
13422
- ext["wstack-auto-review"] = ar;
13423
- decrypted.extensions = ext;
13424
- }
13425
- }, "prefs");
13593
+ },
13594
+ "prefs"
13595
+ );
13426
13596
  }
13427
13597
 
13428
13598
  // src/server/provider-handlers.ts
@@ -13472,13 +13642,13 @@ async function probeModelDescriptors(cfg) {
13472
13642
  }
13473
13643
  }
13474
13644
  function createProviderHandlers(deps2) {
13475
- const { globalConfigPath, vault, broadcast: broadcast2, clients } = deps2;
13645
+ const { globalConfigPath, profileConfigPath, vault, broadcast: broadcast2, clients } = deps2;
13476
13646
  let configWriteLock = deps2.getConfigWriteLock();
13477
13647
  async function loadConfigProviders() {
13478
13648
  return loadSavedProviders(globalConfigPath, vault);
13479
13649
  }
13480
13650
  async function saveConfigProviders(providers) {
13481
- const next = configWriteLock.then(() => saveProviders(globalConfigPath, vault, providers)).catch((err) => {
13651
+ const next = configWriteLock.then(() => saveProviders(globalConfigPath, vault, providers, profileConfigPath)).catch((err) => {
13482
13652
  const msg = toErrorMessage9(err);
13483
13653
  console.error(JSON.stringify({
13484
13654
  level: "error",
@@ -13753,13 +13923,14 @@ function createProviderHandlers(deps2) {
13753
13923
  }
13754
13924
 
13755
13925
  // src/server/routes.ts
13756
- import path22 from "node:path";
13926
+ import path23 from "node:path";
13757
13927
  import {
13758
13928
  buildRefinerContextSections,
13759
13929
  enhanceUserPrompt,
13760
13930
  gatedEnhancerReasoning,
13761
13931
  nextEnhanceTimeout,
13762
13932
  recentTextTurns,
13933
+ resolveConfiguredRefinerRef,
13763
13934
  resolveEnhanceFallbackRef,
13764
13935
  resolveProviderModelList
13765
13936
  } from "@wrongstack/core";
@@ -13963,7 +14134,7 @@ function createModeHandlers(ctx) {
13963
14134
  }
13964
14135
 
13965
14136
  // src/server/project-handlers.ts
13966
- import * as path21 from "node:path";
14137
+ import * as path22 from "node:path";
13967
14138
  function createProjectHandlers(ctx) {
13968
14139
  return {
13969
14140
  listProjects: async (ws) => {
@@ -13989,7 +14160,7 @@ function createProjectHandlers(ctx) {
13989
14160
  selectProject: async (ws, msg) => {
13990
14161
  const payload = msg.payload;
13991
14162
  const root = typeof payload?.root === "string" ? payload.root : "";
13992
- const name2 = typeof payload?.name === "string" ? payload.name : root ? path21.basename(root) : "";
14163
+ const name2 = typeof payload?.name === "string" ? payload.name : root ? path22.basename(root) : "";
13993
14164
  send(ws, {
13994
14165
  type: "projects.selected",
13995
14166
  payload: {
@@ -14443,7 +14614,9 @@ async function enrichProviderModelDescriptors(modelsRegistry, providerId, cfg, m
14443
14614
  if (resolved.capabilities.vision) capabilities.add("vision");
14444
14615
  return {
14445
14616
  ...model,
14446
- contextWindow: model.contextWindow || resolved.capabilities.maxContext || void 0,
14617
+ contextWindow: model.contextWindow ?? resolved.capabilities.maxContext ?? void 0,
14618
+ inputCost: model.inputCost ?? resolved.cost?.input,
14619
+ outputCost: model.outputCost ?? resolved.cost?.output,
14447
14620
  capabilities: [...capabilities]
14448
14621
  };
14449
14622
  })
@@ -14452,6 +14625,7 @@ async function enrichProviderModelDescriptors(modelsRegistry, providerId, cfg, m
14452
14625
  function buildRoutes(state, deps2, cb) {
14453
14626
  const providerHandlers = createProviderHandlers({
14454
14627
  globalConfigPath: deps2.globalConfigPath,
14628
+ profileConfigPath: deps2.profileConfigPath,
14455
14629
  vault: deps2.vault,
14456
14630
  getConfigWriteLock: state.getConfigWriteLock,
14457
14631
  setConfigWriteLock: state.setConfigWriteLock,
@@ -14622,6 +14796,26 @@ function buildRoutes(state, deps2, cb) {
14622
14796
  });
14623
14797
  return;
14624
14798
  }
14799
+ } else {
14800
+ const configuredRef = resolveConfiguredRefinerRef({
14801
+ ...cfg,
14802
+ provider: providerId,
14803
+ model
14804
+ });
14805
+ if (configuredRef) {
14806
+ const slash = configuredRef.indexOf("/");
14807
+ const configuredProvider = slash > 0 ? configuredRef.slice(0, slash) : providerId;
14808
+ const configuredModel = slash > 0 ? configuredRef.slice(slash + 1) : configuredRef;
14809
+ try {
14810
+ const providerCfg = cfg.providers?.[configuredProvider] ?? {
14811
+ type: configuredProvider
14812
+ };
14813
+ provider = deps2.providerRegistry.has(configuredProvider) ? deps2.providerRegistry.create({ ...providerCfg, type: configuredProvider }) : makeProviderFromConfig2(configuredProvider, providerCfg);
14814
+ providerId = configuredProvider;
14815
+ model = configuredModel;
14816
+ } catch {
14817
+ }
14818
+ }
14625
14819
  }
14626
14820
  const baseTimeout = 9e4;
14627
14821
  const timeoutMs = typeof payload.timeoutMs === "number" && payload.timeoutMs > 0 ? payload.timeoutMs : baseTimeout;
@@ -14808,19 +15002,27 @@ function buildRoutes(state, deps2, cb) {
14808
15002
  cfg.favoriteModels = payload["favoriteModels"];
14809
15003
  if (typeof payload["favoriteModelsOnly"] === "boolean")
14810
15004
  cfg.favoriteModelsOnly = payload["favoriteModelsOnly"];
15005
+ if (Array.isArray(payload["modelAvailabilitySchedule"]))
15006
+ cfg.modelAvailabilitySchedule = payload["modelAvailabilitySchedule"];
14811
15007
  if (payload["modelMatrix"] && typeof payload["modelMatrix"] === "object" && !Array.isArray(payload["modelMatrix"])) {
14812
15008
  cfg.modelMatrix = payload["modelMatrix"];
14813
15009
  }
14814
15010
  if (typeof payload["fallbackAuto"] === "boolean") cfg.fallbackAuto = payload["fallbackAuto"];
14815
15011
  const routingPatch = {};
14816
- if (Array.isArray(payload["fallbackModels"])) routingPatch.fallbackModels = payload["fallbackModels"];
15012
+ if (Array.isArray(payload["fallbackModels"]))
15013
+ routingPatch.fallbackModels = payload["fallbackModels"];
14817
15014
  if (payload["fallbackProfiles"] && typeof payload["fallbackProfiles"] === "object" && !Array.isArray(payload["fallbackProfiles"]))
14818
15015
  routingPatch.fallbackProfiles = payload["fallbackProfiles"];
14819
- if (Array.isArray(payload["favoriteModels"])) routingPatch.favoriteModels = payload["favoriteModels"];
14820
- if (typeof payload["favoriteModelsOnly"] === "boolean") routingPatch.favoriteModelsOnly = payload["favoriteModelsOnly"];
15016
+ if (Array.isArray(payload["favoriteModels"]))
15017
+ routingPatch.favoriteModels = payload["favoriteModels"];
15018
+ if (typeof payload["favoriteModelsOnly"] === "boolean")
15019
+ routingPatch.favoriteModelsOnly = payload["favoriteModelsOnly"];
15020
+ if (Array.isArray(payload["modelAvailabilitySchedule"]))
15021
+ routingPatch.modelAvailabilitySchedule = payload["modelAvailabilitySchedule"];
14821
15022
  if (payload["modelMatrix"] && typeof payload["modelMatrix"] === "object" && !Array.isArray(payload["modelMatrix"]))
14822
15023
  routingPatch.modelMatrix = payload["modelMatrix"];
14823
- if (typeof payload["fallbackAuto"] === "boolean") routingPatch.fallbackAuto = payload["fallbackAuto"];
15024
+ if (typeof payload["fallbackAuto"] === "boolean")
15025
+ routingPatch.fallbackAuto = payload["fallbackAuto"];
14824
15026
  if (Object.keys(routingPatch).length > 0)
14825
15027
  deps2.configStore.update(routingPatch);
14826
15028
  if (typeof payload["contextAutoCompact"] === "boolean") {
@@ -14891,7 +15093,7 @@ function buildRoutes(state, deps2, cb) {
14891
15093
  }
14892
15094
  return handleMailboxMessages(
14893
15095
  ws,
14894
- { projectRoot: state.getProjectRoot(), globalRoot: path22.dirname(deps2.globalConfigPath) },
15096
+ { projectRoot: state.getProjectRoot(), globalRoot: path23.dirname(deps2.globalConfigPath) },
14895
15097
  parsed.value
14896
15098
  );
14897
15099
  },
@@ -14903,13 +15105,13 @@ function buildRoutes(state, deps2, cb) {
14903
15105
  }
14904
15106
  return handleMailboxAgents(
14905
15107
  ws,
14906
- { projectRoot: state.getProjectRoot(), globalRoot: path22.dirname(deps2.globalConfigPath) },
15108
+ { projectRoot: state.getProjectRoot(), globalRoot: path23.dirname(deps2.globalConfigPath) },
14907
15109
  parsed.value
14908
15110
  );
14909
15111
  },
14910
15112
  clear: (ws) => handleMailboxClear(ws, {
14911
15113
  projectRoot: state.getProjectRoot(),
14912
- globalRoot: path22.dirname(deps2.globalConfigPath)
15114
+ globalRoot: path23.dirname(deps2.globalConfigPath)
14913
15115
  }),
14914
15116
  purge: (ws, msg) => {
14915
15117
  const parsed = validateMailboxPurgePayload(msg.payload);
@@ -14919,14 +15121,14 @@ function buildRoutes(state, deps2, cb) {
14919
15121
  }
14920
15122
  return handleMailboxPurge(
14921
15123
  ws,
14922
- { projectRoot: state.getProjectRoot(), globalRoot: path22.dirname(deps2.globalConfigPath) },
15124
+ { projectRoot: state.getProjectRoot(), globalRoot: path23.dirname(deps2.globalConfigPath) },
14923
15125
  parsed.value
14924
15126
  );
14925
15127
  },
14926
15128
  compact: (ws, msg) => {
14927
15129
  return handleMailboxCompact(
14928
15130
  ws,
14929
- { projectRoot: state.getProjectRoot(), globalRoot: path22.dirname(deps2.globalConfigPath) },
15131
+ { projectRoot: state.getProjectRoot(), globalRoot: path23.dirname(deps2.globalConfigPath) },
14930
15132
  msg.payload ?? {}
14931
15133
  );
14932
15134
  }
@@ -15077,7 +15279,9 @@ async function startWebUI(opts = {}) {
15077
15279
  let projectRoot = boot.projectRoot;
15078
15280
  let workingDir = projectRoot;
15079
15281
  const configWriteLock = { lock: Promise.resolve() };
15080
- const prefHelperDeps = { globalConfigPath, vault, logger };
15282
+ const activeProfile = config.activeProfile ?? "default";
15283
+ const profileConfigPath = wpaths.profileConfig(activeProfile);
15284
+ const prefHelperDeps = { globalConfigPath, profileConfigPath, vault, logger };
15081
15285
  const updateGlobalConfig2 = async (mutate, errorLabel) => updateGlobalConfig(prefHelperDeps, configWriteLock, mutate, errorLabel);
15082
15286
  console.log("[WebUI] Config loaded:", config.provider ?? "(none)", "/", config.model ?? "(none)");
15083
15287
  if (!config.provider && config.providers && typeof config.providers === "object" && config.providers !== null && !Array.isArray(config.providers) && Object.keys(config.providers).length > 0) {
@@ -15239,21 +15443,21 @@ async function startWebUI(opts = {}) {
15239
15443
  wpaths
15240
15444
  }, watcherMetricsRef);
15241
15445
  async function touchProjectEntry(root, workDir) {
15242
- const resolved = path23.resolve(root);
15446
+ const resolved = path24.resolve(root);
15243
15447
  const manifest = await loadManifest(globalConfigPath);
15244
15448
  const now = (/* @__PURE__ */ new Date()).toISOString();
15245
- const existing = manifest.projects.find((p) => path23.resolve(p.root) === resolved);
15449
+ const existing = manifest.projects.find((p) => path24.resolve(p.root) === resolved);
15246
15450
  if (existing) {
15247
15451
  existing.lastSeen = now;
15248
- if (workDir) existing.lastWorkingDir = path23.resolve(workDir);
15452
+ if (workDir) existing.lastWorkingDir = path24.resolve(workDir);
15249
15453
  } else {
15250
15454
  manifest.projects.push({
15251
- name: path23.basename(resolved),
15455
+ name: path24.basename(resolved),
15252
15456
  root: resolved,
15253
15457
  slug: generateProjectSlug(resolved),
15254
15458
  createdAt: now,
15255
15459
  lastSeen: now,
15256
- lastWorkingDir: workDir ? path23.resolve(workDir) : void 0
15460
+ lastWorkingDir: workDir ? path24.resolve(workDir) : void 0
15257
15461
  });
15258
15462
  }
15259
15463
  await saveManifest(manifest, globalConfigPath);
@@ -15360,6 +15564,7 @@ async function startWebUI(opts = {}) {
15360
15564
  persistPrefsToConfig: persistPrefsToConfig2,
15361
15565
  prefSnapshot: prefSnapshot2
15362
15566
  };
15567
+ const watchConfigPath = profileConfigPath ?? globalConfigPath;
15363
15568
  let credentialWatcherClose;
15364
15569
  if (process.env["WRONGSTACK_DISABLE_CONFIG_WATCH"] !== "1") {
15365
15570
  let lastActiveCfg = JSON.stringify(
@@ -15367,7 +15572,7 @@ async function startWebUI(opts = {}) {
15367
15572
  );
15368
15573
  let lastUiLocale = state.getConfig().uiLocale;
15369
15574
  const credentialWatcher = watchProviderConfig(
15370
- globalConfigPath,
15575
+ watchConfigPath,
15371
15576
  vault,
15372
15577
  (snapshot) => {
15373
15578
  state.setConfig(
@@ -15508,7 +15713,7 @@ async function startWebUI(opts = {}) {
15508
15713
  archiveLowConfidenceAfterDays: config.superMemory?.hygiene?.archiveLowConfidenceAfterDays
15509
15714
  }).catch((err) => logger.warn(`super-memory session hygiene failed: ${toErrorMessage10(err)}`));
15510
15715
  }
15511
- await unregisterInstance(process.pid, path23.dirname(globalConfigPath));
15716
+ await unregisterInstance(process.pid, path24.dirname(globalConfigPath));
15512
15717
  }
15513
15718
  });
15514
15719
  }
@@ -15719,6 +15924,7 @@ export {
15719
15924
  handleSuperMemoryForFile,
15720
15925
  handleSuperMemoryGet,
15721
15926
  handleSuperMemoryList,
15927
+ handleSuperMemoryListPage,
15722
15928
  handleSuperMemoryRecover,
15723
15929
  handleSuperMemoryRemember,
15724
15930
  handleSuperMemoryUpdate,
@@ -15744,6 +15950,7 @@ export {
15744
15950
  normalizeCodeMapFileTarget,
15745
15951
  normalizeKeys,
15746
15952
  openBrowser,
15953
+ paginateKanbanBoards,
15747
15954
  patchConfig,
15748
15955
  persistPrefsToConfig,
15749
15956
  projectSavedProviders,