@wrongstack/cli 0.306.4 → 0.307.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 (73) hide show
  1. package/dist/{auth-L3WGSGDX.js → auth-TVOOHL4Q.js} +45 -106
  2. package/dist/boot/short-circuit-flags.d.ts +2 -2
  3. package/dist/chimera-cascade-evidence.d.ts +1 -1
  4. package/dist/chimera-reviewer-policy.d.ts +1 -1
  5. package/dist/chunk-2VMJ3E5Z.js +71 -0
  6. package/dist/{chunk-RHLQT3EV.js → chunk-CB35UVXO.js} +1636 -1528
  7. package/dist/{chunk-YKBRFSGL.js → chunk-DOJLAULS.js} +28 -28
  8. package/dist/{chunk-KXN5HZDL.js → chunk-PKHGLWUJ.js} +24 -6
  9. package/dist/{chunk-HITHEXE3.js → chunk-TOG7OMKJ.js} +2 -2
  10. package/dist/{chunk-ARWGQUB3.js → chunk-XSG33WHN.js} +15 -5
  11. package/dist/{cli-main-4TZCCZR6.js → cli-main-DD3I7KOO.js} +29198 -28411
  12. package/dist/cli-main.d.ts +0 -20
  13. package/dist/{execution-547PM3NI.js → execution-4ASWJ7EY.js} +309 -273
  14. package/dist/fleet/host-learning-scheduler.d.ts +27 -0
  15. package/dist/fleet/host-shadow-manager.d.ts +64 -0
  16. package/dist/fleet/host.d.ts +5 -140
  17. package/dist/{hq-ZK6PGN3W.js → hq-PA6RH2UW.js} +2 -2
  18. package/dist/hq-server/mailbox-gateway-manager.d.ts +32 -0
  19. package/dist/hq-server/routes/auth/bootstrap-routes.d.ts +5 -0
  20. package/dist/hq-server/routes/auth/common.d.ts +11 -0
  21. package/dist/hq-server/routes/auth/password-routes.d.ts +9 -0
  22. package/dist/hq-server/routes/auth/session-audit-routes.d.ts +6 -0
  23. package/dist/hq-server/routes/auth/totp-routes.d.ts +9 -0
  24. package/dist/hq-server/routes/auth-handlers.d.ts +5 -132
  25. package/dist/hq-server/routes/command-handlers.d.ts +7 -0
  26. package/dist/hq-server/routes/data-handlers.d.ts +8 -0
  27. package/dist/hq-server/routes/mailbox-handlers.d.ts +22 -0
  28. package/dist/hq-server/routes.d.ts +3 -3
  29. package/dist/hq-server/server-lifecycle.d.ts +37 -0
  30. package/dist/hq-server/snapshot.d.ts +6 -5
  31. package/dist/hq-server/types.d.ts +4 -0
  32. package/dist/hq-server/upgrade-handler.d.ts +34 -0
  33. package/dist/hq-server/ws-client-events.d.ts +25 -0
  34. package/dist/hq-server/ws-leader.d.ts +47 -0
  35. package/dist/hq-server/ws-resume.d.ts +27 -0
  36. package/dist/hq-server/ws.d.ts +4 -71
  37. package/dist/{hq-server-IVZLVCFV.js → hq-server-PBRU275L.js} +2 -2
  38. package/dist/hq-server.d.ts +0 -38
  39. package/dist/index.js +1267 -122
  40. package/dist/{mcp-W7HHN7IA.js → mcp-CMLFHRG2.js} +14 -7
  41. package/dist/mcp-serve.d.ts +12 -1
  42. package/dist/{modeldiag-6VS5KDP5.js → modeldiag-ICMPHT5L.js} +868 -831
  43. package/dist/{providers-models-HQN3AT3K.js → providers-models-4FAKM2WO.js} +4 -4
  44. package/dist/repl-eternal-loop.d.ts +8 -0
  45. package/dist/repl-goal-banner.d.ts +21 -0
  46. package/dist/slash-commands/brain-council.d.ts +36 -0
  47. package/dist/slash-commands/brain-status.d.ts +12 -0
  48. package/dist/slash-commands/kanban-board-handlers.d.ts +13 -0
  49. package/dist/slash-commands/kanban-lifecycle-diagnostics.d.ts +16 -0
  50. package/dist/slash-commands/kanban-task-subcommands.d.ts +8 -0
  51. package/dist/slash-commands/memory-audience.d.ts +14 -0
  52. package/dist/slash-commands/memory-gather.d.ts +9 -0
  53. package/dist/slash-commands/memory-stats.d.ts +5 -0
  54. package/dist/slash-commands/settings-mutations.d.ts +6 -0
  55. package/dist/slash-commands/settings-view.d.ts +5 -0
  56. package/dist/subcommands/handlers/modeldiag-bench.d.ts +10 -0
  57. package/dist/subcommands/handlers/modeldiag-eval.d.ts +27 -0
  58. package/dist/subcommands/handlers/modeldiag-test.d.ts +8 -0
  59. package/dist/{version-help-ASKQYXDJ.js → version-help-TFD6VPF3.js} +2 -2
  60. package/dist/vibe-protocol-wiring.d.ts +2 -0
  61. package/dist/webui-server/credential-watcher.d.ts +15 -0
  62. package/dist/webui-server/domain-handlers.d.ts +21 -0
  63. package/dist/webui-server/route-contexts.d.ts +41 -0
  64. package/dist/{webui-server-FTG7JAZU.js → webui-server-IPHGJGXE.js} +515 -437
  65. package/dist/wiring/cli-execute-builder.d.ts +88 -0
  66. package/dist/wiring/cli-heap-watchdog.d.ts +33 -0
  67. package/dist/wiring/cli-prompt-and-tools-setup.d.ts +25 -0
  68. package/dist/wiring/cli-slash-commands-setup.d.ts +79 -0
  69. package/dist/wiring/heap-watchdog-setup.d.ts +53 -0
  70. package/dist/wiring/metrics.d.ts +1 -0
  71. package/dist/wiring/prompt-journal-recorder.d.ts +45 -0
  72. package/dist/wiring/replay-governance-setup.d.ts +31 -0
  73. package/package.json +27 -27
@@ -18,36 +18,22 @@ import "./chunk-7OCVIDC7.js";
18
18
 
19
19
  // src/webui-server.ts
20
20
  import { findPackageJSON } from "node:module";
21
- import * as os from "node:os";
22
- import * as path5 from "node:path";
23
- import { TOKENS } from "@wrongstack/core/kernel";
21
+ import * as path7 from "node:path";
24
22
  import { createCompatibilityTrustBoundary, DefaultSecretScrubber } from "@wrongstack/core/security";
25
- import { SkillInstaller } from "@wrongstack/core/skills";
26
- import { PromptUsageStore, watchProviderConfig } from "@wrongstack/core/storage";
27
23
  import {
28
- resolveWstackPaths as resolveWstackPaths2,
29
24
  startSharedHeapWatchdog,
30
25
  wstackGlobalRoot
31
26
  } from "@wrongstack/core/utils";
32
- import { toErrorMessage } from "@wrongstack/core/utils/error";
33
- import { makeProviderFromConfig } from "@wrongstack/providers";
27
+ import { toErrorMessage as toErrorMessage2 } from "@wrongstack/core/utils/error";
34
28
  import {
35
- buildSddWizardDeps,
36
29
  buildWebUIAccessUrl,
37
30
  createCustomModeStore,
38
31
  createEmbeddedMessageRouter,
39
32
  createEmbeddedProviderOperations,
40
- createMailboxRouteHandlers,
41
33
  envFlag,
42
34
  findFreePort,
43
- GoalWebSocketHandler,
44
35
  resolveAuthToken,
45
- SddBoardWebSocketHandler,
46
- SddWizardWebSocketHandler,
47
- SpecsWebSocketHandler,
48
- sendSerialized,
49
- TerminalWebSocketHandler,
50
- WorktreeWebSocketHandler
36
+ sendSerialized
51
37
  } from "@wrongstack/webui-server";
