@wrongstack/webui-server 0.284.1 → 0.285.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/dist/index.d.ts +11 -2300
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +1019 -277
  4. package/dist/index.js.map +7 -1
  5. package/dist/server/autophase-routes.d.ts +10 -0
  6. package/dist/server/autophase-routes.d.ts.map +1 -0
  7. package/dist/server/autophase-ws-handler.d.ts +87 -0
  8. package/dist/server/autophase-ws-handler.d.ts.map +1 -0
  9. package/dist/server/backend-services.d.ts +114 -0
  10. package/dist/server/backend-services.d.ts.map +1 -0
  11. package/dist/server/boot.d.ts +19 -0
  12. package/dist/server/boot.d.ts.map +1 -0
  13. package/dist/server/brain-routes.d.ts +9 -0
  14. package/dist/server/brain-routes.d.ts.map +1 -0
  15. package/dist/server/codebase-indexing.d.ts +15 -0
  16. package/dist/server/codebase-indexing.d.ts.map +1 -0
  17. package/dist/server/collaboration-ws-handler.d.ts +159 -0
  18. package/dist/server/collaboration-ws-handler.d.ts.map +1 -0
  19. package/dist/server/completion-handlers.d.ts +39 -0
  20. package/dist/server/completion-handlers.d.ts.map +1 -0
  21. package/dist/server/connection-handler.d.ts +72 -0
  22. package/dist/server/connection-handler.d.ts.map +1 -0
  23. package/dist/server/context-meta.d.ts +28 -0
  24. package/dist/server/context-meta.d.ts.map +1 -0
  25. package/dist/server/custom-context-modes.d.ts +43 -0
  26. package/dist/server/custom-context-modes.d.ts.map +1 -0
  27. package/dist/server/design-handlers.d.ts +40 -0
  28. package/dist/server/design-handlers.d.ts.map +1 -0
  29. package/dist/server/discover-mailbox-bridge.d.ts +26 -0
  30. package/dist/server/discover-mailbox-bridge.d.ts.map +1 -0
  31. package/dist/server/entry.d.ts +2 -2
  32. package/dist/server/entry.d.ts.map +1 -0
  33. package/dist/server/entry.js +811 -300
  34. package/dist/server/entry.js.map +7 -1
  35. package/dist/server/eternal-iteration-broadcast.d.ts +11 -0
  36. package/dist/server/eternal-iteration-broadcast.d.ts.map +1 -0
  37. package/dist/server/file-handlers.d.ts +45 -0
  38. package/dist/server/file-handlers.d.ts.map +1 -0
  39. package/dist/server/file-picker.d.ts +26 -0
  40. package/dist/server/file-picker.d.ts.map +1 -0
  41. package/dist/server/git-handlers.d.ts +55 -0
  42. package/dist/server/git-handlers.d.ts.map +1 -0
  43. package/dist/server/goal-handlers.d.ts +19 -0
  44. package/dist/server/goal-handlers.d.ts.map +1 -0
  45. package/dist/server/handlers/index.d.ts +12 -0
  46. package/dist/server/handlers/index.d.ts.map +1 -0
  47. package/dist/server/handlers/worklist-handlers.d.ts +49 -0
  48. package/dist/server/handlers/worklist-handlers.d.ts.map +1 -0
  49. package/dist/server/handlers.d.ts +1 -51
  50. package/dist/server/handlers.js +17 -8
  51. package/dist/server/handlers.js.map +7 -1
  52. package/dist/server/http-server/analytics-handler.d.ts +35 -0
  53. package/dist/server/http-server/analytics-handler.d.ts.map +1 -0
  54. package/dist/server/http-server/api-handlers.d.ts +44 -0
  55. package/dist/server/http-server/api-handlers.d.ts.map +1 -0
  56. package/dist/server/http-server.d.ts +108 -0
  57. package/dist/server/http-server.d.ts.map +1 -0
  58. package/dist/server/index.d.ts +93 -0
  59. package/dist/server/index.d.ts.map +1 -0
  60. package/dist/server/instance-registry.d.ts +63 -0
  61. package/dist/server/instance-registry.d.ts.map +1 -0
  62. package/dist/server/kanban-routes.d.ts +7 -0
  63. package/dist/server/kanban-routes.d.ts.map +1 -0
  64. package/dist/server/lifecycle.d.ts +51 -0
  65. package/dist/server/lifecycle.d.ts.map +1 -0
  66. package/dist/server/mailbox-handlers.d.ts +51 -0
  67. package/dist/server/mailbox-handlers.d.ts.map +1 -0
  68. package/dist/server/mailbox-routes.d.ts +10 -0
  69. package/dist/server/mailbox-routes.d.ts.map +1 -0
  70. package/dist/server/mcp-handlers.d.ts +46 -0
  71. package/dist/server/mcp-handlers.d.ts.map +1 -0
  72. package/dist/server/mcp-routes.d.ts +37 -0
  73. package/dist/server/mcp-routes.d.ts.map +1 -0
  74. package/dist/server/memory-handlers.d.ts +28 -0
  75. package/dist/server/memory-handlers.d.ts.map +1 -0
  76. package/dist/server/message-dispatcher.d.ts +41 -0
  77. package/dist/server/message-dispatcher.d.ts.map +1 -0
  78. package/dist/server/mode-handlers.d.ts +50 -0
  79. package/dist/server/mode-handlers.d.ts.map +1 -0
  80. package/dist/server/mode-routes.d.ts +8 -0
  81. package/dist/server/mode-routes.d.ts.map +1 -0
  82. package/dist/server/model-auto-discovery.d.ts +9 -0
  83. package/dist/server/model-auto-discovery.d.ts.map +1 -0
  84. package/dist/server/model-catalog.d.ts +16 -0
  85. package/dist/server/model-catalog.d.ts.map +1 -0
  86. package/dist/server/open-browser.d.ts +16 -0
  87. package/dist/server/open-browser.d.ts.map +1 -0
  88. package/dist/server/path-containment.d.ts +3 -0
  89. package/dist/server/path-containment.d.ts.map +1 -0
  90. package/dist/server/pending-confirms.d.ts +14 -0
  91. package/dist/server/pending-confirms.d.ts.map +1 -0
  92. package/dist/server/port-utils.d.ts +28 -0
  93. package/dist/server/port-utils.d.ts.map +1 -0
  94. package/dist/server/pre-context-services.d.ts +58 -0
  95. package/dist/server/pre-context-services.d.ts.map +1 -0
  96. package/dist/server/pref-helpers.d.ts +43 -0
  97. package/dist/server/pref-helpers.d.ts.map +1 -0
  98. package/dist/server/prefs-routes.d.ts +27 -0
  99. package/dist/server/prefs-routes.d.ts.map +1 -0
  100. package/dist/server/process-handlers.d.ts +20 -0
  101. package/dist/server/process-handlers.d.ts.map +1 -0
  102. package/dist/server/project-handlers.d.ts +53 -0
  103. package/dist/server/project-handlers.d.ts.map +1 -0
  104. package/dist/server/project-routes.d.ts +10 -0
  105. package/dist/server/project-routes.d.ts.map +1 -0
  106. package/dist/server/projects-manifest.d.ts +18 -0
  107. package/dist/server/projects-manifest.d.ts.map +1 -0
  108. package/dist/server/prompts-handlers.d.ts +38 -0
  109. package/dist/server/prompts-handlers.d.ts.map +1 -0
  110. package/dist/server/provider-config-io.d.ts +15 -0
  111. package/dist/server/provider-config-io.d.ts.map +1 -0
  112. package/dist/server/provider-config-standalone.d.ts +13 -0
  113. package/dist/server/provider-config-standalone.d.ts.map +1 -0
  114. package/dist/server/provider-handlers.d.ts +74 -0
  115. package/dist/server/provider-handlers.d.ts.map +1 -0
  116. package/dist/server/provider-keys.d.ts +53 -0
  117. package/dist/server/provider-keys.d.ts.map +1 -0
  118. package/dist/server/provider-routes.d.ts +13 -0
  119. package/dist/server/provider-routes.d.ts.map +1 -0
  120. package/dist/server/provider-store.d.ts +26 -0
  121. package/dist/server/provider-store.d.ts.map +1 -0
  122. package/dist/server/routes.d.ts +182 -0
  123. package/dist/server/routes.d.ts.map +1 -0
  124. package/dist/server/sdd-board-routes.d.ts +11 -0
  125. package/dist/server/sdd-board-routes.d.ts.map +1 -0
  126. package/dist/server/sdd-board-ws-handler.d.ts +57 -0
  127. package/dist/server/sdd-board-ws-handler.d.ts.map +1 -0
  128. package/dist/server/sdd-wizard-routes.d.ts +16 -0
  129. package/dist/server/sdd-wizard-routes.d.ts.map +1 -0
  130. package/dist/server/sdd-wizard-wiring.d.ts +26 -0
  131. package/dist/server/sdd-wizard-wiring.d.ts.map +1 -0
  132. package/dist/server/sdd-wizard-ws-handler.d.ts +66 -0
  133. package/dist/server/sdd-wizard-ws-handler.d.ts.map +1 -0
  134. package/dist/server/server-runtime.d.ts +118 -0
  135. package/dist/server/server-runtime.d.ts.map +1 -0
  136. package/dist/server/session-handlers.d.ts +44 -0
  137. package/dist/server/session-handlers.d.ts.map +1 -0
  138. package/dist/server/session-routes.d.ts +23 -0
  139. package/dist/server/session-routes.d.ts.map +1 -0
  140. package/dist/server/setup-events.d.ts +67 -0
  141. package/dist/server/setup-events.d.ts.map +1 -0
  142. package/dist/server/setup-screen.d.ts +44 -0
  143. package/dist/server/setup-screen.d.ts.map +1 -0
  144. package/dist/server/shell-git-routes.d.ts +10 -0
  145. package/dist/server/shell-git-routes.d.ts.map +1 -0
  146. package/dist/server/shell-open.d.ts +12 -0
  147. package/dist/server/shell-open.d.ts.map +1 -0
  148. package/dist/server/skills-handlers.d.ts +77 -0
  149. package/dist/server/skills-handlers.d.ts.map +1 -0
  150. package/dist/server/specs-routes.d.ts +11 -0
  151. package/dist/server/specs-routes.d.ts.map +1 -0
  152. package/dist/server/specs-ws-handler.d.ts +34 -0
  153. package/dist/server/specs-ws-handler.d.ts.map +1 -0
  154. package/dist/server/start-webui.d.ts +29 -0
  155. package/dist/server/start-webui.d.ts.map +1 -0
  156. package/dist/server/terminal-ws-handler.d.ts +70 -0
  157. package/dist/server/terminal-ws-handler.d.ts.map +1 -0
  158. package/dist/server/token-estimator.d.ts +66 -0
  159. package/dist/server/token-estimator.d.ts.map +1 -0
  160. package/dist/server/types.d.ts +143 -0
  161. package/dist/server/types.d.ts.map +1 -0
  162. package/dist/server/usage-cost.d.ts +39 -0
  163. package/dist/server/usage-cost.d.ts.map +1 -0
  164. package/dist/server/worktree-ws-handler.d.ts +67 -0
  165. package/dist/server/worktree-ws-handler.d.ts.map +1 -0
  166. package/dist/server/ws-auth.d.ts +83 -0
  167. package/dist/server/ws-auth.d.ts.map +1 -0
  168. package/dist/server/ws-payload-validation.d.ts +122 -0
  169. package/dist/server/ws-payload-validation.d.ts.map +1 -0
  170. package/dist/server/ws-utils.d.ts +38 -0
  171. package/dist/server/ws-utils.d.ts.map +1 -0
  172. package/dist/server/zip.d.ts +9 -0
  173. package/dist/server/zip.d.ts.map +1 -0
  174. package/package.json +11 -12
@@ -1,35 +1,18 @@
1
1
  #!/usr/bin/env node
2
- import { DefaultSecretScrubber, ToolValidationError, expectDefined, createDefaultPipelines, resolveSessionLoggingConfig, createSessionEventBridge, watchProviderConfig, atomicWrite, bootConfig as bootConfig$1, DefaultModelsRegistry, TOKENS, ProviderRegistry, ToolRegistry, DefaultMemoryStore, EventBus, makeMailboxTool, makeMailSendTool, makeMailInboxTool, makeFleetStatusTool, applyToolDescriptionModes, applyToolResultRenderModes, DefaultSessionStore, getSessionRegistry, DEFAULT_SESSION_PRUNE_DAYS, DefaultSessionReader, AnnotationsStore, FleetNotifier, AgentStatusTracker, DefaultModeStore, DefaultSkillLoader, SkillInstaller, DefaultPromptLoader, PromptUsageStore, DefaultSystemPromptBuilder, GlobalMailbox, Context, resolveContextWindowPolicy, CollaborationBus, collabInjectMiddleware, collabPauseMiddleware, installDesignStudioMiddleware, createStrategyCompactor, AutoCompactionMiddleware, estimateRequestTokensCalibrated, ToolExecutor, DEFAULT_TOOLS_CONFIG, Agent, ObservableBrainArbiter, createTieredBrainArbiter, DefaultBrainArbiter, BrainMonitor, mailboxSessionTag, resolveProjectDir, wstackGlobalRoot, createAutonomyBrain, PhaseStore, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, AutoPhasePlanner, assignNickname, computeTaskProgress, DEFAULT_CONTEXT_WINDOW_MODE_ID, repairToolUseAdjacency, resolveWstackPaths, recentTextTurns, gatedEnhancerReasoning, enhanceUserPrompt, resolveProviderModelList, listContextWindowModes, projectSlug, allServers, ConfigError, deserializeTaskGraph, serializeTaskGraph, loadActiveKit, getDesignKitLoader, applyTokenOverrides, runDesignVerify, isDesignStack, materializeTokens, recordOverrides, setDesignOverrides, getDesignState, setActiveKit, recordKitChoice } from '@wrongstack/core';
3
- import { spawnSync, spawn } from 'child_process';
4
- import { toErrorMessage, atomicWrite as atomicWrite$1, resolveWstackPaths as resolveWstackPaths$1, wstackGlobalRoot as wstackGlobalRoot$1 } from '@wrongstack/core/utils';
5
- import * as path5 from 'path';
6
- import path5__default, { resolve, join, sep, dirname } from 'path';
7
- import { searchCodebaseIndex } from '@wrongstack/tools/codebase-index/index';
8
- import { randomUUID, randomBytes, timingSafeEqual } from 'crypto';
9
- import { WebSocket, WebSocketServer } from 'ws';
10
- import * as fs4 from 'fs/promises';
11
- import * as http from 'http';
12
- import { Buffer } from 'buffer';
13
- import * as os from 'os';
14
- import { MCPRegistry, discoverMcp, restartMcp, disableMcp, enableMcp, removeMcp, updateMcp, addMcp, listMcp } from '@wrongstack/mcp';
15
- import * as net from 'net';
16
- import { decryptConfigSecrets, encryptConfigSecrets } from '@wrongstack/core/security';
17
- import { SpecStore, TaskGraphStore, SddBoardStore, applySddLifecycle, SddRunRegistry, SddInterviewDriver, cleanupStaleSddWorktrees, makeCommandVerifier, SddSupervisor, makeLlmSubtaskGenerator, startSddRun } from '@wrongstack/sdd';
18
- import * as fs12 from 'fs';
19
- import { watch, existsSync, promises } from 'fs';
20
- import JSZip from 'jszip';
21
- import { createRequire } from 'module';
22
- import { fileURLToPath } from 'url';
23
- import { makeProviderFromConfig, buildProviderFactoriesFromRegistry, discoverOpenAICompatibleModels, capabilitiesFor, COMPATIBLE_PRESETS } from '@wrongstack/providers';
24
- import { DefaultTokenCounter } from '@wrongstack/core/infrastructure';
25
- import { createDefaultContainer, makeLightSubagentFactory } from '@wrongstack/runtime';
26
- import { ensureSessionShell, builtinToolsPack, rememberTool, forgetTool, searchMemoryTool, relatedMemoryTool, configureExecPolicy, configureDangerBypass, runStartupIndex, isIndexableFile, enqueueReindex, cancelPendingReindexes, shutdownCodebaseIndexHost } from '@wrongstack/tools';
27
- import { readLiveLock } from '@wrongstack/core/coordination';
28
- import { removeColumn, addColumn, getTask, removeTask, assignTask, updateGoalMetricOnTask, addGoalMetricToTask, releaseTaskClaim, claimReadyTask, getTaskChain, setTaskChain, copyTaskToBoard, transferTaskToBoard, moveTask, updateTask, mergeTasks, splitTask, addTask, syncBoardFromTaskGraph, exportBoardToTaskGraph, getKanbanOrchestrationSnapshot, listReadyTasks, createBoard, generateBoardFromDescription, parseLinesIntoTasks, getBoard, removeBoard, duplicateBoard, updateBoard, getKanbanQueueHealth, listBoards } from '@wrongstack/kanban';
29
- import { beginOAuthLogin } from '@wrongstack/providers/oauth';
30
- import { probeLocalLlm } from '@wrongstack/runtime/probe';
31
-
32
2
  // src/server/entry.ts