52
38
  import { WebSocketServer } from "ws";
53
39
 
@@ -177,9 +163,310 @@ function createConnectionHandler(deps) {
177
163
  });
178
164
  }
179
165
 
166
+ // src/webui-server/credential-watcher.ts
167
+ import { TOKENS } from "@wrongstack/core/kernel";
168
+ import { watchProviderConfig } from "@wrongstack/core/storage";
169
+ import { toErrorMessage } from "@wrongstack/core/utils/error";
170
+ import { makeProviderFromConfig } from "@wrongstack/providers";
171
+
172
+ // src/webui-server/provider-config.ts
173
+ import * as path from "node:path";
174
+ import { DefaultSecretVault } from "@wrongstack/core/security";
175
+ function getVault(globalConfigPath) {
176
+ const configDir = path.dirname(globalConfigPath ?? "");
177
+ const parentDir = path.dirname(configDir);
178
+ const globalRoot = path.basename(parentDir) === "profiles" ? path.dirname(parentDir) : configDir;
179
+ const keyFile = path.join(globalRoot, ".key");
180
+ return new DefaultSecretVault({ keyFile });
181
+ }
182
+ async function loadSavedProviders(globalConfigPath) {
183
+ if (!globalConfigPath) return {};
184
+ return loadConfigProviders(globalConfigPath, getVault(globalConfigPath));
185
+ }
186
+ async function saveProviders(globalConfigPath, providers) {
187
+ if (!globalConfigPath) return;
188
+ await mutateConfigProviders(
189
+ globalConfigPath,
190
+ getVault(globalConfigPath),
191
+ (existing) => {
192
+ for (const key of Object.keys(existing)) delete existing[key];
193
+ Object.assign(existing, providers);
194
+ }
195
+ );
196
+ }
197
+ function createProviderConfigStore(globalConfigPath, configProvidersRef) {
198
+ return {
199
+ load: () => configProvidersRef ? Promise.resolve(configProvidersRef()) : loadSavedProviders(globalConfigPath),
200
+ save: (providers) => saveProviders(globalConfigPath, providers)
201
+ };
202
+ }
203
+
204
+ // src/webui-server/credential-watcher.ts
205
+ function startWebuiCredentialWatcher({
206
+ opts,
207
+ profileConfigPath,
208
+ broadcast,
209
+ broadcastSaved
210
+ }) {
211
+ const watchConfigPath = profileConfigPath;
212
+ if (!watchConfigPath || process.env["WRONGSTACK_DISABLE_CONFIG_WATCH"] === "1") {
213
+ return void 0;
214
+ }
215
+ let lastActiveCfg = JSON.stringify(
216
+ opts.appConfig?.providers?.[opts.agent.ctx.provider.id] ?? null
217
+ );
218
+ let lastUiLocale = opts.appConfig?.uiLocale;
219
+ const watcher = watchProviderConfig(
220
+ watchConfigPath,
221
+ // Vault key lives at ~/.wrongstack/.key (derived from globalConfigPath),
222
+ // not inside the profile directory — resolve via global root to stay correct.
223
+ getVault(opts.globalConfigPath),
224
+ (snapshot) => {
225
+ const hadFallbackBridge = Boolean(opts.appConfig?.fallbackBridge?.trim());
226
+ try {
227
+ if (opts.appConfig && !Object.isFrozen(opts.appConfig)) {
228
+ opts.appConfig.providers = snapshot.providers;
229
+ if (snapshot.uiLocale) opts.appConfig.uiLocale = snapshot.uiLocale;
230
+ if (snapshot.fallbackBridge !== void 0) {
231
+ opts.appConfig.fallbackBridge = snapshot.fallbackBridge;
232
+ } else {
233
+ delete opts.appConfig.fallbackBridge;
234
+ }
235
+ }
236
+ } catch {
237
+ }
238
+ const routingChanged = snapshot.fallbackModels !== void 0 || snapshot.fallbackBridge !== void 0 || hadFallbackBridge || snapshot.fallbackProfiles !== void 0 || snapshot.favoriteModels !== void 0 || snapshot.favoriteModelsOnly !== void 0 || snapshot.modelMatrix !== void 0 || snapshot.fallbackAuto !== void 0;
239
+ if (routingChanged) {
240
+ const configStore = opts.agent.container?.safeResolve?.(TOKENS.ConfigStore);
241
+ configStore?.update({
242
+ ...snapshot.fallbackModels !== void 0 ? { fallbackModels: snapshot.fallbackModels } : {},
243
+ fallbackBridge: snapshot.fallbackBridge ?? "",
244
+ ...snapshot.fallbackProfiles !== void 0 ? { fallbackProfiles: snapshot.fallbackProfiles } : {},
245
+ ...snapshot.favoriteModels !== void 0 ? { favoriteModels: snapshot.favoriteModels } : {},
246
+ ...snapshot.favoriteModelsOnly !== void 0 ? { favoriteModelsOnly: snapshot.favoriteModelsOnly } : {},
247
+ ...snapshot.modelMatrix !== void 0 ? { modelMatrix: snapshot.modelMatrix } : {},
248
+ ...snapshot.fallbackAuto !== void 0 ? { fallbackAuto: snapshot.fallbackAuto } : {}
249
+ });
250
+ }
251
+ broadcastSaved(snapshot.providers);
252
+ if (snapshot.uiLocale !== lastUiLocale) {
253
+ lastUiLocale = snapshot.uiLocale;
254
+ if (snapshot.uiLocale) {
255
+ opts.agent.ctx.meta["uiLocale"] = snapshot.uiLocale;
256
+ broadcast({ type: "prefs.updated", payload: { uiLocale: snapshot.uiLocale } });
257
+ }
258
+ }
259
+ const activeId = opts.agent.ctx.provider.id;
260
+ const newCfgStr = JSON.stringify(snapshot.providers[activeId] ?? null);
261
+ if (newCfgStr === lastActiveCfg) return;
262
+ lastActiveCfg = newCfgStr;
263
+ try {
264
+ const newCfg = snapshot.providers[activeId] ?? {
265
+ type: activeId,
266
+ ...snapshot.apiKey !== void 0 ? { apiKey: snapshot.apiKey } : {},
267
+ ...snapshot.baseUrl !== void 0 ? { baseUrl: snapshot.baseUrl } : {}
268
+ };
269
+ const oldMax = opts.agent.ctx.provider.capabilities?.maxContext;
270
+ const prov = makeProviderFromConfig(activeId, {
271
+ ...newCfg,
272
+ type: newCfg.type ?? activeId
273
+ });
274
+ if (oldMax != null && prov.capabilities) prov.capabilities.maxContext = oldMax;
275
+ opts.agent.ctx.provider = prov;
276
+ console.log(`[WebUI] Provider credentials reloaded from config.json (${activeId})`);
277
+ } catch (err) {
278
+ console.warn(
279
+ `[WebUI] Credential hot-reload failed for ${activeId}: ${toErrorMessage(err)}`
280
+ );
281
+ }
282
+ },
283
+ {
284
+ warn: (m) => console.warn(
285
+ JSON.stringify({
286
+ level: "warn",
287
+ event: "webui.config_watcher",
288
+ message: m,
289
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
290
+ })
291
+ )
292
+ }
293
+ );
294
+ return watcher.close;
295
+ }
296
+
297
+ // src/webui-server/domain-handlers.ts
298
+ import * as os from "node:os";
299
+ import * as path3 from "node:path";
300
+ import { resolveWstackPaths } from "@wrongstack/core/utils";
301
+ import {
302
+ buildSddWizardDeps,
303
+ GoalWebSocketHandler,
304
+ SddBoardWebSocketHandler,
305
+ SddWizardWebSocketHandler,
306
+ SpecsWebSocketHandler,
307
+ TerminalWebSocketHandler,
308
+ WorktreeWebSocketHandler
309
+ } from "@wrongstack/webui-server";
310
+
311
+ // src/webui-server/logger-shim.ts
312
+ var structuredLine = (level, message) => JSON.stringify({
313
+ level,
314
+ event: "webui.goal",
315
+ message,
316
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
317
+ });
318
+ var consoleLogger = {
319
+ level: "debug",
320
+ error(msg, _ctx) {
321
+ console.error(structuredLine("error", msg));
322
+ },
323
+ warn(msg, _ctx) {
324
+ console.warn(structuredLine("warn", msg));
325
+ },
326
+ info(msg, _ctx) {
327
+ console.log(structuredLine("info", msg));
328
+ },
329
+ debug(msg, _ctx) {
330
+ console.debug(structuredLine("debug", msg));
331
+ },
332
+ trace(msg, _ctx) {
333
+ console.debug(structuredLine("trace", msg));
334
+ },
335
+ child(_bindings) {
336
+ return this;
337
+ }
338
+ };
339
+
340
+ // src/webui-server/node-pty-loader.ts
341
+ import { existsSync } from "node:fs";
342
+ import { createRequire } from "node:module";
343
+ import * as path2 from "node:path";
344
+ import { fileURLToPath } from "node:url";
345
+ function createNodePtyLoader(importMetaUrl, logger) {
346
+ const requireFromCli = createRequire(importMetaUrl);
347
+ let cachedNodePty;
348
+ return () => {
349
+ if (cachedNodePty !== void 0) return cachedNodePty;
350
+ try {
351
+ const webuiEntry = requireFromCli.resolve("@wrongstack/webui");
352
+ const webuiDir = webuiEntry.replace(/[\\/]dist.*$/, "");
353
+ const webuiPkgJson = path2.join(webuiDir, "package.json");
354
+ cachedNodePty = createRequire(webuiPkgJson)("node-pty");
355
+ if (cachedNodePty) {
356
+ logger.debug?.(`[terminal] node-pty loaded via webui package (${webuiDir})`);
357
+ return cachedNodePty;
358
+ }
359
+ } catch (err) {
360
+ logger.debug?.(`[terminal] webui-route failed: ${err.message}`);
361
+ }
362
+ try {
363
+ cachedNodePty = requireFromCli("node-pty");
364
+ logger.debug?.("[terminal] node-pty loaded via direct requireFromCli");
365
+ return cachedNodePty;
366
+ } catch (err) {
367
+ logger.debug?.(`[terminal] direct require failed: ${err.message}`);
368
+ }
369
+ try {
370
+ const cliPkgJson = path2.join(
371
+ path2.dirname(fileURLToPath(importMetaUrl)),
372
+ "..",
373
+ "package.json"
374
+ );
375
+ let dir = path2.dirname(cliPkgJson);
376
+ for (let i = 0; i < 6; i++) {
377
+ const candidate = path2.join(dir, "node_modules", "node-pty");
378
+ if (existsSync(candidate)) {
379
+ cachedNodePty = createRequire(candidate)("node-pty");
380
+ if (cachedNodePty) {
381
+ logger.debug?.(`[terminal] node-pty loaded via workspace root (${candidate})`);
382
+ return cachedNodePty;
383
+ }
384
+ }
385
+ const parent = path2.dirname(dir);
386
+ if (parent === dir) break;
387
+ dir = parent;
388
+ }
389
+ } catch (err) {
390
+ logger.debug?.(`[terminal] workspace-root walk failed: ${err.message}`);
391
+ }
392
+ cachedNodePty = null;
393
+ logger.debug?.(
394
+ '[terminal] node-pty resolution failed; terminal panel will report "unavailable"'
395
+ );
396
+ return cachedNodePty;
397
+ };
398
+ }
399
+
400
+ // src/webui-server/domain-handlers.ts
401
+ function createWebuiDomainHandlers(opts, trustBoundary, kanbanRunMirror) {
402
+ const goalStoreDir = opts.projectRoot ? path3.join(opts.projectRoot, ".wrongstack", "goal") : path3.join(os.tmpdir(), ".wrongstack", "goal");
403
+ const goalHandler = new GoalWebSocketHandler(
404
+ opts.agent,
405
+ opts.agent.ctx,
406
+ consoleLogger,
407
+ goalStoreDir,
408
+ opts.events,
409
+ opts.projectRoot,
410
+ kanbanRunMirror ? (graphId, state) => kanbanRunMirror.onGoalState(graphId, state) : void 0
411
+ );
412
+ const worktreeHandler = new WorktreeWebSocketHandler(opts.events, consoleLogger);
413
+ const loadNodePtyViaWebui = createNodePtyLoader(import.meta.url, consoleLogger);
414
+ const terminalHandler = new TerminalWebSocketHandler(
415
+ () => opts.agent.ctx.cwd ?? opts.projectRoot ?? process.cwd(),
416
+ consoleLogger,
417
+ loadNodePtyViaWebui,
418
+ void 0,
419
+ trustBoundary
420
+ );
421
+ const specsPaths = opts.projectRoot ? resolveWstackPaths({ projectRoot: opts.projectRoot }) : null;
422
+ const specsHandler = new SpecsWebSocketHandler(
423
+ specsPaths?.projectSpecs ?? path3.join(os.tmpdir(), ".wrongstack", "specs"),
424
+ specsPaths?.projectTaskGraphs ?? path3.join(os.tmpdir(), ".wrongstack", "task-graphs")
425
+ );
426
+ const sddBoardHandler = new SddBoardWebSocketHandler(
427
+ specsPaths?.projectSddBoards ?? path3.join(os.tmpdir(), ".wrongstack", "sdd-boards"),
428
+ opts.events,
429
+ specsPaths && opts.projectRoot ? {
430
+ projectRoot: opts.projectRoot,
431
+ paths: {
432
+ projectSpecs: specsPaths.projectSpecs,
433
+ projectTaskGraphs: specsPaths.projectTaskGraphs,
434
+ projectSddSession: specsPaths.projectSddSession,
435
+ projectSddBoards: specsPaths.projectSddBoards
436
+ }
437
+ } : void 0,
438
+ { trustBoundary, logger: consoleLogger }
439
+ );
440
+ const sddWizardHandler = opts.sddSubagentFactory && specsPaths ? new SddWizardWebSocketHandler(
441
+ buildSddWizardDeps({
442
+ agent: opts.agent,
443
+ events: opts.events,
444
+ projectRoot: opts.projectRoot ?? process.cwd(),
445
+ subagentFactory: opts.sddSubagentFactory,
446
+ ...opts.brain ? { brain: opts.brain } : {},
447
+ paths: {
448
+ projectSpecs: specsPaths.projectSpecs,
449
+ projectTaskGraphs: specsPaths.projectTaskGraphs,
450
+ projectSddBoards: specsPaths.projectSddBoards,
451
+ projectDir: specsPaths.projectDir,
452
+ projectSddSession: specsPaths.projectSddSession
453
+ }
454
+ })
455
+ ) : null;
456
+ return {
457
+ goalHandler,
458
+ worktreeHandler,
459
+ terminalHandler,
460
+ specsHandler,
461
+ sddBoardHandler,
462
+ sddWizardHandler,
463
+ specsPaths
464
+ };
465
+ }
466
+
180
467
  // src/webui-server/kanban-host-adapter.ts
181
468
  import { PhaseGraphBuilder } from "@wrongstack/core/goal";
182
- import { resolveWstackPaths } from "@wrongstack/core/utils";
469
+ import { resolveWstackPaths as resolveWstackPaths2 } from "@wrongstack/core/utils";
183
470
  import { exportBoardToTaskGraph } from "@wrongstack/kanban";
184
471
  import { TaskGraphStore } from "@wrongstack/sdd";
185
472
  import {
@@ -310,7 +597,7 @@ function createCliKanbanHostRoutes(deps) {
310
597
  if (!opts.sddSubagentFactory) {
311
598
  return failRun("SDD runs need the multi-agent host, which is not available here.");
312
599
  }
313
- const paths = resolveWstackPaths({ projectRoot });
600
+ const paths = resolveWstackPaths2({ projectRoot });
314
601
  const taskStore = new TaskGraphStore({ baseDir: paths.projectTaskGraphs });
315
602
  await taskStore.save(exported.graph);
316
603
  const handle = await startSddRunFromGraph(
@@ -406,99 +693,10 @@ async function startIpv6LoopbackProxy(args) {
406
693
  return v6;
407
694
  }
408
695
 
409
- // src/webui-server/logger-shim.ts
410
- var structuredLine = (level, message) => JSON.stringify({
411
- level,
412
- event: "webui.goal",
413
- message,
414
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
415
- });
416
- var consoleLogger = {
417
- level: "debug",
418
- error(msg, _ctx) {
419
- console.error(structuredLine("error", msg));
420
- },
421
- warn(msg, _ctx) {
422
- console.warn(structuredLine("warn", msg));
423
- },
424
- info(msg, _ctx) {
425
- console.log(structuredLine("info", msg));
426
- },
427
- debug(msg, _ctx) {
428
- console.debug(structuredLine("debug", msg));
429
- },
430
- trace(msg, _ctx) {
431
- console.debug(structuredLine("trace", msg));
432
- },
433
- child(_bindings) {
434
- return this;
435
- }
436
- };
437
-
438
- // src/webui-server/node-pty-loader.ts
439
- import { existsSync } from "node:fs";
440
- import { createRequire } from "node:module";
441
- import * as path from "node:path";
442
- import { fileURLToPath } from "node:url";
443
- function createNodePtyLoader(importMetaUrl, logger) {
444
- const requireFromCli = createRequire(importMetaUrl);
445
- let cachedNodePty;
446
- return () => {
447
- if (cachedNodePty !== void 0) return cachedNodePty;
448
- try {
449
- const webuiEntry = requireFromCli.resolve("@wrongstack/webui");
450
- const webuiDir = webuiEntry.replace(/[\\/]dist.*$/, "");
451
- const webuiPkgJson = path.join(webuiDir, "package.json");
452
- cachedNodePty = createRequire(webuiPkgJson)("node-pty");
453
- if (cachedNodePty) {
454
- logger.debug?.(`[terminal] node-pty loaded via webui package (${webuiDir})`);
455
- return cachedNodePty;
456
- }
457
- } catch (err) {
458
- logger.debug?.(`[terminal] webui-route failed: ${err.message}`);
459
- }
460
- try {
461
- cachedNodePty = requireFromCli("node-pty");
462
- logger.debug?.("[terminal] node-pty loaded via direct requireFromCli");
463
- return cachedNodePty;
464
- } catch (err) {
465
- logger.debug?.(`[terminal] direct require failed: ${err.message}`);
466
- }
467
- try {
468
- const cliPkgJson = path.join(
469
- path.dirname(fileURLToPath(importMetaUrl)),
470
- "..",
471
- "package.json"
472
- );
473
- let dir = path.dirname(cliPkgJson);
474
- for (let i = 0; i < 6; i++) {
475
- const candidate = path.join(dir, "node_modules", "node-pty");
476
- if (existsSync(candidate)) {
477
- cachedNodePty = createRequire(candidate)("node-pty");
478
- if (cachedNodePty) {
479
- logger.debug?.(`[terminal] node-pty loaded via workspace root (${candidate})`);
480
- return cachedNodePty;
481
- }
482
- }
483
- const parent = path.dirname(dir);
484
- if (parent === dir) break;
485
- dir = parent;
486
- }
487
- } catch (err) {
488
- logger.debug?.(`[terminal] workspace-root walk failed: ${err.message}`);
489
- }
490
- cachedNodePty = null;
491
- logger.debug?.(
492
- '[terminal] node-pty resolution failed; terminal panel will report "unavailable"'
493
- );
494
- return cachedNodePty;
495
- };
496
- }
497
-
498
696
  // src/webui-server/prefs-seeding.ts
499
697
  import * as fs from "node:fs/promises";
500
- import * as path2 from "node:path";
501
- import { DefaultSecretVault } from "@wrongstack/core/security";
698
+ import * as path4 from "node:path";
699
+ import { DefaultSecretVault as DefaultSecretVault2 } from "@wrongstack/core/security";
502
700
  import {
503
701
  PREF_KEYS,
504
702
  persistPrefsToConfig,
@@ -553,8 +751,8 @@ function createPrefsSeeding(opts) {
553
751
  patchLiveAppConfig({ modelMatrix: payload["modelMatrix"] });
554
752
  }
555
753
  if (!opts.profileConfigPath) return;
556
- const globalRoot = opts.globalConfigPath ? path2.dirname(opts.globalConfigPath) : path2.dirname(opts.profileConfigPath);
557
- const vault = new DefaultSecretVault({ keyFile: path2.join(globalRoot, ".key") });
754
+ const globalRoot = opts.globalConfigPath ? path4.dirname(opts.globalConfigPath) : path4.dirname(opts.profileConfigPath);
755
+ const vault = new DefaultSecretVault2({ keyFile: path4.join(globalRoot, ".key") });
558
756
  await persistPrefsToConfig(
559
757
  {
560
758
  profileConfigPath: opts.profileConfigPath,
@@ -576,46 +774,172 @@ function createPrefsSeeding(opts) {
576
774
  payload
577
775
  );
578
776
  };
579
- return {
580
- prefSnapshot: () => snapshotPrefs(opts.agent.ctx.meta),
581
- persistPrefs
777
+ return {
778
+ prefSnapshot: () => snapshotPrefs(opts.agent.ctx.meta),
779
+ persistPrefs
780
+ };
781
+ }
782
+
783
+ // src/webui-server/route-contexts.ts
784
+ import * as path5 from "node:path";
785
+ import { TOKENS as TOKENS2 } from "@wrongstack/core/kernel";
786
+ import { SkillInstaller } from "@wrongstack/core/skills";
787
+ import { PromptUsageStore } from "@wrongstack/core/storage";
788
+ import { resolveWstackPaths as resolveWstackPaths3 } from "@wrongstack/core/utils";
789
+ import {
790
+ createMailboxRouteHandlers
791
+ } from "@wrongstack/webui-server";
792
+ function createWebuiRouteContexts({
793
+ opts,
794
+ profileConfigPath,
795
+ profileDir,
796
+ globalRoot,
797
+ sessionStartedAt,
798
+ currentSessionId,
799
+ getCustomModeStore,
800
+ buildSessionStartPayload,
801
+ prefSnapshot,
802
+ persistPrefs,
803
+ pendingConfirms,
804
+ abortControllers,
805
+ getAbortController,
806
+ clearAbortController,
807
+ send,
808
+ broadcast
809
+ }) {
810
+ const brainCtx = {
811
+ brainSettings: opts.brainSettings,
812
+ brainRuntime: opts.brainRuntime,
813
+ getBrainLog: opts.getBrainLog,
814
+ resolveArbiter: () => opts.brain ?? (opts.agent.container.has(TOKENS2.BrainArbiter) ? opts.agent.container.resolve(TOKENS2.BrainArbiter) : void 0),
815
+ getSessionId: currentSessionId,
816
+ send
817
+ };
818
+ const introspectionConfigStore = opts.agent.container?.safeResolve?.(TOKENS2.ConfigStore);
819
+ const introspectionCtx = {
820
+ agent: opts.agent,
821
+ modelsRegistry: opts.modelsRegistry,
822
+ configStore: introspectionConfigStore,
823
+ getConfig: () => {
824
+ const cfg = introspectionConfigStore?.get() ?? opts.appConfig;
825
+ if (!cfg)
826
+ throw new Error(
827
+ "Introspection route requires a config but neither ConfigStore nor opts.appConfig is available"
828
+ );
829
+ return cfg;
830
+ },
831
+ getProjectRoot: () => opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "",
832
+ getSessionId: currentSessionId,
833
+ getSessionStartedAt: () => sessionStartedAt,
834
+ getModeId: () => opts.modeId ?? "default",
835
+ send
836
+ };
837
+ const skillsProjectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "";
838
+ const skillsPaths = skillsProjectRoot ? resolveWstackPaths3({
839
+ projectRoot: skillsProjectRoot,
840
+ globalRoot
841
+ }) : void 0;
842
+ const skillsCtx = {
843
+ skillLoader: opts.skillLoader,
844
+ skillInstaller: opts.skillLoader ? new SkillInstaller({
845
+ manifestPath: path5.join(skillsPaths?.configDir ?? profileDir, "installed-skills.json"),
846
+ projectSkillsDir: skillsPaths?.inProjectSkills ?? path5.join(skillsProjectRoot, ".wrongstack", "skills"),
847
+ globalSkillsDir: skillsPaths?.globalSkills ?? path5.join(profileDir, "skills"),
848
+ projectHash: skillsPaths?.projectHash ?? "",
849
+ skillLoader: opts.skillLoader
850
+ }) : void 0,
851
+ projectRoot: skillsProjectRoot,
852
+ projectSkillsDir: skillsPaths?.inProjectSkills,
853
+ globalSkillsDir: skillsPaths?.globalSkills
854
+ };
855
+ const promptsCtx = {
856
+ promptLoader: opts.promptLoader,
857
+ promptUsage: new PromptUsageStore(
858
+ skillsPaths?.promptUsage ?? path5.join(profileDir, "prompt-usage.json")
859
+ )
860
+ };
861
+ const designCtx = {
862
+ projectRoot: skillsProjectRoot,
863
+ agentMeta: opts.agent.ctx
864
+ };
865
+ const agentConfigCtx = {
866
+ agent: opts.agent,
867
+ modeStore: opts.modeStore,
868
+ loadSavedProviders: () => loadSavedProviders(profileConfigPath),
869
+ buildSessionStart: (overrides) => buildSessionStartPayload(overrides),
870
+ modelsRegistry: opts.modelsRegistry,
871
+ memoryStore: opts.memoryStore,
872
+ getConfig: () => opts.appConfig,
873
+ onMaxContextResolved: opts.onModelContextResolved,
874
+ persistPrefs,
875
+ send,
876
+ broadcast,
877
+ log: (m) => console.log(m)
878
+ };
879
+ const prefsCtx = {
880
+ meta: opts.agent.ctx.meta,
881
+ snapshot: prefSnapshot,
882
+ persist: persistPrefs,
883
+ setYolo: opts.onYoloSwitch,
884
+ setAutonomy: opts.onAutonomySwitch,
885
+ pendingConfirms,
886
+ configStore: opts.agent.container?.safeResolve?.(TOKENS2.ConfigStore),
887
+ send,
888
+ broadcast
889
+ };
890
+ const projectsCtx = {
891
+ opts,
892
+ abortControllers,
893
+ abortLegacyRun: () => {
894
+ const controller = getAbortController();
895
+ if (controller) {
896
+ controller.abort();
897
+ clearAbortController();
898
+ }
899
+ },
900
+ buildSessionStart: (overrides) => buildSessionStartPayload(overrides),
901
+ send,
902
+ broadcast,
903
+ log: (m) => console.log(m)
904
+ };
905
+ const mailboxRoutes = createMailboxRouteHandlers({
906
+ getProjectRoot: () => opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "",
907
+ getGlobalRoot: () => opts.globalConfigPath ? path5.dirname(opts.globalConfigPath) : "",
908
+ events: opts.events
909
+ });
910
+ const sessionsCtx = {
911
+ opts,
912
+ buildSessionStart: (overrides) => buildSessionStartPayload(overrides),
913
+ getCustomModeStore,
914
+ send,
915
+ broadcast,
916
+ log: (m) => console.log(m)
917
+ };
918
+ const connectionCtx = {
919
+ agent: opts.agent,
920
+ abortControllers,
921
+ pendingConfirms,
922
+ send,
923
+ broadcast,
924
+ log: (m) => console.log(m)
582
925
  };
583
- }
584
-
585
- // src/webui-server/provider-config.ts
586
- import * as path3 from "node:path";
587
- import { DefaultSecretVault as DefaultSecretVault2 } from "@wrongstack/core/security";
588
- function getVault(globalConfigPath) {
589
- const configDir = path3.dirname(globalConfigPath ?? "");
590
- const parentDir = path3.dirname(configDir);
591
- const globalRoot = path3.basename(parentDir) === "profiles" ? path3.dirname(parentDir) : configDir;
592
- const keyFile = path3.join(globalRoot, ".key");
593
- return new DefaultSecretVault2({ keyFile });
594
- }
595
- async function loadSavedProviders(globalConfigPath) {
596
- if (!globalConfigPath) return {};
597
- return loadConfigProviders(globalConfigPath, getVault(globalConfigPath));
598
- }
599
- async function saveProviders(globalConfigPath, providers) {
600
- if (!globalConfigPath) return;
601
- await mutateConfigProviders(
602
- globalConfigPath,
603
- getVault(globalConfigPath),
604
- (existing) => {
605
- for (const key of Object.keys(existing)) delete existing[key];
606
- Object.assign(existing, providers);
607
- }
608
- );
609
- }
610
- function createProviderConfigStore(globalConfigPath, configProvidersRef) {
611
926
  return {
612
- load: () => configProvidersRef ? Promise.resolve(configProvidersRef()) : loadSavedProviders(globalConfigPath),
613
- save: (providers) => saveProviders(globalConfigPath, providers)
927
+ brainCtx,
928
+ introspectionCtx,
929
+ skillsCtx,
930
+ promptsCtx,
931
+ designCtx,
932
+ agentConfigCtx,
933
+ prefsCtx,
934
+ projectsCtx,
935
+ mailboxRoutes,
936
+ sessionsCtx,
937
+ connectionCtx
614
938
  };
615
939
  }
616
940
 
617
941
  // src/webui-server/session-start-payload.ts
618
- import * as path4 from "node:path";
942
+ import * as path6 from "node:path";
619
943
  import { DEFAULT_CONTEXT_WINDOW_MODE_ID } from "@wrongstack/core/types";
620
944
  import { protocolAdvertisement } from "@wrongstack/webui-server/protocol";
621
945
 
@@ -656,7 +980,7 @@ function createSessionStartPayloadBuilder(deps) {
656
980
  model: deps.agent.ctx.model,
657
981
  provider: deps.agent.ctx.provider.id,
658
982
  mode: deps.modeId ?? "default",
659
- projectName: deps.projectRoot ? path4.basename(deps.projectRoot) : void 0,
983
+ projectName: deps.projectRoot ? path6.basename(deps.projectRoot) : void 0,
660
984
  // Frontend reads `projectRoot` from session.start (ws-handlers setEnv) —
661
985
  // omitting it left the store's projectRoot empty after a project switch.
662
986
  projectRoot: deps.projectRoot ?? deps.agent.ctx.projectRoot ?? "",
@@ -889,15 +1213,15 @@ async function runWebUI(opts) {
889
1213
  httpPort = await findFreePort(host, requestedHttpPort);
890
1214
  }
891
1215
  const wsPort = httpPort;
892
- const globalRoot = opts.globalConfigPath ? path5.dirname(opts.globalConfigPath) : wstackGlobalRoot();
893
- const profileConfigPath = opts.profileConfigPath ?? opts.globalConfigPath ?? path5.join(globalRoot, "config.json");
1216
+ const globalRoot = opts.globalConfigPath ? path7.dirname(opts.globalConfigPath) : wstackGlobalRoot();
1217
+ const profileConfigPath = opts.profileConfigPath ?? opts.globalConfigPath ?? path7.join(globalRoot, "config.json");
894
1218
  const rateLimitMax = Number.parseInt(process.env["WEBUI_RATE_LIMIT"] ?? "600", 10);
895
1219
  const clients = /* @__PURE__ */ new Map();
896
1220
  const pendingConfirms = /* @__PURE__ */ new Map();
897
1221
  const secretScrubber = new DefaultSecretScrubber();
898
1222
  let abortController = null;
899
1223
  const abortControllers = /* @__PURE__ */ new Map();
900
- const profileDir = path5.dirname(profileConfigPath);
1224
+ const profileDir = path7.dirname(profileConfigPath);
901
1225
  let customModeStoreP = null;
902
1226
  const getCustomModeStore = () => {
903
1227
  customModeStoreP ??= (async () => {
@@ -907,7 +1231,6 @@ async function runWebUI(opts) {
907
1231
  })();
908
1232
  return customModeStoreP;
909
1233
  };
910
- const goalStoreDir = opts.projectRoot ? path5.join(opts.projectRoot, ".wrongstack", "goal") : path5.join(os.tmpdir(), ".wrongstack", "goal");
911
1234
  const kanbanRunMirror = opts.projectRoot ? createKanbanRunMirror({
912
1235
  projectRoot: opts.projectRoot,
913
1236
  events: opts.events,
@@ -931,61 +1254,14 @@ async function runWebUI(opts) {
931
1254
  };
932
1255
  }
933
1256
  }) : void 0;
934
- const goalHandler = new GoalWebSocketHandler(
935
- opts.agent,
936
- opts.agent.ctx,
937
- consoleLogger,
938
- goalStoreDir,
939
- opts.events,
940
- opts.projectRoot,
941
- kanbanRunMirror ? (graphId, state) => kanbanRunMirror.onGoalState(graphId, state) : void 0
942
- );
943
- const worktreeHandler = new WorktreeWebSocketHandler(opts.events, consoleLogger);
944
- const loadNodePtyViaWebui = createNodePtyLoader(import.meta.url, consoleLogger);
945
- const terminalHandler = new TerminalWebSocketHandler(
946
- () => opts.agent.ctx.cwd ?? opts.projectRoot ?? process.cwd(),
947
- consoleLogger,
948
- loadNodePtyViaWebui,
949
- void 0,
950
- trustBoundary
951
- );
952
- const specsPaths = opts.projectRoot ? resolveWstackPaths2({ projectRoot: opts.projectRoot }) : null;
953
- const specsHandler = new SpecsWebSocketHandler(
954
- specsPaths?.projectSpecs ?? path5.join(os.tmpdir(), ".wrongstack", "specs"),
955
- specsPaths?.projectTaskGraphs ?? path5.join(os.tmpdir(), ".wrongstack", "task-graphs")
956
- );
957
- const sddBoardHandler = new SddBoardWebSocketHandler(
958
- specsPaths?.projectSddBoards ?? path5.join(os.tmpdir(), ".wrongstack", "sdd-boards"),
959
- opts.events,
960
- specsPaths && opts.projectRoot ? {
961
- projectRoot: opts.projectRoot,
962
- paths: {
963
- projectSpecs: specsPaths.projectSpecs,
964
- projectTaskGraphs: specsPaths.projectTaskGraphs,
965
- projectSddSession: specsPaths.projectSddSession,
966
- projectSddBoards: specsPaths.projectSddBoards
967
- }
968
- } : void 0,
969
- { trustBoundary, logger: consoleLogger }
970
- );
971
- const sddWizardHandler = opts.sddSubagentFactory && specsPaths ? new SddWizardWebSocketHandler(
972
- buildSddWizardDeps({
973
- agent: opts.agent,
974
- events: opts.events,
975
- projectRoot: opts.projectRoot ?? process.cwd(),
976
- subagentFactory: opts.sddSubagentFactory,
977
- // Failure supervisor parity with /sdd execute — omit only when
978
- // the host never bound a BrainArbiter.
979
- ...opts.brain ? { brain: opts.brain } : {},
980
- paths: {
981
- projectSpecs: specsPaths.projectSpecs,
982
- projectTaskGraphs: specsPaths.projectTaskGraphs,
983
- projectSddBoards: specsPaths.projectSddBoards,
984
- projectDir: specsPaths.projectDir,
985
- projectSddSession: specsPaths.projectSddSession
986
- }
987
- })
988
- ) : null;
1257
+ const {
1258
+ goalHandler,
1259
+ worktreeHandler,
1260
+ terminalHandler,
1261
+ specsHandler,
1262
+ sddBoardHandler,
1263
+ sddWizardHandler
1264
+ } = createWebuiDomainHandlers(opts, trustBoundary, kanbanRunMirror);
989
1265
  await seedConfigToMeta(opts);
990
1266
  if (typeof opts.agent.ctx?.meta?.["yolo"] === "boolean") {
991
1267
  opts.onYoloSwitch?.(opts.agent.ctx.meta["yolo"]);
@@ -999,11 +1275,6 @@ async function runWebUI(opts) {
999
1275
  events: opts.events,
1000
1276
  hqSessionId: opts.session.id,
1001
1277
  getSessionId: () => opts.agent.ctx.session?.id ?? opts.session.id,
1002
- // Two-way HQ control for the WebUI client: steer/btw/queue/broadcast
1003
- // land in the project mailbox (the WebUI's agent reads it like any
1004
- // other surface), and abort tears down every in-flight run on this
1005
- // instance. Without this block the WebUI would be HQ-invisible to the
1006
- // Control tab — telemetry only.
1007
1278
  hqControl: {
1008
1279
  interruptLeader: () => {
1009
1280
  let aborted = false;
@@ -1055,16 +1326,11 @@ async function runWebUI(opts) {
1055
1326
  void fleetBroadcastCli?.();
1056
1327
  },
1057
1328
  onTechStackEvent: (event) => broadcast(event),
1058
- // Read through the live agent context so a mid-session model switch or a
1059
- // credential hot-reload takes effect on the next analyze.
1060
1329
  getLlm: () => opts.agent.ctx.provider && opts.agent.ctx.model ? { provider: opts.agent.ctx.provider, model: opts.agent.ctx.model } : void 0,
1061
1330
  projectRoot: opts.projectRoot,
1062
1331
  publicWsUrl,
1063
1332
  apiToken: wsToken,
1064
1333
  requireToken,
1065
- // SimpleUI only: defer listen until after the WebSocketServer is
1066
- // attached, so a WS upgrade request cannot arrive between listen()
1067
- // and the WS handler registration.
1068
1334
  deferListen: surface === "simpleui"
1069
1335
  });
1070
1336
  const wss = httpServer ? new WebSocketServer({ server: httpServer.server, maxPayload: 20 * 1024 * 1024 }) : new WebSocketServer({ port: httpPort, host, maxPayload: 20 * 1024 * 1024 });
@@ -1114,8 +1380,6 @@ async function runWebUI(opts) {
1114
1380
  projectRoot: opts.projectRoot,
1115
1381
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
1116
1382
  registryBaseDir,
1117
- // Lets a same-project TUI/REPL authenticate `POST /api/fleet/ping` now
1118
- // that the API requires a token on every bind (H3).
1119
1383
  authToken: wsToken,
1120
1384
  ...opts.webuiSessionChild ? {
1121
1385
  role: "session-child",
@@ -1169,7 +1433,7 @@ async function runWebUI(opts) {
1169
1433
  secretScrubber,
1170
1434
  getClients: () => clients,
1171
1435
  eventUnsubscribers,
1172
- globalConfigPath: path5.join(globalRoot, "config.json"),
1436
+ globalConfigPath: path7.join(globalRoot, "config.json"),
1173
1437
  onFleetBroadcaster: (fn) => {
1174
1438
  fleetBroadcastCli = fn;
1175
1439
  },
@@ -1178,11 +1442,6 @@ async function runWebUI(opts) {
1178
1442
  const wsHandlerCtx = {
1179
1443
  providerStore: createProviderConfigStore(
1180
1444
  profileConfigPath,
1181
- // Use the in-memory merged config providers so the WebUI sees the
1182
- // same provider list the agent uses. Without this, providers stored
1183
- // only in the project-local config (config.local.json) would be
1184
- // invisible to the WebUI's saved-providers panel because the store
1185
- // reads exclusively from the global config file.
1186
1445
  () => opts.appConfig?.providers ?? {}
1187
1446
  ),
1188
1447
  modelsRegistry: opts.modelsRegistry,
@@ -1191,207 +1450,32 @@ async function runWebUI(opts) {
1191
1450
  log: (m) => console.log(m)
1192
1451
  };
1193
1452
  const embeddedProviderOperations = createEmbeddedProviderOperations(wsHandlerCtx);
1194
- const watchConfigPath = profileConfigPath;
1195
- let credentialWatcherClose;
1196
- if (watchConfigPath && process.env["WRONGSTACK_DISABLE_CONFIG_WATCH"] !== "1") {
1197
- let lastActiveCfg = JSON.stringify(
1198
- opts.appConfig?.providers?.[opts.agent.ctx.provider.id] ?? null
1199
- );
1200
- let lastUiLocale = opts.appConfig?.uiLocale;
1201
- const watcher = watchProviderConfig(
1202
- watchConfigPath,
1203
- // Vault key lives at ~/.wrongstack/.key (derived from globalConfigPath),
1204
- // not inside the profile directory — resolve via global root to stay correct.
1205
- getVault(opts.globalConfigPath),
1206
- (snapshot) => {
1207
- const hadFallbackBridge = Boolean(opts.appConfig?.fallbackBridge?.trim());
1208
- try {
1209
- if (opts.appConfig && !Object.isFrozen(opts.appConfig)) {
1210
- opts.appConfig.providers = snapshot.providers;
1211
- if (snapshot.uiLocale) opts.appConfig.uiLocale = snapshot.uiLocale;
1212
- if (snapshot.fallbackBridge !== void 0) {
1213
- opts.appConfig.fallbackBridge = snapshot.fallbackBridge;
1214
- } else {
1215
- delete opts.appConfig.fallbackBridge;
1216
- }
1217
- }
1218
- } catch {
1219
- }
1220
- const routingChanged = snapshot.fallbackModels !== void 0 || snapshot.fallbackBridge !== void 0 || hadFallbackBridge || snapshot.fallbackProfiles !== void 0 || snapshot.favoriteModels !== void 0 || snapshot.favoriteModelsOnly !== void 0 || snapshot.modelMatrix !== void 0 || snapshot.fallbackAuto !== void 0;
1221
- if (routingChanged) {
1222
- const configStore = opts.agent.container?.safeResolve?.(TOKENS.ConfigStore);
1223
- configStore?.update({
1224
- ...snapshot.fallbackModels !== void 0 ? { fallbackModels: snapshot.fallbackModels } : {},
1225
- fallbackBridge: snapshot.fallbackBridge ?? "",
1226
- ...snapshot.fallbackProfiles !== void 0 ? { fallbackProfiles: snapshot.fallbackProfiles } : {},
1227
- ...snapshot.favoriteModels !== void 0 ? { favoriteModels: snapshot.favoriteModels } : {},
1228
- ...snapshot.favoriteModelsOnly !== void 0 ? { favoriteModelsOnly: snapshot.favoriteModelsOnly } : {},
1229
- ...snapshot.modelMatrix !== void 0 ? { modelMatrix: snapshot.modelMatrix } : {},
1230
- ...snapshot.fallbackAuto !== void 0 ? { fallbackAuto: snapshot.fallbackAuto } : {}
1231
- });
1232
- }
1233
- embeddedProviderOperations.broadcastSaved(snapshot.providers);
1234
- if (snapshot.uiLocale !== lastUiLocale) {
1235
- lastUiLocale = snapshot.uiLocale;
1236
- if (snapshot.uiLocale) {
1237
- opts.agent.ctx.meta["uiLocale"] = snapshot.uiLocale;
1238
- broadcast({ type: "prefs.updated", payload: { uiLocale: snapshot.uiLocale } });
1239
- }
1240
- }
1241
- const activeId = opts.agent.ctx.provider.id;
1242
- const newCfgStr = JSON.stringify(snapshot.providers[activeId] ?? null);
1243
- if (newCfgStr === lastActiveCfg) return;
1244
- lastActiveCfg = newCfgStr;
1245
- try {
1246
- const newCfg = snapshot.providers[activeId] ?? {
1247
- type: activeId,
1248
- ...snapshot.apiKey !== void 0 ? { apiKey: snapshot.apiKey } : {},
1249
- ...snapshot.baseUrl !== void 0 ? { baseUrl: snapshot.baseUrl } : {}
1250
- };
1251
- const oldMax = opts.agent.ctx.provider.capabilities?.maxContext;
1252
- const prov = makeProviderFromConfig(activeId, {
1253
- ...newCfg,
1254
- type: newCfg.type ?? activeId
1255
- });
1256
- if (oldMax != null && prov.capabilities) prov.capabilities.maxContext = oldMax;
1257
- opts.agent.ctx.provider = prov;
1258
- console.log(`[WebUI] Provider credentials reloaded from config.json (${activeId})`);
1259
- } catch (err) {
1260
- console.warn(
1261
- `[WebUI] Credential hot-reload failed for ${activeId}: ${toErrorMessage(err)}`
1262
- );
1263
- }
1264
- },
1265
- {
1266
- warn: (m) => console.warn(
1267
- JSON.stringify({
1268
- level: "warn",
1269
- event: "webui.config_watcher",
1270
- message: m,
1271
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1272
- })
1273
- )
1274
- }
1275
- );
1276
- credentialWatcherClose = watcher.close;
1277
- }
1278
- const brainCtx = {
1279
- brainSettings: opts.brainSettings,
1280
- brainRuntime: opts.brainRuntime,
1281
- getBrainLog: opts.getBrainLog,
1282
- // Prefer the host-supplied arbiter; otherwise resolve the one bound
1283
- // in the agent container (if any). Mirrors the former inline lookup.
1284
- resolveArbiter: () => opts.brain ?? (opts.agent.container.has(TOKENS.BrainArbiter) ? opts.agent.container.resolve(TOKENS.BrainArbiter) : void 0),
1285
- getSessionId: currentSessionId,
1286
- send
1287
- };
1288
- const introspectionConfigStore = opts.agent.container?.safeResolve?.(TOKENS.ConfigStore);
1289
- const introspectionCtx = {
1290
- agent: opts.agent,
1291
- modelsRegistry: opts.modelsRegistry,
1292
- configStore: introspectionConfigStore,
1293
- getConfig: () => {
1294
- const cfg = introspectionConfigStore?.get() ?? opts.appConfig;
1295
- if (!cfg)
1296
- throw new Error(
1297
- "Introspection route requires a config but neither ConfigStore nor opts.appConfig is available"
1298
- );
1299
- return cfg;
1300
- },
1301
- getProjectRoot: () => opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "",
1302
- getSessionId: currentSessionId,
1303
- getSessionStartedAt: () => sessionStartedAt,
1304
- getModeId: () => opts.modeId ?? "default",
1305
- send
1306
- };
1307
- const skillsProjectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "";
1308
- const skillsPaths = skillsProjectRoot ? resolveWstackPaths2({
1309
- projectRoot: skillsProjectRoot,
1310
- globalRoot
1311
- }) : void 0;
1312
- const skillsCtx = {
1313
- skillLoader: opts.skillLoader,
1314
- skillInstaller: opts.skillLoader ? new SkillInstaller({
1315
- manifestPath: path5.join(skillsPaths?.configDir ?? profileDir, "installed-skills.json"),
1316
- projectSkillsDir: skillsPaths?.inProjectSkills ?? path5.join(skillsProjectRoot, ".wrongstack", "skills"),
1317
- globalSkillsDir: skillsPaths?.globalSkills ?? path5.join(profileDir, "skills"),
1318
- projectHash: skillsPaths?.projectHash ?? "",
1319
- skillLoader: opts.skillLoader
1320
- }) : void 0,
1321
- projectRoot: skillsProjectRoot,
1322
- projectSkillsDir: skillsPaths?.inProjectSkills,
1323
- globalSkillsDir: skillsPaths?.globalSkills
1324
- };
1325
- const promptsCtx = {
1326
- promptLoader: opts.promptLoader,
1327
- promptUsage: new PromptUsageStore(
1328
- skillsPaths?.promptUsage ?? path5.join(profileDir, "prompt-usage.json")
1329
- )
1330
- };
1331
- const designCtx = {
1332
- projectRoot: skillsProjectRoot,
1333
- agentMeta: opts.agent.ctx
1334
- };
1335
- const agentConfigCtx = {
1336
- agent: opts.agent,
1337
- modeStore: opts.modeStore,
1338
- loadSavedProviders: () => loadSavedProviders(profileConfigPath),
1339
- buildSessionStart: (overrides) => buildSessionStartPayload(overrides),
1340
- modelsRegistry: opts.modelsRegistry,
1341
- memoryStore: opts.memoryStore,
1342
- getConfig: () => opts.appConfig,
1343
- onMaxContextResolved: opts.onModelContextResolved,
1344
- persistPrefs,
1345
- send,
1346
- broadcast,
1347
- log: (m) => console.log(m)
1348
- };
1349
- const prefsCtx = {
1350
- meta: opts.agent.ctx.meta,
1351
- snapshot: prefSnapshot,
1352
- persist: persistPrefs,
1353
- setYolo: opts.onYoloSwitch,
1354
- setAutonomy: opts.onAutonomySwitch,
1355
- pendingConfirms,
1356
- configStore: opts.agent.container?.safeResolve?.(TOKENS.ConfigStore),
1357
- send,
1358
- broadcast
1359
- };
1360
- const projectsCtx = {
1453
+ let credentialWatcherClose = startWebuiCredentialWatcher({
1361
1454
  opts,
1362
- abortControllers,
1363
- abortLegacyRun: () => {
1364
- if (abortController) {
1365
- abortController.abort();
1366
- abortController = null;
1367
- }
1368
- },
1369
- buildSessionStart: (overrides) => buildSessionStartPayload(overrides),
1370
- send,
1455
+ profileConfigPath,
1371
1456
  broadcast,
1372
- log: (m) => console.log(m)
1373
- };
1374
- const mailboxRoutes = createMailboxRouteHandlers({
1375
- getProjectRoot: () => opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "",
1376
- getGlobalRoot: () => opts.globalConfigPath ? path5.dirname(opts.globalConfigPath) : "",
1377
- events: opts.events
1457
+ broadcastSaved: (providers) => embeddedProviderOperations.broadcastSaved(providers)
1378
1458
  });
1379
- const sessionsCtx = {
1459
+ const routeContexts = createWebuiRouteContexts({
1380
1460
  opts,
1381
- buildSessionStart: (overrides) => buildSessionStartPayload(overrides),
1461
+ profileConfigPath,
1462
+ profileDir,
1463
+ globalRoot,
1464
+ sessionStartedAt,
1465
+ currentSessionId,
1382
1466
  getCustomModeStore,
1383
- send,
1384
- broadcast,
1385
- log: (m) => console.log(m)
1386
- };
1387
- const connectionCtx = {
1388
- agent: opts.agent,
1389
- abortControllers,
1467
+ buildSessionStartPayload,
1468
+ prefSnapshot,
1469
+ persistPrefs,
1390
1470
  pendingConfirms,
1471
+ abortControllers,
1472
+ getAbortController: () => abortController,
1473
+ clearAbortController: () => {
1474
+ abortController = null;
1475
+ },
1391
1476
  send,
1392
- broadcast,
1393
- log: (m) => console.log(m)
1394
- };
1477
+ broadcast
1478
+ });
1395
1479
  const kanbanHostRoutes = createCliKanbanHostRoutes({
1396
1480
  opts,
1397
1481
  send,
@@ -1412,17 +1496,17 @@ async function runWebUI(opts) {
1412
1496
  currentSessionId,
1413
1497
  shutdown,
1414
1498
  providerCtx: wsHandlerCtx,
1415
- brainCtx,
1416
- introspectionCtx,
1417
- skillsCtx,
1418
- promptsCtx,
1419
- designCtx,
1420
- agentConfigCtx,
1421
- prefsCtx,
1422
- projectCtx: projectsCtx,
1423
- mailboxRoutes,
1424
- sessionCtx: sessionsCtx,
1425
- conversationCtx: connectionCtx,
1499
+ brainCtx: routeContexts.brainCtx,
1500
+ introspectionCtx: routeContexts.introspectionCtx,
1501
+ skillsCtx: routeContexts.skillsCtx,
1502
+ promptsCtx: routeContexts.promptsCtx,
1503
+ designCtx: routeContexts.designCtx,
1504
+ agentConfigCtx: routeContexts.agentConfigCtx,
1505
+ prefsCtx: routeContexts.prefsCtx,
1506
+ projectCtx: routeContexts.projectsCtx,
1507
+ mailboxRoutes: routeContexts.mailboxRoutes,
1508
+ sessionCtx: routeContexts.sessionsCtx,
1509
+ conversationCtx: routeContexts.connectionCtx,
1426
1510
  goalHandler,
1427
1511
  specsHandler,
1428
1512
  sddBoardHandler,
@@ -1449,7 +1533,7 @@ async function runWebUI(opts) {
1449
1533
  webuiInstanceRegistered
1450
1534
  });
1451
1535
  } catch (err) {
1452
- consoleLogger.error("setup_events_failed", { message: toErrorMessage(err) });
1536
+ consoleLogger.error("setup_events_failed", { message: toErrorMessage2(err) });
1453
1537
  }
1454
1538
  };
1455
1539
  wss.on("listening", announceListening);
@@ -1487,9 +1571,6 @@ async function runWebUI(opts) {
1487
1571
  const usage = opts.agent.ctx.tokenCounter.total();
1488
1572
  return { messages: opts.agent.ctx.messages, usage };
1489
1573
  },
1490
- // Reads the on-disk transcripts when the in-memory ring is cold, which
1491
- // it always is after a resume — the ring only holds what this process
1492
- // watched happen.
1493
1574
  loadAgentSessions: async () => await opts.agentTranscripts?.loadSessionsFromDisk() ?? [],
1494
1575
  needsSetup: opts.needsSetup ?? false
1495
1576
  })
@@ -1517,9 +1598,6 @@ async function runWebUI(opts) {
1517
1598
  flushAllStreamBuffers();
1518
1599
  for (const unsub of eventUnsubscribers) unsub();
1519
1600
  },
1520
- // Issue #322: kill session-owned children (MCP, bash/exec, tool-spawned
1521
- // dev servers) before closing the listen socket so Windows process trees
1522
- // do not outlive the parent and strand the event loop on open stdio.
1523
1601
  stopOwnedChildren: async () => {
1524
1602
  try {
1525
1603
  const { getProcessRegistry } = await import("@wrongstack/tools");
@@ -1591,4 +1669,4 @@ async function runWebUI(opts) {
1591
1669
  export {
1592
1670
  runWebUI
1593
1671
  };
1594
- //# sourceMappingURL=webui-server-FTG7JAZU.js.map
1672
+ //# sourceMappingURL=webui-server-IPHGJGXE.js.map