3
+ import { ToolValidationError as ToolValidationError6 } from "@wrongstack/core";
4
+
5
+ // src/server/autophase-ws-handler.ts
6
+ import { spawnSync } from "node:child_process";
7
+ import { toErrorMessage } from "@wrongstack/core/utils";
8
+ import {
9
+ assignNickname,
10
+ AutoPhasePlanner,
11
+ PhaseGraphBuilder,
12
+ PhaseOrchestrator,
13
+ PhaseStore,
14
+ WorktreeManager
15
+ } from "@wrongstack/core";
33
16
  function deriveTitle(goal) {
34
17
  const firstLine = goal.split("\n").map((l) => l.trim()).find(Boolean);
35
18
  if (!firstLine) return "AutoPhase";
@@ -520,6 +503,14 @@ Type: ${task.type}`;
520
503
  }
521
504
  }
522
505
  };
506
+
507
+ // src/server/completion-handlers.ts
508
+ import * as path from "node:path";
509
+ import { searchCodebaseIndex } from "@wrongstack/tools/codebase-index/index";
510
+
511
+ // src/server/ws-utils.ts
512
+ import { randomBytes } from "node:crypto";
513
+ import { WebSocket } from "ws";
523
514
  function send(ws, msg) {
524
515
  if (ws.readyState === WebSocket.OPEN) {
525
516
  ws.send(JSON.stringify(msg));
@@ -651,8 +642,8 @@ async function handleCompletionRequest(ws, msg, opts) {
651
642
  return;
652
643
  }
653
644
  const payload = parsed.payload;
654
- const projectRoot = path5.resolve(opts.projectRoot);
655
- const resolved = path5.resolve(projectRoot, payload.filePath);
645
+ const projectRoot = path.resolve(opts.projectRoot);
646
+ const resolved = path.resolve(projectRoot, payload.filePath);
656
647
  if (!isInside(projectRoot, resolved)) {
657
648
  send(ws, {
658
649
  type: "completion.result",
@@ -1051,7 +1042,7 @@ function buildSearchQuery(linePrefix, filePath) {
1051
1042
  if (memberMatch?.[1]) return memberMatch[1];
1052
1043
  const token = linePrefix.match(/([A-Za-z_$][\w$]*)$/)?.[1];
1053
1044
  if (token && token.length >= 2) return token;
1054
- return path5.basename(filePath, path5.extname(filePath));
1045
+ return path.basename(filePath, path.extname(filePath));
1055
1046
  }
1056
1047
  function currentLinePrefix(prefix) {
1057
1048
  const idx = Math.max(prefix.lastIndexOf("\n"), prefix.lastIndexOf("\r"));
@@ -1081,11 +1072,16 @@ function head(value, max) {
1081
1072
  return value.length <= max ? value : value.slice(0, max);
1082
1073
  }
1083
1074
  function isInside(root, target) {
1084
- return target === root || target.startsWith(root + path5.sep);
1075
+ return target === root || target.startsWith(root + path.sep);
1085
1076
  }
1077
+
1078
+ // src/server/custom-context-modes.ts
1079
+ import { listContextWindowModes, atomicWrite } from "@wrongstack/core";
1080
+ import * as fs from "node:fs/promises";
1081
+ import * as path2 from "node:path";
1086
1082
  var STORE_FILENAME = "custom-context-modes.json";
1087
1083
  function storePath(wrongstackDir) {
1088
- return path5.join(wrongstackDir, STORE_FILENAME);
1084
+ return path2.join(wrongstackDir, STORE_FILENAME);
1089
1085
  }
1090
1086
  var BUILTIN_IDS = /* @__PURE__ */ new Set(["balanced", "frugal", "deep", "archival"]);
1091
1087
  function createCustomModeStore(wrongstackDir) {
@@ -1093,7 +1089,7 @@ function createCustomModeStore(wrongstackDir) {
1093
1089
  const load2 = async () => {
1094
1090
  modes.clear();
1095
1091
  try {
1096
- const raw = await fs4.readFile(storePath(wrongstackDir), "utf8");
1092
+ const raw = await fs.readFile(storePath(wrongstackDir), "utf8");
1097
1093
  const parsed = JSON.parse(raw);
1098
1094
  if (Array.isArray(parsed.modes)) {
1099
1095
  for (const m of parsed.modes) {
@@ -1198,6 +1194,23 @@ function createCustomModeStore(wrongstackDir) {
1198
1194
  };
1199
1195
  return { modes, load: load2, save: save2, create, update, remove, list };
1200
1196
  }
1197
+
1198
+ // src/server/design-handlers.ts
1199
+ import * as fs2 from "node:fs/promises";
1200
+ import * as path3 from "node:path";
1201
+ import {
1202
+ applyTokenOverrides,
1203
+ getDesignKitLoader,
1204
+ getDesignState,
1205
+ isDesignStack,
1206
+ loadActiveKit,
1207
+ materializeTokens,
1208
+ recordKitChoice,
1209
+ recordOverrides,
1210
+ runDesignVerify,
1211
+ setActiveKit,
1212
+ setDesignOverrides
1213
+ } from "@wrongstack/core";
1201
1214
  function readOverrides(value) {
1202
1215
  const out = {};
1203
1216
  if (value && typeof value === "object") {
@@ -1345,9 +1358,9 @@ async function handleDesignMaterialize(ws, ctx, msg) {
1345
1358
  kitId: active.kit,
1346
1359
  outPath: typeof payload.out === "string" ? payload.out : void 0
1347
1360
  });
1348
- const abs = path5.join(ctx.projectRoot, result.path);
1349
- await fs4.mkdir(path5.dirname(abs), { recursive: true });
1350
- await fs4.writeFile(abs, result.content);
1361
+ const abs = path3.join(ctx.projectRoot, result.path);
1362
+ await fs2.mkdir(path3.dirname(abs), { recursive: true });
1363
+ await fs2.writeFile(abs, result.content);
1351
1364
  send(ws, {
1352
1365
  type: "design.materialize",
1353
1366
  payload: { ok: true, path: result.path, format: result.format, stack }
@@ -1406,6 +1419,11 @@ function createEternalSubscription(subscribe, broadcast2, clientsRef) {
1406
1419
  };
1407
1420
  }
1408
1421
 
1422
+ // src/server/file-handlers.ts
1423
+ import * as fs4 from "node:fs/promises";
1424
+ import * as path5 from "node:path";
1425
+ import { atomicWrite as atomicWrite2, ToolValidationError as ToolValidationError2 } from "@wrongstack/core";
1426
+
1409
1427
  // src/server/file-picker.ts
1410
1428
  var SKIP_DIRS = /* @__PURE__ */ new Set([
1411
1429
  ".git",
@@ -1453,15 +1471,20 @@ function rankFiles(paths, query, limit) {
1453
1471
  scored.sort((a, b) => b.score - a.score || a.path.localeCompare(b.path));
1454
1472
  return scored.slice(0, limit).map((s) => s.path);
1455
1473
  }
1474
+
1475
+ // src/server/path-containment.ts
1476
+ import * as fs3 from "node:fs/promises";
1477
+ import * as path4 from "node:path";
1478
+ import { ToolValidationError } from "@wrongstack/core";
1456
1479
  function isPathInside(root, target) {
1457
- const relative4 = path5.relative(root, target);
1458
- return relative4 === "" || !relative4.startsWith("..") && !path5.isAbsolute(relative4);
1480
+ const relative4 = path4.relative(root, target);
1481
+ return relative4 === "" || !relative4.startsWith("..") && !path4.isAbsolute(relative4);
1459
1482
  }
1460
1483
  async function resolveWorkingDirInsideProject(projectRoot, inputPath) {
1461
- const resolved = path5.resolve(projectRoot, inputPath);
1484
+ const resolved = path4.resolve(projectRoot, inputPath);
1462
1485
  let stat2;
1463
1486
  try {
1464
- stat2 = await fs4.stat(resolved);
1487
+ stat2 = await fs3.stat(resolved);
1465
1488
  } catch {
1466
1489
  throw new ToolValidationError({
1467
1490
  message: `Directory not found or not accessible: ${resolved}`,
@@ -1475,8 +1498,8 @@ async function resolveWorkingDirInsideProject(projectRoot, inputPath) {
1475
1498
  });
1476
1499
  }
1477
1500
  const [realProjectRoot, realResolved] = await Promise.all([
1478
- fs4.realpath(projectRoot),
1479
- fs4.realpath(resolved)
1501
+ fs3.realpath(projectRoot),
1502
+ fs3.realpath(resolved)
1480
1503
  ]);
1481
1504
  if (!isPathInside(realProjectRoot, realResolved)) {
1482
1505
  throw new ToolValidationError({
@@ -1491,14 +1514,14 @@ async function resolveWorkingDirInsideProject(projectRoot, inputPath) {
1491
1514
  async function resolveFileInsideProject(projectRoot, filePath) {
1492
1515
  const resolved = path5.resolve(projectRoot, filePath);
1493
1516
  if (!isPathInside(projectRoot, resolved)) {
1494
- throw new ToolValidationError({ message: "Path outside project root", field: "path" });
1517
+ throw new ToolValidationError2({ message: "Path outside project root", field: "path" });
1495
1518
  }
1496
1519
  const { parent, base } = splitParentAndBase(resolved);
1497
1520
  const realProjectRoot = await fs4.realpath(projectRoot);
1498
1521
  const realParent = await realpathAllowMissing(parent);
1499
1522
  const realFull = path5.join(realParent, base);
1500
1523
  if (!isPathInside(realProjectRoot, realFull)) {
1501
- throw new ToolValidationError({ message: "Path outside project root", field: "path" });
1524
+ throw new ToolValidationError2({ message: "Path outside project root", field: "path" });
1502
1525
  }
1503
1526
  return realFull;
1504
1527
  }
@@ -1518,7 +1541,7 @@ async function realpathAllowMissing(p) {
1518
1541
  while (true) {
1519
1542
  const parent = path5.dirname(cursor);
1520
1543
  if (parent === cursor) {
1521
- throw new ToolValidationError({ message: "Path outside project root", field: "path" });
1544
+ throw new ToolValidationError2({ message: "Path outside project root", field: "path" });
1522
1545
  }
1523
1546
  segments.unshift(path5.basename(cursor));
1524
1547
  try {
@@ -1651,7 +1674,7 @@ async function handleFilesWrite(ws, msg, projectRoot, opts = {}) {
1651
1674
  return;
1652
1675
  }
1653
1676
  try {
1654
- await atomicWrite(realResolved, content);
1677
+ await atomicWrite2(realResolved, content);
1655
1678
  send(ws, { type: "files.written", payload: { filePath, success: true } });
1656
1679
  if (opts.onWritten) {
1657
1680
  void Promise.resolve(opts.onWritten(realResolved)).catch(() => void 0);
@@ -1715,10 +1738,13 @@ async function handleFilesList(ws, msg, projectRoot) {
1715
1738
  payload: { files: rankFiles(results, payload.query ?? "", limit) }
1716
1739
  });
1717
1740
  }
1741
+
1742
+ // src/server/git-handlers.ts
1743
+ import nodePath from "node:path";
1718
1744
  async function handleGitInfo(ws, projectRoot) {
1719
1745
  const cwd = projectRoot || void 0;
1720
1746
  try {
1721
- const { execFile: ef } = await import('child_process');
1747
+ const { execFile: ef } = await import("node:child_process");
1722
1748
  const git = (args) => new Promise((resolve10) => {
1723
1749
  ef("git", args, { cwd, timeout: 3e3 }, (err, stdout) => {
1724
1750
  resolve10(err ? "" : stdout.trim());
@@ -1746,7 +1772,7 @@ async function handleGitInfo(ws, projectRoot) {
1746
1772
  }
1747
1773
  function makeGit(cwd) {
1748
1774
  return async (args) => {
1749
- const { execFile: ef } = await import('child_process');
1775
+ const { execFile: ef } = await import("node:child_process");
1750
1776
  return new Promise((resolve10) => {
1751
1777
  ef(
1752
1778
  "git",
@@ -1776,15 +1802,15 @@ async function handleGitChanges(ws, projectRoot) {
1776
1802
  if (!m) continue;
1777
1803
  const added = m[1] === "-" ? 0 : Number(m[1]);
1778
1804
  const deleted = m[2] === "-" ? 0 : Number(m[2]);
1779
- let path23 = m[3] ?? "";
1780
- if (path23 === "") {
1805
+ let path22 = m[3] ?? "";
1806
+ if (path22 === "") {
1781
1807
  i += 1;
1782
- path23 = parts[i + 1] ?? parts[i] ?? "";
1808
+ path22 = parts[i + 1] ?? parts[i] ?? "";
1783
1809
  i += 1;
1784
1810
  }
1785
- if (!path23) continue;
1786
- const prev = counts.get(path23) ?? { added: 0, deleted: 0 };
1787
- counts.set(path23, { added: prev.added + added, deleted: prev.deleted + deleted });
1811
+ if (!path22) continue;
1812
+ const prev = counts.get(path22) ?? { added: 0, deleted: 0 };
1813
+ counts.set(path22, { added: prev.added + added, deleted: prev.deleted + deleted });
1788
1814
  }
1789
1815
  };
1790
1816
  parseNumstat(unstagedNumstat);
@@ -1796,7 +1822,7 @@ async function handleGitChanges(ws, projectRoot) {
1796
1822
  if (!rec || rec.length < 3) continue;
1797
1823
  const x = rec[0] ?? " ";
1798
1824
  const y = rec[1] ?? " ";
1799
- const path23 = rec.slice(3);
1825
+ const path22 = rec.slice(3);
1800
1826
  const isRename = x === "R" || x === "C" || y === "R" || y === "C";
1801
1827
  if (isRename) i += 1;
1802
1828
  let status;
@@ -1808,13 +1834,13 @@ async function handleGitChanges(ws, projectRoot) {
1808
1834
  else if (x === "D" || y === "D") status = "D";
1809
1835
  else status = "M";
1810
1836
  const staged = x !== " " && x !== "?";
1811
- let added = counts.get(path23)?.added ?? 0;
1812
- let deleted = counts.get(path23)?.deleted ?? 0;
1837
+ let added = counts.get(path22)?.added ?? 0;
1838
+ let deleted = counts.get(path22)?.deleted ?? 0;
1813
1839
  if (status === "?") {
1814
1840
  added = 0;
1815
1841
  deleted = 0;
1816
1842
  }
1817
- files.push({ path: path23, status, added, deleted, staged });
1843
+ files.push({ path: path22, status, added, deleted, staged });
1818
1844
  }
1819
1845
  send(ws, { type: "git.changes", payload: { files } });
1820
1846
  } catch (err) {
@@ -1825,21 +1851,21 @@ async function handleGitChanges(ws, projectRoot) {
1825
1851
  }
1826
1852
  }
1827
1853
  var MAX_DIFF_BYTES = 2 * 1024 * 1024;
1828
- async function handleGitDiff(ws, projectRoot, path23) {
1854
+ async function handleGitDiff(ws, projectRoot, path22) {
1829
1855
  const cwd = projectRoot || void 0;
1830
- const reply = (extra) => send(ws, { type: "git.diff", payload: { path: path23, ...extra } });
1831
- if (!path23 || path23.includes("\0") || path23.includes("..") || path5__default.isAbsolute(path23)) {
1856
+ const reply = (extra) => send(ws, { type: "git.diff", payload: { path: path22, ...extra } });
1857
+ if (!path22 || path22.includes("\0") || path22.includes("..") || nodePath.isAbsolute(path22)) {
1832
1858
  reply({ oldText: "", newText: "", error: "invalid path" });
1833
1859
  return;
1834
1860
  }
1835
1861
  try {
1836
1862
  const git = makeGit(cwd);
1837
- const { readFile: readFile10 } = await import('fs/promises');
1838
- const { join: join15 } = await import('path');
1839
- const oldText = await git(["show", `HEAD:${path23}`]);
1863
+ const { readFile: readFile10 } = await import("node:fs/promises");
1864
+ const { join: join14 } = await import("node:path");
1865
+ const oldText = await git(["show", `HEAD:${path22}`]);
1840
1866
  let newText = "";
1841
1867
  try {
1842
- const abs = cwd ? join15(cwd, path23) : path23;
1868
+ const abs = cwd ? join14(cwd, path22) : path22;
1843
1869
  const buf = await readFile10(abs);
1844
1870
  if (buf.includes(0)) {
1845
1871
  reply({ oldText: "", newText: "", binary: true });
@@ -1867,6 +1893,11 @@ async function handleGitDiff(ws, projectRoot, path23) {
1867
1893
  }
1868
1894
  }
1869
1895
 
1896
+ // src/server/http-server.ts
1897
+ import * as fs5 from "node:fs/promises";
1898
+ import * as http from "node:http";
1899
+ import * as path6 from "node:path";
1900
+
1870
1901
  // src/server/http-server/api-handlers.ts
1871
1902
  async function handleApiSessions(res, globalRoot) {
1872
1903
  if (!globalRoot) {
@@ -1875,7 +1906,7 @@ async function handleApiSessions(res, globalRoot) {
1875
1906
  return;
1876
1907
  }
1877
1908
  try {
1878
- const { SessionRegistry } = await import('@wrongstack/core');
1909
+ const { SessionRegistry } = await import("@wrongstack/core");
1879
1910
  const registry = new SessionRegistry(globalRoot);
1880
1911
  const sessions = await registry.list();
1881
1912
  const result = sessions.map((s) => ({
@@ -1913,7 +1944,7 @@ async function handleApiSessionAgents(res, globalRoot, sessionId) {
1913
1944
  return;
1914
1945
  }
1915
1946
  try {
1916
- const { SessionRegistry } = await import('@wrongstack/core');
1947
+ const { SessionRegistry } = await import("@wrongstack/core");
1917
1948
  const registry = new SessionRegistry(globalRoot);
1918
1949
  const entry = await registry.get(sessionId);
1919
1950
  if (!entry) {
@@ -2053,7 +2084,7 @@ async function handleApiSessionEvents(res, globalRoot, sessionId, limit) {
2053
2084
  return;
2054
2085
  }
2055
2086
  try {
2056
- const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, DefaultSessionStore: DefaultSessionStore2, DefaultSessionReader: DefaultSessionReader2 } = await import('@wrongstack/core');
2087
+ const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, DefaultSessionStore: DefaultSessionStore2, DefaultSessionReader: DefaultSessionReader2 } = await import("@wrongstack/core");
2057
2088
  const registry = new SessionRegistry(globalRoot);
2058
2089
  const entry = await registry.get(sessionId);
2059
2090
  if (!entry) {
@@ -2135,7 +2166,7 @@ async function handleApiSessionMessage(res, req, globalRoot, sessionId) {
2135
2166
  const priority = ["low", "normal", "high"].includes(rawPriority) ? rawPriority : "high";
2136
2167
  const subject = typeof body["subject"] === "string" && body["subject"].trim() ? body["subject"].trim() : "Message from Fleet HQ";
2137
2168
  try {
2138
- const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, GlobalMailbox: GlobalMailbox4, mailboxSessionTag: mailboxSessionTag2 } = await import('@wrongstack/core');
2169
+ const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, GlobalMailbox: GlobalMailbox4, mailboxSessionTag: mailboxSessionTag2 } = await import("@wrongstack/core");
2139
2170
  const registry = new SessionRegistry(globalRoot);
2140
2171
  const entry = await registry.get(sessionId);
2141
2172
  if (!entry) {
@@ -2161,7 +2192,7 @@ async function handleApiSessionMailbox(res, globalRoot, sessionId) {
2161
2192
  return;
2162
2193
  }
2163
2194
  try {
2164
- const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, GlobalMailbox: GlobalMailbox4, mailboxSessionTag: mailboxSessionTag2 } = await import('@wrongstack/core');
2195
+ const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, GlobalMailbox: GlobalMailbox4, mailboxSessionTag: mailboxSessionTag2 } = await import("@wrongstack/core");
2165
2196
  const registry = new SessionRegistry(globalRoot);
2166
2197
  const entry = await registry.get(sessionId);
2167
2198
  if (!entry) {
@@ -2219,7 +2250,7 @@ async function handleApiSessionInterrupt(res, req, globalRoot, sessionId) {
2219
2250
  const reason = typeof body["reason"] === "string" && body["reason"].trim() ? body["reason"].trim() : "Operator requested stop from Fleet HQ";
2220
2251
  const from = typeof body["from"] === "string" && body["from"].trim() ? body["from"].trim() : "human@webui";
2221
2252
  try {
2222
- const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, GlobalMailbox: GlobalMailbox4, mailboxSessionTag: mailboxSessionTag2 } = await import('@wrongstack/core');
2253
+ const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, GlobalMailbox: GlobalMailbox4, mailboxSessionTag: mailboxSessionTag2 } = await import("@wrongstack/core");
2223
2254
  const registry = new SessionRegistry(globalRoot);
2224
2255
  const entry = await registry.get(sessionId);
2225
2256
  if (!entry) {
@@ -2267,7 +2298,7 @@ async function handleApiFleetBroadcast(res, req, globalRoot) {
2267
2298
  }
2268
2299
  const from = typeof body["from"] === "string" && body["from"].trim() ? body["from"].trim() : "human@webui";
2269
2300
  try {
2270
- const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, GlobalMailbox: GlobalMailbox4, mailboxSessionTag: mailboxSessionTag2 } = await import('@wrongstack/core');
2301
+ const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, GlobalMailbox: GlobalMailbox4, mailboxSessionTag: mailboxSessionTag2 } = await import("@wrongstack/core");
2271
2302
  const registry = new SessionRegistry(globalRoot);
2272
2303
  const all = await registry.list();
2273
2304
  const mySlug = all.find((s) => s.pid === process.pid)?.projectSlug;
@@ -2415,6 +2446,10 @@ async function handleApiAnalyticsSummary(res) {
2415
2446
  })
2416
2447
  );
2417
2448
  }
2449
+
2450
+ // src/server/ws-auth.ts
2451
+ import { Buffer as Buffer2 } from "node:buffer";
2452
+ import { timingSafeEqual } from "node:crypto";
2418
2453
  function isLoopbackHostname(hostname) {
2419
2454
  return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
2420
2455
  }
@@ -2443,8 +2478,8 @@ function allowedHostname(hostname, allowedHostnames) {
2443
2478
  }
2444
2479
  function tokenMatches(provided, expected) {
2445
2480
  if (!provided) return false;
2446
- const a = Buffer.from(provided);
2447
- const b = Buffer.from(expected);
2481
+ const a = Buffer2.from(provided);
2482
+ const b = Buffer2.from(expected);
2448
2483
  if (a.length !== b.length) return false;
2449
2484
  return timingSafeEqual(a, b);
2450
2485
  }
@@ -2600,9 +2635,9 @@ function buildCspHeader(wsPort, requestHost, publicWsUrl) {
2600
2635
  return `default-src 'self'; script-src ${scriptSrc}; style-src 'self' 'unsafe-inline'; connect-src ${Array.from(connect).join(" ")}; img-src 'self' data:; font-src 'self' data:; worker-src 'self' blob:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; form-action 'self'`;
2601
2636
  }
2602
2637
  function isInsideDist(candidate, distDir) {
2603
- const root = path5.resolve(distDir);
2604
- const resolved = path5.resolve(candidate);
2605
- return resolved === root || resolved.startsWith(root + path5.sep);
2638
+ const root = path6.resolve(distDir);
2639
+ const resolved = path6.resolve(candidate);
2640
+ return resolved === root || resolved.startsWith(root + path6.sep);
2606
2641
  }
2607
2642
  function decodeSessionId(segment) {
2608
2643
  try {
@@ -2613,7 +2648,7 @@ function decodeSessionId(segment) {
2613
2648
  }
2614
2649
  function createHttpServer(opts) {
2615
2650
  const port = opts.port ?? Number.parseInt(process.env["PORT"] ?? "3456", 10);
2616
- const distDir = path5.resolve(opts.distDir);
2651
+ const distDir = path6.resolve(opts.distDir);
2617
2652
  const wsPort = opts.wsPort;
2618
2653
  const requireAccessToken = Boolean(opts.requireToken) || !isLoopbackBind(opts.host);
2619
2654
  return http.createServer(async (req, res) => {
@@ -2790,21 +2825,21 @@ function createHttpServer(opts) {
2790
2825
  }
2791
2826
  let filePath;
2792
2827
  if (url.pathname === "/" || url.pathname === "") {
2793
- filePath = path5.join(distDir, "index.html");
2828
+ filePath = path6.join(distDir, "index.html");
2794
2829
  } else if (url.pathname.startsWith("/assets/")) {
2795
- filePath = path5.join(distDir, url.pathname);
2830
+ filePath = path6.join(distDir, url.pathname);
2796
2831
  } else if (url.pathname.startsWith("/")) {
2797
- filePath = path5.join(distDir, url.pathname);
2832
+ filePath = path6.join(distDir, url.pathname);
2798
2833
  } else {
2799
- filePath = path5.join(distDir, "index.html");
2834
+ filePath = path6.join(distDir, "index.html");
2800
2835
  }
2801
- const resolvedPath = path5.resolve(filePath);
2836
+ const resolvedPath = path6.resolve(filePath);
2802
2837
  if (!isInsideDist(resolvedPath, distDir)) {
2803
2838
  res.writeHead(403, { "Content-Type": "text/plain" });
2804
2839
  res.end("Forbidden");
2805
2840
  return;
2806
2841
  }
2807
- const ext = path5.extname(resolvedPath);
2842
+ const ext = path6.extname(resolvedPath);
2808
2843
  const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
2809
2844
  res.setHeader("Content-Type", contentType);
2810
2845
  res.setHeader("X-Content-Type-Options", "nosniff");
@@ -2816,18 +2851,18 @@ function createHttpServer(opts) {
2816
2851
  "Content-Security-Policy",
2817
2852
  buildCspHeader(wsPort, requestHostForCsp(req.headers.host), opts.publicWsUrl)
2818
2853
  );
2819
- const html = await fs4.readFile(resolvedPath, "utf8");
2854
+ const html = await fs5.readFile(resolvedPath, "utf8");
2820
2855
  res.writeHead(200);
2821
2856
  res.end(injectWsConfig(html, { wsPort, publicWsUrl: opts.publicWsUrl }));
2822
2857
  return;
2823
2858
  }
2824
- const fileContent = await fs4.readFile(resolvedPath);
2859
+ const fileContent = await fs5.readFile(resolvedPath);
2825
2860
  res.writeHead(200);
2826
2861
  res.end(fileContent);
2827
2862
  } catch (err) {
2828
2863
  if (err.code === "ENOENT") {
2829
2864
  try {
2830
- const html = await fs4.readFile(path5.join(distDir, "index.html"), "utf8");
2865
+ const html = await fs5.readFile(path6.join(distDir, "index.html"), "utf8");
2831
2866
  res.writeHead(200, {
2832
2867
  "Content-Type": "text/html",
2833
2868
  "X-Content-Type-Options": "nosniff",
@@ -2851,11 +2886,17 @@ function createHttpServer(opts) {
2851
2886
  }
2852
2887
  });
2853
2888
  }
2889
+
2890
+ // src/server/instance-registry.ts
2891
+ import * as os from "node:os";
2892
+ import * as path7 from "node:path";
2893
+ import * as fs6 from "node:fs/promises";
2894
+ import { atomicWrite as atomicWrite3 } from "@wrongstack/core";
2854
2895
  function defaultBaseDir() {
2855
- return path5.join(os.homedir(), ".wrongstack");
2896
+ return path7.join(os.homedir(), ".wrongstack");
2856
2897
  }
2857
2898
  function registryPath(baseDir = defaultBaseDir()) {
2858
- return path5.join(baseDir, "webui-instances.json");
2899
+ return path7.join(baseDir, "webui-instances.json");
2859
2900
  }
2860
2901
  function isPidAlive(pid) {
2861
2902
  if (!Number.isInteger(pid) || pid <= 0) return false;
@@ -2868,7 +2909,7 @@ function isPidAlive(pid) {
2868
2909
  }
2869
2910
  async function load(file) {
2870
2911
  try {
2871
- const raw = await fs4.readFile(file, "utf8");
2912
+ const raw = await fs6.readFile(file, "utf8");
2872
2913
  const parsed = JSON.parse(raw);
2873
2914
  if (parsed?.version === 1 && Array.isArray(parsed.instances)) {
2874
2915
  return parsed;
@@ -2878,7 +2919,7 @@ async function load(file) {
2878
2919
  return { version: 1, instances: [] };
2879
2920
  }
2880
2921
  async function save(file, instances) {
2881
- await atomicWrite(file, `${JSON.stringify({ version: 1, instances }, null, 2)}
2922
+ await atomicWrite3(file, `${JSON.stringify({ version: 1, instances }, null, 2)}
2882
2923
  `, {
2883
2924
  mode: 384
2884
2925
  });
@@ -2923,6 +2964,19 @@ function formatInstances(instances) {
2923
2964
  }
2924
2965
  return lines.join("\n");
2925
2966
  }
2967
+
2968
+ // src/server/mcp-handlers.ts
2969
+ import { allServers } from "@wrongstack/core";
2970
+ import {
2971
+ addMcp,
2972
+ disableMcp,
2973
+ discoverMcp,
2974
+ enableMcp,
2975
+ listMcp,
2976
+ removeMcp,
2977
+ restartMcp,
2978
+ updateMcp
2979
+ } from "@wrongstack/mcp";
2926
2980
  function mapStatus(raw) {
2927
2981
  switch (raw) {
2928
2982
  case "connected":
@@ -3160,6 +3214,9 @@ async function handleMemoryForget(ws, msg, memoryStore) {
3160
3214
  sendResult(ws, false, errMessage(err));
3161
3215
  }
3162
3216
  }
3217
+
3218
+ // src/server/open-browser.ts
3219
+ import { spawn } from "node:child_process";
3163
3220
  function browserOpenCommand(url, platform = process.platform) {
3164
3221
  if (platform === "win32") {
3165
3222
  return { command: "cmd", args: ["/c", "start", "", url] };
@@ -3178,7 +3235,7 @@ function openBrowser(url, platform = process.platform) {
3178
3235
  child.unref();
3179
3236
  if (child.pid) {
3180
3237
  try {
3181
- import('@wrongstack/tools').then(({ getProcessRegistry }) => {
3238
+ import("@wrongstack/tools").then(({ getProcessRegistry }) => {
3182
3239
  const pid = child.pid;
3183
3240
  if (pid === void 0) return;
3184
3241
  getProcessRegistry().register({
@@ -3200,6 +3257,10 @@ function openBrowser(url, platform = process.platform) {
3200
3257
  } catch {
3201
3258
  }
3202
3259
  }
3260
+
3261
+ // src/server/port-utils.ts
3262
+ import * as net from "node:net";
3263
+ import { ToolValidationError as ToolValidationError3 } from "@wrongstack/core";
3203
3264
  function isPortFree(host, port) {
3204
3265
  return new Promise((resolve10) => {
3205
3266
  const srv = net.createServer();
@@ -3225,7 +3286,7 @@ async function findFreePort(host, startPort, opts = {}) {
3225
3286
  }
3226
3287
  port++;
3227
3288
  }
3228
- throw new ToolValidationError({
3289
+ throw new ToolValidationError3({
3229
3290
  message: `No free port found near ${startPort} on ${host} after ${maxTries} attempts.`,
3230
3291
  field: "port"
3231
3292
  });
@@ -3435,10 +3496,15 @@ async function handlePromptsRecent(ws, ctx) {
3435
3496
  send(ws, { type: "prompts.recent", payload: { slugs: [], error: errMessage(err) } });
3436
3497
  }
3437
3498
  }
3499
+
3500
+ // src/server/provider-config-io.ts
3501
+ import * as fs7 from "node:fs/promises";
3502
+ import { ConfigError, atomicWrite as atomicWrite4 } from "@wrongstack/core";
3503
+ import { decryptConfigSecrets, encryptConfigSecrets } from "@wrongstack/core/security";
3438
3504
  async function loadSavedProviders(configPath, vault) {
3439
3505
  let raw;
3440
3506
  try {
3441
- raw = await fs4.readFile(configPath, "utf8");
3507
+ raw = await fs7.readFile(configPath, "utf8");
3442
3508
  } catch {
3443
3509
  return {};
3444
3510
  }
@@ -3455,7 +3521,7 @@ async function saveProviders(configPath, vault, providers) {
3455
3521
  let raw;
3456
3522
  let fileExists = true;
3457
3523
  try {
3458
- raw = await fs4.readFile(configPath, "utf8");
3524
+ raw = await fs7.readFile(configPath, "utf8");
3459
3525
  } catch (err) {
3460
3526
  if (err.code !== "ENOENT") {
3461
3527
  throw new ConfigError({
@@ -3484,8 +3550,11 @@ async function saveProviders(configPath, vault, providers) {
3484
3550
  }
3485
3551
  parsed.providers = providers;
3486
3552
  const encrypted = encryptConfigSecrets(parsed, vault);
3487
- await atomicWrite(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
3553
+ await atomicWrite4(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
3488
3554
  }
3555
+
3556
+ // src/server/provider-keys.ts
3557
+ import { expectDefined } from "@wrongstack/core";
3489
3558
  function normalizeKeys(cfg) {
3490
3559
  if (Array.isArray(cfg.apiKeys) && cfg.apiKeys.length > 0) {
3491
3560
  return cfg.apiKeys.map((k) => ({ ...k }));
@@ -3579,6 +3648,12 @@ function removeProvider(providers, providerId) {
3579
3648
  delete providers[providerId];
3580
3649
  return { ok: true, message: `Provider "${providerId}" removed` };
3581
3650
  }
3651
+
3652
+ // src/server/sdd-board-ws-handler.ts
3653
+ import {
3654
+ applySddLifecycle,
3655
+ SddBoardStore
3656
+ } from "@wrongstack/sdd";
3582
3657
  var CONTROL_TYPES = /* @__PURE__ */ new Set([
3583
3658
  "pause",
3584
3659
  "resume",
@@ -3723,6 +3798,26 @@ var SddBoardWebSocketHandler = class {
3723
3798
  if (client.ws.readyState === 1) client.ws.send(JSON.stringify(msg));
3724
3799
  }
3725
3800
  };
3801
+
3802
+ // src/server/sdd-wizard-wiring.ts
3803
+ import * as path8 from "node:path";
3804
+ import { spawnSync as spawnSync2 } from "node:child_process";
3805
+ import {
3806
+ WorktreeManager as WorktreeManager2,
3807
+ ToolValidationError as ToolValidationError4
3808
+ } from "@wrongstack/core";
3809
+ import {
3810
+ cleanupStaleSddWorktrees,
3811
+ makeCommandVerifier,
3812
+ makeLlmSubtaskGenerator,
3813
+ SddBoardStore as SddBoardStore2,
3814
+ SddInterviewDriver,
3815
+ SddRunRegistry,
3816
+ SddSupervisor,
3817
+ SpecStore,
3818
+ startSddRun,
3819
+ TaskGraphStore
3820
+ } from "@wrongstack/sdd";
3726
3821
  var PLANNING_ONLY_GUARD = "SYSTEM: You are running a PLANNING-ONLY specification interview. Do NOT write, create, or edit any files, and do NOT run shell/terminal commands or use any code-editing tools \u2014 they are disabled here and any attempt will fail and waste the turn. Respond with TEXT ONLY: ask your question, or emit the requested spec / plan / task JSON. All code is written later, automatically, once the plan is approved and the multi-agent run starts.\n\n---\n\n";
3727
3822
  function buildSddWizardDeps(opts) {
3728
3823
  const registry = new SddRunRegistry();
@@ -3746,14 +3841,14 @@ function buildSddWizardDeps(opts) {
3746
3841
  makeDriver: () => new SddInterviewDriver({
3747
3842
  specStore: new SpecStore({ baseDir: opts.paths.projectSpecs }),
3748
3843
  graphStore: new TaskGraphStore({ baseDir: opts.paths.projectTaskGraphs }),
3749
- sessionPath: path5.join(opts.paths.projectDir, "sdd-wizard-session.json")
3844
+ sessionPath: path8.join(opts.paths.projectDir, "sdd-wizard-session.json")
3750
3845
  }),
3751
3846
  runInterviewTurn: (prompt) => runIsolatedTurn(prompt, "Spec Architect"),
3752
3847
  startRun: async (driver, { parallelSlots, defaultModel, defaultProvider, fallbackModels, worktrees: useWorktrees }) => {
3753
3848
  const graph = driver.getGraph();
3754
3849
  const tracker = driver.getTracker();
3755
3850
  if (!graph || !tracker) {
3756
- throw new ToolValidationError({
3851
+ throw new ToolValidationError4({
3757
3852
  message: "No task graph to run \u2014 finish the interview first.",
3758
3853
  field: "taskGraph"
3759
3854
  });
@@ -3761,7 +3856,7 @@ function buildSddWizardDeps(opts) {
3761
3856
  const worktreesEnabled = useWorktrees ?? process.env["WRONGSTACK_SDD_WORKTREES"] !== "0";
3762
3857
  let worktrees;
3763
3858
  if (worktreesEnabled) {
3764
- const inGit = spawnSync("git", ["rev-parse", "--is-inside-work-tree"], {
3859
+ const inGit = spawnSync2("git", ["rev-parse", "--is-inside-work-tree"], {
3765
3860
  cwd: opts.projectRoot,
3766
3861
  encoding: "utf8",
3767
3862
  windowsHide: true
@@ -3771,14 +3866,14 @@ function buildSddWizardDeps(opts) {
3771
3866
  projectRoot: opts.projectRoot,
3772
3867
  boardsDir: opts.paths.projectSddBoards
3773
3868
  }).catch(() => void 0);
3774
- worktrees = new WorktreeManager({
3869
+ worktrees = new WorktreeManager2({
3775
3870
  projectRoot: opts.projectRoot,
3776
3871
  events: opts.events,
3777
3872
  sessionId: () => opts.agent.ctx.session?.id
3778
3873
  });
3779
3874
  }
3780
3875
  }
3781
- const boardStore = new SddBoardStore({ baseDir: opts.paths.projectSddBoards });
3876
+ const boardStore = new SddBoardStore2({ baseDir: opts.paths.projectSddBoards });
3782
3877
  const verifyTask = makeCommandVerifier();
3783
3878
  const superviseFailure = opts.brain ? new SddSupervisor({
3784
3879
  brain: opts.brain,
@@ -3958,20 +4053,25 @@ var SddWizardWebSocketHandler = class {
3958
4053
  if (client.ws.readyState === 1) client.ws.send(JSON.stringify(msg));
3959
4054
  }
3960
4055
  };
4056
+
4057
+ // src/server/shell-open.ts
4058
+ import * as fs8 from "node:fs/promises";
4059
+ import * as path9 from "node:path";
4060
+ import { spawn as spawn2 } from "node:child_process";
3961
4061
  var METACHAR_REGEX = /[&|<>^"'`'\n\r]/;
3962
4062
  function shellQuote(s) {
3963
4063
  return s.replaceAll("'", `'"'"'`);
3964
4064
  }
3965
4065
  async function handleShellOpen(req, logger) {
3966
4066
  try {
3967
- const resolved = path5.resolve(req.path);
3968
- await fs4.access(resolved);
4067
+ const resolved = path9.resolve(req.path);
4068
+ await fs8.access(resolved);
3969
4069
  if (METACHAR_REGEX.test(resolved)) {
3970
4070
  return { success: false, message: "Path contains unsupported characters." };
3971
4071
  }
3972
4072
  const platform = process.platform;
3973
4073
  const launch = (cmd, args, onError) => {
3974
- const child = spawn(cmd, args, {
4074
+ const child = spawn2(cmd, args, {
3975
4075
  detached: true,
3976
4076
  stdio: "ignore",
3977
4077
  windowsHide: true
@@ -4015,6 +4115,12 @@ async function handleShellOpen(req, logger) {
4015
4115
  }
4016
4116
  }
4017
4117
 
4118
+ // src/server/skills-handlers.ts
4119
+ import { promises as fs9 } from "node:fs";
4120
+ import path10 from "node:path";
4121
+ import { atomicWrite as atomicWrite5 } from "@wrongstack/core";
4122
+ import { wstackGlobalRoot } from "@wrongstack/core/utils";
4123
+
4018
4124
  // src/server/ws-payload-validation.ts
4019
4125
  function isRecord(value) {
4020
4126
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -4235,34 +4341,34 @@ var ENUM_PREF_KEYS = {
4235
4341
  reasoningEffort: REASONING_EFFORT_VALUES,
4236
4342
  cacheTtl: CACHE_TTL_VALUES
4237
4343
  };
4238
- function validateModelRuntimeValue(modelRuntime, path23) {
4344
+ function validateModelRuntimeValue(modelRuntime, path22) {
4239
4345
  const reasoning = modelRuntime["reasoning"];
4240
4346
  if (reasoning !== void 0) {
4241
- if (!isRecord(reasoning)) return `${path23}.reasoning must be an object when provided`;
4347
+ if (!isRecord(reasoning)) return `${path22}.reasoning must be an object when provided`;
4242
4348
  const mode = reasoning["mode"];
4243
4349
  const effort = reasoning["effort"];
4244
4350
  const preserve = reasoning["preserve"];
4245
4351
  if (mode !== void 0 && (typeof mode !== "string" || !REASONING_MODE_VALUES.has(mode))) {
4246
- return `${path23}.reasoning.mode must be one of: ${Array.from(REASONING_MODE_VALUES).join(", ")}`;
4352
+ return `${path22}.reasoning.mode must be one of: ${Array.from(REASONING_MODE_VALUES).join(", ")}`;
4247
4353
  }
4248
4354
  if (effort !== void 0 && (typeof effort !== "string" || !REASONING_EFFORT_VALUES.has(effort))) {
4249
- return `${path23}.reasoning.effort must be one of: ${Array.from(REASONING_EFFORT_VALUES).join(", ")}`;
4355
+ return `${path22}.reasoning.effort must be one of: ${Array.from(REASONING_EFFORT_VALUES).join(", ")}`;
4250
4356
  }
4251
4357
  if (preserve !== void 0 && typeof preserve !== "boolean") {
4252
- return `${path23}.reasoning.preserve must be a boolean when provided`;
4358
+ return `${path22}.reasoning.preserve must be a boolean when provided`;
4253
4359
  }
4254
4360
  }
4255
4361
  const cache = modelRuntime["cache"];
4256
4362
  if (cache !== void 0) {
4257
- if (!isRecord(cache)) return `${path23}.cache must be an object when provided`;
4363
+ if (!isRecord(cache)) return `${path22}.cache must be an object when provided`;
4258
4364
  const ttl = cache["ttl"];
4259
4365
  if (ttl !== void 0 && (typeof ttl !== "string" || !CACHE_TTL_VALUES.has(ttl) || ttl === "default")) {
4260
- return `${path23}.cache.ttl must be one of: 5m, 1h`;
4366
+ return `${path22}.cache.ttl must be one of: 5m, 1h`;
4261
4367
  }
4262
4368
  }
4263
4369
  const parameters = modelRuntime["parameters"];
4264
4370
  if (parameters !== void 0 && !isRecord(parameters)) {
4265
- return `${path23}.parameters must be an object when provided`;
4371
+ return `${path22}.parameters must be an object when provided`;
4266
4372
  }
4267
4373
  return null;
4268
4374
  }
@@ -4542,8 +4648,8 @@ function validateShellOpenPayload(payload) {
4542
4648
  if (!isRecord(payload)) {
4543
4649
  return { ok: false, message: "shell.open payload must be an object with string path" };
4544
4650
  }
4545
- const path23 = payload["path"];
4546
- if (typeof path23 !== "string" || path23.trim().length === 0) {
4651
+ const path22 = payload["path"];
4652
+ if (typeof path22 !== "string" || path22.trim().length === 0) {
4547
4653
  return { ok: false, message: "shell.open payload.path must be a non-empty string" };
4548
4654
  }
4549
4655
  const target = payload["target"];
@@ -4556,7 +4662,7 @@ function validateShellOpenPayload(payload) {
4556
4662
  return {
4557
4663
  ok: true,
4558
4664
  value: {
4559
- path: path23,
4665
+ path: path22,
4560
4666
  ...target !== void 0 ? { target } : {}
4561
4667
  }
4562
4668
  };
@@ -4565,14 +4671,89 @@ function validateGitDiffPayload(payload) {
4565
4671
  if (!isRecord(payload)) {
4566
4672
  return { ok: false, message: "git.diff payload must be an object" };
4567
4673
  }
4568
- const path23 = payload["path"];
4569
- if (path23 === void 0 || path23 === null) {
4674
+ const path22 = payload["path"];
4675
+ if (path22 === void 0 || path22 === null) {
4570
4676
  return { ok: true, value: { path: "" } };
4571
4677
  }
4572
- if (typeof path23 !== "string") {
4678
+ if (typeof path22 !== "string") {
4573
4679
  return { ok: false, message: "git.diff payload.path must be a string when provided" };
4574
4680
  }
4575
- return { ok: true, value: { path: path23 } };
4681
+ return { ok: true, value: { path: path22 } };
4682
+ }
4683
+
4684
+ // src/server/zip.ts
4685
+ import { deflateRawSync, inflateRawSync } from "node:zlib";
4686
+ function createZipBuffer(entries) {
4687
+ const localParts = [];
4688
+ const centralParts = [];
4689
+ let offset = 0;
4690
+ const { date, time } = dosDateTime(/* @__PURE__ */ new Date());
4691
+ for (const entry of entries) {
4692
+ const name2 = Buffer.from(normalizeZipPath(entry.name), "utf8");
4693
+ const data = typeof entry.data === "string" ? Buffer.from(entry.data, "utf8") : Buffer.from(entry.data);
4694
+ const compressed = deflateRawSync(data);
4695
+ const crc = crc32(data);
4696
+ const local = Buffer.alloc(30);
4697
+ local.writeUInt32LE(67324752, 0);
4698
+ local.writeUInt16LE(20, 4);
4699
+ local.writeUInt16LE(2048, 6);
4700
+ local.writeUInt16LE(8, 8);
4701
+ local.writeUInt16LE(time, 10);
4702
+ local.writeUInt16LE(date, 12);
4703
+ local.writeUInt32LE(crc, 14);
4704
+ local.writeUInt32LE(compressed.length, 18);
4705
+ local.writeUInt32LE(data.length, 22);
4706
+ local.writeUInt16LE(name2.length, 26);
4707
+ localParts.push(local, name2, compressed);
4708
+ const central = Buffer.alloc(46);
4709
+ central.writeUInt32LE(33639248, 0);
4710
+ central.writeUInt16LE(798, 4);
4711
+ central.writeUInt16LE(20, 6);
4712
+ central.writeUInt16LE(2048, 8);
4713
+ central.writeUInt16LE(8, 10);
4714
+ central.writeUInt16LE(time, 12);
4715
+ central.writeUInt16LE(date, 14);
4716
+ central.writeUInt32LE(crc, 16);
4717
+ central.writeUInt32LE(compressed.length, 20);
4718
+ central.writeUInt32LE(data.length, 24);
4719
+ central.writeUInt16LE(name2.length, 28);
4720
+ central.writeUInt32LE(offset, 42);
4721
+ centralParts.push(central, name2);
4722
+ offset += local.length + name2.length + compressed.length;
4723
+ }
4724
+ const centralDirectory = Buffer.concat(centralParts);
4725
+ const end = Buffer.alloc(22);
4726
+ end.writeUInt32LE(101010256, 0);
4727
+ end.writeUInt16LE(entries.length, 8);
4728
+ end.writeUInt16LE(entries.length, 10);
4729
+ end.writeUInt32LE(centralDirectory.length, 12);
4730
+ end.writeUInt32LE(offset, 16);
4731
+ return Buffer.concat([...localParts, centralDirectory, end]);
4732
+ }
4733
+ function normalizeZipPath(value) {
4734
+ const normalized = value.replace(/\\/g, "/").replace(/^\/+/, "");
4735
+ if (!isSafeZipPath(normalized)) throw new Error(`Unsafe ZIP entry path: ${value}`);
4736
+ return normalized;
4737
+ }
4738
+ function isSafeZipPath(value) {
4739
+ return value.length > 0 && !value.includes("\0") && !value.startsWith("/") && !/^[a-z]:/i.test(value) && !value.split("/").includes("..");
4740
+ }
4741
+ function dosDateTime(value) {
4742
+ const year = Math.max(1980, Math.min(2107, value.getFullYear()));
4743
+ return {
4744
+ date: year - 1980 << 9 | value.getMonth() + 1 << 5 | value.getDate(),
4745
+ time: value.getHours() << 11 | value.getMinutes() << 5 | Math.floor(value.getSeconds() / 2)
4746
+ };
4747
+ }
4748
+ var CRC_TABLE = Array.from({ length: 256 }, (_, index) => {
4749
+ let crc = index;
4750
+ for (let bit = 0; bit < 8; bit++) crc = (crc & 1) !== 0 ? 3988292384 ^ crc >>> 1 : crc >>> 1;
4751
+ return crc >>> 0;
4752
+ });
4753
+ function crc32(data) {
4754
+ let crc = 4294967295;
4755
+ for (const byte of data) crc = crc >>> 8 ^ (CRC_TABLE[(crc ^ byte) & 255] ?? 0);
4756
+ return (crc ^ 4294967295) >>> 0;
4576
4757
  }
4577
4758
 
4578
4759
  // src/server/skills-handlers.ts
@@ -4643,19 +4824,19 @@ async function handleSkillsContent(ws, ctx, msg) {
4643
4824
  send(ws, { type: "skills.content", payload: { name: name2, body: "", path: "", source, relatedFiles: [], references: [], error: `Skill "${name2}" not found` } });
4644
4825
  return;
4645
4826
  }
4646
- const body = await promises.readFile(entry.path, "utf8");
4647
- const skillDir = path5__default.dirname(entry.path);
4827
+ const body = await fs9.readFile(entry.path, "utf8");
4828
+ const skillDir = path10.dirname(entry.path);
4648
4829
  let relatedFiles = [];
4649
4830
  try {
4650
- const files = await promises.readdir(skillDir);
4651
- relatedFiles = files.filter((f) => f !== path5__default.basename(entry.path)).map((f) => path5__default.join(skillDir, f));
4831
+ const files = await fs9.readdir(skillDir);
4832
+ relatedFiles = files.filter((f) => f !== path10.basename(entry.path)).map((f) => path10.join(skillDir, f));
4652
4833
  } catch {
4653
4834
  }
4654
4835
  const nameLower = name2.toLowerCase();
4655
4836
  const refResults = await Promise.all(
4656
4837
  entries.filter((e) => e.name.toLowerCase() !== nameLower).map(async (e) => {
4657
4838
  try {
4658
- const content = await promises.readFile(e.path, "utf8");
4839
+ const content = await fs9.readFile(e.path, "utf8");
4659
4840
  return [e.name, content.toLowerCase().includes(nameLower)];
4660
4841
  } catch {
4661
4842
  return [e.name, false];
@@ -4745,20 +4926,20 @@ async function handleSkillsCreate(ws, ctx, msg) {
4745
4926
  }
4746
4927
  const createPayload = parsed.value;
4747
4928
  try {
4748
- const targetDir = createPayload.scope === "global" ? path5__default.join(
4749
- ctx.globalSkillsDir ?? path5__default.join(wstackGlobalRoot$1(), "skills"),
4929
+ const targetDir = createPayload.scope === "global" ? path10.join(
4930
+ ctx.globalSkillsDir ?? path10.join(wstackGlobalRoot(), "skills"),
4750
4931
  createPayload.name.trim()
4751
- ) : path5__default.join(
4752
- ctx.projectSkillsDir ?? path5__default.join(ctx.projectRoot, ".wrongstack", "skills"),
4932
+ ) : path10.join(
4933
+ ctx.projectSkillsDir ?? path10.join(ctx.projectRoot, ".wrongstack", "skills"),
4753
4934
  createPayload.name.trim()
4754
4935
  );
4755
4936
  try {
4756
- await promises.access(targetDir);
4937
+ await fs9.access(targetDir);
4757
4938
  send(ws, { type: "skills.created", payload: { success: false, error: `Skill "${createPayload.name}" already exists` } });
4758
4939
  return;
4759
4940
  } catch {
4760
4941
  }
4761
- await promises.mkdir(targetDir, { recursive: true });
4942
+ await fs9.mkdir(targetDir, { recursive: true });
4762
4943
  const lines = createPayload.description.trim().split("\n");
4763
4944
  const firstLine = (lines[0] ?? "").trim();
4764
4945
  const bodyLines = lines.slice(1).map((l) => l.trim()).filter(Boolean);
@@ -4806,13 +4987,13 @@ ${trigger}
4806
4987
  "- `bug-hunter` \u2014 for systematic bug detection patterns",
4807
4988
  "- `output-standards` \u2014 for standardized `<nextsteps>` formatting"
4808
4989
  ].join("\n");
4809
- await atomicWrite(path5__default.join(targetDir, "SKILL.md"), skillContent);
4990
+ await atomicWrite5(path10.join(targetDir, "SKILL.md"), skillContent);
4810
4991
  send(ws, {
4811
4992
  type: "skills.created",
4812
4993
  payload: {
4813
4994
  success: true,
4814
4995
  error: null,
4815
- skill: { name: createPayload.name.trim(), path: path5__default.join(targetDir, "SKILL.md"), scope: createPayload.scope }
4996
+ skill: { name: createPayload.name.trim(), path: path10.join(targetDir, "SKILL.md"), scope: createPayload.scope }
4816
4997
  }
4817
4998
  });
4818
4999
  } catch (err) {
@@ -4842,7 +5023,7 @@ async function handleSkillsEdit(ws, ctx, msg) {
4842
5023
  send(ws, { type: "skills.edited", payload: { success: false, error: `${label} skills cannot be edited` } });
4843
5024
  return;
4844
5025
  }
4845
- await atomicWrite(entry.path, editPayload.body);
5026
+ await atomicWrite5(entry.path, editPayload.body);
4846
5027
  send(ws, { type: "skills.edited", payload: { success: true, error: null } });
4847
5028
  } catch (err) {
4848
5029
  send(ws, { type: "skills.edited", payload: { success: false, error: errMessage(err) } });
@@ -4855,29 +5036,35 @@ async function handleSkillsExport(ws, ctx) {
4855
5036
  }
4856
5037
  try {
4857
5038
  const entries = await ctx.skillLoader.listEntries();
4858
- const zip = new JSZip();
5039
+ const zipEntries = [];
4859
5040
  for (const entry of entries) {
4860
5041
  try {
4861
5042
  const body = await ctx.skillLoader.readBody(entry.name);
4862
5043
  const safeName = entry.name.replace(/\//g, "_");
4863
- zip.file(`${safeName}/SKILL.md`, body);
5044
+ zipEntries.push({ name: `${safeName}/SKILL.md`, data: body });
4864
5045
  } catch {
4865
5046
  }
4866
5047
  }
4867
- const zipBuffer = await zip.generateAsync({ type: "nodebuffer", compression: "DEFLATE" });
5048
+ const zipBuffer = createZipBuffer(zipEntries);
4868
5049
  const zipBase64 = zipBuffer.toString("base64");
4869
5050
  send(ws, { type: "skills.exported", payload: { zipBase64, skillCount: entries.length, error: void 0 } });
4870
5051
  } catch (err) {
4871
5052
  send(ws, { type: "skills.exported", payload: { zipBase64: "", skillCount: 0, error: errMessage(err) } });
4872
5053
  }
4873
5054
  }
5055
+
5056
+ // src/server/specs-ws-handler.ts
5057
+ import {
5058
+ computeTaskProgress
5059
+ } from "@wrongstack/core";
5060
+ import { SpecStore as SpecStore2, TaskGraphStore as TaskGraphStore2 } from "@wrongstack/sdd";
4874
5061
  var SpecsWebSocketHandler = class {
4875
5062
  specStore;
4876
5063
  graphStore;
4877
5064
  clients = /* @__PURE__ */ new Set();
4878
5065
  constructor(specsDir, taskGraphsDir) {
4879
- this.specStore = new SpecStore({ baseDir: specsDir });
4880
- this.graphStore = new TaskGraphStore({ baseDir: taskGraphsDir });
5066
+ this.specStore = new SpecStore2({ baseDir: specsDir });
5067
+ this.graphStore = new TaskGraphStore2({ baseDir: taskGraphsDir });
4881
5068
  }
4882
5069
  addClient(ws) {
4883
5070
  const client = { ws, id: crypto.randomUUID() };
@@ -5081,6 +5268,13 @@ function estimateContextBreakdown(input) {
5081
5268
  messages: { total: msgTokens, count: input.messages.length, breakdown: messageBreakdown }
5082
5269
  };
5083
5270
  }
5271
+
5272
+ // src/server/worktree-ws-handler.ts
5273
+ import { join as join7, resolve as resolve6, sep as sep3 } from "node:path";
5274
+ import { WorktreeManager as WorktreeManager3 } from "@wrongstack/core";
5275
+ import { cleanupStaleSddWorktrees as cleanupStaleSddWorktrees2 } from "@wrongstack/sdd";
5276
+ import { toErrorMessage as toErrorMessage2 } from "@wrongstack/core/utils";
5277
+ var MAX_ACTIVITY = 6;
5084
5278
  var ACTIVE_STATUSES = /* @__PURE__ */ new Set(["allocating", "active", "committing", "merging"]);
5085
5279
  var MANAGED_BRANCH_RE = /^wstack\/ap\/[A-Za-z0-9._/-]+$/;
5086
5280
  var WorktreeWebSocketHandler = class {
@@ -5137,13 +5331,13 @@ var WorktreeWebSocketHandler = class {
5137
5331
  // ── orphan management ─────────────────────────────────────────────────────
5138
5332
  /** Absolute managed-worktrees root for this project. */
5139
5333
  worktreesRoot() {
5140
- return resolve(join(this.management.projectRoot, ".wrongstack", "worktrees"));
5334
+ return resolve6(join7(this.management.projectRoot, ".wrongstack", "worktrees"));
5141
5335
  }
5142
5336
  /** True iff `dir` resolves strictly inside the managed worktrees root. */
5143
5337
  underRoot(dir) {
5144
- const abs = resolve(dir);
5338
+ const abs = resolve6(dir);
5145
5339
  const root = this.worktreesRoot();
5146
- return abs !== root && abs.startsWith(root + sep);
5340
+ return abs !== root && abs.startsWith(root + sep3);
5147
5341
  }
5148
5342
  /** Branches of worktrees a live in-session run currently owns. */
5149
5343
  liveActiveBranches() {
@@ -5164,7 +5358,7 @@ var WorktreeWebSocketHandler = class {
5164
5358
  return;
5165
5359
  }
5166
5360
  try {
5167
- const wt = new WorktreeManager({ projectRoot: this.management.projectRoot });
5361
+ const wt = new WorktreeManager3({ projectRoot: this.management.projectRoot });
5168
5362
  const { worktrees, branches } = await wt.listManaged();
5169
5363
  const live = this.liveActiveBranches();
5170
5364
  const orphans = [];
@@ -5188,7 +5382,7 @@ var WorktreeWebSocketHandler = class {
5188
5382
  }
5189
5383
  });
5190
5384
  } catch (err) {
5191
- this.logger.debug?.(`worktree orphan scan failed: ${toErrorMessage(err)}`);
5385
+ this.logger.debug?.(`worktree orphan scan failed: ${toErrorMessage2(err)}`);
5192
5386
  this.broadcast({ type: "worktree.orphans", payload: { orphans: [], canClean: false } });
5193
5387
  }
5194
5388
  }
@@ -5212,7 +5406,7 @@ var WorktreeWebSocketHandler = class {
5212
5406
  });
5213
5407
  return;
5214
5408
  }
5215
- const res = await cleanupStaleSddWorktrees({
5409
+ const res = await cleanupStaleSddWorktrees2({
5216
5410
  projectRoot: this.management.projectRoot,
5217
5411
  boardsDir: this.management.boardsDir
5218
5412
  });
@@ -5251,7 +5445,7 @@ var WorktreeWebSocketHandler = class {
5251
5445
  }
5252
5446
  let removed = false;
5253
5447
  if (dir) {
5254
- const wt = new WorktreeManager({ projectRoot: this.management.projectRoot });
5448
+ const wt = new WorktreeManager3({ projectRoot: this.management.projectRoot });
5255
5449
  ({ removed } = await wt.removeOne(dir, branch));
5256
5450
  }
5257
5451
  for (const [id, h] of [...this.handles]) {
@@ -5275,7 +5469,7 @@ var WorktreeWebSocketHandler = class {
5275
5469
  this.broadcast({ type: "worktree.merge_result", payload: { ok: false, branch, reason: "a run is live on this worktree \u2014 stop it first" } });
5276
5470
  return;
5277
5471
  }
5278
- const wt = new WorktreeManager({ projectRoot: this.management.projectRoot });
5472
+ const wt = new WorktreeManager3({ projectRoot: this.management.projectRoot });
5279
5473
  const res = await wt.mergeBranch(branch);
5280
5474
  this.broadcast({
5281
5475
  type: "worktree.merge_result",
@@ -5290,8 +5484,8 @@ var WorktreeWebSocketHandler = class {
5290
5484
  return;
5291
5485
  }
5292
5486
  const base = baseBranch && MANAGED_BRANCH_RE.test(baseBranch) ? baseBranch : void 0;
5293
- const wt = new WorktreeManager({ projectRoot: this.management.projectRoot });
5294
- const summary = await wt.diffSummary(resolve(dir), base);
5487
+ const wt = new WorktreeManager3({ projectRoot: this.management.projectRoot });
5488
+ const summary = await wt.diffSummary(resolve6(dir), base);
5295
5489
  this.broadcast({ type: "worktree.diff_result", payload: { dir, summary } });
5296
5490
  }
5297
5491
  // ── internals ───────────────────────────────────────────────────────────
@@ -5363,7 +5557,7 @@ var WorktreeWebSocketHandler = class {
5363
5557
  activity(id, kind, text) {
5364
5558
  const cur = this.handles.get(id);
5365
5559
  if (cur) {
5366
- const recentActivity = [...cur.recentActivity, { kind, text, at: Date.now() }].slice(-6);
5560
+ const recentActivity = [...cur.recentActivity, { kind, text, at: Date.now() }].slice(-MAX_ACTIVITY);
5367
5561
  this.handles.set(id, { ...cur, recentActivity });
5368
5562
  }
5369
5563
  this.broadcast({ type: "worktree.event", payload: { kind, handleId: id, text, at: Date.now() } });
@@ -5395,7 +5589,7 @@ var WorktreeWebSocketHandler = class {
5395
5589
  try {
5396
5590
  if (ws.readyState === 1) ws.send(data);
5397
5591
  } catch (err) {
5398
- this.logger.debug?.(`worktree broadcast failed: ${toErrorMessage(err)}`);
5592
+ this.logger.debug?.(`worktree broadcast failed: ${toErrorMessage2(err)}`);
5399
5593
  }
5400
5594
  }
5401
5595
  }
@@ -5407,6 +5601,12 @@ var WorktreeWebSocketHandler = class {
5407
5601
  }
5408
5602
  };
5409
5603
 
5604
+ // src/server/server-runtime.ts
5605
+ import * as path12 from "node:path";
5606
+ import { createRequire } from "node:module";
5607
+ import { fileURLToPath } from "node:url";
5608
+ import { WebSocketServer } from "ws";
5609
+
5410
5610
  // src/server/lifecycle.ts
5411
5611
  function createShutdown(res) {
5412
5612
  const log = res.log ?? ((m) => console.log(m));
@@ -5442,9 +5642,14 @@ function registerShutdownHandlers(res) {
5442
5642
  process.off("SIGTERM", shutdown);
5443
5643
  };
5444
5644
  }
5645
+
5646
+ // src/server/setup-events.ts
5647
+ import * as fs10 from "node:fs/promises";
5648
+ import { watch as fsWatch } from "node:fs";
5649
+ import * as path11 from "node:path";
5445
5650
  function statusProjectHashFromWatchFilename(projectsDir, filename) {
5446
5651
  const raw = String(filename);
5447
- const relative4 = path5.isAbsolute(raw) ? path5.relative(projectsDir, raw) : raw;
5652
+ const relative4 = path11.isAbsolute(raw) ? path11.relative(projectsDir, raw) : raw;
5448
5653
  const parts = relative4.split(/[\\/]+/).filter(Boolean);
5449
5654
  if (parts.length < 2) return null;
5450
5655
  if (parts[parts.length - 1] !== "status.json") return null;
@@ -5581,7 +5786,7 @@ function setupEvents(deps2) {
5581
5786
  try {
5582
5787
  const taskPath = context.meta["task.path"];
5583
5788
  if (typeof taskPath === "string" && taskPath) {
5584
- const { loadTasks } = await import('@wrongstack/core');
5789
+ const { loadTasks } = await import("@wrongstack/core");
5585
5790
  const file = await loadTasks(taskPath);
5586
5791
  broadcast2(clients, { type: "tasks.updated", payload: sessionPayload2({ sessionId: e.sessionId, tasks: file?.tasks ?? [] }) });
5587
5792
  }
@@ -5590,7 +5795,7 @@ function setupEvents(deps2) {
5590
5795
  try {
5591
5796
  const planPath = context.meta["plan.path"];
5592
5797
  if (typeof planPath === "string" && planPath) {
5593
- const { loadPlan } = await import('@wrongstack/core');
5798
+ const { loadPlan } = await import("@wrongstack/core");
5594
5799
  const plan = await loadPlan(planPath);
5595
5800
  broadcast2(clients, { type: "plan.updated", payload: sessionPayload2({ sessionId: e.sessionId, plan: plan ?? { version: 1, sessionId: e.sessionId ?? context.session?.id ?? "", updatedAt: (/* @__PURE__ */ new Date()).toISOString(), items: [] } }) });
5596
5801
  }
@@ -5887,6 +6092,7 @@ function setupEvents(deps2) {
5887
6092
  events.on("subagent.budget_extended", (e) => forwardSubagent("budget_extended", { sessionId: e.sessionId, subagentId: e.subagentId, budgetKind: e.kind, newLimit: e.newLimit, totalExtensions: e.totalExtensions }));
5888
6093
  events.on("subagent.ctx_pct", (e) => forwardSubagent("ctx_pct", { sessionId: e.sessionId, subagentId: e.subagentId, load: e.load, tokens: e.tokens, maxContext: e.maxContext }));
5889
6094
  events.on("subagent.task_completed", (e) => forwardSubagent("task_completed", { sessionId: e.sessionId, subagentId: e.subagentId, status: e.status, iterations: e.iterations, toolCalls: e.toolCalls, finalText: e.finalText, failureReason: e.error?.kind, error: e.error ? { kind: e.error.kind, message: e.error.message } : void 0 }));
6095
+ events.on("subagent.removed", (e) => forwardSubagent("removed", { sessionId: e.sessionId, subagentId: e.subagentId, reason: e.reason }));
5890
6096
  events.on("agent.timeline.message", (e) => {
5891
6097
  broadcast2(clients, {
5892
6098
  type: "agent.timeline.message",
@@ -5984,14 +6190,20 @@ function setupEvents(deps2) {
5984
6190
  payload: sessionPayload2({ event: eventName, ...payload })
5985
6191
  });
5986
6192
  });
6193
+ events.onPattern("memory.*", (eventName, payload) => {
6194
+ broadcast2(clients, {
6195
+ type: "memory.event",
6196
+ payload: sessionPayload2({ event: eventName, ...payload })
6197
+ });
6198
+ });
5987
6199
  events.on("client.status", async (e) => {
5988
6200
  broadcast2(clients, { type: "client.status_update", payload: e });
5989
6201
  if (wpaths?.projectStatus) {
5990
6202
  try {
5991
6203
  const statusFile = wpaths.projectStatus(e.projectHash);
5992
- const dir = path5.dirname(statusFile);
5993
- await fs4.mkdir(dir, { recursive: true });
5994
- await fs4.writeFile(statusFile, JSON.stringify(e, null, 2), "utf-8");
6204
+ const dir = path11.dirname(statusFile);
6205
+ await fs10.mkdir(dir, { recursive: true });
6206
+ await fs10.writeFile(statusFile, JSON.stringify(e, null, 2), "utf-8");
5995
6207
  } catch (err) {
5996
6208
  console.error(
5997
6209
  JSON.stringify({
@@ -6005,7 +6217,7 @@ function setupEvents(deps2) {
6005
6217
  }
6006
6218
  });
6007
6219
  if (wpaths?.projectStatus && wpaths.configDir) {
6008
- const projectsDir = path5.join(wpaths.configDir, "projects");
6220
+ const projectsDir = path11.join(wpaths.configDir, "projects");
6009
6221
  const knownProjectHashes = /* @__PURE__ */ new Set();
6010
6222
  const debounceTimers = /* @__PURE__ */ new Map();
6011
6223
  const DEBOUNCE_MS = 150;
@@ -6069,8 +6281,8 @@ function setupEvents(deps2) {
6069
6281
  let watcher;
6070
6282
  const startWatcher = async () => {
6071
6283
  try {
6072
- await fs4.mkdir(projectsDir, { recursive: true });
6073
- watcher = watch(projectsDir, { persistent: true, recursive: true }, async (eventType, filename) => {
6284
+ await fs10.mkdir(projectsDir, { recursive: true });
6285
+ watcher = fsWatch(projectsDir, { persistent: true, recursive: true }, async (eventType, filename) => {
6074
6286
  if (eventType !== "change" && eventType !== "rename") return;
6075
6287
  if (filename == null) return;
6076
6288
  const projectHash = statusProjectHashFromWatchFilename(projectsDir, filename);
@@ -6079,8 +6291,8 @@ function setupEvents(deps2) {
6079
6291
  if (!knownProjectHashes.has(projectHash)) return;
6080
6292
  if (watcherMetrics) watcherMetrics.filesProcessed++;
6081
6293
  try {
6082
- const targetFile = path5.join(projectsDir, projectHash, "status.json");
6083
- const content = await fs4.readFile(targetFile, "utf-8");
6294
+ const targetFile = path11.join(projectsDir, projectHash, "status.json");
6295
+ const content = await fs10.readFile(targetFile, "utf-8");
6084
6296
  const statusData = JSON.parse(content);
6085
6297
  scheduleBroadcast(projectHash, statusData);
6086
6298
  } catch {
@@ -6132,11 +6344,11 @@ function setupEvents(deps2) {
6132
6344
  }
6133
6345
  });
6134
6346
  }
6135
- const globalRoot = globalConfigPath ? path5.dirname(globalConfigPath) : void 0;
6347
+ const globalRoot = globalConfigPath ? path11.dirname(globalConfigPath) : void 0;
6136
6348
  if (globalRoot) {
6137
6349
  const broadcastSessions = async () => {
6138
6350
  try {
6139
- const { SessionRegistry } = await import('@wrongstack/core');
6351
+ const { SessionRegistry } = await import("@wrongstack/core");
6140
6352
  const registry = new SessionRegistry(globalRoot);
6141
6353
  const sessions = await registry.list();
6142
6354
  const mySlug = sessions.find((s) => s.pid === process.pid)?.projectSlug;
@@ -6179,7 +6391,7 @@ function setupEvents(deps2) {
6179
6391
  disposers.push(() => clearInterval(statusInterval));
6180
6392
  let regDebounce;
6181
6393
  try {
6182
- const regWatcher = watch(globalRoot, { persistent: false }, (_event, filename) => {
6394
+ const regWatcher = fsWatch(globalRoot, { persistent: false }, (_event, filename) => {
6183
6395
  const name2 = filename ? String(filename) : "";
6184
6396
  if (!name2.startsWith("session-registry.json") || name2.endsWith(".lock")) return;
6185
6397
  if (regDebounce) clearTimeout(regDebounce);
@@ -6215,6 +6427,9 @@ function getCostRates(model) {
6215
6427
  function computeUsageCost(usage, rates) {
6216
6428
  return (usage.input * rates.input + usage.output * rates.output + (usage.cacheRead ?? 0) * rates.cacheRead) / 1e6;
6217
6429
  }
6430
+
6431
+ // src/server/model-catalog.ts
6432
+ import { capabilitiesFor } from "@wrongstack/providers";
6218
6433
  var SIBLING_CATALOG = {
6219
6434
  "anthropic-oauth": "anthropic",
6220
6435
  "openai-codex": "openai",
@@ -6321,6 +6536,9 @@ function readConfiguredBool(providerConfig, key) {
6321
6536
  function positiveNumber(value) {
6322
6537
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
6323
6538
  }
6539
+
6540
+ // src/server/server-runtime.ts
6541
+ import { toErrorMessage as toErrorMessage3 } from "@wrongstack/core/utils";
6324
6542
  async function resolvePorts(opts) {
6325
6543
  const requestedWsPort = opts.wsPort ?? 3457;
6326
6544
  const wsHost = opts.wsHost ?? process.env["WEBUI_HOST"] ?? process.env["WS_HOST"] ?? "127.0.0.1";
@@ -6381,7 +6599,7 @@ function createSessionStartPayload(g) {
6381
6599
  inputCost,
6382
6600
  outputCost,
6383
6601
  cacheReadCost,
6384
- projectName: path5.basename(projectRoot) || projectRoot,
6602
+ projectName: path12.basename(projectRoot) || projectRoot,
6385
6603
  projectRoot,
6386
6604
  cwd: g.getWorkingDir(),
6387
6605
  mode: g.getModeId(),
@@ -6419,13 +6637,26 @@ function createWsServers(ports, accessToken) {
6419
6637
  port: ports.wsPort,
6420
6638
  host: ports.wsHost,
6421
6639
  verifyClient: verifyClient2,
6422
- maxPayload: WS_MAX_PAYLOAD
6640
+ maxPayload: WS_MAX_PAYLOAD,
6641
+ // Send a ping every 15s to keep idle connections alive. Without this,
6642
+ // network equipment (routers, proxies, NAT gateways) may drop idle TCP
6643
+ // connections, causing the browser to see a close event and show the
6644
+ // "reconnecting" banner. The browser responds with a pong automatically
6645
+ // (the WebSocket API handles this at the transport level).
6646
+ pingInterval: 15e3,
6647
+ // Wait 5s for a pong before considering the connection dead. A client
6648
+ // that hasn't responded to 3 consecutive pings in a row (45s total
6649
+ // quiet) is almost certainly unreachable — close the socket so the
6650
+ // client can reconnect cleanly rather than hanging indefinitely.
6651
+ pingTimeout: 5e3
6423
6652
  });
6424
6653
  const wssSecondary = ports.wsHost === "127.0.0.1" ? new WebSocketServer({
6425
6654
  port: ports.wsPort,
6426
6655
  host: "::1",
6427
6656
  verifyClient: verifyClient2,
6428
- maxPayload: WS_MAX_PAYLOAD
6657
+ maxPayload: WS_MAX_PAYLOAD,
6658
+ pingInterval: 15e3,
6659
+ pingTimeout: 5e3
6429
6660
  }) : null;
6430
6661
  const clients = /* @__PURE__ */ new Map();
6431
6662
  console.log(
@@ -6447,7 +6678,7 @@ function armEvents(wssPrimary, wssSecondary, wsHost, wsPort, setupInput, watcher
6447
6678
  };
6448
6679
  wssPrimary.on("listening", () => arm(`${wsHost}:${wsPort}`));
6449
6680
  wssPrimary.on("error", (err) => {
6450
- console.error(JSON.stringify({ level: "error", event: "webui.ws_server_error", host: wsHost, message: toErrorMessage(err), timestamp: (/* @__PURE__ */ new Date()).toISOString() }));
6681
+ console.error(JSON.stringify({ level: "error", event: "webui.ws_server_error", host: wsHost, message: toErrorMessage3(err), timestamp: (/* @__PURE__ */ new Date()).toISOString() }));
6451
6682
  });
6452
6683
  if (wssSecondary) {
6453
6684
  wssSecondary.on("listening", () => arm(`::1:${wsPort}`));
@@ -6466,13 +6697,13 @@ function armEvents(wssPrimary, wssSecondary, wsHost, wsPort, setupInput, watcher
6466
6697
  };
6467
6698
  }
6468
6699
  function resolveWebuiDistDir(fromUrl, explicitDistDir) {
6469
- if (explicitDistDir) return path5.resolve(explicitDistDir);
6700
+ if (explicitDistDir) return path12.resolve(explicitDistDir);
6470
6701
  try {
6471
6702
  const requireFromHere2 = createRequire(fromUrl);
6472
6703
  const serverEntry = requireFromHere2.resolve("@wrongstack/webui/server");
6473
- return path5.resolve(path5.dirname(serverEntry), "..");
6704
+ return path12.resolve(path12.dirname(serverEntry), "..");
6474
6705
  } catch {
6475
- return path5.resolve(path5.dirname(fileURLToPath(fromUrl)), "..", "..", "dist");
6706
+ return path12.resolve(path12.dirname(fileURLToPath(fromUrl)), "..", "..", "dist");
6476
6707
  }
6477
6708
  }
6478
6709
  function startHttpServer(opts) {
@@ -6487,13 +6718,13 @@ function startHttpServer(opts) {
6487
6718
  watcherMetrics: opts.watcherMetrics,
6488
6719
  onFleetPing: opts.onFleetPing
6489
6720
  });
6490
- const registryBaseDir = path5.dirname(opts.globalConfigPath);
6721
+ const registryBaseDir = path12.dirname(opts.globalConfigPath);
6491
6722
  httpServer.listen(opts.httpPort, opts.wsHost, () => {
6492
6723
  const openUrl = buildWebUIAccessUrl({ host: opts.wsHost, port: opts.httpPort, token: opts.wsToken, publicUrl: opts.publicUrl });
6493
6724
  console.log(`[WebUI] HTTP server running on ${openUrl}`);
6494
6725
  if (opts.openBrowser) openBrowser(openUrl);
6495
6726
  void registerInstance(
6496
- { pid: process.pid, httpPort: opts.httpPort, wsPort: opts.wsPort, host: opts.wsHost, projectRoot: opts.projectRoot, projectName: path5.basename(opts.projectRoot) || opts.projectRoot, startedAt: (/* @__PURE__ */ new Date()).toISOString(), url: buildWebUIAccessUrl({ host: opts.wsHost, port: opts.httpPort, publicUrl: opts.publicUrl }) },
6727
+ { pid: process.pid, httpPort: opts.httpPort, wsPort: opts.wsPort, host: opts.wsHost, projectRoot: opts.projectRoot, projectName: path12.basename(opts.projectRoot) || opts.projectRoot, startedAt: (/* @__PURE__ */ new Date()).toISOString(), url: buildWebUIAccessUrl({ host: opts.wsHost, port: opts.httpPort, publicUrl: opts.publicUrl }) },
6497
6728
  registryBaseDir
6498
6729
  ).catch((err) => console.warn(JSON.stringify({ level: "warn", event: "webui.instance_record_failed", message: errMessage(err), timestamp: (/* @__PURE__ */ new Date()).toISOString() })));
6499
6730
  });
@@ -6507,6 +6738,63 @@ function registerShutdown(deps2) {
6507
6738
  onShutdown: deps2.onShutdown
6508
6739
  });
6509
6740
  }
6741
+
6742
+ // src/server/pre-context-services.ts
6743
+ import * as path14 from "node:path";
6744
+ import { createRequire as createRequire2 } from "node:module";
6745
+ import { WebSocket as WebSocket2 } from "ws";
6746
+ import { DefaultTokenCounter } from "@wrongstack/core/infrastructure";
6747
+ import {
6748
+ AgentStatusTracker,
6749
+ AnnotationsStore,
6750
+ DEFAULT_SESSION_PRUNE_DAYS,
6751
+ DefaultModelsRegistry,
6752
+ DefaultModeStore,
6753
+ DefaultPromptLoader,
6754
+ DefaultSessionReader,
6755
+ DefaultSessionStore,
6756
+ DefaultSkillLoader,
6757
+ DefaultSystemPromptBuilder,
6758
+ EventBus,
6759
+ FleetNotifier,
6760
+ GlobalMailbox,
6761
+ PromptUsageStore,
6762
+ ProviderRegistry,
6763
+ SkillInstaller,
6764
+ TOKENS,
6765
+ ToolRegistry,
6766
+ Context,
6767
+ applyToolDescriptionModes,
6768
+ applyToolResultRenderModes,
6769
+ configureChildEnvGitIdentity,
6770
+ resolveContextWindowPolicy,
6771
+ getSessionRegistry,
6772
+ makeMailboxTool,
6773
+ makeMailInboxTool,
6774
+ makeFleetStatusTool,
6775
+ makeMailSendTool
6776
+ } from "@wrongstack/core";
6777
+ import {
6778
+ createSuperMemoryTools
6779
+ } from "@wrongstack/super-memory";
6780
+ import { MCPRegistry } from "@wrongstack/mcp";
6781
+ import { buildProviderFactoriesFromRegistry } from "@wrongstack/providers";
6782
+ import { createDefaultContainer } from "@wrongstack/runtime";
6783
+ import {
6784
+ builtinToolsPack,
6785
+ configureDangerBypass,
6786
+ configureExecPolicy,
6787
+ forgetTool,
6788
+ relatedMemoryTool,
6789
+ rememberTool,
6790
+ searchMemoryTool
6791
+ } from "@wrongstack/tools";
6792
+ import { toErrorMessage as toErrorMessage5 } from "@wrongstack/core/utils";
6793
+
6794
+ // src/server/setup-screen.ts
6795
+ import { expectDefined as expectDefined2 } from "@wrongstack/core";
6796
+ import { toErrorMessage as toErrorMessage4 } from "@wrongstack/core/utils";
6797
+ import { makeProviderFromConfig } from "@wrongstack/providers";
6510
6798
  var UNCONFIGURED_CAPABILITIES = {
6511
6799
  tools: false,
6512
6800
  parallelTools: false,
@@ -6546,7 +6834,7 @@ function logCreateFailure(event, err) {
6546
6834
  JSON.stringify({
6547
6835
  level: "error",
6548
6836
  event,
6549
- message: toErrorMessage(err),
6837
+ message: toErrorMessage4(err),
6550
6838
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
6551
6839
  })
6552
6840
  );
@@ -6571,7 +6859,7 @@ function resolveSetupProvider(opts) {
6571
6859
  const savedProviders = config.providers ?? {};
6572
6860
  const firstKey = Object.keys(savedProviders)[0];
6573
6861
  if (firstKey) {
6574
- const firstProvider = expectDefined(savedProviders[firstKey]);
6862
+ const firstProvider = expectDefined2(savedProviders[firstKey]);
6575
6863
  try {
6576
6864
  const provider2 = makeProviderFromConfig(firstKey, {
6577
6865
  ...firstProvider,
@@ -6651,6 +6939,11 @@ function seedContextMeta(config, context) {
6651
6939
  const tgMs = tgExt?.["longToolThresholdMs"];
6652
6940
  meta["tgLongToolMs"] = typeof tgMs === "number" ? tgMs : 3e4;
6653
6941
  }
6942
+
6943
+ // src/server/model-auto-discovery.ts
6944
+ import * as fs11 from "node:fs/promises";
6945
+ import * as path13 from "node:path";
6946
+ import { COMPATIBLE_PRESETS, discoverOpenAICompatibleModels } from "@wrongstack/providers";
6654
6947
  function isOverlayRegistry(value) {
6655
6948
  return !!value && typeof value === "object" && typeof value.mergeOverlay === "function";
6656
6949
  }
@@ -6675,7 +6968,7 @@ function eligibleProviders(config) {
6675
6968
  }
6676
6969
  async function readCache(file) {
6677
6970
  try {
6678
- return JSON.parse(await fs4.readFile(file, "utf8"));
6971
+ return JSON.parse(await fs11.readFile(file, "utf8"));
6679
6972
  } catch {
6680
6973
  return {};
6681
6974
  }
@@ -6685,7 +6978,7 @@ async function discoverAndMergeWebuiProviders(opts) {
6685
6978
  if (!isOverlayRegistry(registry)) return;
6686
6979
  const targets = eligibleProviders(opts.config);
6687
6980
  if (targets.length === 0) return;
6688
- const cacheFile = path5.join(opts.cacheDir, "discovered-models-cache.json");
6981
+ const cacheFile = path13.join(opts.cacheDir, "discovered-models-cache.json");
6689
6982
  const cache = await readCache(cacheFile);
6690
6983
  let cacheDirty = false;
6691
6984
  await Promise.all(
@@ -6722,8 +7015,8 @@ async function discoverAndMergeWebuiProviders(opts) {
6722
7015
  );
6723
7016
  if (cacheDirty) {
6724
7017
  try {
6725
- await fs4.mkdir(path5.dirname(cacheFile), { recursive: true });
6726
- await fs4.writeFile(cacheFile, JSON.stringify(cache), "utf8");
7018
+ await fs11.mkdir(path13.dirname(cacheFile), { recursive: true });
7019
+ await fs11.writeFile(cacheFile, JSON.stringify(cache), "utf8");
6727
7020
  } catch {
6728
7021
  opts.logger?.debug?.("provider auto-discovery cache write failed");
6729
7022
  }
@@ -6745,20 +7038,22 @@ async function createPreContextServices(input) {
6745
7038
  await modelsRegistry.refresh();
6746
7039
  logger.info("models.dev catalog refreshed");
6747
7040
  } catch (err) {
6748
- logger.warn(`models.dev refresh failed (${toErrorMessage(err)}); using cached catalog`);
7041
+ logger.warn(`models.dev refresh failed (${toErrorMessage5(err)}); using cached catalog`);
6749
7042
  }
6750
7043
  }
6751
7044
  try {
6752
7045
  await discoverAndMergeWebuiProviders({
6753
7046
  config,
6754
7047
  registry: modelsRegistry,
6755
- cacheDir: path5.dirname(wpaths.modelsCache),
7048
+ cacheDir: path14.dirname(wpaths.modelsCache),
6756
7049
  logger
6757
7050
  });
6758
7051
  } catch (err) {
6759
- logger.debug(`provider auto-discovery skipped: ${toErrorMessage(err)}`);
7052
+ logger.debug(`provider auto-discovery skipped: ${toErrorMessage5(err)}`);
6760
7053
  }
6761
- const container = createDefaultContainer({ config, wpaths, logger, modelsRegistry });
7054
+ const events = opts.services?.events ?? new EventBus();
7055
+ events.setLogger(logger);
7056
+ const container = createDefaultContainer({ config, wpaths, logger, modelsRegistry, events });
6762
7057
  const configStore = opts.services?.configStore ?? container.resolve(TOKENS.ConfigStore);
6763
7058
  const providerRegistry = new ProviderRegistry();
6764
7059
  try {
@@ -6766,22 +7061,23 @@ async function createPreContextServices(input) {
6766
7061
  for (const f of factories) providerRegistry.register(f);
6767
7062
  console.log("[WebUI] Provider registry loaded:", providerRegistry.list().length, "providers");
6768
7063
  } catch (err) {
6769
- console.warn(JSON.stringify({ level: "warn", event: "webui.provider_registry_load_failed", message: toErrorMessage(err), timestamp: (/* @__PURE__ */ new Date()).toISOString() }));
7064
+ console.warn(JSON.stringify({ level: "warn", event: "webui.provider_registry_load_failed", message: toErrorMessage5(err), timestamp: (/* @__PURE__ */ new Date()).toISOString() }));
6770
7065
  }
6771
7066
  const toolRegistry = opts.services?.toolRegistry ?? (() => {
6772
7067
  const r = new ToolRegistry();
6773
7068
  r.registerAllOrThrow([...builtinToolsPack.tools ?? []], builtinToolsPack.name);
6774
7069
  return r;
6775
7070
  })();
6776
- const memoryStore = new DefaultMemoryStore({ paths: wpaths });
7071
+ const memoryStore = container.resolve(TOKENS.MemoryStore);
6777
7072
  if (config.features.memory) {
6778
7073
  toolRegistry.register(rememberTool(memoryStore));
6779
7074
  toolRegistry.register(forgetTool(memoryStore));
6780
7075
  toolRegistry.register(searchMemoryTool(memoryStore));
6781
7076
  toolRegistry.register(relatedMemoryTool(memoryStore));
7077
+ if (isSuperMemoryService(memoryStore)) {
7078
+ for (const tool of createSuperMemoryTools(memoryStore)) toolRegistry.register(tool);
7079
+ }
6782
7080
  }
6783
- const events = opts.services?.events ?? new EventBus();
6784
- events.setLogger(logger);
6785
7081
  toolRegistry.register(makeMailboxTool({ projectDir: wpaths.projectDir, events }));
6786
7082
  toolRegistry.register(makeMailSendTool({ projectDir: wpaths.projectDir, events }));
6787
7083
  toolRegistry.register(makeMailInboxTool({ projectDir: wpaths.projectDir, events }));
@@ -6790,6 +7086,7 @@ async function createPreContextServices(input) {
6790
7086
  applyToolResultRenderModes(toolRegistry, config.tools?.resultRenderMode);
6791
7087
  configureExecPolicy(config.tools?.exec ?? {});
6792
7088
  configureDangerBypass(config.tools?.exec?.danger ?? {});
7089
+ configureChildEnvGitIdentity(config.git?.identity ?? null);
6793
7090
  console.log("[WebUI] Tool registry loaded:", toolRegistry.list().length, "tools");
6794
7091
  const mcpRegistry = new MCPRegistry({ toolRegistry, events, log: logger, cacheDir: wpaths.cacheDir });
6795
7092
  if (config.features.mcp && config.mcpServers) {
@@ -6841,7 +7138,7 @@ async function createPreContextServices(input) {
6841
7138
  sessionId: session.id,
6842
7139
  projectSlug: wpaths.projectSlug,
6843
7140
  projectRoot,
6844
- projectName: path5.basename(projectRoot),
7141
+ projectName: path14.basename(projectRoot),
6845
7142
  workingDir,
6846
7143
  clientType: "webui",
6847
7144
  pid: process.pid,
@@ -6867,13 +7164,13 @@ async function createPreContextServices(input) {
6867
7164
  startWorktreeTelemetryBridge,
6868
7165
  startToolTelemetryBridge,
6869
7166
  startCostTelemetryBridge
6870
- } = await import('@wrongstack/core');
7167
+ } = await import("@wrongstack/core");
6871
7168
  const hqTelemetry = createHqPublisherFromEnv({
6872
7169
  clientKind: "webui",
6873
7170
  projectRoot,
6874
- projectName: path5.basename(projectRoot),
7171
+ projectName: path14.basename(projectRoot),
6875
7172
  appConfig: config,
6876
- socketFactory: (url) => new WebSocket(url)
7173
+ socketFactory: (url) => new WebSocket2(url)
6877
7174
  });
6878
7175
  if (hqTelemetry) {
6879
7176
  hqTelemetry.connect();
@@ -6883,7 +7180,7 @@ async function createPreContextServices(input) {
6883
7180
  events,
6884
7181
  sessionId: session.id,
6885
7182
  projectRoot,
6886
- projectName: path5.basename(projectRoot),
7183
+ projectName: path14.basename(projectRoot),
6887
7184
  globalRoot: wpaths.globalRoot,
6888
7185
  initialAgents: statusTracker?.getAgents(),
6889
7186
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -6972,7 +7269,7 @@ async function createPreContextServices(input) {
6972
7269
  const modelCapabilitiesRef = { current: modelCapabilities };
6973
7270
  const skillLoader = config.features.skills ? new DefaultSkillLoader({ paths: wpaths }) : void 0;
6974
7271
  const skillInstaller = config.features.skills ? new SkillInstaller({
6975
- manifestPath: path5.join(wpaths.globalRoot, "installed-skills.json"),
7272
+ manifestPath: path14.join(wpaths.globalRoot, "installed-skills.json"),
6976
7273
  projectSkillsDir: wpaths.inProjectSkills,
6977
7274
  globalSkillsDir: wpaths.globalSkills,
6978
7275
  projectHash: wpaths.projectHash,
@@ -6981,8 +7278,8 @@ async function createPreContextServices(input) {
6981
7278
  const promptsEnabled = config.features.prompts !== false;
6982
7279
  const bundledPromptsDir = promptsEnabled ? (() => {
6983
7280
  try {
6984
- const req = createRequire(import.meta.url);
6985
- return path5.join(path5.dirname(req.resolve("@wrongstack/core/package.json")), "data", "prompts");
7281
+ const req = createRequire2(import.meta.url);
7282
+ return path14.join(path14.dirname(req.resolve("@wrongstack/core/package.json")), "data", "prompts");
6986
7283
  } catch {
6987
7284
  return void 0;
6988
7285
  }
@@ -7063,8 +7360,33 @@ async function createPreContextServices(input) {
7063
7360
  needsSetup
7064
7361
  };
7065
7362
  }
7363
+ function isSuperMemoryService(memoryStore) {
7364
+ const value = memoryStore;
7365
+ return typeof value["retrieveForPath"] === "function" && typeof value["searchSuper"] === "function" && typeof value["graphFor"] === "function" && typeof value["verify"] === "function" && typeof value["hygiene"] === "function" && typeof value["listCandidates"] === "function";
7366
+ }
7367
+
7368
+ // src/server/start-webui.ts
7369
+ import * as path21 from "node:path";
7370
+ import {
7371
+ createDefaultPipelines,
7372
+ createSessionEventBridge,
7373
+ DEFAULT_CONTEXT_WINDOW_MODE_ID as DEFAULT_CONTEXT_WINDOW_MODE_ID2,
7374
+ expectDefined as expectDefined3,
7375
+ resolveSessionLoggingConfig,
7376
+ watchProviderConfig
7377
+ } from "@wrongstack/core";
7378
+ import { toErrorMessage as toErrorMessage10 } from "@wrongstack/core/utils";
7379
+ import { makeProviderFromConfig as makeProviderFromConfig3 } from "@wrongstack/providers";
7380
+ import {
7381
+ ensureSessionShell
7382
+ } from "@wrongstack/tools";
7383
+
7384
+ // src/server/boot.ts
7385
+ import {
7386
+ bootConfig as coreBootConfig
7387
+ } from "@wrongstack/core";
7066
7388
  async function bootConfig() {
7067
- const { config, vault, globalConfigPath, projectRoot, wpaths, logger } = await bootConfig$1({
7389
+ const { config, vault, globalConfigPath, projectRoot, wpaths, logger } = await coreBootConfig({
7068
7390
  appLabel: "WebUI"
7069
7391
  });
7070
7392
  return { config, vault, globalConfigPath, projectRoot, wpaths, logger };
@@ -7072,6 +7394,35 @@ async function bootConfig() {
7072
7394
  function patchConfig(config, updates) {
7073
7395
  return Object.freeze({ ...config, ...updates });
7074
7396
  }
7397
+
7398
+ // src/server/backend-services.ts
7399
+ import {
7400
+ Agent,
7401
+ AutoCompactionMiddleware as AutoCompactionMiddlewareCtor,
7402
+ BrainMonitor,
7403
+ CollaborationBus as CollaborationBusCtor,
7404
+ DEFAULT_TOOLS_CONFIG,
7405
+ createStrategyCompactor,
7406
+ collabInjectMiddleware,
7407
+ collabPauseMiddleware,
7408
+ estimateRequestTokensCalibrated,
7409
+ installDesignStudioMiddleware,
7410
+ ObservableBrainArbiter as ObservableBrainArbiterCtor,
7411
+ resolveContextWindowPolicy as resolveContextWindowPolicy2,
7412
+ createAutonomyBrain,
7413
+ createTieredBrainArbiter,
7414
+ DefaultBrainArbiter,
7415
+ GlobalMailbox as GlobalMailbox2,
7416
+ mailboxSessionTag,
7417
+ SessionMemoryConsolidator,
7418
+ TOKENS as TOKENS2,
7419
+ ToolExecutor
7420
+ } from "@wrongstack/core";
7421
+
7422
+ // src/server/collaboration-ws-handler.ts
7423
+ import { randomUUID } from "node:crypto";
7424
+ import { toErrorMessage as toErrorMessage6 } from "@wrongstack/core/utils";
7425
+ var REPLAY_LIMIT = 50;
7075
7426
  var PAUSE_TIMEOUT_MS = 6e4;
7076
7427
  var CollaborationWebSocketHandler = class {
7077
7428
  constructor(events, logger, reader, annotations, bus) {
@@ -7199,7 +7550,7 @@ var CollaborationWebSocketHandler = class {
7199
7550
  if (this.reader) {
7200
7551
  this.replayHistory(ws, sessionId).catch((err) => {
7201
7552
  this.logger.debug?.(
7202
- `collab: replay failed for ${sessionId}: ${toErrorMessage(err)}`
7553
+ `collab: replay failed for ${sessionId}: ${toErrorMessage6(err)}`
7203
7554
  );
7204
7555
  });
7205
7556
  }
@@ -7317,7 +7668,7 @@ var CollaborationWebSocketHandler = class {
7317
7668
  this.send(
7318
7669
  ws,
7319
7670
  this.errorMessage(
7320
- `annotation rejected: ${toErrorMessage(err)}`
7671
+ `annotation rejected: ${toErrorMessage6(err)}`
7321
7672
  )
7322
7673
  );
7323
7674
  }
@@ -7384,7 +7735,7 @@ var CollaborationWebSocketHandler = class {
7384
7735
  this.send(
7385
7736
  ws,
7386
7737
  this.errorMessage(
7387
- `resolve failed: ${toErrorMessage(err)}`
7738
+ `resolve failed: ${toErrorMessage6(err)}`
7388
7739
  )
7389
7740
  );
7390
7741
  }
@@ -7432,7 +7783,7 @@ var CollaborationWebSocketHandler = class {
7432
7783
  if (p.ws.readyState === 1) p.ws.send(data);
7433
7784
  } catch (err) {
7434
7785
  this.logger.debug?.(
7435
- `collab broadcast failed: ${toErrorMessage(err)}`
7786
+ `collab broadcast failed: ${toErrorMessage6(err)}`
7436
7787
  );
7437
7788
  }
7438
7789
  }
@@ -7459,11 +7810,11 @@ var CollaborationWebSocketHandler = class {
7459
7810
  }
7460
7811
  } catch (err) {
7461
7812
  this.logger.debug?.(
7462
- `collab: session reader rejected ${sessionId}: ${toErrorMessage(err)}`
7813
+ `collab: session reader rejected ${sessionId}: ${toErrorMessage6(err)}`
7463
7814
  );
7464
7815
  return;
7465
7816
  }
7466
- const tail2 = all.slice(-50);
7817
+ const tail2 = all.slice(-REPLAY_LIMIT);
7467
7818
  if (tail2.length === 0) return;
7468
7819
  for (const raw of tail2) {
7469
7820
  const ev = raw;
@@ -7540,7 +7891,7 @@ var CollaborationWebSocketHandler = class {
7540
7891
  if (p.ws.readyState === 1) p.ws.send(data);
7541
7892
  } catch (err) {
7542
7893
  this.logger.debug?.(
7543
- `collab broadcast failed: ${toErrorMessage(err)}`
7894
+ `collab broadcast failed: ${toErrorMessage6(err)}`
7544
7895
  );
7545
7896
  }
7546
7897
  }
@@ -7792,6 +8143,17 @@ var CollaborationWebSocketHandler = class {
7792
8143
  }
7793
8144
  }
7794
8145
  };
8146
+
8147
+ // src/server/codebase-indexing.ts
8148
+ import * as fs12 from "node:fs";
8149
+ import * as path15 from "node:path";
8150
+ import {
8151
+ cancelPendingReindexes,
8152
+ enqueueReindex,
8153
+ isIndexableFile,
8154
+ runStartupIndex,
8155
+ shutdownCodebaseIndexHost
8156
+ } from "@wrongstack/tools";
7795
8157
  var IGNORE_DIRS = /* @__PURE__ */ new Set([
7796
8158
  "node_modules",
7797
8159
  ".git",
@@ -7837,7 +8199,7 @@ function setupWebUICodebaseIndexing(deps2) {
7837
8199
  if (!filename) return;
7838
8200
  const rel = filename.toString();
7839
8201
  if (isIgnored(rel)) return;
7840
- const abs = path5.resolve(deps2.projectRoot, rel);
8202
+ const abs = path15.resolve(deps2.projectRoot, rel);
7841
8203
  enqueueFile(abs);
7842
8204
  });
7843
8205
  watcher.on("error", (err) => deps2.logger.debug(`webui codebase index watcher error: ${err}`));
@@ -7850,7 +8212,7 @@ function setupWebUICodebaseIndexing(deps2) {
7850
8212
  }
7851
8213
  function enqueueFile(filePath) {
7852
8214
  if (!idx.onEdit && !idx.watchExternal) return;
7853
- const abs = path5.isAbsolute(filePath) ? path5.normalize(filePath) : path5.resolve(deps2.projectRoot, filePath);
8215
+ const abs = path15.isAbsolute(filePath) ? path15.normalize(filePath) : path15.resolve(deps2.projectRoot, filePath);
7854
8216
  if (!isInside2(deps2.projectRoot, abs) || !isIndexableFile(abs)) return;
7855
8217
  enqueueReindex({
7856
8218
  projectRoot: deps2.projectRoot,
@@ -7887,15 +8249,23 @@ function isIgnored(rel) {
7887
8249
  return rel.split(/[/\\]/).some((seg) => IGNORE_DIRS.has(seg));
7888
8250
  }
7889
8251
  function isInside2(root, target) {
7890
- const normalizedRoot = path5.resolve(root);
7891
- const normalizedTarget = path5.resolve(target);
7892
- return normalizedTarget === normalizedRoot || normalizedTarget.startsWith(normalizedRoot + path5.sep);
8252
+ const normalizedRoot = path15.resolve(root);
8253
+ const normalizedTarget = path15.resolve(target);
8254
+ return normalizedTarget === normalizedRoot || normalizedTarget.startsWith(normalizedRoot + path15.sep);
7893
8255
  }
8256
+
8257
+ // src/server/discover-mailbox-bridge.ts
8258
+ import { spawn as spawn3 } from "node:child_process";
8259
+ import { createRequire as createRequire3 } from "node:module";
8260
+ import { existsSync } from "node:fs";
8261
+ import { dirname as dirname7, join as join11 } from "node:path";
8262
+ import { resolveProjectDir, wstackGlobalRoot as wstackGlobalRoot2 } from "@wrongstack/core";
8263
+ import { readLiveLock } from "@wrongstack/core/coordination";
7894
8264
  var MAILBOX_BRIDGE_BOOT_TIMEOUT_MS = 5e3;
7895
8265
  async function discoverMailboxBridgeForWebui(params) {
7896
8266
  const mode = params.config?.features?.mailboxBridge ?? "auto";
7897
8267
  if (mode === "off") return;
7898
- const projectDir = resolveProjectDir(params.projectRoot, wstackGlobalRoot());
8268
+ const projectDir = resolveProjectDir(params.projectRoot, wstackGlobalRoot2());
7899
8269
  let result = await readLiveLock(projectDir);
7900
8270
  let spawnedPid = null;
7901
8271
  if (result.kind !== "live") {
@@ -7961,7 +8331,7 @@ async function waitForLiveMailboxBridge(projectDir, timeoutMs) {
7961
8331
  function spawnMailboxBridge(projectRoot, logger) {
7962
8332
  const invocation = mailboxServeInvocation(projectRoot);
7963
8333
  try {
7964
- const child = spawn(invocation.command, invocation.args, {
8334
+ const child = spawn3(invocation.command, invocation.args, {
7965
8335
  cwd: projectRoot,
7966
8336
  detached: process.platform !== "win32" && !invocation.useShell,
7967
8337
  env: process.env,
@@ -7992,7 +8362,7 @@ function mailboxServeInvocation(projectRoot) {
7992
8362
  return { command: process.execPath, args: [explicitCliEntry, "mailbox", "serve"] };
7993
8363
  }
7994
8364
  try {
7995
- const require2 = createRequire(import.meta.url);
8365
+ const require2 = createRequire3(import.meta.url);
7996
8366
  const cliEntry = require2.resolve("@wrongstack/cli");
7997
8367
  return { command: process.execPath, args: [cliEntry, "mailbox", "serve"] };
7998
8368
  } catch {
@@ -8016,9 +8386,9 @@ function mailboxServeInvocation(projectRoot) {
8016
8386
  function findWorkspaceCliEntry(projectRoot) {
8017
8387
  let dir = projectRoot;
8018
8388
  for (let i = 0; i < 6; i++) {
8019
- const candidate = join(dir, "packages", "cli", "dist", "index.js");
8389
+ const candidate = join11(dir, "packages", "cli", "dist", "index.js");
8020
8390
  if (existsSync(candidate)) return candidate;
8021
- const parent = dirname(dir);
8391
+ const parent = dirname7(dir);
8022
8392
  if (parent === dir) return null;
8023
8393
  dir = parent;
8024
8394
  }
@@ -8027,10 +8397,15 @@ function findWorkspaceCliEntry(projectRoot) {
8027
8397
  function sleep(ms) {
8028
8398
  return new Promise((resolve10) => setTimeout(resolve10, ms));
8029
8399
  }
8400
+
8401
+ // src/server/terminal-ws-handler.ts
8402
+ import { spawn as spawnChild } from "node:child_process";
8403
+ import { createRequire as createRequire4 } from "node:module";
8404
+ import { toErrorMessage as toErrorMessage7 } from "@wrongstack/core/utils";
8030
8405
  var MAX_SESSIONS_PER_CLIENT = 8;
8031
8406
  var DEFAULT_COLS = 80;
8032
8407
  var DEFAULT_ROWS = 24;
8033
- var requireFromHere = createRequire(import.meta.url);
8408
+ var requireFromHere = createRequire4(import.meta.url);
8034
8409
  var cachedNodePty;
8035
8410
  var TerminalWebSocketHandler = class {
8036
8411
  constructor(getCwd, logger, loadNodePty = defaultLoadNodePty, killProcessTree = defaultKillProcessTree) {
@@ -8108,8 +8483,8 @@ var TerminalWebSocketHandler = class {
8108
8483
  ...windowsPtyOptions()
8109
8484
  });
8110
8485
  } catch (err) {
8111
- const msg = `Integrated terminal failed to start: ${toErrorMessage(err)}`;
8112
- this.logger.warn?.(`terminal spawn failed: ${toErrorMessage(err)}`);
8486
+ const msg = `Integrated terminal failed to start: ${toErrorMessage7(err)}`;
8487
+ this.logger.warn?.(`terminal spawn failed: ${toErrorMessage7(err)}`);
8113
8488
  this.send(ws, { type: "terminal.output", payload: { id: payload.id, data: `${msg}\r
8114
8489
  ` } });
8115
8490
  this.send(ws, { type: "terminal.exit", payload: { id: payload.id, exitCode: -1 } });
@@ -8162,7 +8537,7 @@ var TerminalWebSocketHandler = class {
8162
8537
  try {
8163
8538
  pty.kill();
8164
8539
  } catch (err) {
8165
- this.logger.warn?.(`${reason} failed: ${toErrorMessage(err)}`);
8540
+ this.logger.warn?.(`${reason} failed: ${toErrorMessage7(err)}`);
8166
8541
  }
8167
8542
  }
8168
8543
  killWindowsProcessTree(pty, reason) {
@@ -8174,7 +8549,7 @@ var TerminalWebSocketHandler = class {
8174
8549
  } catch {
8175
8550
  }
8176
8551
  } catch (err) {
8177
- this.logger.warn?.(`${reason} taskkill failed: ${toErrorMessage(err)}`);
8552
+ this.logger.warn?.(`${reason} taskkill failed: ${toErrorMessage7(err)}`);
8178
8553
  }
8179
8554
  }
8180
8555
  send(ws, msg) {
@@ -8205,7 +8580,7 @@ function windowsPtyOptions() {
8205
8580
  return { useConptyDll: true };
8206
8581
  }
8207
8582
  function defaultKillProcessTree(pid) {
8208
- const child = spawn("taskkill", ["/PID", String(pid), "/T", "/F"], {
8583
+ const child = spawnChild("taskkill", ["/PID", String(pid), "/T", "/F"], {
8209
8584
  stdio: "ignore",
8210
8585
  windowsHide: true
8211
8586
  });
@@ -8218,6 +8593,14 @@ function clampDim(value, fallback) {
8218
8593
  if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
8219
8594
  return Math.max(1, Math.min(1e3, Math.floor(value)));
8220
8595
  }
8596
+
8597
+ // src/server/backend-services.ts
8598
+ import { makeLightSubagentFactory } from "@wrongstack/runtime";
8599
+ import {
8600
+ createSuperMemoryToolCallMiddleware,
8601
+ createSuperMemoryTurnMiddleware
8602
+ } from "@wrongstack/super-memory";
8603
+ import { toErrorMessage as toErrorMessage8 } from "@wrongstack/core/utils";
8221
8604
  async function createAgentServices(input) {
8222
8605
  const {
8223
8606
  config,
@@ -8232,15 +8615,37 @@ async function createAgentServices(input) {
8232
8615
  providerRegistry,
8233
8616
  modelsRegistry,
8234
8617
  events,
8618
+ memoryStore,
8235
8619
  modelCapabilitiesRef
8236
8620
  } = input;
8237
- const collabBus = new CollaborationBus();
8621
+ const collabBus = new CollaborationBusCtor();
8238
8622
  const pipelines = input.pipelines;
8239
8623
  const collabInject = collabInjectMiddleware(collabBus, { logger });
8240
8624
  pipelines.toolCall.prepend(collabInject);
8241
8625
  const collabPause = collabPauseMiddleware(collabBus, { logger });
8242
8626
  pipelines.toolCall.prepend(collabPause);
8243
8627
  installDesignStudioMiddleware({ pipelines, ctx: context });
8628
+ if (config.features.memory !== false && config.superMemory?.enabled !== false && isSuperMemoryRetriever(memoryStore)) {
8629
+ if (config.superMemory?.inject?.toolResults !== false) {
8630
+ pipelines.toolCall.use(createSuperMemoryToolCallMiddleware({
8631
+ memory: memoryStore,
8632
+ maxHintsPerTool: config.superMemory?.inject?.maxHintsPerTool,
8633
+ maxCharsPerTool: config.superMemory?.inject?.maxCharsPerTool,
8634
+ minScore: config.superMemory?.inject?.minScore,
8635
+ repeatCooldownMs: config.superMemory?.inject?.repeatCooldownMs,
8636
+ verifyOnMutation: config.superMemory?.hygiene?.autoOnFileChange,
8637
+ triggers: config.superMemory?.inject?.triggers
8638
+ }));
8639
+ }
8640
+ if (config.superMemory?.inject?.turnContext !== false) {
8641
+ pipelines.request.use(createSuperMemoryTurnMiddleware({
8642
+ memory: memoryStore,
8643
+ maxMemories: config.superMemory?.inject?.maxTurnMemories,
8644
+ maxChars: config.superMemory?.inject?.maxCharsPerTurn,
8645
+ minScore: config.superMemory?.inject?.minScore
8646
+ }));
8647
+ }
8648
+ }
8244
8649
  const codebaseIndexing = setupWebUICodebaseIndexing({
8245
8650
  config,
8246
8651
  context,
@@ -8254,7 +8659,7 @@ async function createAgentServices(input) {
8254
8659
  summarizerModel: config.context?.summarizerModel,
8255
8660
  llmSelector: config.context?.llmSelector
8256
8661
  });
8257
- const initialContextPolicy = resolveContextWindowPolicy(config.context);
8662
+ const initialContextPolicy = resolveContextWindowPolicy2(config.context);
8258
8663
  let autoCompactor;
8259
8664
  if (config.context?.autoCompact !== false) {
8260
8665
  let effectiveMaxContext = config.context?.effectiveMaxContext ?? 0;
@@ -8271,7 +8676,7 @@ async function createAgentServices(input) {
8271
8676
  }
8272
8677
  }
8273
8678
  if (!effectiveMaxContext) effectiveMaxContext = provider.capabilities.maxContext;
8274
- autoCompactor = new AutoCompactionMiddleware(
8679
+ autoCompactor = new AutoCompactionMiddlewareCtor(
8275
8680
  compactor,
8276
8681
  effectiveMaxContext,
8277
8682
  (ctx) => estimateRequestTokensCalibrated(
@@ -8299,7 +8704,7 @@ async function createAgentServices(input) {
8299
8704
  const updateAutoCompactionMaxContext = async (newProvider, providerId = newProvider.id, providerCfg) => {
8300
8705
  await modelsRegistry.refresh().catch((err) => {
8301
8706
  logger.warn(
8302
- `models.dev refresh failed for ${providerId}/${context.model}: ${toErrorMessage(err)}; using cached catalog`
8707
+ `models.dev refresh failed for ${providerId}/${context.model}: ${toErrorMessage8(err)}; using cached catalog`
8303
8708
  );
8304
8709
  });
8305
8710
  const currentConfig = input.config;
@@ -8336,9 +8741,9 @@ async function createAgentServices(input) {
8336
8741
  maxContext: newMaxContext
8337
8742
  });
8338
8743
  };
8339
- const secretScrubber = container.resolve(TOKENS.SecretScrubber);
8340
- const renderer = container.has(TOKENS.Renderer) ? container.resolve(TOKENS.Renderer) : void 0;
8341
- const permissionPolicy = container.resolve(TOKENS.PermissionPolicy);
8744
+ const secretScrubber = container.resolve(TOKENS2.SecretScrubber);
8745
+ const renderer = container.has(TOKENS2.Renderer) ? container.resolve(TOKENS2.Renderer) : void 0;
8746
+ const permissionPolicy = container.resolve(TOKENS2.PermissionPolicy);
8342
8747
  const toolExecutor = new ToolExecutor(toolRegistry, {
8343
8748
  permissionPolicy,
8344
8749
  secretScrubber,
@@ -8349,7 +8754,7 @@ async function createAgentServices(input) {
8349
8754
  perIterationOutputCapBytes: config.tools?.perIterationOutputCapBytes ?? DEFAULT_TOOLS_CONFIG.perIterationOutputCapBytes,
8350
8755
  tracer: void 0
8351
8756
  });
8352
- const webuiLogger = container.resolve(TOKENS.Logger);
8757
+ const webuiLogger = container.resolve(TOKENS2.Logger);
8353
8758
  void discoverMailboxBridgeForWebui({
8354
8759
  projectRoot,
8355
8760
  config,
@@ -8375,6 +8780,9 @@ async function createAgentServices(input) {
8375
8780
  confirmAwaiter: void 0,
8376
8781
  toolExecutor
8377
8782
  });
8783
+ if (config.features.memory && config.features.memoryConsolidation !== false) {
8784
+ agent.extensions.register(new SessionMemoryConsolidator({ memoryStore }));
8785
+ }
8378
8786
  console.log("[WebUI] Agent initialized");
8379
8787
  const brainSettings = { maxAutoRisk: "medium" };
8380
8788
  const autonomousBrain = {
@@ -8385,7 +8793,7 @@ async function createAgentServices(input) {
8385
8793
  // the tiered ceiling gates risk — keep inner permissive
8386
8794
  }).decide(request)
8387
8795
  };
8388
- const brain = new ObservableBrainArbiter(
8796
+ const brain = new ObservableBrainArbiterCtor(
8389
8797
  createTieredBrainArbiter({
8390
8798
  policy: new DefaultBrainArbiter(),
8391
8799
  autonomous: autonomousBrain,
@@ -8393,7 +8801,7 @@ async function createAgentServices(input) {
8393
8801
  }),
8394
8802
  events
8395
8803
  );
8396
- container.bind(TOKENS.BrainArbiter, () => brain);
8804
+ container.bind(TOKENS2.BrainArbiter, () => brain);
8397
8805
  const brainLog = [];
8398
8806
  const pushBrainLog = (entry) => {
8399
8807
  brainLog.push(entry);
@@ -8426,7 +8834,7 @@ async function createAgentServices(input) {
8426
8834
  outcome: e.decision.type === "deny" ? e.decision.reason : ""
8427
8835
  })
8428
8836
  );
8429
- const brainMailbox = new GlobalMailbox(wpaths.projectDir, events);
8837
+ const brainMailbox = new GlobalMailbox2(wpaths.projectDir, events);
8430
8838
  const brainMonitor = new BrainMonitor({
8431
8839
  events,
8432
8840
  brain,
@@ -8519,14 +8927,14 @@ async function createAgentServices(input) {
8519
8927
  updateAutoCompactionMaxContext
8520
8928
  };
8521
8929
  }
8930
+ function isSuperMemoryRetriever(memoryStore) {
8931
+ const value = memoryStore;
8932
+ return typeof value["retrieveForPath"] === "function" && typeof value["searchSuper"] === "function";
8933
+ }
8522
8934
 
8523
8935
  // src/server/pending-confirms.ts
8524
- function isDestructivePendingConfirm(confirm) {
8525
- return confirm.riskTier === "destructive" || confirm.decisionSource === "yolo_destructive";
8526
- }
8527
8936
  function resolveYoloEligiblePendingConfirms(pendingConfirms) {
8528
8937
  for (const [id, confirm] of pendingConfirms) {
8529
- if (isDestructivePendingConfirm(confirm)) continue;
8530
8938
  pendingConfirms.delete(id);
8531
8939
  confirm.resolve("yes");
8532
8940
  }
@@ -8682,6 +9090,9 @@ function createConnectionHandler(opts) {
8682
9090
  };
8683
9091
  }
8684
9092
 
9093
+ // src/server/message-dispatcher.ts
9094
+ import path16 from "node:path";
9095
+
8685
9096
  // src/server/autophase-routes.ts
8686
9097
  async function handleAutoPhaseRoute(_ws, msg, handlers) {
8687
9098
  if (!msg.type.startsWith("autophase.")) return false;
@@ -8705,10 +9116,13 @@ async function handleBrainRoute(ws, msg, handlers) {
8705
9116
  return false;
8706
9117
  }
8707
9118
  }
9119
+
9120
+ // src/server/goal-handlers.ts
9121
+ import { resolveWstackPaths } from "@wrongstack/core/utils";
8708
9122
  async function handleGoalGet(projectRoot, broadcast2) {
8709
9123
  try {
8710
- const goalPath = resolveWstackPaths$1({ projectRoot }).projectGoal;
8711
- const { readFile: readFile10 } = await import('fs/promises');
9124
+ const goalPath = resolveWstackPaths({ projectRoot }).projectGoal;
9125
+ const { readFile: readFile10 } = await import("node:fs/promises");
8712
9126
  const raw = await readFile10(goalPath, "utf8");
8713
9127
  const goal = JSON.parse(raw);
8714
9128
  broadcast2({ type: "goal.updated", payload: goal });
@@ -8762,7 +9176,7 @@ async function handleTasksGet(ctx, ws) {
8762
9176
  const taskPath = ctx.context.meta["task.path"];
8763
9177
  if (typeof taskPath === "string" && taskPath) {
8764
9178
  try {
8765
- const { loadTasks } = await import('@wrongstack/core');
9179
+ const { loadTasks } = await import("@wrongstack/core");
8766
9180
  const file = await loadTasks(taskPath);
8767
9181
  ctx.send(ws, { type: "tasks.updated", payload: sessionPayload(ctx, { tasks: file?.tasks ?? [] }) });
8768
9182
  } catch {
@@ -8782,7 +9196,7 @@ async function handleTaskUpdate(ctx, ws, payload) {
8782
9196
  return;
8783
9197
  }
8784
9198
  try {
8785
- const { loadTasks, saveTasks } = await import('@wrongstack/core');
9199
+ const { loadTasks, saveTasks } = await import("@wrongstack/core");
8786
9200
  const file = await loadTasks(taskPath);
8787
9201
  if (!file) {
8788
9202
  sendResult2(ws, ctx, false, "No task file found.");
@@ -8811,7 +9225,7 @@ async function handlePlanGet(ctx, ws) {
8811
9225
  const sessionId = ctx.context.session?.id ?? "";
8812
9226
  if (typeof planPath === "string" && planPath) {
8813
9227
  try {
8814
- const { loadPlan } = await import('@wrongstack/core');
9228
+ const { loadPlan } = await import("@wrongstack/core");
8815
9229
  const plan = await loadPlan(planPath);
8816
9230
  ctx.send(ws, {
8817
9231
  type: "plan.updated",
@@ -8852,7 +9266,7 @@ async function handlePlanTemplateUse(ctx, ws, template) {
8852
9266
  return;
8853
9267
  }
8854
9268
  try {
8855
- const { getPlanTemplate, loadPlan, savePlan, emptyPlan, addPlanItem } = await import('@wrongstack/core');
9269
+ const { getPlanTemplate, loadPlan, savePlan, emptyPlan, addPlanItem } = await import("@wrongstack/core");
8856
9270
  const tpl = getPlanTemplate(template);
8857
9271
  if (!tpl) {
8858
9272
  sendResult2(ws, ctx, false, `Unknown template "${template}".`);
@@ -8877,7 +9291,7 @@ async function handlePlanItemUpdate(ctx, ws, payload) {
8877
9291
  return;
8878
9292
  }
8879
9293
  try {
8880
- const { mutatePlan, setPlanItemStatus } = await import('@wrongstack/core');
9294
+ const { mutatePlan, setPlanItemStatus } = await import("@wrongstack/core");
8881
9295
  let changed = false;
8882
9296
  const plan = await mutatePlan(planPath, sessionId, async (p) => {
8883
9297
  const before = p.updatedAt;
@@ -8944,6 +9358,45 @@ async function handleWorklistMessage(ctx, ws, msg) {
8944
9358
  return;
8945
9359
  }
8946
9360
  }
9361
+
9362
+ // src/server/kanban-routes.ts
9363
+ import {
9364
+ deserializeTaskGraph,
9365
+ serializeTaskGraph
9366
+ } from "@wrongstack/core";
9367
+ import {
9368
+ addColumn,
9369
+ addGoalMetricToTask,
9370
+ addTask,
9371
+ assignTask,
9372
+ claimReadyTask,
9373
+ copyTaskToBoard,
9374
+ createBoard,
9375
+ duplicateBoard,
9376
+ exportBoardToTaskGraph,
9377
+ generateBoardFromDescription,
9378
+ getBoard,
9379
+ getKanbanOrchestrationSnapshot,
9380
+ getKanbanQueueHealth,
9381
+ getTask,
9382
+ getTaskChain,
9383
+ listReadyTasks,
9384
+ listBoards,
9385
+ mergeTasks,
9386
+ moveTask,
9387
+ parseLinesIntoTasks,
9388
+ removeBoard,
9389
+ removeColumn,
9390
+ removeTask,
9391
+ releaseTaskClaim,
9392
+ setTaskChain,
9393
+ splitTask,
9394
+ syncBoardFromTaskGraph,
9395
+ transferTaskToBoard,
9396
+ updateBoard,
9397
+ updateGoalMetricOnTask,
9398
+ updateTask
9399
+ } from "@wrongstack/kanban";
8947
9400
  function ok(ws, type, data) {
8948
9401
  send(ws, { type, payload: { success: true, data: data ?? null } });
8949
9402
  }
@@ -9544,7 +9997,7 @@ async function handlePrefsRoute(ws, msg, handlers) {
9544
9997
  // src/server/process-handlers.ts
9545
9998
  async function handleProcessList(ws) {
9546
9999
  try {
9547
- const { getProcessRegistry } = await import('@wrongstack/tools');
10000
+ const { getProcessRegistry } = await import("@wrongstack/tools");
9548
10001
  const procs = getProcessRegistry().list();
9549
10002
  send(ws, {
9550
10003
  type: "process.list",
@@ -9571,7 +10024,7 @@ async function handleProcessKill(ws, payload) {
9571
10024
  }
9572
10025
  const { pid } = parsed.value;
9573
10026
  try {
9574
- const { getProcessRegistry } = await import('@wrongstack/tools');
10027
+ const { getProcessRegistry } = await import("@wrongstack/tools");
9575
10028
  const proc = getProcessRegistry().get(pid);
9576
10029
  if (proc?.protected) {
9577
10030
  sendResult(ws, false, `Cannot kill protected process (PID ${pid})`);
@@ -9585,7 +10038,7 @@ async function handleProcessKill(ws, payload) {
9585
10038
  }
9586
10039
  async function handleProcessKillAll(ws) {
9587
10040
  try {
9588
- const { getProcessRegistry } = await import('@wrongstack/tools');
10041
+ const { getProcessRegistry } = await import("@wrongstack/tools");
9589
10042
  getProcessRegistry().killAll();
9590
10043
  sendResult(ws, true, "All processes killed");
9591
10044
  } catch (err) {
@@ -9895,7 +10348,7 @@ function createMessageDispatcher(opts) {
9895
10348
  skillLoader: deps2.skillLoader,
9896
10349
  skillInstaller: deps2.skillInstaller,
9897
10350
  projectRoot,
9898
- projectSkillsDir: path5__default.join(projectRoot, ".wrongstack", "skills"),
10351
+ projectSkillsDir: path16.join(projectRoot, ".wrongstack", "skills"),
9899
10352
  globalSkillsDir: deps2.wpaths.globalSkills
9900
10353
  };
9901
10354
  }
@@ -10350,6 +10803,7 @@ function createMessageDispatcher(opts) {
10350
10803
  break;
10351
10804
  }
10352
10805
  case "prefs.update": {
10806
+ void ws;
10353
10807
  throw new Error("handlePrefsRoute did not claim prefs.update \u2014 check chain order");
10354
10808
  }
10355
10809
  case "prefs.get": {
@@ -10363,6 +10817,11 @@ function createMessageDispatcher(opts) {
10363
10817
  }
10364
10818
  };
10365
10819
  }
10820
+
10821
+ // src/server/pref-helpers.ts
10822
+ import { atomicWrite as atomicWrite6 } from "@wrongstack/core/utils";
10823
+ import { decryptConfigSecrets as decryptConfigSecrets2, encryptConfigSecrets as encryptConfigSecrets2 } from "@wrongstack/core/security";
10824
+ import * as fs13 from "node:fs/promises";
10366
10825
  var PREF_KEYS = [
10367
10826
  "autonomy",
10368
10827
  "autonomyDelayMs",
@@ -10422,7 +10881,7 @@ async function updateGlobalConfig(deps2, holder, mutate, errorLabel) {
10422
10881
  const write = async () => {
10423
10882
  let raw;
10424
10883
  try {
10425
- raw = await fs4.readFile(globalConfigPath, "utf8");
10884
+ raw = await fs13.readFile(globalConfigPath, "utf8");
10426
10885
  } catch {
10427
10886
  raw = "{}";
10428
10887
  }
@@ -10433,10 +10892,10 @@ async function updateGlobalConfig(deps2, holder, mutate, errorLabel) {
10433
10892
  logger.warn(`${errorLabel}: refusing to overwrite corrupt config at ${globalConfigPath}`);
10434
10893
  return;
10435
10894
  }
10436
- const decrypted = decryptConfigSecrets(parsed, vault);
10895
+ const decrypted = decryptConfigSecrets2(parsed, vault);
10437
10896
  mutate(decrypted);
10438
- const encrypted = encryptConfigSecrets(decrypted, vault);
10439
- await atomicWrite$1(globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
10897
+ const encrypted = encryptConfigSecrets2(decrypted, vault);
10898
+ await atomicWrite6(globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
10440
10899
  };
10441
10900
  const next = holder.lock.then(write);
10442
10901
  holder.lock = next.then(
@@ -10598,13 +11057,18 @@ async function persistPrefsToConfig(deps2, holder, payload) {
10598
11057
  }
10599
11058
  }, "prefs");
10600
11059
  }
11060
+
11061
+ // src/server/projects-manifest.ts
11062
+ import * as fs14 from "node:fs/promises";
11063
+ import * as path17 from "node:path";
11064
+ import { projectSlug } from "@wrongstack/core";
10601
11065
  function projectsJsonPath(globalConfigPath) {
10602
- const base = path5.dirname(globalConfigPath);
10603
- return path5.join(base, "projects.json");
11066
+ const base = path17.dirname(globalConfigPath);
11067
+ return path17.join(base, "projects.json");
10604
11068
  }
10605
11069
  async function loadManifest(globalConfigPath) {
10606
11070
  try {
10607
- const raw = await fs4.readFile(projectsJsonPath(globalConfigPath), "utf8");
11071
+ const raw = await fs14.readFile(projectsJsonPath(globalConfigPath), "utf8");
10608
11072
  const parsed = JSON.parse(raw);
10609
11073
  return { projects: parsed.projects ?? [] };
10610
11074
  } catch {
@@ -10613,18 +11077,26 @@ async function loadManifest(globalConfigPath) {
10613
11077
  }
10614
11078
  async function saveManifest(manifest, globalConfigPath) {
10615
11079
  const file = projectsJsonPath(globalConfigPath);
10616
- await fs4.mkdir(path5.dirname(file), { recursive: true });
10617
- await fs4.writeFile(file, JSON.stringify(manifest, null, 2), "utf8");
11080
+ await fs14.mkdir(path17.dirname(file), { recursive: true });
11081
+ await fs14.writeFile(file, JSON.stringify(manifest, null, 2), "utf8");
10618
11082
  }
10619
11083
  function generateProjectSlug(rootPath) {
10620
11084
  return projectSlug(rootPath);
10621
11085
  }
10622
11086
  async function ensureProjectDataDir(slug, globalConfigPath) {
10623
- const base = path5.dirname(globalConfigPath);
10624
- const dir = path5.join(base, "projects", slug);
10625
- await fs4.mkdir(dir, { recursive: true });
11087
+ const base = path17.dirname(globalConfigPath);
11088
+ const dir = path17.join(base, "projects", slug);
11089
+ await fs14.mkdir(dir, { recursive: true });
10626
11090
  return dir;
10627
11091
  }
11092
+
11093
+ // src/server/provider-handlers.ts
11094
+ import { DefaultSecretScrubber } from "@wrongstack/core";
11095
+ import {
11096
+ beginOAuthLogin
11097
+ } from "@wrongstack/providers/oauth";
11098
+ import { probeLocalLlm } from "@wrongstack/runtime/probe";
11099
+ import { toErrorMessage as toErrorMessage9 } from "@wrongstack/core/utils";
10628
11100
  function projectSavedProviders(providers) {
10629
11101
  return Object.entries(providers).map(([id, cfg]) => {
10630
11102
  const keys = normalizeKeys(cfg);
@@ -10655,7 +11127,7 @@ function createProviderHandlers(deps2) {
10655
11127
  }
10656
11128
  async function saveConfigProviders(providers) {
10657
11129
  const next = configWriteLock.then(() => saveProviders(globalConfigPath, vault, providers)).catch((err) => {
10658
- const msg = toErrorMessage(err);
11130
+ const msg = toErrorMessage9(err);
10659
11131
  console.error(JSON.stringify({
10660
11132
  level: "error",
10661
11133
  event: "webui.provider_save_failed",
@@ -10927,10 +11399,23 @@ function createProviderHandlers(deps2) {
10927
11399
  loadConfigProviders
10928
11400
  };
10929
11401
  }
11402
+
11403
+ // src/server/routes.ts
11404
+ import path20 from "node:path";
11405
+ import {
11406
+ enhanceUserPrompt,
11407
+ gatedEnhancerReasoning,
11408
+ recentTextTurns,
11409
+ resolveProviderModelList
11410
+ } from "@wrongstack/core";
11411
+ import { makeProviderFromConfig as makeProviderFromConfig2 } from "@wrongstack/providers";
11412
+
11413
+ // src/server/mailbox-handlers.ts
11414
+ import { GlobalMailbox as GlobalMailbox3, resolveProjectDir as resolveProjectDir2 } from "@wrongstack/core";
10930
11415
  async function handleMailboxMessages(ws, deps2, payload) {
10931
11416
  try {
10932
- const dir = resolveProjectDir(deps2.projectRoot, deps2.globalRoot);
10933
- const mb = new GlobalMailbox(dir);
11417
+ const dir = resolveProjectDir2(deps2.projectRoot, deps2.globalRoot);
11418
+ const mb = new GlobalMailbox3(dir);
10934
11419
  const limit = payload?.limit ?? 30;
10935
11420
  const unreadForAgent = payload?.unreadOnly === true && payload.agentId !== void 0;
10936
11421
  const messages = await mb.query({
@@ -10970,8 +11455,8 @@ async function handleMailboxMessages(ws, deps2, payload) {
10970
11455
  }
10971
11456
  async function handleMailboxAgents(ws, deps2, payload) {
10972
11457
  try {
10973
- const dir = resolveProjectDir(deps2.projectRoot, deps2.globalRoot);
10974
- const mb = new GlobalMailbox(dir);
11458
+ const dir = resolveProjectDir2(deps2.projectRoot, deps2.globalRoot);
11459
+ const mb = new GlobalMailbox3(dir);
10975
11460
  const agents = payload?.onlineOnly ? await mb.getOnlineAgents() : await mb.getAgentStatuses();
10976
11461
  send(ws, {
10977
11462
  type: "mailbox.agents",
@@ -10999,8 +11484,8 @@ async function handleMailboxAgents(ws, deps2, payload) {
10999
11484
  }
11000
11485
  async function handleMailboxClear(ws, deps2) {
11001
11486
  try {
11002
- const dir = resolveProjectDir(deps2.projectRoot, deps2.globalRoot);
11003
- const mb = new GlobalMailbox(dir);
11487
+ const dir = resolveProjectDir2(deps2.projectRoot, deps2.globalRoot);
11488
+ const mb = new GlobalMailbox3(dir);
11004
11489
  await mb.clearAll();
11005
11490
  send(ws, { type: "mailbox.cleared", payload: {} });
11006
11491
  } catch (err) {
@@ -11009,14 +11494,21 @@ async function handleMailboxClear(ws, deps2) {
11009
11494
  }
11010
11495
  async function handleMailboxPurge(ws, deps2, opts) {
11011
11496
  try {
11012
- const dir = resolveProjectDir(deps2.projectRoot, deps2.globalRoot);
11013
- const mb = new GlobalMailbox(dir);
11497
+ const dir = resolveProjectDir2(deps2.projectRoot, deps2.globalRoot);
11498
+ const mb = new GlobalMailbox3(dir);
11014
11499
  const result = await mb.purgeStale(opts);
11015
11500
  send(ws, { type: "mailbox.purged", payload: result });
11016
11501
  } catch (err) {
11017
11502
  send(ws, { type: "mailbox.purged", payload: { error: errMessage(err) } });
11018
11503
  }
11019
11504
  }
11505
+
11506
+ // src/server/mode-handlers.ts
11507
+ import {
11508
+ DefaultSystemPromptBuilder as DefaultSystemPromptBuilder2,
11509
+ resolveWstackPaths as resolveWstackPaths2,
11510
+ ToolValidationError as ToolValidationError5
11511
+ } from "@wrongstack/core";
11020
11512
  function createModeHandlers(ctx) {
11021
11513
  return {
11022
11514
  listModes: async (ws) => {
@@ -11059,14 +11551,14 @@ function createModeHandlers(ctx) {
11059
11551
  } else {
11060
11552
  const found = await ctx.modeStore.getMode(id);
11061
11553
  if (!found) {
11062
- throw new ToolValidationError({ message: `Unknown mode "${id}"`, field: "modeId" });
11554
+ throw new ToolValidationError5({ message: `Unknown mode "${id}"`, field: "modeId" });
11063
11555
  }
11064
11556
  await ctx.modeStore.setActiveMode(id);
11065
11557
  }
11066
11558
  ctx.setModeId(id);
11067
11559
  const modePrompt = id === "default" ? "" : (await ctx.modeStore.getMode(id))?.prompt ?? "";
11068
- const paths = resolveWstackPaths({ projectRoot: ctx.projectRoot, globalRoot: ctx.globalRoot });
11069
- const freshBuilder = new DefaultSystemPromptBuilder({
11560
+ const paths = resolveWstackPaths2({ projectRoot: ctx.projectRoot, globalRoot: ctx.globalRoot });
11561
+ const freshBuilder = new DefaultSystemPromptBuilder2({
11070
11562
  memoryStore: ctx.memoryStore,
11071
11563
  skillLoader: ctx.skillLoader,
11072
11564
  modeStore: ctx.modeStore,
@@ -11096,6 +11588,9 @@ function createModeHandlers(ctx) {
11096
11588
  }
11097
11589
  };
11098
11590
  }
11591
+
11592
+ // src/server/project-handlers.ts
11593
+ import * as path18 from "node:path";
11099
11594
  function createProjectHandlers(ctx) {
11100
11595
  return {
11101
11596
  listProjects: async (ws) => {
@@ -11121,7 +11616,7 @@ function createProjectHandlers(ctx) {
11121
11616
  selectProject: async (ws, msg) => {
11122
11617
  const payload = msg.payload;
11123
11618
  const root = typeof payload?.root === "string" ? payload.root : "";
11124
- const name2 = typeof payload?.name === "string" ? payload.name : root ? path5.basename(root) : "";
11619
+ const name2 = typeof payload?.name === "string" ? payload.name : root ? path18.basename(root) : "";
11125
11620
  send(ws, {
11126
11621
  type: "projects.selected",
11127
11622
  payload: {
@@ -11154,6 +11649,14 @@ function createProjectHandlers(ctx) {
11154
11649
  }
11155
11650
  };
11156
11651
  }
11652
+
11653
+ // src/server/session-handlers.ts
11654
+ import * as path19 from "node:path";
11655
+ import {
11656
+ DEFAULT_CONTEXT_WINDOW_MODE_ID,
11657
+ repairToolUseAdjacency,
11658
+ resolveContextWindowPolicy as resolveContextWindowPolicy3
11659
+ } from "@wrongstack/core";
11157
11660
  function createSessionHandlers(ctx) {
11158
11661
  const currentSessionId = () => ctx.getSession().id;
11159
11662
  const sessionPayload2 = (payload) => {
@@ -11307,7 +11810,7 @@ function createSessionHandlers(ctx) {
11307
11810
  return;
11308
11811
  }
11309
11812
  const { id } = parsed.value;
11310
- let policy = resolveContextWindowPolicy({}, id);
11813
+ let policy = resolveContextWindowPolicy3({}, id);
11311
11814
  if (policy.id !== id) {
11312
11815
  const customModes = ctx.customModeStore.list().filter((m) => m.custom === true);
11313
11816
  const custom = customModes.find((m) => m.id === id);
@@ -11379,7 +11882,7 @@ function createSessionHandlers(ctx) {
11379
11882
  const { id } = parsed.value;
11380
11883
  if (String(ctx.context.meta["contextWindowMode"] ?? "") === id) {
11381
11884
  ctx.context.meta["contextWindowMode"] = DEFAULT_CONTEXT_WINDOW_MODE_ID;
11382
- ctx.context.meta["contextWindowPolicy"] = resolveContextWindowPolicy({}, DEFAULT_CONTEXT_WINDOW_MODE_ID);
11885
+ ctx.context.meta["contextWindowPolicy"] = resolveContextWindowPolicy3({}, DEFAULT_CONTEXT_WINDOW_MODE_ID);
11383
11886
  }
11384
11887
  const result = ctx.customModeStore.remove(id);
11385
11888
  sendResult(ws, result.ok, result.error ?? `Mode "${id}" deleted`);
@@ -11499,10 +12002,10 @@ function createSessionHandlers(ctx) {
11499
12002
  listCheckpoints: async (ws, msg) => {
11500
12003
  if (!ensureCurrentSession(ws, msg, "session.checkpoints")) return;
11501
12004
  try {
11502
- const { DefaultSessionRewinder } = await import('@wrongstack/core');
12005
+ const { DefaultSessionRewinder } = await import("@wrongstack/core");
11503
12006
  const projectRoot = ctx.getProjectRoot();
11504
12007
  const rewinder = new DefaultSessionRewinder(
11505
- path5.join(projectRoot, ".wrongstack", "sessions"),
12008
+ path19.join(projectRoot, ".wrongstack", "sessions"),
11506
12009
  projectRoot
11507
12010
  );
11508
12011
  const checkpoints = await rewinder.listCheckpoints(ctx.getSession().id);
@@ -11515,10 +12018,10 @@ function createSessionHandlers(ctx) {
11515
12018
  if (!ensureCurrentSession(ws, msg, "session.rewind")) return;
11516
12019
  const { checkpointIndex } = msg.payload;
11517
12020
  try {
11518
- const { DefaultSessionRewinder } = await import('@wrongstack/core');
12021
+ const { DefaultSessionRewinder } = await import("@wrongstack/core");
11519
12022
  const projectRoot = ctx.getProjectRoot();
11520
12023
  const rewinder = new DefaultSessionRewinder(
11521
- path5.join(projectRoot, ".wrongstack", "sessions"),
12024
+ path19.join(projectRoot, ".wrongstack", "sessions"),
11522
12025
  projectRoot
11523
12026
  );
11524
12027
  await rewinder.rewindToCheckpoint(ctx.getSession().id, checkpointIndex);
@@ -11603,11 +12106,13 @@ function buildRoutes(state, deps2, cb) {
11603
12106
  const saved = await providerHandlers.loadConfigProviders();
11604
12107
  const cfg = saved[providerId];
11605
12108
  const provider = await resolveProviderCatalogForModels(deps2.modelsRegistry, providerId, cfg);
12109
+ const siblingId = SIBLING_CATALOG[providerId];
12110
+ const siblingCatalog = siblingId && siblingId !== providerId ? await deps2.modelsRegistry.getProvider(siblingId).catch(() => void 0) : void 0;
11606
12111
  const models = await enrichProviderModelDescriptors(
11607
12112
  deps2.modelsRegistry,
11608
12113
  providerId,
11609
12114
  cfg,
11610
- resolveProviderModelList(cfg?.models, provider, cfg?.type ?? providerId)
12115
+ resolveProviderModelList(cfg?.models, provider, cfg?.type ?? providerId, siblingCatalog)
11611
12116
  );
11612
12117
  send(ws, {
11613
12118
  type: "provider.models",
@@ -11633,7 +12138,7 @@ function buildRoutes(state, deps2, cb) {
11633
12138
  const providerCfg = newCfg.providers?.[newProvider] ?? {
11634
12139
  type: newProvider
11635
12140
  };
11636
- const newProv = deps2.providerRegistry.has(newProvider) ? deps2.providerRegistry.create({ ...providerCfg, type: newProvider }) : makeProviderFromConfig(newProvider, providerCfg);
12141
+ const newProv = deps2.providerRegistry.has(newProvider) ? deps2.providerRegistry.create({ ...providerCfg, type: newProvider }) : makeProviderFromConfig2(newProvider, providerCfg);
11637
12142
  deps2.context.provider = newProv;
11638
12143
  await cb.updateAutoCompactionMaxContext(newProv, newProvider, providerCfg);
11639
12144
  await cb.updateGlobalConfig((config) => {
@@ -11876,7 +12381,7 @@ function buildRoutes(state, deps2, cb) {
11876
12381
  }
11877
12382
  return handleMailboxMessages(
11878
12383
  ws,
11879
- { projectRoot: state.getProjectRoot(), globalRoot: path5__default.dirname(deps2.globalConfigPath) },
12384
+ { projectRoot: state.getProjectRoot(), globalRoot: path20.dirname(deps2.globalConfigPath) },
11880
12385
  parsed.value
11881
12386
  );
11882
12387
  },
@@ -11888,13 +12393,13 @@ function buildRoutes(state, deps2, cb) {
11888
12393
  }
11889
12394
  return handleMailboxAgents(
11890
12395
  ws,
11891
- { projectRoot: state.getProjectRoot(), globalRoot: path5__default.dirname(deps2.globalConfigPath) },
12396
+ { projectRoot: state.getProjectRoot(), globalRoot: path20.dirname(deps2.globalConfigPath) },
11892
12397
  parsed.value
11893
12398
  );
11894
12399
  },
11895
12400
  clear: (ws) => handleMailboxClear(ws, {
11896
12401
  projectRoot: state.getProjectRoot(),
11897
- globalRoot: path5__default.dirname(deps2.globalConfigPath)
12402
+ globalRoot: path20.dirname(deps2.globalConfigPath)
11898
12403
  }),
11899
12404
  purge: (ws, msg) => {
11900
12405
  const parsed = validateMailboxPurgePayload(msg.payload);
@@ -11904,7 +12409,7 @@ function buildRoutes(state, deps2, cb) {
11904
12409
  }
11905
12410
  return handleMailboxPurge(
11906
12411
  ws,
11907
- { projectRoot: state.getProjectRoot(), globalRoot: path5__default.dirname(deps2.globalConfigPath) },
12412
+ { projectRoot: state.getProjectRoot(), globalRoot: path20.dirname(deps2.globalConfigPath) },
11908
12413
  parsed.value
11909
12414
  );
11910
12415
  }
@@ -12012,7 +12517,7 @@ async function startWebUI(opts = {}) {
12012
12517
  const updateGlobalConfig2 = async (mutate, errorLabel) => updateGlobalConfig(prefHelperDeps, configWriteLock, mutate, errorLabel);
12013
12518
  console.log("[WebUI] Config loaded:", config.provider ?? "(none)", "/", config.model ?? "(none)");
12014
12519
  if (!config.provider && config.providers && typeof config.providers === "object" && config.providers !== null && !Array.isArray(config.providers) && Object.keys(config.providers).length > 0) {
12015
- const firstKey = expectDefined(Object.keys(config.providers)[0]);
12520
+ const firstKey = expectDefined3(Object.keys(config.providers)[0]);
12016
12521
  config = patchConfig(config, { provider: firstKey });
12017
12522
  console.log("[WebUI] No active provider \u2014 auto-selected:", firstKey);
12018
12523
  }
@@ -12112,7 +12617,7 @@ async function startWebUI(opts = {}) {
12112
12617
  getProjectRoot: () => projectRoot,
12113
12618
  getWorkingDir: () => workingDir,
12114
12619
  getModeId: () => modeId,
12115
- getContextMode: () => String(context.meta["contextWindowMode"] ?? DEFAULT_CONTEXT_WINDOW_MODE_ID),
12620
+ getContextMode: () => String(context.meta["contextWindowMode"] ?? DEFAULT_CONTEXT_WINDOW_MODE_ID2),
12116
12621
  getNeedsSetup: () => needsSetup,
12117
12622
  modelsRegistry
12118
12623
  });
@@ -12155,21 +12660,21 @@ async function startWebUI(opts = {}) {
12155
12660
  wpaths
12156
12661
  }, watcherMetricsRef);
12157
12662
  async function touchProjectEntry(root, workDir) {
12158
- const resolved = path5.resolve(root);
12663
+ const resolved = path21.resolve(root);
12159
12664
  const manifest = await loadManifest(globalConfigPath);
12160
12665
  const now = (/* @__PURE__ */ new Date()).toISOString();
12161
- const existing = manifest.projects.find((p) => path5.resolve(p.root) === resolved);
12666
+ const existing = manifest.projects.find((p) => path21.resolve(p.root) === resolved);
12162
12667
  if (existing) {
12163
12668
  existing.lastSeen = now;
12164
- if (workDir) existing.lastWorkingDir = path5.resolve(workDir);
12669
+ if (workDir) existing.lastWorkingDir = path21.resolve(workDir);
12165
12670
  } else {
12166
12671
  manifest.projects.push({
12167
- name: path5.basename(resolved),
12672
+ name: path21.basename(resolved),
12168
12673
  root: resolved,
12169
12674
  slug: generateProjectSlug(resolved),
12170
12675
  createdAt: now,
12171
12676
  lastSeen: now,
12172
- lastWorkingDir: workDir ? path5.resolve(workDir) : void 0
12677
+ lastWorkingDir: workDir ? path21.resolve(workDir) : void 0
12173
12678
  });
12174
12679
  }
12175
12680
  await saveManifest(manifest, globalConfigPath);
@@ -12314,13 +12819,13 @@ async function startWebUI(opts = {}) {
12314
12819
  ...snapshot.apiKey !== void 0 ? { apiKey: snapshot.apiKey } : {},
12315
12820
  ...snapshot.baseUrl !== void 0 ? { baseUrl: snapshot.baseUrl } : {}
12316
12821
  };
12317
- const newProv = deps2.providerRegistry.has(activeId) ? deps2.providerRegistry.create({ ...providerCfg, type: activeId }) : makeProviderFromConfig(activeId, { ...providerCfg, type: activeId });
12822
+ const newProv = deps2.providerRegistry.has(activeId) ? deps2.providerRegistry.create({ ...providerCfg, type: activeId }) : makeProviderFromConfig3(activeId, { ...providerCfg, type: activeId });
12318
12823
  deps2.context.provider = newProv;
12319
12824
  void updateAutoCompactionMaxContext(newProv).catch(() => void 0);
12320
12825
  console.log(`[WebUI] Provider credentials reloaded from config.json (${activeId})`);
12321
12826
  } catch (err) {
12322
12827
  console.warn(
12323
- `[WebUI] Credential hot-reload failed for ${activeId}: ${toErrorMessage(err)}`
12828
+ `[WebUI] Credential hot-reload failed for ${activeId}: ${toErrorMessage10(err)}`
12324
12829
  );
12325
12830
  }
12326
12831
  },
@@ -12387,17 +12892,24 @@ async function startWebUI(opts = {}) {
12387
12892
  },
12388
12893
  clients: () => clients.keys(),
12389
12894
  servers: [httpServer, wssPrimary, ...wssSecondary ? [wssSecondary] : []],
12390
- onShutdown: () => {
12895
+ onShutdown: async () => {
12391
12896
  credentialWatcherClose?.();
12392
12897
  brainMonitor.stop();
12393
- void mcpRegistry.stopAll().catch(() => void 0);
12898
+ await mcpRegistry.stopAll().catch(() => void 0);
12394
12899
  eventArming.getDispose()?.();
12395
12900
  if (eternalSubscription) {
12396
12901
  eternalSubscription.dispose();
12397
12902
  eternalSubscription = null;
12398
12903
  }
12399
12904
  codebaseIndexing.dispose();
12400
- return unregisterInstance(process.pid, path5.dirname(globalConfigPath));
12905
+ if (config.superMemory?.enabled !== false && config.superMemory?.hygiene?.autoAfterSession !== false) {
12906
+ const candidate = memoryStore;
12907
+ await candidate.hygiene?.({
12908
+ retentionDays: config.superMemory?.hygiene?.retentionDays,
12909
+ archiveLowConfidenceAfterDays: config.superMemory?.hygiene?.archiveLowConfidenceAfterDays
12910
+ }).catch((err) => logger.warn(`super-memory session hygiene failed: ${toErrorMessage10(err)}`));
12911
+ }
12912
+ await unregisterInstance(process.pid, path21.dirname(globalConfigPath));
12401
12913
  }
12402
12914
  });
12403
12915
  }
@@ -12412,7 +12924,7 @@ function readArg(names) {
12412
12924
  if (current === name2) {
12413
12925
  const next = argv[i + 1];
12414
12926
  if (!next || next.startsWith("-")) {
12415
- throw new ToolValidationError({ message: `${name2} requires a value`, field: name2 });
12927
+ throw new ToolValidationError6({ message: `${name2} requires a value`, field: name2 });
12416
12928
  }
12417
12929
  return next;
12418
12930
  }
@@ -12425,7 +12937,7 @@ function parsePort(value, fallback, label) {
12425
12937
  if (value === void 0) return fallback;
12426
12938
  const parsed = Number.parseInt(value, 10);
12427
12939
  if (!Number.isInteger(parsed) || parsed <= 0 || parsed > 65535) {
12428
- throw new ToolValidationError({
12940
+ throw new ToolValidationError6({
12429
12941
  message: `${label} must be a port between 1 and 65535`,
12430
12942
  field: "port"
12431
12943
  });
@@ -12517,4 +13029,3 @@ if (argv.includes("--help") || argv.includes("-h")) {
12517
13029
  });
12518
13030
  }
12519
13031
  //# sourceMappingURL=entry.js.map
12520
- //# sourceMappingURL=entry.js.map