@wrongstack/webui-server 0.284.1 → 0.286.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +11 -2300
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1807 -539
- package/dist/index.js.map +7 -1
- package/dist/server/autophase-routes.d.ts +10 -0
- package/dist/server/autophase-routes.d.ts.map +1 -0
- package/dist/server/autophase-ws-handler.d.ts +87 -0
- package/dist/server/autophase-ws-handler.d.ts.map +1 -0
- package/dist/server/backend-services.d.ts +103 -0
- package/dist/server/backend-services.d.ts.map +1 -0
- package/dist/server/boot.d.ts +19 -0
- package/dist/server/boot.d.ts.map +1 -0
- package/dist/server/brain-routes.d.ts +11 -0
- package/dist/server/brain-routes.d.ts.map +1 -0
- package/dist/server/codebase-indexing.d.ts +15 -0
- package/dist/server/codebase-indexing.d.ts.map +1 -0
- package/dist/server/collaboration-ws-handler.d.ts +167 -0
- package/dist/server/collaboration-ws-handler.d.ts.map +1 -0
- package/dist/server/completion-handlers.d.ts +39 -0
- package/dist/server/completion-handlers.d.ts.map +1 -0
- package/dist/server/connection-handler.d.ts +72 -0
- package/dist/server/connection-handler.d.ts.map +1 -0
- package/dist/server/context-meta.d.ts +28 -0
- package/dist/server/context-meta.d.ts.map +1 -0
- package/dist/server/custom-context-modes.d.ts +43 -0
- package/dist/server/custom-context-modes.d.ts.map +1 -0
- package/dist/server/design-handlers.d.ts +40 -0
- package/dist/server/design-handlers.d.ts.map +1 -0
- package/dist/server/discover-mailbox-bridge.d.ts +26 -0
- package/dist/server/discover-mailbox-bridge.d.ts.map +1 -0
- package/dist/server/entry.d.ts +2 -2
- package/dist/server/entry.d.ts.map +1 -0
- package/dist/server/entry.js +1595 -562
- package/dist/server/entry.js.map +7 -1
- package/dist/server/eternal-iteration-broadcast.d.ts +11 -0
- package/dist/server/eternal-iteration-broadcast.d.ts.map +1 -0
- package/dist/server/file-handlers.d.ts +45 -0
- package/dist/server/file-handlers.d.ts.map +1 -0
- package/dist/server/file-picker.d.ts +26 -0
- package/dist/server/file-picker.d.ts.map +1 -0
- package/dist/server/git-handlers.d.ts +55 -0
- package/dist/server/git-handlers.d.ts.map +1 -0
- package/dist/server/goal-handlers.d.ts +19 -0
- package/dist/server/goal-handlers.d.ts.map +1 -0
- package/dist/server/handlers/index.d.ts +12 -0
- package/dist/server/handlers/index.d.ts.map +1 -0
- package/dist/server/handlers/worklist-handlers.d.ts +49 -0
- package/dist/server/handlers/worklist-handlers.d.ts.map +1 -0
- package/dist/server/handlers.d.ts +1 -51
- package/dist/server/handlers.js +17 -8
- package/dist/server/handlers.js.map +7 -1
- package/dist/server/http-server/analytics-handler.d.ts +35 -0
- package/dist/server/http-server/analytics-handler.d.ts.map +1 -0
- package/dist/server/http-server/api-handlers.d.ts +44 -0
- package/dist/server/http-server/api-handlers.d.ts.map +1 -0
- package/dist/server/http-server.d.ts +108 -0
- package/dist/server/http-server.d.ts.map +1 -0
- package/dist/server/index.d.ts +93 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/instance-registry.d.ts +63 -0
- package/dist/server/instance-registry.d.ts.map +1 -0
- package/dist/server/kanban-routes.d.ts +7 -0
- package/dist/server/kanban-routes.d.ts.map +1 -0
- package/dist/server/lifecycle.d.ts +51 -0
- package/dist/server/lifecycle.d.ts.map +1 -0
- package/dist/server/mailbox-handlers.d.ts +63 -0
- package/dist/server/mailbox-handlers.d.ts.map +1 -0
- package/dist/server/mailbox-routes.d.ts +11 -0
- package/dist/server/mailbox-routes.d.ts.map +1 -0
- package/dist/server/mcp-handlers.d.ts +55 -0
- package/dist/server/mcp-handlers.d.ts.map +1 -0
- package/dist/server/mcp-routes.d.ts +41 -0
- package/dist/server/mcp-routes.d.ts.map +1 -0
- package/dist/server/memory-handlers.d.ts +28 -0
- package/dist/server/memory-handlers.d.ts.map +1 -0
- package/dist/server/message-dispatcher.d.ts +41 -0
- package/dist/server/message-dispatcher.d.ts.map +1 -0
- package/dist/server/mode-handlers.d.ts +50 -0
- package/dist/server/mode-handlers.d.ts.map +1 -0
- package/dist/server/mode-routes.d.ts +8 -0
- package/dist/server/mode-routes.d.ts.map +1 -0
- package/dist/server/model-auto-discovery.d.ts +9 -0
- package/dist/server/model-auto-discovery.d.ts.map +1 -0
- package/dist/server/model-catalog.d.ts +16 -0
- package/dist/server/model-catalog.d.ts.map +1 -0
- package/dist/server/open-browser.d.ts +16 -0
- package/dist/server/open-browser.d.ts.map +1 -0
- package/dist/server/path-containment.d.ts +3 -0
- package/dist/server/path-containment.d.ts.map +1 -0
- package/dist/server/pending-confirms.d.ts +14 -0
- package/dist/server/pending-confirms.d.ts.map +1 -0
- package/dist/server/port-utils.d.ts +28 -0
- package/dist/server/port-utils.d.ts.map +1 -0
- package/dist/server/pre-context-services.d.ts +60 -0
- package/dist/server/pre-context-services.d.ts.map +1 -0
- package/dist/server/pref-helpers.d.ts +43 -0
- package/dist/server/pref-helpers.d.ts.map +1 -0
- package/dist/server/prefs-routes.d.ts +27 -0
- package/dist/server/prefs-routes.d.ts.map +1 -0
- package/dist/server/process-handlers.d.ts +20 -0
- package/dist/server/process-handlers.d.ts.map +1 -0
- package/dist/server/project-handlers.d.ts +53 -0
- package/dist/server/project-handlers.d.ts.map +1 -0
- package/dist/server/project-routes.d.ts +10 -0
- package/dist/server/project-routes.d.ts.map +1 -0
- package/dist/server/projects-manifest.d.ts +18 -0
- package/dist/server/projects-manifest.d.ts.map +1 -0
- package/dist/server/prompts-handlers.d.ts +38 -0
- package/dist/server/prompts-handlers.d.ts.map +1 -0
- package/dist/server/provider-config-io.d.ts +15 -0
- package/dist/server/provider-config-io.d.ts.map +1 -0
- package/dist/server/provider-config-standalone.d.ts +13 -0
- package/dist/server/provider-config-standalone.d.ts.map +1 -0
- package/dist/server/provider-handlers.d.ts +74 -0
- package/dist/server/provider-handlers.d.ts.map +1 -0
- package/dist/server/provider-keys.d.ts +53 -0
- package/dist/server/provider-keys.d.ts.map +1 -0
- package/dist/server/provider-routes.d.ts +13 -0
- package/dist/server/provider-routes.d.ts.map +1 -0
- package/dist/server/provider-store.d.ts +26 -0
- package/dist/server/provider-store.d.ts.map +1 -0
- package/dist/server/routes.d.ts +187 -0
- package/dist/server/routes.d.ts.map +1 -0
- package/dist/server/sdd-board-routes.d.ts +11 -0
- package/dist/server/sdd-board-routes.d.ts.map +1 -0
- package/dist/server/sdd-board-ws-handler.d.ts +57 -0
- package/dist/server/sdd-board-ws-handler.d.ts.map +1 -0
- package/dist/server/sdd-wizard-routes.d.ts +16 -0
- package/dist/server/sdd-wizard-routes.d.ts.map +1 -0
- package/dist/server/sdd-wizard-wiring.d.ts +26 -0
- package/dist/server/sdd-wizard-wiring.d.ts.map +1 -0
- package/dist/server/sdd-wizard-ws-handler.d.ts +66 -0
- package/dist/server/sdd-wizard-ws-handler.d.ts.map +1 -0
- package/dist/server/server-runtime.d.ts +118 -0
- package/dist/server/server-runtime.d.ts.map +1 -0
- package/dist/server/session-handlers.d.ts +57 -0
- package/dist/server/session-handlers.d.ts.map +1 -0
- package/dist/server/session-routes.d.ts +23 -0
- package/dist/server/session-routes.d.ts.map +1 -0
- package/dist/server/setup-events.d.ts +67 -0
- package/dist/server/setup-events.d.ts.map +1 -0
- package/dist/server/setup-screen.d.ts +44 -0
- package/dist/server/setup-screen.d.ts.map +1 -0
- package/dist/server/shell-git-routes.d.ts +10 -0
- package/dist/server/shell-git-routes.d.ts.map +1 -0
- package/dist/server/shell-open.d.ts +12 -0
- package/dist/server/shell-open.d.ts.map +1 -0
- package/dist/server/skills-handlers.d.ts +77 -0
- package/dist/server/skills-handlers.d.ts.map +1 -0
- package/dist/server/specs-routes.d.ts +11 -0
- package/dist/server/specs-routes.d.ts.map +1 -0
- package/dist/server/specs-ws-handler.d.ts +34 -0
- package/dist/server/specs-ws-handler.d.ts.map +1 -0
- package/dist/server/standalone-session-identity.d.ts +35 -0
- package/dist/server/standalone-session-identity.d.ts.map +1 -0
- package/dist/server/start-webui.d.ts +29 -0
- package/dist/server/start-webui.d.ts.map +1 -0
- package/dist/server/terminal-ws-handler.d.ts +70 -0
- package/dist/server/terminal-ws-handler.d.ts.map +1 -0
- package/dist/server/token-estimator.d.ts +66 -0
- package/dist/server/token-estimator.d.ts.map +1 -0
- package/dist/server/types.d.ts +143 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/usage-cost.d.ts +39 -0
- package/dist/server/usage-cost.d.ts.map +1 -0
- package/dist/server/worktree-ws-handler.d.ts +67 -0
- package/dist/server/worktree-ws-handler.d.ts.map +1 -0
- package/dist/server/ws-auth.d.ts +83 -0
- package/dist/server/ws-auth.d.ts.map +1 -0
- package/dist/server/ws-payload-validation.d.ts +126 -0
- package/dist/server/ws-payload-validation.d.ts.map +1 -0
- package/dist/server/ws-utils.d.ts +38 -0
- package/dist/server/ws-utils.d.ts.map +1 -0
- package/dist/server/zip.d.ts +9 -0
- package/dist/server/zip.d.ts.map +1 -0
- package/package.json +11 -12
package/dist/index.js
CHANGED
|
@@ -1,34 +1,14 @@
|
|
|
1
|
-
import { spawn, spawnSync } from 'child_process';
|
|
2
|
-
import { toErrorMessage, wstackGlobalRoot, resolveWstackPaths, atomicWrite as atomicWrite$1 } from '@wrongstack/core/utils';
|
|
3
|
-
import { DefaultSecretScrubber, PhaseStore, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, AutoPhasePlanner, assignNickname, getDesignState, getDesignKitLoader, isDesignStack, loadActiveKit, setActiveKit, recordKitChoice, applyTokenOverrides, recordOverrides, setDesignOverrides, materializeTokens, runDesignVerify, ToolValidationError, atomicWrite, allServers, ConfigError, DefaultSecretVault, expectDefined, computeTaskProgress, bootConfig as bootConfig$1, resolveProjectDir, GlobalMailbox, createDefaultPipelines, resolveSessionLoggingConfig, createSessionEventBridge, watchProviderConfig, listContextWindowModes, 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, Context, resolveContextWindowPolicy, CollaborationBus, collabInjectMiddleware, collabPauseMiddleware, installDesignStudioMiddleware, createStrategyCompactor, AutoCompactionMiddleware, estimateRequestTokensCalibrated, ToolExecutor, DEFAULT_TOOLS_CONFIG, Agent, ObservableBrainArbiter, createTieredBrainArbiter, DefaultBrainArbiter, BrainMonitor, mailboxSessionTag, wstackGlobalRoot as wstackGlobalRoot$1, createAutonomyBrain, DEFAULT_CONTEXT_WINDOW_MODE_ID, repairToolUseAdjacency, resolveWstackPaths as resolveWstackPaths$1, recentTextTurns, gatedEnhancerReasoning, enhanceUserPrompt, resolveProviderModelList, projectSlug, deserializeTaskGraph, serializeTaskGraph } from '@wrongstack/core';
|
|
4
|
-
import * as path5 from 'path';
|
|
5
|
-
import path5__default, { resolve, join, sep, dirname } from 'path';
|
|
6
|
-
import { searchCodebaseIndex } from '@wrongstack/tools/codebase-index/index';
|
|
7
|
-
import { randomBytes, timingSafeEqual, randomUUID } from 'crypto';
|
|
8
|
-
import { WebSocket, WebSocketServer } from 'ws';
|
|
9
|
-
import * as fs4 from 'fs/promises';
|
|
10
|
-
import * as http from 'http';
|
|
11
|
-
import { Buffer } from 'buffer';
|
|
12
|
-
import * as os from 'os';
|
|
13
|
-
import { listMcp, addMcp, updateMcp, removeMcp, enableMcp, disableMcp, restartMcp, discoverMcp, MCPRegistry } from '@wrongstack/mcp';
|
|
14
|
-
import * as net from 'net';
|
|
15
|
-
import { decryptConfigSecrets, encryptConfigSecrets } from '@wrongstack/core/security';
|
|
16
|
-
import { SddBoardStore, applySddLifecycle, SddRunRegistry, SddInterviewDriver, cleanupStaleSddWorktrees, makeCommandVerifier, SddSupervisor, makeLlmSubtaskGenerator, startSddRun, TaskGraphStore, SpecStore } from '@wrongstack/sdd';
|
|
17
|
-
import * as fs12 from 'fs';
|
|
18
|
-
import { promises, watch, existsSync } from 'fs';
|
|
19
|
-
import JSZip from 'jszip';
|
|
20
|
-
import { createRequire } from 'module';
|
|
21
|
-
import { fileURLToPath } from 'url';
|
|
22
|
-
import { capabilitiesFor, makeProviderFromConfig, buildProviderFactoriesFromRegistry, discoverOpenAICompatibleModels, COMPATIBLE_PRESETS } from '@wrongstack/providers';
|
|
23
|
-
import { DefaultTokenCounter } from '@wrongstack/core/infrastructure';
|
|
24
|
-
import { createDefaultContainer, makeLightSubagentFactory } from '@wrongstack/runtime';
|
|
25
|
-
import { runStartupIndex, isIndexableFile, enqueueReindex, cancelPendingReindexes, shutdownCodebaseIndexHost, ensureSessionShell, builtinToolsPack, rememberTool, forgetTool, searchMemoryTool, relatedMemoryTool, configureExecPolicy, configureDangerBypass } from '@wrongstack/tools';
|
|
26
|
-
import { readLiveLock } from '@wrongstack/core/coordination';
|
|
27
|
-
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';
|
|
28
|
-
import { beginOAuthLogin } from '@wrongstack/providers/oauth';
|
|
29
|
-
import { probeLocalLlm } from '@wrongstack/runtime/probe';
|
|
30
|
-
|
|
31
1
|
// src/server/autophase-ws-handler.ts
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { toErrorMessage } from "@wrongstack/core/utils";
|
|
4
|
+
import {
|
|
5
|
+
assignNickname,
|
|
6
|
+
AutoPhasePlanner,
|
|
7
|
+
PhaseGraphBuilder,
|
|
8
|
+
PhaseOrchestrator,
|
|
9
|
+
PhaseStore,
|
|
10
|
+
WorktreeManager
|
|
11
|
+
} from "@wrongstack/core";
|
|
32
12
|
function deriveTitle(goal) {
|
|
33
13
|
const firstLine = goal.split("\n").map((l) => l.trim()).find(Boolean);
|
|
34
14
|
if (!firstLine) return "AutoPhase";
|
|
@@ -519,6 +499,14 @@ Type: ${task.type}`;
|
|
|
519
499
|
}
|
|
520
500
|
}
|
|
521
501
|
};
|
|
502
|
+
|
|
503
|
+
// src/server/completion-handlers.ts
|
|
504
|
+
import * as path from "node:path";
|
|
505
|
+
import { searchCodebaseIndex } from "@wrongstack/tools/codebase-index/index";
|
|
506
|
+
|
|
507
|
+
// src/server/ws-utils.ts
|
|
508
|
+
import { randomBytes } from "node:crypto";
|
|
509
|
+
import { WebSocket } from "ws";
|
|
522
510
|
function send(ws, msg) {
|
|
523
511
|
if (ws.readyState === WebSocket.OPEN) {
|
|
524
512
|
ws.send(JSON.stringify(msg));
|
|
@@ -650,8 +638,8 @@ async function handleCompletionRequest(ws, msg, opts) {
|
|
|
650
638
|
return;
|
|
651
639
|
}
|
|
652
640
|
const payload = parsed.payload;
|
|
653
|
-
const projectRoot =
|
|
654
|
-
const resolved =
|
|
641
|
+
const projectRoot = path.resolve(opts.projectRoot);
|
|
642
|
+
const resolved = path.resolve(projectRoot, payload.filePath);
|
|
655
643
|
if (!isInside(projectRoot, resolved)) {
|
|
656
644
|
send(ws, {
|
|
657
645
|
type: "completion.result",
|
|
@@ -1050,7 +1038,7 @@ function buildSearchQuery(linePrefix, filePath) {
|
|
|
1050
1038
|
if (memberMatch?.[1]) return memberMatch[1];
|
|
1051
1039
|
const token = linePrefix.match(/([A-Za-z_$][\w$]*)$/)?.[1];
|
|
1052
1040
|
if (token && token.length >= 2) return token;
|
|
1053
|
-
return
|
|
1041
|
+
return path.basename(filePath, path.extname(filePath));
|
|
1054
1042
|
}
|
|
1055
1043
|
function currentLinePrefix(prefix) {
|
|
1056
1044
|
const idx = Math.max(prefix.lastIndexOf("\n"), prefix.lastIndexOf("\r"));
|
|
@@ -1080,11 +1068,16 @@ function head(value, max) {
|
|
|
1080
1068
|
return value.length <= max ? value : value.slice(0, max);
|
|
1081
1069
|
}
|
|
1082
1070
|
function isInside(root, target) {
|
|
1083
|
-
return target === root || target.startsWith(root +
|
|
1071
|
+
return target === root || target.startsWith(root + path.sep);
|
|
1084
1072
|
}
|
|
1073
|
+
|
|
1074
|
+
// src/server/custom-context-modes.ts
|
|
1075
|
+
import { listContextWindowModes, atomicWrite } from "@wrongstack/core";
|
|
1076
|
+
import * as fs from "node:fs/promises";
|
|
1077
|
+
import * as path2 from "node:path";
|
|
1085
1078
|
var STORE_FILENAME = "custom-context-modes.json";
|
|
1086
1079
|
function storePath(wrongstackDir) {
|
|
1087
|
-
return
|
|
1080
|
+
return path2.join(wrongstackDir, STORE_FILENAME);
|
|
1088
1081
|
}
|
|
1089
1082
|
var BUILTIN_IDS = /* @__PURE__ */ new Set(["balanced", "frugal", "deep", "archival"]);
|
|
1090
1083
|
function createCustomModeStore(wrongstackDir) {
|
|
@@ -1092,7 +1085,7 @@ function createCustomModeStore(wrongstackDir) {
|
|
|
1092
1085
|
const load2 = async () => {
|
|
1093
1086
|
modes.clear();
|
|
1094
1087
|
try {
|
|
1095
|
-
const raw = await
|
|
1088
|
+
const raw = await fs.readFile(storePath(wrongstackDir), "utf8");
|
|
1096
1089
|
const parsed = JSON.parse(raw);
|
|
1097
1090
|
if (Array.isArray(parsed.modes)) {
|
|
1098
1091
|
for (const m of parsed.modes) {
|
|
@@ -1197,6 +1190,23 @@ function createCustomModeStore(wrongstackDir) {
|
|
|
1197
1190
|
};
|
|
1198
1191
|
return { modes, load: load2, save: save2, create, update, remove, list };
|
|
1199
1192
|
}
|
|
1193
|
+
|
|
1194
|
+
// src/server/design-handlers.ts
|
|
1195
|
+
import * as fs2 from "node:fs/promises";
|
|
1196
|
+
import * as path3 from "node:path";
|
|
1197
|
+
import {
|
|
1198
|
+
applyTokenOverrides,
|
|
1199
|
+
getDesignKitLoader,
|
|
1200
|
+
getDesignState,
|
|
1201
|
+
isDesignStack,
|
|
1202
|
+
loadActiveKit,
|
|
1203
|
+
materializeTokens,
|
|
1204
|
+
recordKitChoice,
|
|
1205
|
+
recordOverrides,
|
|
1206
|
+
runDesignVerify,
|
|
1207
|
+
setActiveKit,
|
|
1208
|
+
setDesignOverrides
|
|
1209
|
+
} from "@wrongstack/core";
|
|
1200
1210
|
function readOverrides(value) {
|
|
1201
1211
|
const out = {};
|
|
1202
1212
|
if (value && typeof value === "object") {
|
|
@@ -1344,9 +1354,9 @@ async function handleDesignMaterialize(ws, ctx, msg) {
|
|
|
1344
1354
|
kitId: active.kit,
|
|
1345
1355
|
outPath: typeof payload.out === "string" ? payload.out : void 0
|
|
1346
1356
|
});
|
|
1347
|
-
const abs =
|
|
1348
|
-
await
|
|
1349
|
-
await
|
|
1357
|
+
const abs = path3.join(ctx.projectRoot, result.path);
|
|
1358
|
+
await fs2.mkdir(path3.dirname(abs), { recursive: true });
|
|
1359
|
+
await fs2.writeFile(abs, result.content);
|
|
1350
1360
|
send(ws, {
|
|
1351
1361
|
type: "design.materialize",
|
|
1352
1362
|
payload: { ok: true, path: result.path, format: result.format, stack }
|
|
@@ -1405,6 +1415,11 @@ function createEternalSubscription(subscribe, broadcast2, clientsRef) {
|
|
|
1405
1415
|
};
|
|
1406
1416
|
}
|
|
1407
1417
|
|
|
1418
|
+
// src/server/file-handlers.ts
|
|
1419
|
+
import * as fs4 from "node:fs/promises";
|
|
1420
|
+
import * as path5 from "node:path";
|
|
1421
|
+
import { atomicWrite as atomicWrite2, ToolValidationError as ToolValidationError2 } from "@wrongstack/core";
|
|
1422
|
+
|
|
1408
1423
|
// src/server/file-picker.ts
|
|
1409
1424
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
1410
1425
|
".git",
|
|
@@ -1452,15 +1467,20 @@ function rankFiles(paths, query, limit) {
|
|
|
1452
1467
|
scored.sort((a, b) => b.score - a.score || a.path.localeCompare(b.path));
|
|
1453
1468
|
return scored.slice(0, limit).map((s) => s.path);
|
|
1454
1469
|
}
|
|
1470
|
+
|
|
1471
|
+
// src/server/path-containment.ts
|
|
1472
|
+
import * as fs3 from "node:fs/promises";
|
|
1473
|
+
import * as path4 from "node:path";
|
|
1474
|
+
import { ToolValidationError } from "@wrongstack/core";
|
|
1455
1475
|
function isPathInside(root, target) {
|
|
1456
|
-
const relative4 =
|
|
1457
|
-
return relative4 === "" || !relative4.startsWith("..") && !
|
|
1476
|
+
const relative4 = path4.relative(root, target);
|
|
1477
|
+
return relative4 === "" || !relative4.startsWith("..") && !path4.isAbsolute(relative4);
|
|
1458
1478
|
}
|
|
1459
1479
|
async function resolveWorkingDirInsideProject(projectRoot, inputPath) {
|
|
1460
|
-
const resolved =
|
|
1480
|
+
const resolved = path4.resolve(projectRoot, inputPath);
|
|
1461
1481
|
let stat2;
|
|
1462
1482
|
try {
|
|
1463
|
-
stat2 = await
|
|
1483
|
+
stat2 = await fs3.stat(resolved);
|
|
1464
1484
|
} catch {
|
|
1465
1485
|
throw new ToolValidationError({
|
|
1466
1486
|
message: `Directory not found or not accessible: ${resolved}`,
|
|
@@ -1474,8 +1494,8 @@ async function resolveWorkingDirInsideProject(projectRoot, inputPath) {
|
|
|
1474
1494
|
});
|
|
1475
1495
|
}
|
|
1476
1496
|
const [realProjectRoot, realResolved] = await Promise.all([
|
|
1477
|
-
|
|
1478
|
-
|
|
1497
|
+
fs3.realpath(projectRoot),
|
|
1498
|
+
fs3.realpath(resolved)
|
|
1479
1499
|
]);
|
|
1480
1500
|
if (!isPathInside(realProjectRoot, realResolved)) {
|
|
1481
1501
|
throw new ToolValidationError({
|
|
@@ -1490,14 +1510,14 @@ async function resolveWorkingDirInsideProject(projectRoot, inputPath) {
|
|
|
1490
1510
|
async function resolveFileInsideProject(projectRoot, filePath) {
|
|
1491
1511
|
const resolved = path5.resolve(projectRoot, filePath);
|
|
1492
1512
|
if (!isPathInside(projectRoot, resolved)) {
|
|
1493
|
-
throw new
|
|
1513
|
+
throw new ToolValidationError2({ message: "Path outside project root", field: "path" });
|
|
1494
1514
|
}
|
|
1495
1515
|
const { parent, base } = splitParentAndBase(resolved);
|
|
1496
1516
|
const realProjectRoot = await fs4.realpath(projectRoot);
|
|
1497
1517
|
const realParent = await realpathAllowMissing(parent);
|
|
1498
1518
|
const realFull = path5.join(realParent, base);
|
|
1499
1519
|
if (!isPathInside(realProjectRoot, realFull)) {
|
|
1500
|
-
throw new
|
|
1520
|
+
throw new ToolValidationError2({ message: "Path outside project root", field: "path" });
|
|
1501
1521
|
}
|
|
1502
1522
|
return realFull;
|
|
1503
1523
|
}
|
|
@@ -1517,7 +1537,7 @@ async function realpathAllowMissing(p) {
|
|
|
1517
1537
|
while (true) {
|
|
1518
1538
|
const parent = path5.dirname(cursor);
|
|
1519
1539
|
if (parent === cursor) {
|
|
1520
|
-
throw new
|
|
1540
|
+
throw new ToolValidationError2({ message: "Path outside project root", field: "path" });
|
|
1521
1541
|
}
|
|
1522
1542
|
segments.unshift(path5.basename(cursor));
|
|
1523
1543
|
try {
|
|
@@ -1650,7 +1670,7 @@ async function handleFilesWrite(ws, msg, projectRoot, opts = {}) {
|
|
|
1650
1670
|
return;
|
|
1651
1671
|
}
|
|
1652
1672
|
try {
|
|
1653
|
-
await
|
|
1673
|
+
await atomicWrite2(realResolved, content);
|
|
1654
1674
|
send(ws, { type: "files.written", payload: { filePath, success: true } });
|
|
1655
1675
|
if (opts.onWritten) {
|
|
1656
1676
|
void Promise.resolve(opts.onWritten(realResolved)).catch(() => void 0);
|
|
@@ -1714,10 +1734,13 @@ async function handleFilesList(ws, msg, projectRoot) {
|
|
|
1714
1734
|
payload: { files: rankFiles(results, payload.query ?? "", limit) }
|
|
1715
1735
|
});
|
|
1716
1736
|
}
|
|
1737
|
+
|
|
1738
|
+
// src/server/git-handlers.ts
|
|
1739
|
+
import nodePath from "node:path";
|
|
1717
1740
|
async function handleGitInfo(ws, projectRoot) {
|
|
1718
1741
|
const cwd = projectRoot || void 0;
|
|
1719
1742
|
try {
|
|
1720
|
-
const { execFile: ef } = await import(
|
|
1743
|
+
const { execFile: ef } = await import("node:child_process");
|
|
1721
1744
|
const git = (args) => new Promise((resolve10) => {
|
|
1722
1745
|
ef("git", args, { cwd, timeout: 3e3 }, (err, stdout) => {
|
|
1723
1746
|
resolve10(err ? "" : stdout.trim());
|
|
@@ -1745,7 +1768,7 @@ async function handleGitInfo(ws, projectRoot) {
|
|
|
1745
1768
|
}
|
|
1746
1769
|
function makeGit(cwd) {
|
|
1747
1770
|
return async (args) => {
|
|
1748
|
-
const { execFile: ef } = await import(
|
|
1771
|
+
const { execFile: ef } = await import("node:child_process");
|
|
1749
1772
|
return new Promise((resolve10) => {
|
|
1750
1773
|
ef(
|
|
1751
1774
|
"git",
|
|
@@ -1827,14 +1850,14 @@ var MAX_DIFF_BYTES = 2 * 1024 * 1024;
|
|
|
1827
1850
|
async function handleGitDiff(ws, projectRoot, path23) {
|
|
1828
1851
|
const cwd = projectRoot || void 0;
|
|
1829
1852
|
const reply = (extra) => send(ws, { type: "git.diff", payload: { path: path23, ...extra } });
|
|
1830
|
-
if (!path23 || path23.includes("\0") || path23.includes("..") ||
|
|
1853
|
+
if (!path23 || path23.includes("\0") || path23.includes("..") || nodePath.isAbsolute(path23)) {
|
|
1831
1854
|
reply({ oldText: "", newText: "", error: "invalid path" });
|
|
1832
1855
|
return;
|
|
1833
1856
|
}
|
|
1834
1857
|
try {
|
|
1835
1858
|
const git = makeGit(cwd);
|
|
1836
|
-
const { readFile: readFile11 } = await import(
|
|
1837
|
-
const { join: join15 } = await import(
|
|
1859
|
+
const { readFile: readFile11 } = await import("node:fs/promises");
|
|
1860
|
+
const { join: join15 } = await import("node:path");
|
|
1838
1861
|
const oldText = await git(["show", `HEAD:${path23}`]);
|
|
1839
1862
|
let newText = "";
|
|
1840
1863
|
try {
|
|
@@ -1866,6 +1889,11 @@ async function handleGitDiff(ws, projectRoot, path23) {
|
|
|
1866
1889
|
}
|
|
1867
1890
|
}
|
|
1868
1891
|
|
|
1892
|
+
// src/server/http-server.ts
|
|
1893
|
+
import * as fs5 from "node:fs/promises";
|
|
1894
|
+
import * as http from "node:http";
|
|
1895
|
+
import * as path6 from "node:path";
|
|
1896
|
+
|
|
1869
1897
|
// src/server/http-server/api-handlers.ts
|
|
1870
1898
|
async function handleApiSessions(res, globalRoot) {
|
|
1871
1899
|
if (!globalRoot) {
|
|
@@ -1874,7 +1902,7 @@ async function handleApiSessions(res, globalRoot) {
|
|
|
1874
1902
|
return;
|
|
1875
1903
|
}
|
|
1876
1904
|
try {
|
|
1877
|
-
const { SessionRegistry } = await import(
|
|
1905
|
+
const { SessionRegistry } = await import("@wrongstack/core");
|
|
1878
1906
|
const registry = new SessionRegistry(globalRoot);
|
|
1879
1907
|
const sessions = await registry.list();
|
|
1880
1908
|
const result = sessions.map((s) => ({
|
|
@@ -1912,7 +1940,7 @@ async function handleApiSessionAgents(res, globalRoot, sessionId) {
|
|
|
1912
1940
|
return;
|
|
1913
1941
|
}
|
|
1914
1942
|
try {
|
|
1915
|
-
const { SessionRegistry } = await import(
|
|
1943
|
+
const { SessionRegistry } = await import("@wrongstack/core");
|
|
1916
1944
|
const registry = new SessionRegistry(globalRoot);
|
|
1917
1945
|
const entry = await registry.get(sessionId);
|
|
1918
1946
|
if (!entry) {
|
|
@@ -2052,7 +2080,7 @@ async function handleApiSessionEvents(res, globalRoot, sessionId, limit) {
|
|
|
2052
2080
|
return;
|
|
2053
2081
|
}
|
|
2054
2082
|
try {
|
|
2055
|
-
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, DefaultSessionStore: DefaultSessionStore2, DefaultSessionReader: DefaultSessionReader2 } = await import(
|
|
2083
|
+
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, DefaultSessionStore: DefaultSessionStore2, DefaultSessionReader: DefaultSessionReader2 } = await import("@wrongstack/core");
|
|
2056
2084
|
const registry = new SessionRegistry(globalRoot);
|
|
2057
2085
|
const entry = await registry.get(sessionId);
|
|
2058
2086
|
if (!entry) {
|
|
@@ -2134,7 +2162,7 @@ async function handleApiSessionMessage(res, req, globalRoot, sessionId) {
|
|
|
2134
2162
|
const priority = ["low", "normal", "high"].includes(rawPriority) ? rawPriority : "high";
|
|
2135
2163
|
const subject = typeof body["subject"] === "string" && body["subject"].trim() ? body["subject"].trim() : "Message from Fleet HQ";
|
|
2136
2164
|
try {
|
|
2137
|
-
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, GlobalMailbox: GlobalMailbox4, mailboxSessionTag: mailboxSessionTag2 } = await import(
|
|
2165
|
+
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, GlobalMailbox: GlobalMailbox4, mailboxSessionTag: mailboxSessionTag2 } = await import("@wrongstack/core");
|
|
2138
2166
|
const registry = new SessionRegistry(globalRoot);
|
|
2139
2167
|
const entry = await registry.get(sessionId);
|
|
2140
2168
|
if (!entry) {
|
|
@@ -2160,7 +2188,7 @@ async function handleApiSessionMailbox(res, globalRoot, sessionId) {
|
|
|
2160
2188
|
return;
|
|
2161
2189
|
}
|
|
2162
2190
|
try {
|
|
2163
|
-
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, GlobalMailbox: GlobalMailbox4, mailboxSessionTag: mailboxSessionTag2 } = await import(
|
|
2191
|
+
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, GlobalMailbox: GlobalMailbox4, mailboxSessionTag: mailboxSessionTag2 } = await import("@wrongstack/core");
|
|
2164
2192
|
const registry = new SessionRegistry(globalRoot);
|
|
2165
2193
|
const entry = await registry.get(sessionId);
|
|
2166
2194
|
if (!entry) {
|
|
@@ -2218,7 +2246,7 @@ async function handleApiSessionInterrupt(res, req, globalRoot, sessionId) {
|
|
|
2218
2246
|
const reason = typeof body["reason"] === "string" && body["reason"].trim() ? body["reason"].trim() : "Operator requested stop from Fleet HQ";
|
|
2219
2247
|
const from = typeof body["from"] === "string" && body["from"].trim() ? body["from"].trim() : "human@webui";
|
|
2220
2248
|
try {
|
|
2221
|
-
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, GlobalMailbox: GlobalMailbox4, mailboxSessionTag: mailboxSessionTag2 } = await import(
|
|
2249
|
+
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, GlobalMailbox: GlobalMailbox4, mailboxSessionTag: mailboxSessionTag2 } = await import("@wrongstack/core");
|
|
2222
2250
|
const registry = new SessionRegistry(globalRoot);
|
|
2223
2251
|
const entry = await registry.get(sessionId);
|
|
2224
2252
|
if (!entry) {
|
|
@@ -2266,7 +2294,7 @@ async function handleApiFleetBroadcast(res, req, globalRoot) {
|
|
|
2266
2294
|
}
|
|
2267
2295
|
const from = typeof body["from"] === "string" && body["from"].trim() ? body["from"].trim() : "human@webui";
|
|
2268
2296
|
try {
|
|
2269
|
-
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, GlobalMailbox: GlobalMailbox4, mailboxSessionTag: mailboxSessionTag2 } = await import(
|
|
2297
|
+
const { SessionRegistry, resolveWstackPaths: resolveWstackPaths3, GlobalMailbox: GlobalMailbox4, mailboxSessionTag: mailboxSessionTag2 } = await import("@wrongstack/core");
|
|
2270
2298
|
const registry = new SessionRegistry(globalRoot);
|
|
2271
2299
|
const all = await registry.list();
|
|
2272
2300
|
const mySlug = all.find((s) => s.pid === process.pid)?.projectSlug;
|
|
@@ -2422,6 +2450,10 @@ function clearAnalyticsBuffer() {
|
|
|
2422
2450
|
function getAnalyticsBuffer() {
|
|
2423
2451
|
return [...EVENT_BUFFER];
|
|
2424
2452
|
}
|
|
2453
|
+
|
|
2454
|
+
// src/server/ws-auth.ts
|
|
2455
|
+
import { Buffer as Buffer2 } from "node:buffer";
|
|
2456
|
+
import { timingSafeEqual } from "node:crypto";
|
|
2425
2457
|
function isLoopbackHostname(hostname) {
|
|
2426
2458
|
return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
2427
2459
|
}
|
|
@@ -2450,8 +2482,8 @@ function allowedHostname(hostname, allowedHostnames) {
|
|
|
2450
2482
|
}
|
|
2451
2483
|
function tokenMatches(provided, expected) {
|
|
2452
2484
|
if (!provided) return false;
|
|
2453
|
-
const a =
|
|
2454
|
-
const b =
|
|
2485
|
+
const a = Buffer2.from(provided);
|
|
2486
|
+
const b = Buffer2.from(expected);
|
|
2455
2487
|
if (a.length !== b.length) return false;
|
|
2456
2488
|
return timingSafeEqual(a, b);
|
|
2457
2489
|
}
|
|
@@ -2607,9 +2639,9 @@ function buildCspHeader(wsPort, requestHost, publicWsUrl) {
|
|
|
2607
2639
|
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'`;
|
|
2608
2640
|
}
|
|
2609
2641
|
function isInsideDist(candidate, distDir) {
|
|
2610
|
-
const root =
|
|
2611
|
-
const resolved =
|
|
2612
|
-
return resolved === root || resolved.startsWith(root +
|
|
2642
|
+
const root = path6.resolve(distDir);
|
|
2643
|
+
const resolved = path6.resolve(candidate);
|
|
2644
|
+
return resolved === root || resolved.startsWith(root + path6.sep);
|
|
2613
2645
|
}
|
|
2614
2646
|
function decodeSessionId(segment) {
|
|
2615
2647
|
try {
|
|
@@ -2620,7 +2652,7 @@ function decodeSessionId(segment) {
|
|
|
2620
2652
|
}
|
|
2621
2653
|
function createHttpServer(opts) {
|
|
2622
2654
|
const port = opts.port ?? Number.parseInt(process.env["PORT"] ?? "3456", 10);
|
|
2623
|
-
const distDir =
|
|
2655
|
+
const distDir = path6.resolve(opts.distDir);
|
|
2624
2656
|
const wsPort = opts.wsPort;
|
|
2625
2657
|
const requireAccessToken = Boolean(opts.requireToken) || !isLoopbackBind(opts.host);
|
|
2626
2658
|
return http.createServer(async (req, res) => {
|
|
@@ -2797,21 +2829,21 @@ function createHttpServer(opts) {
|
|
|
2797
2829
|
}
|
|
2798
2830
|
let filePath;
|
|
2799
2831
|
if (url.pathname === "/" || url.pathname === "") {
|
|
2800
|
-
filePath =
|
|
2832
|
+
filePath = path6.join(distDir, "index.html");
|
|
2801
2833
|
} else if (url.pathname.startsWith("/assets/")) {
|
|
2802
|
-
filePath =
|
|
2834
|
+
filePath = path6.join(distDir, url.pathname);
|
|
2803
2835
|
} else if (url.pathname.startsWith("/")) {
|
|
2804
|
-
filePath =
|
|
2836
|
+
filePath = path6.join(distDir, url.pathname);
|
|
2805
2837
|
} else {
|
|
2806
|
-
filePath =
|
|
2838
|
+
filePath = path6.join(distDir, "index.html");
|
|
2807
2839
|
}
|
|
2808
|
-
const resolvedPath =
|
|
2840
|
+
const resolvedPath = path6.resolve(filePath);
|
|
2809
2841
|
if (!isInsideDist(resolvedPath, distDir)) {
|
|
2810
2842
|
res.writeHead(403, { "Content-Type": "text/plain" });
|
|
2811
2843
|
res.end("Forbidden");
|
|
2812
2844
|
return;
|
|
2813
2845
|
}
|
|
2814
|
-
const ext =
|
|
2846
|
+
const ext = path6.extname(resolvedPath);
|
|
2815
2847
|
const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
2816
2848
|
res.setHeader("Content-Type", contentType);
|
|
2817
2849
|
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
@@ -2823,18 +2855,18 @@ function createHttpServer(opts) {
|
|
|
2823
2855
|
"Content-Security-Policy",
|
|
2824
2856
|
buildCspHeader(wsPort, requestHostForCsp(req.headers.host), opts.publicWsUrl)
|
|
2825
2857
|
);
|
|
2826
|
-
const html = await
|
|
2858
|
+
const html = await fs5.readFile(resolvedPath, "utf8");
|
|
2827
2859
|
res.writeHead(200);
|
|
2828
2860
|
res.end(injectWsConfig(html, { wsPort, publicWsUrl: opts.publicWsUrl }));
|
|
2829
2861
|
return;
|
|
2830
2862
|
}
|
|
2831
|
-
const fileContent = await
|
|
2863
|
+
const fileContent = await fs5.readFile(resolvedPath);
|
|
2832
2864
|
res.writeHead(200);
|
|
2833
2865
|
res.end(fileContent);
|
|
2834
2866
|
} catch (err) {
|
|
2835
2867
|
if (err.code === "ENOENT") {
|
|
2836
2868
|
try {
|
|
2837
|
-
const html = await
|
|
2869
|
+
const html = await fs5.readFile(path6.join(distDir, "index.html"), "utf8");
|
|
2838
2870
|
res.writeHead(200, {
|
|
2839
2871
|
"Content-Type": "text/html",
|
|
2840
2872
|
"X-Content-Type-Options": "nosniff",
|
|
@@ -2858,11 +2890,17 @@ function createHttpServer(opts) {
|
|
|
2858
2890
|
}
|
|
2859
2891
|
});
|
|
2860
2892
|
}
|
|
2893
|
+
|
|
2894
|
+
// src/server/instance-registry.ts
|
|
2895
|
+
import * as os from "node:os";
|
|
2896
|
+
import * as path7 from "node:path";
|
|
2897
|
+
import * as fs6 from "node:fs/promises";
|
|
2898
|
+
import { atomicWrite as atomicWrite3 } from "@wrongstack/core";
|
|
2861
2899
|
function defaultBaseDir() {
|
|
2862
|
-
return
|
|
2900
|
+
return path7.join(os.homedir(), ".wrongstack");
|
|
2863
2901
|
}
|
|
2864
2902
|
function registryPath(baseDir = defaultBaseDir()) {
|
|
2865
|
-
return
|
|
2903
|
+
return path7.join(baseDir, "webui-instances.json");
|
|
2866
2904
|
}
|
|
2867
2905
|
function isPidAlive(pid) {
|
|
2868
2906
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
@@ -2875,7 +2913,7 @@ function isPidAlive(pid) {
|
|
|
2875
2913
|
}
|
|
2876
2914
|
async function load(file) {
|
|
2877
2915
|
try {
|
|
2878
|
-
const raw = await
|
|
2916
|
+
const raw = await fs6.readFile(file, "utf8");
|
|
2879
2917
|
const parsed = JSON.parse(raw);
|
|
2880
2918
|
if (parsed?.version === 1 && Array.isArray(parsed.instances)) {
|
|
2881
2919
|
return parsed;
|
|
@@ -2885,7 +2923,7 @@ async function load(file) {
|
|
|
2885
2923
|
return { version: 1, instances: [] };
|
|
2886
2924
|
}
|
|
2887
2925
|
async function save(file, instances) {
|
|
2888
|
-
await
|
|
2926
|
+
await atomicWrite3(file, `${JSON.stringify({ version: 1, instances }, null, 2)}
|
|
2889
2927
|
`, {
|
|
2890
2928
|
mode: 384
|
|
2891
2929
|
});
|
|
@@ -2930,6 +2968,19 @@ function formatInstances(instances) {
|
|
|
2930
2968
|
}
|
|
2931
2969
|
return lines.join("\n");
|
|
2932
2970
|
}
|
|
2971
|
+
|
|
2972
|
+
// src/server/mcp-handlers.ts
|
|
2973
|
+
import { allServers } from "@wrongstack/core";
|
|
2974
|
+
import {
|
|
2975
|
+
addMcp,
|
|
2976
|
+
disableMcp,
|
|
2977
|
+
discoverMcp,
|
|
2978
|
+
enableMcp,
|
|
2979
|
+
listMcp,
|
|
2980
|
+
removeMcp,
|
|
2981
|
+
restartMcp,
|
|
2982
|
+
updateMcp
|
|
2983
|
+
} from "@wrongstack/mcp";
|
|
2933
2984
|
function mapStatus(raw) {
|
|
2934
2985
|
switch (raw) {
|
|
2935
2986
|
case "connected":
|
|
@@ -2945,7 +2996,7 @@ function mapStatus(raw) {
|
|
|
2945
2996
|
return "stopped";
|
|
2946
2997
|
}
|
|
2947
2998
|
}
|
|
2948
|
-
function toView(info) {
|
|
2999
|
+
function toView(info, health) {
|
|
2949
3000
|
const view = {
|
|
2950
3001
|
name: info.name,
|
|
2951
3002
|
transport: info.transport,
|
|
@@ -2957,6 +3008,7 @@ function toView(info) {
|
|
|
2957
3008
|
};
|
|
2958
3009
|
if (info.description !== void 0) view.description = info.description;
|
|
2959
3010
|
if (info.lazy !== void 0) view.lazy = info.lazy;
|
|
3011
|
+
if (health !== void 0) view.health = health;
|
|
2960
3012
|
return view;
|
|
2961
3013
|
}
|
|
2962
3014
|
function deps(ws, globalConfigPath, registry) {
|
|
@@ -2982,7 +3034,13 @@ async function handleMcpList(ws, _msg, globalConfigPath, mcpRegistry) {
|
|
|
2982
3034
|
registry: mcpRegistry,
|
|
2983
3035
|
presets: allServers()
|
|
2984
3036
|
});
|
|
2985
|
-
|
|
3037
|
+
const health = new Map(
|
|
3038
|
+
(typeof mcpRegistry.operationalHealth === "function" ? mcpRegistry.operationalHealth() : []).map((item) => [item.name, item])
|
|
3039
|
+
);
|
|
3040
|
+
send(ws, {
|
|
3041
|
+
type: "mcp.list",
|
|
3042
|
+
payload: { servers: servers.map((server) => toView(server, health.get(server.name))) }
|
|
3043
|
+
});
|
|
2986
3044
|
}
|
|
2987
3045
|
async function handleMcpAdd(ws, msg, globalConfigPath, mcpRegistry) {
|
|
2988
3046
|
const d = deps(ws, globalConfigPath, mcpRegistry);
|
|
@@ -3132,6 +3190,99 @@ async function handleMcpDiscover(ws, msg, globalConfigPath, mcpRegistry) {
|
|
|
3132
3190
|
payload: { success: result.ok, message: result.message }
|
|
3133
3191
|
});
|
|
3134
3192
|
}
|
|
3193
|
+
async function handleMcpResources(ws, msg, _globalConfigPath, mcpRegistry) {
|
|
3194
|
+
if (!mcpRegistry) return sendContentError(ws, "resources", "", "MCP registry is not available.");
|
|
3195
|
+
const payload = payloadRecord(msg);
|
|
3196
|
+
let serverName = "";
|
|
3197
|
+
try {
|
|
3198
|
+
serverName = requiredPayloadString(payload, "name");
|
|
3199
|
+
const refresh = payload["refresh"] === true;
|
|
3200
|
+
const [resources, resourceTemplates] = await Promise.all([
|
|
3201
|
+
mcpRegistry.listResources(serverName, { refresh }),
|
|
3202
|
+
mcpRegistry.listResourceTemplates(serverName, { refresh })
|
|
3203
|
+
]);
|
|
3204
|
+
send(ws, {
|
|
3205
|
+
type: "mcp.resources",
|
|
3206
|
+
payload: { name: serverName, resources, resourceTemplates }
|
|
3207
|
+
});
|
|
3208
|
+
} catch (err) {
|
|
3209
|
+
sendContentError(ws, "resources", serverName, errorMessage(err));
|
|
3210
|
+
}
|
|
3211
|
+
}
|
|
3212
|
+
async function handleMcpPrompts(ws, msg, _globalConfigPath, mcpRegistry) {
|
|
3213
|
+
if (!mcpRegistry) return sendContentError(ws, "prompts", "", "MCP registry is not available.");
|
|
3214
|
+
const payload = payloadRecord(msg);
|
|
3215
|
+
let serverName = "";
|
|
3216
|
+
try {
|
|
3217
|
+
serverName = requiredPayloadString(payload, "name");
|
|
3218
|
+
const prompts = await mcpRegistry.listPrompts(serverName, {
|
|
3219
|
+
refresh: payload["refresh"] === true
|
|
3220
|
+
});
|
|
3221
|
+
send(ws, { type: "mcp.prompts", payload: { name: serverName, prompts } });
|
|
3222
|
+
} catch (err) {
|
|
3223
|
+
sendContentError(ws, "prompts", serverName, errorMessage(err));
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
async function handleMcpResourceRead(ws, msg, _globalConfigPath, mcpRegistry) {
|
|
3227
|
+
if (!mcpRegistry)
|
|
3228
|
+
return sendContentError(ws, "resource.read", "", "MCP registry is not available.");
|
|
3229
|
+
const payload = payloadRecord(msg);
|
|
3230
|
+
let serverName = "";
|
|
3231
|
+
try {
|
|
3232
|
+
serverName = requiredPayloadString(payload, "name");
|
|
3233
|
+
const insertion = await mcpRegistry.selectResourceForInsertion(
|
|
3234
|
+
serverName,
|
|
3235
|
+
requiredPayloadString(payload, "uri")
|
|
3236
|
+
);
|
|
3237
|
+
send(ws, { type: "mcp.content.selected", payload: insertion });
|
|
3238
|
+
} catch (err) {
|
|
3239
|
+
sendContentError(ws, "resource.read", serverName, errorMessage(err));
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3242
|
+
async function handleMcpPromptGet(ws, msg, _globalConfigPath, mcpRegistry) {
|
|
3243
|
+
if (!mcpRegistry) return sendContentError(ws, "prompt.get", "", "MCP registry is not available.");
|
|
3244
|
+
const payload = payloadRecord(msg);
|
|
3245
|
+
let serverName = "";
|
|
3246
|
+
try {
|
|
3247
|
+
serverName = requiredPayloadString(payload, "name");
|
|
3248
|
+
const insertion = await mcpRegistry.selectPromptForInsertion(
|
|
3249
|
+
serverName,
|
|
3250
|
+
requiredPayloadString(payload, "prompt"),
|
|
3251
|
+
promptArguments(payload["arguments"])
|
|
3252
|
+
);
|
|
3253
|
+
send(ws, { type: "mcp.content.selected", payload: insertion });
|
|
3254
|
+
} catch (err) {
|
|
3255
|
+
sendContentError(ws, "prompt.get", serverName, errorMessage(err));
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
function payloadRecord(msg) {
|
|
3259
|
+
return msg.payload && typeof msg.payload === "object" && !Array.isArray(msg.payload) ? msg.payload : {};
|
|
3260
|
+
}
|
|
3261
|
+
function requiredPayloadString(payload, field) {
|
|
3262
|
+
const value = payload[field];
|
|
3263
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
3264
|
+
throw new Error(`MCP payload field "${field}" must be a non-empty string`);
|
|
3265
|
+
}
|
|
3266
|
+
return value;
|
|
3267
|
+
}
|
|
3268
|
+
function promptArguments(value) {
|
|
3269
|
+
if (value === void 0) return void 0;
|
|
3270
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
3271
|
+
throw new Error("MCP prompt arguments must be an object");
|
|
3272
|
+
}
|
|
3273
|
+
const args = {};
|
|
3274
|
+
for (const [key, item] of Object.entries(value)) {
|
|
3275
|
+
if (typeof item !== "string") throw new Error(`MCP prompt argument "${key}" must be a string`);
|
|
3276
|
+
args[key] = item;
|
|
3277
|
+
}
|
|
3278
|
+
return args;
|
|
3279
|
+
}
|
|
3280
|
+
function sendContentError(ws, action, name2, error) {
|
|
3281
|
+
send(ws, { type: "mcp.content.error", payload: { action, name: name2, error } });
|
|
3282
|
+
}
|
|
3283
|
+
function errorMessage(err) {
|
|
3284
|
+
return err instanceof Error ? err.message : String(err);
|
|
3285
|
+
}
|
|
3135
3286
|
|
|
3136
3287
|
// src/server/memory-handlers.ts
|
|
3137
3288
|
async function handleMemoryList(ws, memoryStore) {
|
|
@@ -3167,6 +3318,9 @@ async function handleMemoryForget(ws, msg, memoryStore) {
|
|
|
3167
3318
|
sendResult(ws, false, errMessage(err));
|
|
3168
3319
|
}
|
|
3169
3320
|
}
|
|
3321
|
+
|
|
3322
|
+
// src/server/open-browser.ts
|
|
3323
|
+
import { spawn } from "node:child_process";
|
|
3170
3324
|
function browserOpenCommand(url, platform = process.platform) {
|
|
3171
3325
|
if (platform === "win32") {
|
|
3172
3326
|
return { command: "cmd", args: ["/c", "start", "", url] };
|
|
@@ -3185,7 +3339,7 @@ function openBrowser(url, platform = process.platform) {
|
|
|
3185
3339
|
child.unref();
|
|
3186
3340
|
if (child.pid) {
|
|
3187
3341
|
try {
|
|
3188
|
-
import(
|
|
3342
|
+
import("@wrongstack/tools").then(({ getProcessRegistry }) => {
|
|
3189
3343
|
const pid = child.pid;
|
|
3190
3344
|
if (pid === void 0) return;
|
|
3191
3345
|
getProcessRegistry().register({
|
|
@@ -3207,6 +3361,10 @@ function openBrowser(url, platform = process.platform) {
|
|
|
3207
3361
|
} catch {
|
|
3208
3362
|
}
|
|
3209
3363
|
}
|
|
3364
|
+
|
|
3365
|
+
// src/server/port-utils.ts
|
|
3366
|
+
import * as net from "node:net";
|
|
3367
|
+
import { ToolValidationError as ToolValidationError3 } from "@wrongstack/core";
|
|
3210
3368
|
function isPortFree(host, port) {
|
|
3211
3369
|
return new Promise((resolve10) => {
|
|
3212
3370
|
const srv = net.createServer();
|
|
@@ -3232,7 +3390,7 @@ async function findFreePort(host, startPort, opts = {}) {
|
|
|
3232
3390
|
}
|
|
3233
3391
|
port++;
|
|
3234
3392
|
}
|
|
3235
|
-
throw new
|
|
3393
|
+
throw new ToolValidationError3({
|
|
3236
3394
|
message: `No free port found near ${startPort} on ${host} after ${maxTries} attempts.`,
|
|
3237
3395
|
field: "port"
|
|
3238
3396
|
});
|
|
@@ -3442,10 +3600,19 @@ async function handlePromptsRecent(ws, ctx) {
|
|
|
3442
3600
|
send(ws, { type: "prompts.recent", payload: { slugs: [], error: errMessage(err) } });
|
|
3443
3601
|
}
|
|
3444
3602
|
}
|
|
3603
|
+
|
|
3604
|
+
// src/server/provider-config-standalone.ts
|
|
3605
|
+
import * as path8 from "node:path";
|
|
3606
|
+
import { DefaultSecretVault } from "@wrongstack/core";
|
|
3607
|
+
|
|
3608
|
+
// src/server/provider-config-io.ts
|
|
3609
|
+
import * as fs7 from "node:fs/promises";
|
|
3610
|
+
import { ConfigError, atomicWrite as atomicWrite4 } from "@wrongstack/core";
|
|
3611
|
+
import { decryptConfigSecrets, encryptConfigSecrets } from "@wrongstack/core/security";
|
|
3445
3612
|
async function loadSavedProviders(configPath, vault) {
|
|
3446
3613
|
let raw;
|
|
3447
3614
|
try {
|
|
3448
|
-
raw = await
|
|
3615
|
+
raw = await fs7.readFile(configPath, "utf8");
|
|
3449
3616
|
} catch {
|
|
3450
3617
|
return {};
|
|
3451
3618
|
}
|
|
@@ -3462,7 +3629,7 @@ async function saveProviders(configPath, vault, providers) {
|
|
|
3462
3629
|
let raw;
|
|
3463
3630
|
let fileExists = true;
|
|
3464
3631
|
try {
|
|
3465
|
-
raw = await
|
|
3632
|
+
raw = await fs7.readFile(configPath, "utf8");
|
|
3466
3633
|
} catch (err) {
|
|
3467
3634
|
if (err.code !== "ENOENT") {
|
|
3468
3635
|
throw new ConfigError({
|
|
@@ -3491,16 +3658,21 @@ async function saveProviders(configPath, vault, providers) {
|
|
|
3491
3658
|
}
|
|
3492
3659
|
parsed.providers = providers;
|
|
3493
3660
|
const encrypted = encryptConfigSecrets(parsed, vault);
|
|
3494
|
-
await
|
|
3661
|
+
await atomicWrite4(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
3495
3662
|
}
|
|
3663
|
+
|
|
3664
|
+
// src/server/provider-config-standalone.ts
|
|
3496
3665
|
function createProviderConfigIO(configPath) {
|
|
3497
|
-
const keyFile =
|
|
3666
|
+
const keyFile = path8.join(path8.dirname(configPath), ".key");
|
|
3498
3667
|
const vault = new DefaultSecretVault({ keyFile });
|
|
3499
3668
|
return {
|
|
3500
3669
|
load: () => loadSavedProviders(configPath, vault),
|
|
3501
3670
|
save: (providers) => saveProviders(configPath, vault, providers)
|
|
3502
3671
|
};
|
|
3503
3672
|
}
|
|
3673
|
+
|
|
3674
|
+
// src/server/provider-keys.ts
|
|
3675
|
+
import { expectDefined } from "@wrongstack/core";
|
|
3504
3676
|
function normalizeKeys(cfg) {
|
|
3505
3677
|
if (Array.isArray(cfg.apiKeys) && cfg.apiKeys.length > 0) {
|
|
3506
3678
|
return cfg.apiKeys.map((k) => ({ ...k }));
|
|
@@ -3594,6 +3766,12 @@ function removeProvider(providers, providerId) {
|
|
|
3594
3766
|
delete providers[providerId];
|
|
3595
3767
|
return { ok: true, message: `Provider "${providerId}" removed` };
|
|
3596
3768
|
}
|
|
3769
|
+
|
|
3770
|
+
// src/server/sdd-board-ws-handler.ts
|
|
3771
|
+
import {
|
|
3772
|
+
applySddLifecycle,
|
|
3773
|
+
SddBoardStore
|
|
3774
|
+
} from "@wrongstack/sdd";
|
|
3597
3775
|
var CONTROL_TYPES = /* @__PURE__ */ new Set([
|
|
3598
3776
|
"pause",
|
|
3599
3777
|
"resume",
|
|
@@ -3738,6 +3916,26 @@ var SddBoardWebSocketHandler = class {
|
|
|
3738
3916
|
if (client.ws.readyState === 1) client.ws.send(JSON.stringify(msg));
|
|
3739
3917
|
}
|
|
3740
3918
|
};
|
|
3919
|
+
|
|
3920
|
+
// src/server/sdd-wizard-wiring.ts
|
|
3921
|
+
import * as path9 from "node:path";
|
|
3922
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
3923
|
+
import {
|
|
3924
|
+
WorktreeManager as WorktreeManager2,
|
|
3925
|
+
ToolValidationError as ToolValidationError4
|
|
3926
|
+
} from "@wrongstack/core";
|
|
3927
|
+
import {
|
|
3928
|
+
cleanupStaleSddWorktrees,
|
|
3929
|
+
makeCommandVerifier,
|
|
3930
|
+
makeLlmSubtaskGenerator,
|
|
3931
|
+
SddBoardStore as SddBoardStore2,
|
|
3932
|
+
SddInterviewDriver,
|
|
3933
|
+
SddRunRegistry,
|
|
3934
|
+
SddSupervisor,
|
|
3935
|
+
SpecStore,
|
|
3936
|
+
startSddRun,
|
|
3937
|
+
TaskGraphStore
|
|
3938
|
+
} from "@wrongstack/sdd";
|
|
3741
3939
|
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";
|
|
3742
3940
|
function buildSddWizardDeps(opts) {
|
|
3743
3941
|
const registry = new SddRunRegistry();
|
|
@@ -3761,14 +3959,14 @@ function buildSddWizardDeps(opts) {
|
|
|
3761
3959
|
makeDriver: () => new SddInterviewDriver({
|
|
3762
3960
|
specStore: new SpecStore({ baseDir: opts.paths.projectSpecs }),
|
|
3763
3961
|
graphStore: new TaskGraphStore({ baseDir: opts.paths.projectTaskGraphs }),
|
|
3764
|
-
sessionPath:
|
|
3962
|
+
sessionPath: path9.join(opts.paths.projectDir, "sdd-wizard-session.json")
|
|
3765
3963
|
}),
|
|
3766
3964
|
runInterviewTurn: (prompt) => runIsolatedTurn(prompt, "Spec Architect"),
|
|
3767
3965
|
startRun: async (driver, { parallelSlots, defaultModel, defaultProvider, fallbackModels, worktrees: useWorktrees }) => {
|
|
3768
3966
|
const graph = driver.getGraph();
|
|
3769
3967
|
const tracker = driver.getTracker();
|
|
3770
3968
|
if (!graph || !tracker) {
|
|
3771
|
-
throw new
|
|
3969
|
+
throw new ToolValidationError4({
|
|
3772
3970
|
message: "No task graph to run \u2014 finish the interview first.",
|
|
3773
3971
|
field: "taskGraph"
|
|
3774
3972
|
});
|
|
@@ -3776,7 +3974,7 @@ function buildSddWizardDeps(opts) {
|
|
|
3776
3974
|
const worktreesEnabled = useWorktrees ?? process.env["WRONGSTACK_SDD_WORKTREES"] !== "0";
|
|
3777
3975
|
let worktrees;
|
|
3778
3976
|
if (worktreesEnabled) {
|
|
3779
|
-
const inGit =
|
|
3977
|
+
const inGit = spawnSync2("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
3780
3978
|
cwd: opts.projectRoot,
|
|
3781
3979
|
encoding: "utf8",
|
|
3782
3980
|
windowsHide: true
|
|
@@ -3786,14 +3984,14 @@ function buildSddWizardDeps(opts) {
|
|
|
3786
3984
|
projectRoot: opts.projectRoot,
|
|
3787
3985
|
boardsDir: opts.paths.projectSddBoards
|
|
3788
3986
|
}).catch(() => void 0);
|
|
3789
|
-
worktrees = new
|
|
3987
|
+
worktrees = new WorktreeManager2({
|
|
3790
3988
|
projectRoot: opts.projectRoot,
|
|
3791
3989
|
events: opts.events,
|
|
3792
3990
|
sessionId: () => opts.agent.ctx.session?.id
|
|
3793
3991
|
});
|
|
3794
3992
|
}
|
|
3795
3993
|
}
|
|
3796
|
-
const boardStore = new
|
|
3994
|
+
const boardStore = new SddBoardStore2({ baseDir: opts.paths.projectSddBoards });
|
|
3797
3995
|
const verifyTask = makeCommandVerifier();
|
|
3798
3996
|
const superviseFailure = opts.brain ? new SddSupervisor({
|
|
3799
3997
|
brain: opts.brain,
|
|
@@ -3973,20 +4171,25 @@ var SddWizardWebSocketHandler = class {
|
|
|
3973
4171
|
if (client.ws.readyState === 1) client.ws.send(JSON.stringify(msg));
|
|
3974
4172
|
}
|
|
3975
4173
|
};
|
|
4174
|
+
|
|
4175
|
+
// src/server/shell-open.ts
|
|
4176
|
+
import * as fs8 from "node:fs/promises";
|
|
4177
|
+
import * as path10 from "node:path";
|
|
4178
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
3976
4179
|
var METACHAR_REGEX = /[&|<>^"'`'\n\r]/;
|
|
3977
4180
|
function shellQuote(s) {
|
|
3978
4181
|
return s.replaceAll("'", `'"'"'`);
|
|
3979
4182
|
}
|
|
3980
4183
|
async function handleShellOpen(req, logger) {
|
|
3981
4184
|
try {
|
|
3982
|
-
const resolved =
|
|
3983
|
-
await
|
|
4185
|
+
const resolved = path10.resolve(req.path);
|
|
4186
|
+
await fs8.access(resolved);
|
|
3984
4187
|
if (METACHAR_REGEX.test(resolved)) {
|
|
3985
4188
|
return { success: false, message: "Path contains unsupported characters." };
|
|
3986
4189
|
}
|
|
3987
4190
|
const platform = process.platform;
|
|
3988
4191
|
const launch = (cmd, args, onError) => {
|
|
3989
|
-
const child =
|
|
4192
|
+
const child = spawn2(cmd, args, {
|
|
3990
4193
|
detached: true,
|
|
3991
4194
|
stdio: "ignore",
|
|
3992
4195
|
windowsHide: true
|
|
@@ -4030,6 +4233,12 @@ async function handleShellOpen(req, logger) {
|
|
|
4030
4233
|
}
|
|
4031
4234
|
}
|
|
4032
4235
|
|
|
4236
|
+
// src/server/skills-handlers.ts
|
|
4237
|
+
import { promises as fs9 } from "node:fs";
|
|
4238
|
+
import path11 from "node:path";
|
|
4239
|
+
import { atomicWrite as atomicWrite5 } from "@wrongstack/core";
|
|
4240
|
+
import { wstackGlobalRoot } from "@wrongstack/core/utils";
|
|
4241
|
+
|
|
4033
4242
|
// src/server/ws-payload-validation.ts
|
|
4034
4243
|
function isRecord(value) {
|
|
4035
4244
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -4163,6 +4372,16 @@ function validateBrainAskPayload(payload) {
|
|
|
4163
4372
|
}
|
|
4164
4373
|
return { ok: true, value: { question: question.trim() } };
|
|
4165
4374
|
}
|
|
4375
|
+
function validateBrainConfigSetPayload(payload) {
|
|
4376
|
+
if (!isRecord(payload)) {
|
|
4377
|
+
return { ok: false, message: "brain.config.set payload must be an object with a patch object" };
|
|
4378
|
+
}
|
|
4379
|
+
const patch = payload["patch"];
|
|
4380
|
+
if (!isRecord(patch)) {
|
|
4381
|
+
return { ok: false, message: "brain.config.set payload.patch must be an object" };
|
|
4382
|
+
}
|
|
4383
|
+
return { ok: true, value: { patch } };
|
|
4384
|
+
}
|
|
4166
4385
|
function validateAutonomySwitchPayload(payload) {
|
|
4167
4386
|
if (!isRecord(payload)) {
|
|
4168
4387
|
return { ok: false, message: "autonomy.switch payload must be an object with string mode" };
|
|
@@ -4224,7 +4443,9 @@ var BOOLEAN_PREF_KEYS = /* @__PURE__ */ new Set([
|
|
|
4224
4443
|
"hqEnabled",
|
|
4225
4444
|
"hqRawContent",
|
|
4226
4445
|
"fallbackAuto",
|
|
4227
|
-
"favoriteModelsOnly"
|
|
4446
|
+
"favoriteModelsOnly",
|
|
4447
|
+
"breakerEnabled",
|
|
4448
|
+
"debugStream"
|
|
4228
4449
|
]);
|
|
4229
4450
|
var STRING_ARRAY_PREF_KEYS = /* @__PURE__ */ new Set(["fallbackModels", "favoriteModels"]);
|
|
4230
4451
|
var STRING_ARRAY_RECORD_PREF_KEYS = /* @__PURE__ */ new Set(["fallbackProfiles"]);
|
|
@@ -4235,9 +4456,17 @@ var NUMBER_PREF_KEYS = /* @__PURE__ */ new Set([
|
|
|
4235
4456
|
"maxIterations",
|
|
4236
4457
|
"maxConcurrent",
|
|
4237
4458
|
"enhanceDelayMs",
|
|
4238
|
-
"tgLongToolMs"
|
|
4459
|
+
"tgLongToolMs",
|
|
4460
|
+
"breakerAutoKillResetMs"
|
|
4461
|
+
]);
|
|
4462
|
+
var STRING_PREF_KEYS = /* @__PURE__ */ new Set([
|
|
4463
|
+
"hqUrl",
|
|
4464
|
+
"hqToken",
|
|
4465
|
+
"uiLocale",
|
|
4466
|
+
"thinkingWord",
|
|
4467
|
+
"refinerProvider",
|
|
4468
|
+
"refinerModel"
|
|
4239
4469
|
]);
|
|
4240
|
-
var STRING_PREF_KEYS = /* @__PURE__ */ new Set(["hqUrl", "hqToken", "uiLocale"]);
|
|
4241
4470
|
var ENUM_PREF_KEYS = {
|
|
4242
4471
|
autonomy: AUTONOMY_VALUES,
|
|
4243
4472
|
contextStrategy: CONTEXT_STRATEGY_VALUES,
|
|
@@ -4248,7 +4477,10 @@ var ENUM_PREF_KEYS = {
|
|
|
4248
4477
|
auditLevel: AUDIT_LEVEL_VALUES,
|
|
4249
4478
|
reasoningMode: REASONING_MODE_VALUES,
|
|
4250
4479
|
reasoningEffort: REASONING_EFFORT_VALUES,
|
|
4251
|
-
cacheTtl: CACHE_TTL_VALUES
|
|
4480
|
+
cacheTtl: CACHE_TTL_VALUES,
|
|
4481
|
+
statuslineMode: /* @__PURE__ */ new Set(["minimum", "detailed", "no-color"]),
|
|
4482
|
+
animationStyle: /* @__PURE__ */ new Set(["rainbow", "wave", "pulse", "dots", "breathe", "cycle"]),
|
|
4483
|
+
fsAccess: /* @__PURE__ */ new Set(["unrestricted", "project"])
|
|
4252
4484
|
};
|
|
4253
4485
|
function validateModelRuntimeValue(modelRuntime, path23) {
|
|
4254
4486
|
const reasoning = modelRuntime["reasoning"];
|
|
@@ -4624,6 +4856,123 @@ function validateProjectsSelectPayload(payload) {
|
|
|
4624
4856
|
};
|
|
4625
4857
|
}
|
|
4626
4858
|
|
|
4859
|
+
// src/server/zip.ts
|
|
4860
|
+
import { deflateRawSync, inflateRawSync } from "node:zlib";
|
|
4861
|
+
function createZipBuffer(entries) {
|
|
4862
|
+
const localParts = [];
|
|
4863
|
+
const centralParts = [];
|
|
4864
|
+
let offset = 0;
|
|
4865
|
+
const { date, time } = dosDateTime(/* @__PURE__ */ new Date());
|
|
4866
|
+
for (const entry of entries) {
|
|
4867
|
+
const name2 = Buffer.from(normalizeZipPath(entry.name), "utf8");
|
|
4868
|
+
const data = typeof entry.data === "string" ? Buffer.from(entry.data, "utf8") : Buffer.from(entry.data);
|
|
4869
|
+
const compressed = deflateRawSync(data);
|
|
4870
|
+
const crc = crc32(data);
|
|
4871
|
+
const local = Buffer.alloc(30);
|
|
4872
|
+
local.writeUInt32LE(67324752, 0);
|
|
4873
|
+
local.writeUInt16LE(20, 4);
|
|
4874
|
+
local.writeUInt16LE(2048, 6);
|
|
4875
|
+
local.writeUInt16LE(8, 8);
|
|
4876
|
+
local.writeUInt16LE(time, 10);
|
|
4877
|
+
local.writeUInt16LE(date, 12);
|
|
4878
|
+
local.writeUInt32LE(crc, 14);
|
|
4879
|
+
local.writeUInt32LE(compressed.length, 18);
|
|
4880
|
+
local.writeUInt32LE(data.length, 22);
|
|
4881
|
+
local.writeUInt16LE(name2.length, 26);
|
|
4882
|
+
localParts.push(local, name2, compressed);
|
|
4883
|
+
const central = Buffer.alloc(46);
|
|
4884
|
+
central.writeUInt32LE(33639248, 0);
|
|
4885
|
+
central.writeUInt16LE(798, 4);
|
|
4886
|
+
central.writeUInt16LE(20, 6);
|
|
4887
|
+
central.writeUInt16LE(2048, 8);
|
|
4888
|
+
central.writeUInt16LE(8, 10);
|
|
4889
|
+
central.writeUInt16LE(time, 12);
|
|
4890
|
+
central.writeUInt16LE(date, 14);
|
|
4891
|
+
central.writeUInt32LE(crc, 16);
|
|
4892
|
+
central.writeUInt32LE(compressed.length, 20);
|
|
4893
|
+
central.writeUInt32LE(data.length, 24);
|
|
4894
|
+
central.writeUInt16LE(name2.length, 28);
|
|
4895
|
+
central.writeUInt32LE(offset, 42);
|
|
4896
|
+
centralParts.push(central, name2);
|
|
4897
|
+
offset += local.length + name2.length + compressed.length;
|
|
4898
|
+
}
|
|
4899
|
+
const centralDirectory = Buffer.concat(centralParts);
|
|
4900
|
+
const end = Buffer.alloc(22);
|
|
4901
|
+
end.writeUInt32LE(101010256, 0);
|
|
4902
|
+
end.writeUInt16LE(entries.length, 8);
|
|
4903
|
+
end.writeUInt16LE(entries.length, 10);
|
|
4904
|
+
end.writeUInt32LE(centralDirectory.length, 12);
|
|
4905
|
+
end.writeUInt32LE(offset, 16);
|
|
4906
|
+
return Buffer.concat([...localParts, centralDirectory, end]);
|
|
4907
|
+
}
|
|
4908
|
+
function readZipEntries(archive) {
|
|
4909
|
+
const maxEntries = 1e3;
|
|
4910
|
+
const maxEntryBytes = 64 * 1024 * 1024;
|
|
4911
|
+
const maxTotalBytes = 128 * 1024 * 1024;
|
|
4912
|
+
const buffer = Buffer.from(archive);
|
|
4913
|
+
const result = /* @__PURE__ */ new Map();
|
|
4914
|
+
let totalBytes = 0;
|
|
4915
|
+
let offset = 0;
|
|
4916
|
+
while (offset + 4 <= buffer.length && buffer.readUInt32LE(offset) === 67324752) {
|
|
4917
|
+
if (offset + 30 > buffer.length) throw new Error("Truncated ZIP local header.");
|
|
4918
|
+
const flags = buffer.readUInt16LE(offset + 6);
|
|
4919
|
+
const method = buffer.readUInt16LE(offset + 8);
|
|
4920
|
+
if ((flags & 1) !== 0) throw new Error("Encrypted ZIP entries are not supported.");
|
|
4921
|
+
if ((flags & 8) !== 0) throw new Error("ZIP data descriptors are not supported.");
|
|
4922
|
+
const compressedSize = buffer.readUInt32LE(offset + 18);
|
|
4923
|
+
const expectedSize = buffer.readUInt32LE(offset + 22);
|
|
4924
|
+
if (result.size >= maxEntries) throw new Error("ZIP contains too many entries.");
|
|
4925
|
+
if (expectedSize > maxEntryBytes || totalBytes + expectedSize > maxTotalBytes) {
|
|
4926
|
+
throw new Error("ZIP expands beyond the configured size limit.");
|
|
4927
|
+
}
|
|
4928
|
+
if (compressedSize > 0 && expectedSize / compressedSize > 1e3) {
|
|
4929
|
+
throw new Error("ZIP entry has a suspicious compression ratio.");
|
|
4930
|
+
}
|
|
4931
|
+
const nameLength = buffer.readUInt16LE(offset + 26);
|
|
4932
|
+
const extraLength = buffer.readUInt16LE(offset + 28);
|
|
4933
|
+
const nameStart = offset + 30;
|
|
4934
|
+
const dataStart = nameStart + nameLength + extraLength;
|
|
4935
|
+
const dataEnd = dataStart + compressedSize;
|
|
4936
|
+
if (dataEnd > buffer.length) throw new Error("Truncated ZIP entry.");
|
|
4937
|
+
const name2 = buffer.subarray(nameStart, nameStart + nameLength).toString("utf8");
|
|
4938
|
+
if (!isSafeZipPath(name2)) throw new Error(`Unsafe ZIP entry path: ${name2}`);
|
|
4939
|
+
const compressed = buffer.subarray(dataStart, dataEnd);
|
|
4940
|
+
const data = method === 0 ? Buffer.from(compressed) : method === 8 ? inflateRawSync(compressed) : void 0;
|
|
4941
|
+
if (!data) throw new Error(`Unsupported ZIP compression method: ${method}`);
|
|
4942
|
+
if (data.length !== expectedSize) throw new Error(`ZIP size mismatch for ${name2}`);
|
|
4943
|
+
if (crc32(data) !== buffer.readUInt32LE(offset + 14)) throw new Error(`ZIP CRC mismatch for ${name2}`);
|
|
4944
|
+
result.set(name2, data);
|
|
4945
|
+
totalBytes += data.length;
|
|
4946
|
+
offset = dataEnd;
|
|
4947
|
+
}
|
|
4948
|
+
return result;
|
|
4949
|
+
}
|
|
4950
|
+
function normalizeZipPath(value) {
|
|
4951
|
+
const normalized = value.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
4952
|
+
if (!isSafeZipPath(normalized)) throw new Error(`Unsafe ZIP entry path: ${value}`);
|
|
4953
|
+
return normalized;
|
|
4954
|
+
}
|
|
4955
|
+
function isSafeZipPath(value) {
|
|
4956
|
+
return value.length > 0 && !value.includes("\0") && !value.startsWith("/") && !/^[a-z]:/i.test(value) && !value.split("/").includes("..");
|
|
4957
|
+
}
|
|
4958
|
+
function dosDateTime(value) {
|
|
4959
|
+
const year = Math.max(1980, Math.min(2107, value.getFullYear()));
|
|
4960
|
+
return {
|
|
4961
|
+
date: year - 1980 << 9 | value.getMonth() + 1 << 5 | value.getDate(),
|
|
4962
|
+
time: value.getHours() << 11 | value.getMinutes() << 5 | Math.floor(value.getSeconds() / 2)
|
|
4963
|
+
};
|
|
4964
|
+
}
|
|
4965
|
+
var CRC_TABLE = Array.from({ length: 256 }, (_, index) => {
|
|
4966
|
+
let crc = index;
|
|
4967
|
+
for (let bit = 0; bit < 8; bit++) crc = (crc & 1) !== 0 ? 3988292384 ^ crc >>> 1 : crc >>> 1;
|
|
4968
|
+
return crc >>> 0;
|
|
4969
|
+
});
|
|
4970
|
+
function crc32(data) {
|
|
4971
|
+
let crc = 4294967295;
|
|
4972
|
+
for (const byte of data) crc = crc >>> 8 ^ (CRC_TABLE[(crc ^ byte) & 255] ?? 0);
|
|
4973
|
+
return (crc ^ 4294967295) >>> 0;
|
|
4974
|
+
}
|
|
4975
|
+
|
|
4627
4976
|
// src/server/skills-handlers.ts
|
|
4628
4977
|
async function handleSkillsList(ws, ctx) {
|
|
4629
4978
|
if (!ctx.skillLoader) {
|
|
@@ -4692,19 +5041,19 @@ async function handleSkillsContent(ws, ctx, msg) {
|
|
|
4692
5041
|
send(ws, { type: "skills.content", payload: { name: name2, body: "", path: "", source, relatedFiles: [], references: [], error: `Skill "${name2}" not found` } });
|
|
4693
5042
|
return;
|
|
4694
5043
|
}
|
|
4695
|
-
const body = await
|
|
4696
|
-
const skillDir =
|
|
5044
|
+
const body = await fs9.readFile(entry.path, "utf8");
|
|
5045
|
+
const skillDir = path11.dirname(entry.path);
|
|
4697
5046
|
let relatedFiles = [];
|
|
4698
5047
|
try {
|
|
4699
|
-
const files = await
|
|
4700
|
-
relatedFiles = files.filter((f) => f !==
|
|
5048
|
+
const files = await fs9.readdir(skillDir);
|
|
5049
|
+
relatedFiles = files.filter((f) => f !== path11.basename(entry.path)).map((f) => path11.join(skillDir, f));
|
|
4701
5050
|
} catch {
|
|
4702
5051
|
}
|
|
4703
5052
|
const nameLower = name2.toLowerCase();
|
|
4704
5053
|
const refResults = await Promise.all(
|
|
4705
5054
|
entries.filter((e) => e.name.toLowerCase() !== nameLower).map(async (e) => {
|
|
4706
5055
|
try {
|
|
4707
|
-
const content = await
|
|
5056
|
+
const content = await fs9.readFile(e.path, "utf8");
|
|
4708
5057
|
return [e.name, content.toLowerCase().includes(nameLower)];
|
|
4709
5058
|
} catch {
|
|
4710
5059
|
return [e.name, false];
|
|
@@ -4794,20 +5143,20 @@ async function handleSkillsCreate(ws, ctx, msg) {
|
|
|
4794
5143
|
}
|
|
4795
5144
|
const createPayload = parsed.value;
|
|
4796
5145
|
try {
|
|
4797
|
-
const targetDir = createPayload.scope === "global" ?
|
|
4798
|
-
ctx.globalSkillsDir ??
|
|
5146
|
+
const targetDir = createPayload.scope === "global" ? path11.join(
|
|
5147
|
+
ctx.globalSkillsDir ?? path11.join(wstackGlobalRoot(), "skills"),
|
|
4799
5148
|
createPayload.name.trim()
|
|
4800
|
-
) :
|
|
4801
|
-
ctx.projectSkillsDir ??
|
|
5149
|
+
) : path11.join(
|
|
5150
|
+
ctx.projectSkillsDir ?? path11.join(ctx.projectRoot, ".wrongstack", "skills"),
|
|
4802
5151
|
createPayload.name.trim()
|
|
4803
5152
|
);
|
|
4804
5153
|
try {
|
|
4805
|
-
await
|
|
5154
|
+
await fs9.access(targetDir);
|
|
4806
5155
|
send(ws, { type: "skills.created", payload: { success: false, error: `Skill "${createPayload.name}" already exists` } });
|
|
4807
5156
|
return;
|
|
4808
5157
|
} catch {
|
|
4809
5158
|
}
|
|
4810
|
-
await
|
|
5159
|
+
await fs9.mkdir(targetDir, { recursive: true });
|
|
4811
5160
|
const lines = createPayload.description.trim().split("\n");
|
|
4812
5161
|
const firstLine = (lines[0] ?? "").trim();
|
|
4813
5162
|
const bodyLines = lines.slice(1).map((l) => l.trim()).filter(Boolean);
|
|
@@ -4855,13 +5204,13 @@ ${trigger}
|
|
|
4855
5204
|
"- `bug-hunter` \u2014 for systematic bug detection patterns",
|
|
4856
5205
|
"- `output-standards` \u2014 for standardized `<nextsteps>` formatting"
|
|
4857
5206
|
].join("\n");
|
|
4858
|
-
await
|
|
5207
|
+
await atomicWrite5(path11.join(targetDir, "SKILL.md"), skillContent);
|
|
4859
5208
|
send(ws, {
|
|
4860
5209
|
type: "skills.created",
|
|
4861
5210
|
payload: {
|
|
4862
5211
|
success: true,
|
|
4863
5212
|
error: null,
|
|
4864
|
-
skill: { name: createPayload.name.trim(), path:
|
|
5213
|
+
skill: { name: createPayload.name.trim(), path: path11.join(targetDir, "SKILL.md"), scope: createPayload.scope }
|
|
4865
5214
|
}
|
|
4866
5215
|
});
|
|
4867
5216
|
} catch (err) {
|
|
@@ -4891,7 +5240,7 @@ async function handleSkillsEdit(ws, ctx, msg) {
|
|
|
4891
5240
|
send(ws, { type: "skills.edited", payload: { success: false, error: `${label} skills cannot be edited` } });
|
|
4892
5241
|
return;
|
|
4893
5242
|
}
|
|
4894
|
-
await
|
|
5243
|
+
await atomicWrite5(entry.path, editPayload.body);
|
|
4895
5244
|
send(ws, { type: "skills.edited", payload: { success: true, error: null } });
|
|
4896
5245
|
} catch (err) {
|
|
4897
5246
|
send(ws, { type: "skills.edited", payload: { success: false, error: errMessage(err) } });
|
|
@@ -4904,29 +5253,35 @@ async function handleSkillsExport(ws, ctx) {
|
|
|
4904
5253
|
}
|
|
4905
5254
|
try {
|
|
4906
5255
|
const entries = await ctx.skillLoader.listEntries();
|
|
4907
|
-
const
|
|
5256
|
+
const zipEntries = [];
|
|
4908
5257
|
for (const entry of entries) {
|
|
4909
5258
|
try {
|
|
4910
5259
|
const body = await ctx.skillLoader.readBody(entry.name);
|
|
4911
5260
|
const safeName = entry.name.replace(/\//g, "_");
|
|
4912
|
-
|
|
5261
|
+
zipEntries.push({ name: `${safeName}/SKILL.md`, data: body });
|
|
4913
5262
|
} catch {
|
|
4914
5263
|
}
|
|
4915
5264
|
}
|
|
4916
|
-
const zipBuffer =
|
|
5265
|
+
const zipBuffer = createZipBuffer(zipEntries);
|
|
4917
5266
|
const zipBase64 = zipBuffer.toString("base64");
|
|
4918
5267
|
send(ws, { type: "skills.exported", payload: { zipBase64, skillCount: entries.length, error: void 0 } });
|
|
4919
5268
|
} catch (err) {
|
|
4920
5269
|
send(ws, { type: "skills.exported", payload: { zipBase64: "", skillCount: 0, error: errMessage(err) } });
|
|
4921
5270
|
}
|
|
4922
5271
|
}
|
|
5272
|
+
|
|
5273
|
+
// src/server/specs-ws-handler.ts
|
|
5274
|
+
import {
|
|
5275
|
+
computeTaskProgress
|
|
5276
|
+
} from "@wrongstack/core";
|
|
5277
|
+
import { SpecStore as SpecStore2, TaskGraphStore as TaskGraphStore2 } from "@wrongstack/sdd";
|
|
4923
5278
|
var SpecsWebSocketHandler = class {
|
|
4924
5279
|
specStore;
|
|
4925
5280
|
graphStore;
|
|
4926
5281
|
clients = /* @__PURE__ */ new Set();
|
|
4927
5282
|
constructor(specsDir, taskGraphsDir) {
|
|
4928
|
-
this.specStore = new
|
|
4929
|
-
this.graphStore = new
|
|
5283
|
+
this.specStore = new SpecStore2({ baseDir: specsDir });
|
|
5284
|
+
this.graphStore = new TaskGraphStore2({ baseDir: taskGraphsDir });
|
|
4930
5285
|
}
|
|
4931
5286
|
addClient(ws) {
|
|
4932
5287
|
const client = { ws, id: crypto.randomUUID() };
|
|
@@ -5130,6 +5485,13 @@ function estimateContextBreakdown(input) {
|
|
|
5130
5485
|
messages: { total: msgTokens, count: input.messages.length, breakdown: messageBreakdown }
|
|
5131
5486
|
};
|
|
5132
5487
|
}
|
|
5488
|
+
|
|
5489
|
+
// src/server/worktree-ws-handler.ts
|
|
5490
|
+
import { join as join8, resolve as resolve6, sep as sep3 } from "node:path";
|
|
5491
|
+
import { WorktreeManager as WorktreeManager3 } from "@wrongstack/core";
|
|
5492
|
+
import { cleanupStaleSddWorktrees as cleanupStaleSddWorktrees2 } from "@wrongstack/sdd";
|
|
5493
|
+
import { toErrorMessage as toErrorMessage2 } from "@wrongstack/core/utils";
|
|
5494
|
+
var MAX_ACTIVITY = 6;
|
|
5133
5495
|
var ACTIVE_STATUSES = /* @__PURE__ */ new Set(["allocating", "active", "committing", "merging"]);
|
|
5134
5496
|
var MANAGED_BRANCH_RE = /^wstack\/ap\/[A-Za-z0-9._/-]+$/;
|
|
5135
5497
|
var WorktreeWebSocketHandler = class {
|
|
@@ -5186,13 +5548,13 @@ var WorktreeWebSocketHandler = class {
|
|
|
5186
5548
|
// ── orphan management ─────────────────────────────────────────────────────
|
|
5187
5549
|
/** Absolute managed-worktrees root for this project. */
|
|
5188
5550
|
worktreesRoot() {
|
|
5189
|
-
return
|
|
5551
|
+
return resolve6(join8(this.management.projectRoot, ".wrongstack", "worktrees"));
|
|
5190
5552
|
}
|
|
5191
5553
|
/** True iff `dir` resolves strictly inside the managed worktrees root. */
|
|
5192
5554
|
underRoot(dir) {
|
|
5193
|
-
const abs =
|
|
5555
|
+
const abs = resolve6(dir);
|
|
5194
5556
|
const root = this.worktreesRoot();
|
|
5195
|
-
return abs !== root && abs.startsWith(root +
|
|
5557
|
+
return abs !== root && abs.startsWith(root + sep3);
|
|
5196
5558
|
}
|
|
5197
5559
|
/** Branches of worktrees a live in-session run currently owns. */
|
|
5198
5560
|
liveActiveBranches() {
|
|
@@ -5213,7 +5575,7 @@ var WorktreeWebSocketHandler = class {
|
|
|
5213
5575
|
return;
|
|
5214
5576
|
}
|
|
5215
5577
|
try {
|
|
5216
|
-
const wt = new
|
|
5578
|
+
const wt = new WorktreeManager3({ projectRoot: this.management.projectRoot });
|
|
5217
5579
|
const { worktrees, branches } = await wt.listManaged();
|
|
5218
5580
|
const live = this.liveActiveBranches();
|
|
5219
5581
|
const orphans = [];
|
|
@@ -5237,7 +5599,7 @@ var WorktreeWebSocketHandler = class {
|
|
|
5237
5599
|
}
|
|
5238
5600
|
});
|
|
5239
5601
|
} catch (err) {
|
|
5240
|
-
this.logger.debug?.(`worktree orphan scan failed: ${
|
|
5602
|
+
this.logger.debug?.(`worktree orphan scan failed: ${toErrorMessage2(err)}`);
|
|
5241
5603
|
this.broadcast({ type: "worktree.orphans", payload: { orphans: [], canClean: false } });
|
|
5242
5604
|
}
|
|
5243
5605
|
}
|
|
@@ -5261,7 +5623,7 @@ var WorktreeWebSocketHandler = class {
|
|
|
5261
5623
|
});
|
|
5262
5624
|
return;
|
|
5263
5625
|
}
|
|
5264
|
-
const res = await
|
|
5626
|
+
const res = await cleanupStaleSddWorktrees2({
|
|
5265
5627
|
projectRoot: this.management.projectRoot,
|
|
5266
5628
|
boardsDir: this.management.boardsDir
|
|
5267
5629
|
});
|
|
@@ -5300,7 +5662,7 @@ var WorktreeWebSocketHandler = class {
|
|
|
5300
5662
|
}
|
|
5301
5663
|
let removed = false;
|
|
5302
5664
|
if (dir) {
|
|
5303
|
-
const wt = new
|
|
5665
|
+
const wt = new WorktreeManager3({ projectRoot: this.management.projectRoot });
|
|
5304
5666
|
({ removed } = await wt.removeOne(dir, branch));
|
|
5305
5667
|
}
|
|
5306
5668
|
for (const [id, h] of [...this.handles]) {
|
|
@@ -5324,7 +5686,7 @@ var WorktreeWebSocketHandler = class {
|
|
|
5324
5686
|
this.broadcast({ type: "worktree.merge_result", payload: { ok: false, branch, reason: "a run is live on this worktree \u2014 stop it first" } });
|
|
5325
5687
|
return;
|
|
5326
5688
|
}
|
|
5327
|
-
const wt = new
|
|
5689
|
+
const wt = new WorktreeManager3({ projectRoot: this.management.projectRoot });
|
|
5328
5690
|
const res = await wt.mergeBranch(branch);
|
|
5329
5691
|
this.broadcast({
|
|
5330
5692
|
type: "worktree.merge_result",
|
|
@@ -5339,8 +5701,8 @@ var WorktreeWebSocketHandler = class {
|
|
|
5339
5701
|
return;
|
|
5340
5702
|
}
|
|
5341
5703
|
const base = baseBranch && MANAGED_BRANCH_RE.test(baseBranch) ? baseBranch : void 0;
|
|
5342
|
-
const wt = new
|
|
5343
|
-
const summary = await wt.diffSummary(
|
|
5704
|
+
const wt = new WorktreeManager3({ projectRoot: this.management.projectRoot });
|
|
5705
|
+
const summary = await wt.diffSummary(resolve6(dir), base);
|
|
5344
5706
|
this.broadcast({ type: "worktree.diff_result", payload: { dir, summary } });
|
|
5345
5707
|
}
|
|
5346
5708
|
// ── internals ───────────────────────────────────────────────────────────
|
|
@@ -5412,7 +5774,7 @@ var WorktreeWebSocketHandler = class {
|
|
|
5412
5774
|
activity(id, kind, text) {
|
|
5413
5775
|
const cur = this.handles.get(id);
|
|
5414
5776
|
if (cur) {
|
|
5415
|
-
const recentActivity = [...cur.recentActivity, { kind, text, at: Date.now() }].slice(-
|
|
5777
|
+
const recentActivity = [...cur.recentActivity, { kind, text, at: Date.now() }].slice(-MAX_ACTIVITY);
|
|
5416
5778
|
this.handles.set(id, { ...cur, recentActivity });
|
|
5417
5779
|
}
|
|
5418
5780
|
this.broadcast({ type: "worktree.event", payload: { kind, handleId: id, text, at: Date.now() } });
|
|
@@ -5444,7 +5806,7 @@ var WorktreeWebSocketHandler = class {
|
|
|
5444
5806
|
try {
|
|
5445
5807
|
if (ws.readyState === 1) ws.send(data);
|
|
5446
5808
|
} catch (err) {
|
|
5447
|
-
this.logger.debug?.(`worktree broadcast failed: ${
|
|
5809
|
+
this.logger.debug?.(`worktree broadcast failed: ${toErrorMessage2(err)}`);
|
|
5448
5810
|
}
|
|
5449
5811
|
}
|
|
5450
5812
|
}
|
|
@@ -5456,6 +5818,12 @@ var WorktreeWebSocketHandler = class {
|
|
|
5456
5818
|
}
|
|
5457
5819
|
};
|
|
5458
5820
|
|
|
5821
|
+
// src/server/server-runtime.ts
|
|
5822
|
+
import * as path13 from "node:path";
|
|
5823
|
+
import { createRequire } from "node:module";
|
|
5824
|
+
import { fileURLToPath } from "node:url";
|
|
5825
|
+
import { WebSocketServer } from "ws";
|
|
5826
|
+
|
|
5459
5827
|
// src/server/lifecycle.ts
|
|
5460
5828
|
function createShutdown(res) {
|
|
5461
5829
|
const log = res.log ?? ((m) => console.log(m));
|
|
@@ -5491,9 +5859,14 @@ function registerShutdownHandlers(res) {
|
|
|
5491
5859
|
process.off("SIGTERM", shutdown);
|
|
5492
5860
|
};
|
|
5493
5861
|
}
|
|
5862
|
+
|
|
5863
|
+
// src/server/setup-events.ts
|
|
5864
|
+
import * as fs10 from "node:fs/promises";
|
|
5865
|
+
import { watch as fsWatch } from "node:fs";
|
|
5866
|
+
import * as path12 from "node:path";
|
|
5494
5867
|
function statusProjectHashFromWatchFilename(projectsDir, filename) {
|
|
5495
5868
|
const raw = String(filename);
|
|
5496
|
-
const relative4 =
|
|
5869
|
+
const relative4 = path12.isAbsolute(raw) ? path12.relative(projectsDir, raw) : raw;
|
|
5497
5870
|
const parts = relative4.split(/[\\/]+/).filter(Boolean);
|
|
5498
5871
|
if (parts.length < 2) return null;
|
|
5499
5872
|
if (parts[parts.length - 1] !== "status.json") return null;
|
|
@@ -5506,6 +5879,10 @@ function shouldLogWatcherStats() {
|
|
|
5506
5879
|
function setupEvents(deps2) {
|
|
5507
5880
|
const { events, broadcast: broadcast2, clients, config, context, pendingConfirms, globalConfigPath, sessionBridge, wpaths, watcherMetrics, onFleetBroadcaster } = deps2;
|
|
5508
5881
|
const disposers = [];
|
|
5882
|
+
let disposed = false;
|
|
5883
|
+
const on = (event, listener) => {
|
|
5884
|
+
disposers.push(events.on(event, listener));
|
|
5885
|
+
};
|
|
5509
5886
|
const currentSessionId = () => context.session?.id ?? "";
|
|
5510
5887
|
const sessionPayload2 = (payload) => {
|
|
5511
5888
|
const provided = payload["sessionId"];
|
|
@@ -5521,20 +5898,20 @@ function setupEvents(deps2) {
|
|
|
5521
5898
|
sessionBridge?.append(event).catch(() => {
|
|
5522
5899
|
});
|
|
5523
5900
|
};
|
|
5524
|
-
|
|
5901
|
+
on("iteration.started", (e) => {
|
|
5525
5902
|
const maxIt = typeof context.meta["maxIterations"] === "number" ? context.meta["maxIterations"] : config.tools?.maxIterations ?? 100;
|
|
5526
5903
|
broadcast2(clients, {
|
|
5527
5904
|
type: "iteration.started",
|
|
5528
5905
|
payload: sessionPayload2({ sessionId: e.sessionId, index: e.index, maxIterations: maxIt })
|
|
5529
5906
|
});
|
|
5530
5907
|
});
|
|
5531
|
-
|
|
5908
|
+
on("iteration.completed", (e) => {
|
|
5532
5909
|
broadcast2(clients, {
|
|
5533
5910
|
type: "iteration.completed",
|
|
5534
5911
|
payload: sessionPayload2({ sessionId: e.sessionId, index: e.index, totalIterations: e.index + 1 })
|
|
5535
5912
|
});
|
|
5536
5913
|
});
|
|
5537
|
-
|
|
5914
|
+
on("iteration.limit_reached", (e) => {
|
|
5538
5915
|
broadcast2(clients, {
|
|
5539
5916
|
type: "iteration.limit_reached",
|
|
5540
5917
|
payload: sessionPayload2({
|
|
@@ -5544,19 +5921,19 @@ function setupEvents(deps2) {
|
|
|
5544
5921
|
})
|
|
5545
5922
|
});
|
|
5546
5923
|
});
|
|
5547
|
-
|
|
5924
|
+
on("provider.text_delta", (e) => {
|
|
5548
5925
|
broadcast2(clients, { type: "provider.text_delta", payload: sessionPayload2({ sessionId: e.sessionId, text: e.text, messageId: "current" }) });
|
|
5549
5926
|
});
|
|
5550
|
-
|
|
5927
|
+
on("provider.thinking_delta", (e) => {
|
|
5551
5928
|
broadcast2(clients, { type: "provider.thinking_delta", payload: sessionPayload2({ sessionId: e.sessionId, text: e.text }) });
|
|
5552
5929
|
});
|
|
5553
|
-
|
|
5930
|
+
on("provider.stream_error", (e) => {
|
|
5554
5931
|
broadcast2(clients, {
|
|
5555
5932
|
type: "provider.stream_error",
|
|
5556
5933
|
payload: sessionPayload2({ sessionId: e.sessionId, eventType: e.eventType, message: e.msg })
|
|
5557
5934
|
});
|
|
5558
5935
|
});
|
|
5559
|
-
|
|
5936
|
+
on("tool.started", (e) => {
|
|
5560
5937
|
broadcast2(clients, {
|
|
5561
5938
|
type: "tool.started",
|
|
5562
5939
|
payload: sessionPayload2({ sessionId: e.sessionId, id: e.id, name: e.name, input: e.input, messageId: `tool_${e.id}` })
|
|
@@ -5569,7 +5946,7 @@ function setupEvents(deps2) {
|
|
|
5569
5946
|
input: e.input
|
|
5570
5947
|
});
|
|
5571
5948
|
});
|
|
5572
|
-
|
|
5949
|
+
on("tool.progress", (e) => {
|
|
5573
5950
|
broadcast2(clients, {
|
|
5574
5951
|
type: "tool.progress",
|
|
5575
5952
|
// Nested `event` shape — the client handler reads `payload.event?.text`
|
|
@@ -5590,7 +5967,7 @@ function setupEvents(deps2) {
|
|
|
5590
5967
|
}
|
|
5591
5968
|
});
|
|
5592
5969
|
});
|
|
5593
|
-
|
|
5970
|
+
on("tool.executed", (e) => {
|
|
5594
5971
|
broadcast2(clients, {
|
|
5595
5972
|
type: "tool.executed",
|
|
5596
5973
|
payload: sessionPayload2({ sessionId: e.sessionId, id: e.id, name: e.name, durationMs: e.durationMs, ok: e.ok, input: e.input, output: e.output })
|
|
@@ -5630,7 +6007,7 @@ function setupEvents(deps2) {
|
|
|
5630
6007
|
try {
|
|
5631
6008
|
const taskPath = context.meta["task.path"];
|
|
5632
6009
|
if (typeof taskPath === "string" && taskPath) {
|
|
5633
|
-
const { loadTasks } = await import(
|
|
6010
|
+
const { loadTasks } = await import("@wrongstack/core");
|
|
5634
6011
|
const file = await loadTasks(taskPath);
|
|
5635
6012
|
broadcast2(clients, { type: "tasks.updated", payload: sessionPayload2({ sessionId: e.sessionId, tasks: file?.tasks ?? [] }) });
|
|
5636
6013
|
}
|
|
@@ -5639,7 +6016,7 @@ function setupEvents(deps2) {
|
|
|
5639
6016
|
try {
|
|
5640
6017
|
const planPath = context.meta["plan.path"];
|
|
5641
6018
|
if (typeof planPath === "string" && planPath) {
|
|
5642
|
-
const { loadPlan } = await import(
|
|
6019
|
+
const { loadPlan } = await import("@wrongstack/core");
|
|
5643
6020
|
const plan = await loadPlan(planPath);
|
|
5644
6021
|
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: [] } }) });
|
|
5645
6022
|
}
|
|
@@ -5648,7 +6025,7 @@ function setupEvents(deps2) {
|
|
|
5648
6025
|
})();
|
|
5649
6026
|
}
|
|
5650
6027
|
});
|
|
5651
|
-
|
|
6028
|
+
on("tool.loop_detected", (e) => {
|
|
5652
6029
|
broadcast2(clients, {
|
|
5653
6030
|
type: "tool.loop_detected",
|
|
5654
6031
|
payload: sessionPayload2({
|
|
@@ -5660,19 +6037,19 @@ function setupEvents(deps2) {
|
|
|
5660
6037
|
})
|
|
5661
6038
|
});
|
|
5662
6039
|
});
|
|
5663
|
-
|
|
6040
|
+
on("trust.persisted", (e) => {
|
|
5664
6041
|
broadcast2(clients, {
|
|
5665
6042
|
type: "trust.persisted",
|
|
5666
6043
|
payload: sessionPayload2({ sessionId: e.sessionId, tool: e.tool, pattern: e.pattern, decision: e.decision })
|
|
5667
6044
|
});
|
|
5668
6045
|
});
|
|
5669
|
-
|
|
6046
|
+
on("delegate.started", (e) => {
|
|
5670
6047
|
broadcast2(clients, {
|
|
5671
6048
|
type: "delegate.started",
|
|
5672
6049
|
payload: sessionPayload2({ sessionId: e.sessionId, target: e.target, task: e.task })
|
|
5673
6050
|
});
|
|
5674
6051
|
});
|
|
5675
|
-
|
|
6052
|
+
on("delegate.completed", (e) => {
|
|
5676
6053
|
broadcast2(clients, {
|
|
5677
6054
|
type: "delegate.completed",
|
|
5678
6055
|
payload: sessionPayload2({
|
|
@@ -5690,7 +6067,7 @@ function setupEvents(deps2) {
|
|
|
5690
6067
|
})
|
|
5691
6068
|
});
|
|
5692
6069
|
});
|
|
5693
|
-
|
|
6070
|
+
on("provider.response", (e) => {
|
|
5694
6071
|
broadcast2(clients, {
|
|
5695
6072
|
type: "provider.response",
|
|
5696
6073
|
payload: sessionPayload2({
|
|
@@ -5702,7 +6079,7 @@ function setupEvents(deps2) {
|
|
|
5702
6079
|
})
|
|
5703
6080
|
});
|
|
5704
6081
|
});
|
|
5705
|
-
|
|
6082
|
+
on("ctx.pct", (e) => {
|
|
5706
6083
|
broadcast2(clients, {
|
|
5707
6084
|
type: "ctx.pct",
|
|
5708
6085
|
payload: sessionPayload2({ sessionId: e.sessionId, load: e.load, tokens: e.tokens, maxContext: e.maxContext })
|
|
@@ -5719,28 +6096,28 @@ function setupEvents(deps2) {
|
|
|
5719
6096
|
})
|
|
5720
6097
|
});
|
|
5721
6098
|
});
|
|
5722
|
-
|
|
6099
|
+
on("ctx.max_context", (e) => {
|
|
5723
6100
|
broadcast2(clients, {
|
|
5724
6101
|
type: "ctx.max_context",
|
|
5725
6102
|
payload: sessionPayload2({ sessionId: e.sessionId, providerId: e.providerId, modelId: e.modelId, maxContext: e.maxContext })
|
|
5726
6103
|
});
|
|
5727
6104
|
});
|
|
5728
|
-
|
|
6105
|
+
on("token.threshold", (e) => {
|
|
5729
6106
|
broadcast2(clients, {
|
|
5730
6107
|
type: "token.threshold",
|
|
5731
6108
|
payload: sessionPayload2({ sessionId: e.sessionId, used: e.used, limit: e.limit })
|
|
5732
6109
|
});
|
|
5733
6110
|
});
|
|
5734
|
-
|
|
6111
|
+
on("token.cost_estimate_unavailable", (e) => {
|
|
5735
6112
|
broadcast2(clients, {
|
|
5736
6113
|
type: "token.cost_estimate_unavailable",
|
|
5737
6114
|
payload: sessionPayload2({ sessionId: e.sessionId, model: e.model })
|
|
5738
6115
|
});
|
|
5739
6116
|
});
|
|
5740
|
-
|
|
6117
|
+
on("context.repaired", (e) => {
|
|
5741
6118
|
broadcast2(clients, { type: "context.repaired", payload: sessionPayload2({ sessionId: e.sessionId, removedToolUses: e.removedToolUses, removedToolResults: e.removedToolResults, removedMessages: e.removedMessages }) });
|
|
5742
6119
|
});
|
|
5743
|
-
|
|
6120
|
+
on("tool.confirm_needed", (e) => {
|
|
5744
6121
|
const id = e.toolUseId ?? `confirm_${Date.now()}`;
|
|
5745
6122
|
const payload = sessionPayload2({ sessionId: e.sessionId, id, toolName: e.tool?.name ?? "unknown", input: e.input, suggestedPattern: e.suggestedPattern, decisionSource: e.decisionSource, riskTier: e.riskTier });
|
|
5746
6123
|
pendingConfirms.set(id, {
|
|
@@ -5751,7 +6128,7 @@ function setupEvents(deps2) {
|
|
|
5751
6128
|
});
|
|
5752
6129
|
broadcast2(clients, { type: "tool.confirm_needed", payload });
|
|
5753
6130
|
});
|
|
5754
|
-
|
|
6131
|
+
on("error", (e) => {
|
|
5755
6132
|
broadcast2(clients, { type: "error", payload: sessionPayload2({ sessionId: e.sessionId, phase: e.phase, message: e.err instanceof Error ? e.err.message : String(e.err) }) });
|
|
5756
6133
|
appendForCurrentSession(e.sessionId, {
|
|
5757
6134
|
type: "error",
|
|
@@ -5760,13 +6137,13 @@ function setupEvents(deps2) {
|
|
|
5760
6137
|
phase: e.phase
|
|
5761
6138
|
});
|
|
5762
6139
|
});
|
|
5763
|
-
|
|
6140
|
+
on("session.damaged", (e) => {
|
|
5764
6141
|
broadcast2(clients, {
|
|
5765
6142
|
type: "session.damaged",
|
|
5766
6143
|
payload: { sessionId: e.sessionId, detail: e.detail }
|
|
5767
6144
|
});
|
|
5768
6145
|
});
|
|
5769
|
-
|
|
6146
|
+
on("session.rewound", (e) => {
|
|
5770
6147
|
broadcast2(clients, {
|
|
5771
6148
|
type: "session.rewound",
|
|
5772
6149
|
payload: sessionPayload2({
|
|
@@ -5777,7 +6154,7 @@ function setupEvents(deps2) {
|
|
|
5777
6154
|
})
|
|
5778
6155
|
});
|
|
5779
6156
|
});
|
|
5780
|
-
|
|
6157
|
+
on("checkpoint.written", (e) => {
|
|
5781
6158
|
broadcast2(clients, {
|
|
5782
6159
|
type: "checkpoint.written",
|
|
5783
6160
|
payload: sessionPayload2({
|
|
@@ -5789,19 +6166,19 @@ function setupEvents(deps2) {
|
|
|
5789
6166
|
})
|
|
5790
6167
|
});
|
|
5791
6168
|
});
|
|
5792
|
-
|
|
6169
|
+
on("in_flight.started", (e) => {
|
|
5793
6170
|
broadcast2(clients, {
|
|
5794
6171
|
type: "in_flight.started",
|
|
5795
6172
|
payload: sessionPayload2({ sessionId: e.sessionId, context: e.context, ts: e.ts })
|
|
5796
6173
|
});
|
|
5797
6174
|
});
|
|
5798
|
-
|
|
6175
|
+
on("in_flight.ended", (e) => {
|
|
5799
6176
|
broadcast2(clients, {
|
|
5800
6177
|
type: "in_flight.ended",
|
|
5801
6178
|
payload: sessionPayload2({ sessionId: e.sessionId, reason: e.reason, ts: e.ts })
|
|
5802
6179
|
});
|
|
5803
6180
|
});
|
|
5804
|
-
|
|
6181
|
+
on("provider.retry", (e) => {
|
|
5805
6182
|
broadcast2(clients, {
|
|
5806
6183
|
type: "provider.retry",
|
|
5807
6184
|
payload: sessionPayload2({
|
|
@@ -5823,7 +6200,7 @@ function setupEvents(deps2) {
|
|
|
5823
6200
|
description: e.description
|
|
5824
6201
|
});
|
|
5825
6202
|
});
|
|
5826
|
-
|
|
6203
|
+
on("provider.error", (e) => {
|
|
5827
6204
|
broadcast2(clients, {
|
|
5828
6205
|
type: "provider.error",
|
|
5829
6206
|
payload: sessionPayload2({
|
|
@@ -5843,7 +6220,7 @@ function setupEvents(deps2) {
|
|
|
5843
6220
|
retryable: e.retryable
|
|
5844
6221
|
});
|
|
5845
6222
|
});
|
|
5846
|
-
|
|
6223
|
+
on("provider.fallback", (e) => {
|
|
5847
6224
|
broadcast2(clients, {
|
|
5848
6225
|
type: "provider.fallback",
|
|
5849
6226
|
payload: sessionPayload2({
|
|
@@ -5855,7 +6232,7 @@ function setupEvents(deps2) {
|
|
|
5855
6232
|
})
|
|
5856
6233
|
});
|
|
5857
6234
|
});
|
|
5858
|
-
|
|
6235
|
+
on("compaction.fired", (e) => {
|
|
5859
6236
|
broadcast2(clients, {
|
|
5860
6237
|
type: "context.compacted",
|
|
5861
6238
|
payload: sessionPayload2({
|
|
@@ -5867,7 +6244,7 @@ function setupEvents(deps2) {
|
|
|
5867
6244
|
})
|
|
5868
6245
|
});
|
|
5869
6246
|
});
|
|
5870
|
-
|
|
6247
|
+
on("compaction.failed", (e) => {
|
|
5871
6248
|
broadcast2(clients, {
|
|
5872
6249
|
type: "compaction.failed",
|
|
5873
6250
|
payload: sessionPayload2({
|
|
@@ -5882,25 +6259,25 @@ function setupEvents(deps2) {
|
|
|
5882
6259
|
})
|
|
5883
6260
|
});
|
|
5884
6261
|
});
|
|
5885
|
-
|
|
6262
|
+
on("mcp.server.connected", (e) => {
|
|
5886
6263
|
broadcast2(clients, {
|
|
5887
6264
|
type: "mcp.server.connected",
|
|
5888
6265
|
payload: { name: e.name, toolCount: e.toolCount }
|
|
5889
6266
|
});
|
|
5890
6267
|
});
|
|
5891
|
-
|
|
6268
|
+
on("mcp.server.reconnected", (e) => {
|
|
5892
6269
|
broadcast2(clients, {
|
|
5893
6270
|
type: "mcp.server.reconnected",
|
|
5894
6271
|
payload: { name: e.name, toolCount: e.toolCount }
|
|
5895
6272
|
});
|
|
5896
6273
|
});
|
|
5897
|
-
|
|
6274
|
+
on("mcp.server.disconnected", (e) => {
|
|
5898
6275
|
broadcast2(clients, {
|
|
5899
6276
|
type: "mcp.server.disconnected",
|
|
5900
6277
|
payload: { name: e.name, reason: e.reason }
|
|
5901
6278
|
});
|
|
5902
6279
|
});
|
|
5903
|
-
|
|
6280
|
+
on("coordinator.stats", (e) => {
|
|
5904
6281
|
broadcast2(clients, {
|
|
5905
6282
|
type: "coordinator.stats",
|
|
5906
6283
|
payload: sessionPayload2({
|
|
@@ -5928,15 +6305,16 @@ function setupEvents(deps2) {
|
|
|
5928
6305
|
broadcast2(clients, { type: "mailbox.agent_registered", payload });
|
|
5929
6306
|
});
|
|
5930
6307
|
const forwardSubagent = (kind, payload) => broadcast2(clients, { type: "subagent.event", payload: sessionPayload2({ kind, ...payload }) });
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
6308
|
+
on("subagent.spawned", (e) => forwardSubagent("spawned", { sessionId: e.sessionId, subagentId: e.subagentId, taskId: e.taskId, name: e.name, provider: e.provider, model: e.model, description: e.description }));
|
|
6309
|
+
on("subagent.task_started", (e) => forwardSubagent("task_started", { sessionId: e.sessionId, subagentId: e.subagentId, taskId: e.taskId, description: e.description }));
|
|
6310
|
+
on("subagent.tool_executed", (e) => forwardSubagent("tool_executed", { sessionId: e.sessionId, subagentId: e.subagentId, toolName: e.name, durationMs: e.durationMs, ok: e.ok }));
|
|
6311
|
+
on("subagent.iteration_summary", (e) => forwardSubagent("iteration_summary", { sessionId: e.sessionId, subagentId: e.subagentId, iteration: e.iteration, toolCalls: e.toolCalls, costUsd: e.costUsd, currentTool: e.currentTool, partialText: e.partialText }));
|
|
6312
|
+
on("subagent.budget_warning", (e) => forwardSubagent("budget_warning", { sessionId: e.sessionId, subagentId: e.subagentId, budgetKind: e.kind, used: e.used, limit: e.limit }));
|
|
6313
|
+
on("subagent.budget_extended", (e) => forwardSubagent("budget_extended", { sessionId: e.sessionId, subagentId: e.subagentId, budgetKind: e.kind, newLimit: e.newLimit, totalExtensions: e.totalExtensions }));
|
|
6314
|
+
on("subagent.ctx_pct", (e) => forwardSubagent("ctx_pct", { sessionId: e.sessionId, subagentId: e.subagentId, load: e.load, tokens: e.tokens, maxContext: e.maxContext }));
|
|
6315
|
+
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 }));
|
|
6316
|
+
on("subagent.removed", (e) => forwardSubagent("removed", { sessionId: e.sessionId, subagentId: e.subagentId, reason: e.reason }));
|
|
6317
|
+
on("agent.timeline.message", (e) => {
|
|
5940
6318
|
broadcast2(clients, {
|
|
5941
6319
|
type: "agent.timeline.message",
|
|
5942
6320
|
payload: sessionPayload2({
|
|
@@ -5952,7 +6330,7 @@ function setupEvents(deps2) {
|
|
|
5952
6330
|
})
|
|
5953
6331
|
});
|
|
5954
6332
|
});
|
|
5955
|
-
|
|
6333
|
+
on("agent.status_changed", (e) => {
|
|
5956
6334
|
broadcast2(clients, {
|
|
5957
6335
|
type: "agent.status_changed",
|
|
5958
6336
|
payload: sessionPayload2({
|
|
@@ -5967,7 +6345,7 @@ function setupEvents(deps2) {
|
|
|
5967
6345
|
});
|
|
5968
6346
|
});
|
|
5969
6347
|
let leaderSpawned = false;
|
|
5970
|
-
|
|
6348
|
+
on("iteration.started", (e) => {
|
|
5971
6349
|
if (!leaderSpawned) {
|
|
5972
6350
|
leaderSpawned = true;
|
|
5973
6351
|
const provider = context.provider?.id ?? "unknown";
|
|
@@ -5981,7 +6359,7 @@ function setupEvents(deps2) {
|
|
|
5981
6359
|
});
|
|
5982
6360
|
}
|
|
5983
6361
|
});
|
|
5984
|
-
|
|
6362
|
+
on("tool.executed", (e) => {
|
|
5985
6363
|
forwardSubagent("tool_executed", {
|
|
5986
6364
|
sessionId: e.sessionId,
|
|
5987
6365
|
subagentId: "leader",
|
|
@@ -5990,7 +6368,7 @@ function setupEvents(deps2) {
|
|
|
5990
6368
|
ok: e.ok
|
|
5991
6369
|
});
|
|
5992
6370
|
});
|
|
5993
|
-
|
|
6371
|
+
on("provider.response", (e) => {
|
|
5994
6372
|
if (e.usage?.input != null) {
|
|
5995
6373
|
const maxCtx = context.provider.capabilities.maxContext;
|
|
5996
6374
|
const rawLoad = maxCtx > 0 ? e.usage.input / maxCtx : 0;
|
|
@@ -6007,7 +6385,7 @@ function setupEvents(deps2) {
|
|
|
6007
6385
|
});
|
|
6008
6386
|
}
|
|
6009
6387
|
});
|
|
6010
|
-
|
|
6388
|
+
on("iteration.completed", (e) => {
|
|
6011
6389
|
if (!leaderSpawned) {
|
|
6012
6390
|
leaderSpawned = true;
|
|
6013
6391
|
const provider = context.provider?.id ?? "unknown";
|
|
@@ -6033,14 +6411,20 @@ function setupEvents(deps2) {
|
|
|
6033
6411
|
payload: sessionPayload2({ event: eventName, ...payload })
|
|
6034
6412
|
});
|
|
6035
6413
|
});
|
|
6036
|
-
events.
|
|
6414
|
+
events.onPattern("memory.*", (eventName, payload) => {
|
|
6415
|
+
broadcast2(clients, {
|
|
6416
|
+
type: "memory.event",
|
|
6417
|
+
payload: sessionPayload2({ event: eventName, ...payload })
|
|
6418
|
+
});
|
|
6419
|
+
});
|
|
6420
|
+
on("client.status", async (e) => {
|
|
6037
6421
|
broadcast2(clients, { type: "client.status_update", payload: e });
|
|
6038
6422
|
if (wpaths?.projectStatus) {
|
|
6039
6423
|
try {
|
|
6040
6424
|
const statusFile = wpaths.projectStatus(e.projectHash);
|
|
6041
|
-
const dir =
|
|
6042
|
-
await
|
|
6043
|
-
await
|
|
6425
|
+
const dir = path12.dirname(statusFile);
|
|
6426
|
+
await fs10.mkdir(dir, { recursive: true });
|
|
6427
|
+
await fs10.writeFile(statusFile, JSON.stringify(e, null, 2), "utf-8");
|
|
6044
6428
|
} catch (err) {
|
|
6045
6429
|
console.error(
|
|
6046
6430
|
JSON.stringify({
|
|
@@ -6054,7 +6438,7 @@ function setupEvents(deps2) {
|
|
|
6054
6438
|
}
|
|
6055
6439
|
});
|
|
6056
6440
|
if (wpaths?.projectStatus && wpaths.configDir) {
|
|
6057
|
-
const projectsDir =
|
|
6441
|
+
const projectsDir = path12.join(wpaths.configDir, "projects");
|
|
6058
6442
|
const knownProjectHashes = /* @__PURE__ */ new Set();
|
|
6059
6443
|
const debounceTimers = /* @__PURE__ */ new Map();
|
|
6060
6444
|
const DEBOUNCE_MS = 150;
|
|
@@ -6118,8 +6502,9 @@ function setupEvents(deps2) {
|
|
|
6118
6502
|
let watcher;
|
|
6119
6503
|
const startWatcher = async () => {
|
|
6120
6504
|
try {
|
|
6121
|
-
await
|
|
6122
|
-
|
|
6505
|
+
await fs10.mkdir(projectsDir, { recursive: true });
|
|
6506
|
+
if (disposed) return;
|
|
6507
|
+
watcher = fsWatch(projectsDir, { persistent: true, recursive: true }, async (eventType, filename) => {
|
|
6123
6508
|
if (eventType !== "change" && eventType !== "rename") return;
|
|
6124
6509
|
if (filename == null) return;
|
|
6125
6510
|
const projectHash = statusProjectHashFromWatchFilename(projectsDir, filename);
|
|
@@ -6128,8 +6513,8 @@ function setupEvents(deps2) {
|
|
|
6128
6513
|
if (!knownProjectHashes.has(projectHash)) return;
|
|
6129
6514
|
if (watcherMetrics) watcherMetrics.filesProcessed++;
|
|
6130
6515
|
try {
|
|
6131
|
-
const targetFile =
|
|
6132
|
-
const content = await
|
|
6516
|
+
const targetFile = path12.join(projectsDir, projectHash, "status.json");
|
|
6517
|
+
const content = await fs10.readFile(targetFile, "utf-8");
|
|
6133
6518
|
const statusData = JSON.parse(content);
|
|
6134
6519
|
scheduleBroadcast(projectHash, statusData);
|
|
6135
6520
|
} catch {
|
|
@@ -6149,7 +6534,7 @@ function setupEvents(deps2) {
|
|
|
6149
6534
|
);
|
|
6150
6535
|
}
|
|
6151
6536
|
};
|
|
6152
|
-
|
|
6537
|
+
on("client.status", (e) => {
|
|
6153
6538
|
if (e.projectHash) {
|
|
6154
6539
|
const hash = String(e.projectHash);
|
|
6155
6540
|
if (!knownProjectHashes.has(hash)) {
|
|
@@ -6181,15 +6566,17 @@ function setupEvents(deps2) {
|
|
|
6181
6566
|
}
|
|
6182
6567
|
});
|
|
6183
6568
|
}
|
|
6184
|
-
const globalRoot = globalConfigPath ?
|
|
6569
|
+
const globalRoot = globalConfigPath ? path12.dirname(globalConfigPath) : void 0;
|
|
6185
6570
|
if (globalRoot) {
|
|
6186
6571
|
const broadcastSessions = async () => {
|
|
6187
6572
|
try {
|
|
6188
|
-
const { SessionRegistry } = await import(
|
|
6573
|
+
const { SessionRegistry } = await import("@wrongstack/core");
|
|
6189
6574
|
const registry = new SessionRegistry(globalRoot);
|
|
6190
6575
|
const sessions = await registry.list();
|
|
6191
6576
|
const mySlug = sessions.find((s) => s.pid === process.pid)?.projectSlug;
|
|
6192
|
-
const live = sessions.filter(
|
|
6577
|
+
const live = sessions.filter(
|
|
6578
|
+
(s) => s.status === "active" || s.status === "idle"
|
|
6579
|
+
).filter((s) => mySlug ? s.projectSlug === mySlug : true).map((s) => ({
|
|
6193
6580
|
sessionId: s.sessionId,
|
|
6194
6581
|
projectName: s.projectName,
|
|
6195
6582
|
projectSlug: s.projectSlug,
|
|
@@ -6228,7 +6615,7 @@ function setupEvents(deps2) {
|
|
|
6228
6615
|
disposers.push(() => clearInterval(statusInterval));
|
|
6229
6616
|
let regDebounce;
|
|
6230
6617
|
try {
|
|
6231
|
-
const regWatcher =
|
|
6618
|
+
const regWatcher = fsWatch(globalRoot, { persistent: false }, (_event, filename) => {
|
|
6232
6619
|
const name2 = filename ? String(filename) : "";
|
|
6233
6620
|
if (!name2.startsWith("session-registry.json") || name2.endsWith(".lock")) return;
|
|
6234
6621
|
if (regDebounce) clearTimeout(regDebounce);
|
|
@@ -6243,6 +6630,8 @@ function setupEvents(deps2) {
|
|
|
6243
6630
|
void broadcastSessions();
|
|
6244
6631
|
}
|
|
6245
6632
|
return () => {
|
|
6633
|
+
if (disposed) return;
|
|
6634
|
+
disposed = true;
|
|
6246
6635
|
for (const dispose of disposers) {
|
|
6247
6636
|
try {
|
|
6248
6637
|
dispose();
|
|
@@ -6264,6 +6653,9 @@ function getCostRates(model) {
|
|
|
6264
6653
|
function computeUsageCost(usage, rates) {
|
|
6265
6654
|
return (usage.input * rates.input + usage.output * rates.output + (usage.cacheRead ?? 0) * rates.cacheRead) / 1e6;
|
|
6266
6655
|
}
|
|
6656
|
+
|
|
6657
|
+
// src/server/model-catalog.ts
|
|
6658
|
+
import { capabilitiesFor } from "@wrongstack/providers";
|
|
6267
6659
|
var SIBLING_CATALOG = {
|
|
6268
6660
|
"anthropic-oauth": "anthropic",
|
|
6269
6661
|
"openai-codex": "openai",
|
|
@@ -6370,6 +6762,9 @@ function readConfiguredBool(providerConfig, key) {
|
|
|
6370
6762
|
function positiveNumber(value) {
|
|
6371
6763
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
|
|
6372
6764
|
}
|
|
6765
|
+
|
|
6766
|
+
// src/server/server-runtime.ts
|
|
6767
|
+
import { toErrorMessage as toErrorMessage3 } from "@wrongstack/core/utils";
|
|
6373
6768
|
async function resolvePorts(opts) {
|
|
6374
6769
|
const requestedWsPort = opts.wsPort ?? 3457;
|
|
6375
6770
|
const wsHost = opts.wsHost ?? process.env["WEBUI_HOST"] ?? process.env["WS_HOST"] ?? "127.0.0.1";
|
|
@@ -6430,7 +6825,7 @@ function createSessionStartPayload(g) {
|
|
|
6430
6825
|
inputCost,
|
|
6431
6826
|
outputCost,
|
|
6432
6827
|
cacheReadCost,
|
|
6433
|
-
projectName:
|
|
6828
|
+
projectName: path13.basename(projectRoot) || projectRoot,
|
|
6434
6829
|
projectRoot,
|
|
6435
6830
|
cwd: g.getWorkingDir(),
|
|
6436
6831
|
mode: g.getModeId(),
|
|
@@ -6463,18 +6858,31 @@ function createWsServers(ports, accessToken) {
|
|
|
6463
6858
|
allowedHostnames: publicHostnames,
|
|
6464
6859
|
allowBrowserUrlToken: Boolean(ports.publicWsUrl)
|
|
6465
6860
|
});
|
|
6466
|
-
const WS_MAX_PAYLOAD =
|
|
6861
|
+
const WS_MAX_PAYLOAD = 20 * 1024 * 1024;
|
|
6467
6862
|
const wssPrimary = new WebSocketServer({
|
|
6468
6863
|
port: ports.wsPort,
|
|
6469
6864
|
host: ports.wsHost,
|
|
6470
6865
|
verifyClient: verifyClient2,
|
|
6471
|
-
maxPayload: WS_MAX_PAYLOAD
|
|
6866
|
+
maxPayload: WS_MAX_PAYLOAD,
|
|
6867
|
+
// Send a ping every 15s to keep idle connections alive. Without this,
|
|
6868
|
+
// network equipment (routers, proxies, NAT gateways) may drop idle TCP
|
|
6869
|
+
// connections, causing the browser to see a close event and show the
|
|
6870
|
+
// "reconnecting" banner. The browser responds with a pong automatically
|
|
6871
|
+
// (the WebSocket API handles this at the transport level).
|
|
6872
|
+
pingInterval: 15e3,
|
|
6873
|
+
// Wait 5s for a pong before considering the connection dead. A client
|
|
6874
|
+
// that hasn't responded to 3 consecutive pings in a row (45s total
|
|
6875
|
+
// quiet) is almost certainly unreachable — close the socket so the
|
|
6876
|
+
// client can reconnect cleanly rather than hanging indefinitely.
|
|
6877
|
+
pingTimeout: 5e3
|
|
6472
6878
|
});
|
|
6473
6879
|
const wssSecondary = ports.wsHost === "127.0.0.1" ? new WebSocketServer({
|
|
6474
6880
|
port: ports.wsPort,
|
|
6475
6881
|
host: "::1",
|
|
6476
6882
|
verifyClient: verifyClient2,
|
|
6477
|
-
maxPayload: WS_MAX_PAYLOAD
|
|
6883
|
+
maxPayload: WS_MAX_PAYLOAD,
|
|
6884
|
+
pingInterval: 15e3,
|
|
6885
|
+
pingTimeout: 5e3
|
|
6478
6886
|
}) : null;
|
|
6479
6887
|
const clients = /* @__PURE__ */ new Map();
|
|
6480
6888
|
console.log(
|
|
@@ -6496,7 +6904,7 @@ function armEvents(wssPrimary, wssSecondary, wsHost, wsPort, setupInput, watcher
|
|
|
6496
6904
|
};
|
|
6497
6905
|
wssPrimary.on("listening", () => arm(`${wsHost}:${wsPort}`));
|
|
6498
6906
|
wssPrimary.on("error", (err) => {
|
|
6499
|
-
console.error(JSON.stringify({ level: "error", event: "webui.ws_server_error", host: wsHost, message:
|
|
6907
|
+
console.error(JSON.stringify({ level: "error", event: "webui.ws_server_error", host: wsHost, message: toErrorMessage3(err), timestamp: (/* @__PURE__ */ new Date()).toISOString() }));
|
|
6500
6908
|
});
|
|
6501
6909
|
if (wssSecondary) {
|
|
6502
6910
|
wssSecondary.on("listening", () => arm(`::1:${wsPort}`));
|
|
@@ -6515,13 +6923,13 @@ function armEvents(wssPrimary, wssSecondary, wsHost, wsPort, setupInput, watcher
|
|
|
6515
6923
|
};
|
|
6516
6924
|
}
|
|
6517
6925
|
function resolveWebuiDistDir(fromUrl, explicitDistDir) {
|
|
6518
|
-
if (explicitDistDir) return
|
|
6926
|
+
if (explicitDistDir) return path13.resolve(explicitDistDir);
|
|
6519
6927
|
try {
|
|
6520
6928
|
const requireFromHere2 = createRequire(fromUrl);
|
|
6521
6929
|
const serverEntry = requireFromHere2.resolve("@wrongstack/webui/server");
|
|
6522
|
-
return
|
|
6930
|
+
return path13.resolve(path13.dirname(serverEntry), "..");
|
|
6523
6931
|
} catch {
|
|
6524
|
-
return
|
|
6932
|
+
return path13.resolve(path13.dirname(fileURLToPath(fromUrl)), "..", "..", "dist");
|
|
6525
6933
|
}
|
|
6526
6934
|
}
|
|
6527
6935
|
function startHttpServer(opts) {
|
|
@@ -6536,13 +6944,13 @@ function startHttpServer(opts) {
|
|
|
6536
6944
|
watcherMetrics: opts.watcherMetrics,
|
|
6537
6945
|
onFleetPing: opts.onFleetPing
|
|
6538
6946
|
});
|
|
6539
|
-
const registryBaseDir =
|
|
6947
|
+
const registryBaseDir = path13.dirname(opts.globalConfigPath);
|
|
6540
6948
|
httpServer.listen(opts.httpPort, opts.wsHost, () => {
|
|
6541
6949
|
const openUrl = buildWebUIAccessUrl({ host: opts.wsHost, port: opts.httpPort, token: opts.wsToken, publicUrl: opts.publicUrl });
|
|
6542
6950
|
console.log(`[WebUI] HTTP server running on ${openUrl}`);
|
|
6543
6951
|
if (opts.openBrowser) openBrowser(openUrl);
|
|
6544
6952
|
void registerInstance(
|
|
6545
|
-
{ pid: process.pid, httpPort: opts.httpPort, wsPort: opts.wsPort, host: opts.wsHost, projectRoot: opts.projectRoot, projectName:
|
|
6953
|
+
{ pid: process.pid, httpPort: opts.httpPort, wsPort: opts.wsPort, host: opts.wsHost, projectRoot: opts.projectRoot, projectName: path13.basename(opts.projectRoot) || opts.projectRoot, startedAt: (/* @__PURE__ */ new Date()).toISOString(), url: buildWebUIAccessUrl({ host: opts.wsHost, port: opts.httpPort, publicUrl: opts.publicUrl }) },
|
|
6546
6954
|
registryBaseDir
|
|
6547
6955
|
).catch((err) => console.warn(JSON.stringify({ level: "warn", event: "webui.instance_record_failed", message: errMessage(err), timestamp: (/* @__PURE__ */ new Date()).toISOString() })));
|
|
6548
6956
|
});
|
|
@@ -6556,6 +6964,65 @@ function registerShutdown(deps2) {
|
|
|
6556
6964
|
onShutdown: deps2.onShutdown
|
|
6557
6965
|
});
|
|
6558
6966
|
}
|
|
6967
|
+
|
|
6968
|
+
// src/server/pre-context-services.ts
|
|
6969
|
+
import * as path16 from "node:path";
|
|
6970
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
6971
|
+
import { DefaultTokenCounter } from "@wrongstack/core/infrastructure";
|
|
6972
|
+
import {
|
|
6973
|
+
AnnotationsStore,
|
|
6974
|
+
DEFAULT_SESSION_PRUNE_DAYS,
|
|
6975
|
+
DefaultModelsRegistry,
|
|
6976
|
+
DefaultModeStore,
|
|
6977
|
+
DefaultPromptLoader,
|
|
6978
|
+
DefaultSessionReader,
|
|
6979
|
+
DefaultSessionStore,
|
|
6980
|
+
DefaultSkillLoader,
|
|
6981
|
+
DefaultSystemPromptBuilder,
|
|
6982
|
+
EventBus,
|
|
6983
|
+
GlobalMailbox,
|
|
6984
|
+
PromptUsageStore,
|
|
6985
|
+
ProviderRegistry,
|
|
6986
|
+
SkillInstaller,
|
|
6987
|
+
TOKENS,
|
|
6988
|
+
ToolRegistry,
|
|
6989
|
+
Context,
|
|
6990
|
+
applyToolDescriptionModes,
|
|
6991
|
+
applyToolResultRenderModes,
|
|
6992
|
+
configureChildEnvGitIdentity,
|
|
6993
|
+
getSessionRegistry as getSessionRegistry2,
|
|
6994
|
+
resolveContextWindowPolicy,
|
|
6995
|
+
makeMailboxTool,
|
|
6996
|
+
makeMailInboxTool,
|
|
6997
|
+
makeFleetStatusTool,
|
|
6998
|
+
makeMailSendTool
|
|
6999
|
+
} from "@wrongstack/core";
|
|
7000
|
+
import {
|
|
7001
|
+
createSuperMemoryTools
|
|
7002
|
+
} from "@wrongstack/super-memory";
|
|
7003
|
+
import {
|
|
7004
|
+
MCPAuthorizationManager,
|
|
7005
|
+
MCPRegistry,
|
|
7006
|
+
MCPVaultTokenStore,
|
|
7007
|
+
createVaultBackedMcpAuthorizationProviderFactory
|
|
7008
|
+
} from "@wrongstack/mcp";
|
|
7009
|
+
import { buildProviderFactoriesFromRegistry } from "@wrongstack/providers";
|
|
7010
|
+
import { createDefaultContainer } from "@wrongstack/runtime";
|
|
7011
|
+
import {
|
|
7012
|
+
builtinToolsPack,
|
|
7013
|
+
configureDangerBypass,
|
|
7014
|
+
configureExecPolicy,
|
|
7015
|
+
forgetTool,
|
|
7016
|
+
relatedMemoryTool,
|
|
7017
|
+
rememberTool,
|
|
7018
|
+
searchMemoryTool
|
|
7019
|
+
} from "@wrongstack/tools";
|
|
7020
|
+
import { sessionScopedPath, toErrorMessage as toErrorMessage5 } from "@wrongstack/core/utils";
|
|
7021
|
+
|
|
7022
|
+
// src/server/setup-screen.ts
|
|
7023
|
+
import { expectDefined as expectDefined2 } from "@wrongstack/core";
|
|
7024
|
+
import { toErrorMessage as toErrorMessage4 } from "@wrongstack/core/utils";
|
|
7025
|
+
import { makeProviderFromConfig } from "@wrongstack/providers";
|
|
6559
7026
|
var UNCONFIGURED_CAPABILITIES = {
|
|
6560
7027
|
tools: false,
|
|
6561
7028
|
parallelTools: false,
|
|
@@ -6595,7 +7062,7 @@ function logCreateFailure(event, err) {
|
|
|
6595
7062
|
JSON.stringify({
|
|
6596
7063
|
level: "error",
|
|
6597
7064
|
event,
|
|
6598
|
-
message:
|
|
7065
|
+
message: toErrorMessage4(err),
|
|
6599
7066
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
6600
7067
|
})
|
|
6601
7068
|
);
|
|
@@ -6620,7 +7087,7 @@ function resolveSetupProvider(opts) {
|
|
|
6620
7087
|
const savedProviders = config.providers ?? {};
|
|
6621
7088
|
const firstKey = Object.keys(savedProviders)[0];
|
|
6622
7089
|
if (firstKey) {
|
|
6623
|
-
const firstProvider =
|
|
7090
|
+
const firstProvider = expectDefined2(savedProviders[firstKey]);
|
|
6624
7091
|
try {
|
|
6625
7092
|
const provider2 = makeProviderFromConfig(firstKey, {
|
|
6626
7093
|
...firstProvider,
|
|
@@ -6688,6 +7155,20 @@ function seedContextMeta(config, context) {
|
|
|
6688
7155
|
meta["reasoningPreserve"] = mr.reasoning?.preserve === true;
|
|
6689
7156
|
meta["cacheTtl"] = mr.cache?.ttl ?? "default";
|
|
6690
7157
|
}
|
|
7158
|
+
meta["refinerProvider"] = autonomyCfg["refinerProvider"] ?? "";
|
|
7159
|
+
meta["refinerModel"] = autonomyCfg["refinerModel"] ?? "";
|
|
7160
|
+
meta["thinkingWord"] = autonomyCfg["thinkingWord"] ?? "thinking";
|
|
7161
|
+
meta["statuslineMode"] = autonomyCfg["statuslineMode"] ?? "detailed";
|
|
7162
|
+
meta["animationStyle"] = autonomyCfg["animationStyle"] ?? "rainbow";
|
|
7163
|
+
meta["breakerEnabled"] = config.circuitBreaker?.enabled === true;
|
|
7164
|
+
meta["breakerAutoKillResetMs"] = config.circuitBreaker?.autoKillResetMs ?? 6e4;
|
|
7165
|
+
{
|
|
7166
|
+
const featuresAllow = config.features?.allowOutsideProjectRoot;
|
|
7167
|
+
const toolsRestrict = config.tools?.restrictToProjectRoot;
|
|
7168
|
+
const allow = featuresAllow !== void 0 ? featuresAllow : toolsRestrict !== void 0 ? !toolsRestrict : true;
|
|
7169
|
+
meta["fsAccess"] = allow ? "unrestricted" : "project";
|
|
7170
|
+
}
|
|
7171
|
+
meta["debugStream"] = config.debugStream === true;
|
|
6691
7172
|
const hqConfig = config.hq;
|
|
6692
7173
|
meta["hqEnabled"] = hqConfig?.enabled === true;
|
|
6693
7174
|
meta["hqUrl"] = hqConfig?.url ?? "";
|
|
@@ -6700,6 +7181,11 @@ function seedContextMeta(config, context) {
|
|
|
6700
7181
|
const tgMs = tgExt?.["longToolThresholdMs"];
|
|
6701
7182
|
meta["tgLongToolMs"] = typeof tgMs === "number" ? tgMs : 3e4;
|
|
6702
7183
|
}
|
|
7184
|
+
|
|
7185
|
+
// src/server/model-auto-discovery.ts
|
|
7186
|
+
import * as fs11 from "node:fs/promises";
|
|
7187
|
+
import * as path14 from "node:path";
|
|
7188
|
+
import { COMPATIBLE_PRESETS, discoverOpenAICompatibleModels } from "@wrongstack/providers";
|
|
6703
7189
|
function isOverlayRegistry(value) {
|
|
6704
7190
|
return !!value && typeof value === "object" && typeof value.mergeOverlay === "function";
|
|
6705
7191
|
}
|
|
@@ -6724,7 +7210,7 @@ function eligibleProviders(config) {
|
|
|
6724
7210
|
}
|
|
6725
7211
|
async function readCache(file) {
|
|
6726
7212
|
try {
|
|
6727
|
-
return JSON.parse(await
|
|
7213
|
+
return JSON.parse(await fs11.readFile(file, "utf8"));
|
|
6728
7214
|
} catch {
|
|
6729
7215
|
return {};
|
|
6730
7216
|
}
|
|
@@ -6734,7 +7220,7 @@ async function discoverAndMergeWebuiProviders(opts) {
|
|
|
6734
7220
|
if (!isOverlayRegistry(registry)) return;
|
|
6735
7221
|
const targets = eligibleProviders(opts.config);
|
|
6736
7222
|
if (targets.length === 0) return;
|
|
6737
|
-
const cacheFile =
|
|
7223
|
+
const cacheFile = path14.join(opts.cacheDir, "discovered-models-cache.json");
|
|
6738
7224
|
const cache = await readCache(cacheFile);
|
|
6739
7225
|
let cacheDirty = false;
|
|
6740
7226
|
await Promise.all(
|
|
@@ -6771,18 +7257,196 @@ async function discoverAndMergeWebuiProviders(opts) {
|
|
|
6771
7257
|
);
|
|
6772
7258
|
if (cacheDirty) {
|
|
6773
7259
|
try {
|
|
6774
|
-
await
|
|
6775
|
-
await
|
|
7260
|
+
await fs11.mkdir(path14.dirname(cacheFile), { recursive: true });
|
|
7261
|
+
await fs11.writeFile(cacheFile, JSON.stringify(cache), "utf8");
|
|
6776
7262
|
} catch {
|
|
6777
7263
|
opts.logger?.debug?.("provider auto-discovery cache write failed");
|
|
6778
7264
|
}
|
|
6779
7265
|
}
|
|
6780
7266
|
}
|
|
6781
7267
|
|
|
7268
|
+
// src/server/standalone-session-identity.ts
|
|
7269
|
+
import * as path15 from "node:path";
|
|
7270
|
+
import {
|
|
7271
|
+
AgentStatusTracker,
|
|
7272
|
+
FleetNotifier,
|
|
7273
|
+
getSessionRegistry,
|
|
7274
|
+
RecoveryLock
|
|
7275
|
+
} from "@wrongstack/core";
|
|
7276
|
+
import { WebSocket as WebSocket2 } from "ws";
|
|
7277
|
+
async function createStandaloneSessionIdentityLifecycle(opts) {
|
|
7278
|
+
const { paths, events, logger } = opts;
|
|
7279
|
+
const registry = opts.sessionRegistry ?? getSessionRegistry(paths.globalRoot);
|
|
7280
|
+
const fleetNotifier = new FleetNotifier({
|
|
7281
|
+
baseDir: paths.globalRoot,
|
|
7282
|
+
projectRoot: paths.projectRoot,
|
|
7283
|
+
selfPid: process.pid
|
|
7284
|
+
});
|
|
7285
|
+
let activeSessionId = opts.initialSessionId;
|
|
7286
|
+
let stopped = false;
|
|
7287
|
+
let transition = Promise.resolve();
|
|
7288
|
+
const statusTracker = new AgentStatusTracker({
|
|
7289
|
+
events,
|
|
7290
|
+
registry,
|
|
7291
|
+
sessionId: () => activeSessionId,
|
|
7292
|
+
onUpdate: () => fleetNotifier.notify()
|
|
7293
|
+
});
|
|
7294
|
+
const register = async (sessionId) => {
|
|
7295
|
+
try {
|
|
7296
|
+
await registry.register({
|
|
7297
|
+
sessionId,
|
|
7298
|
+
projectSlug: paths.projectSlug,
|
|
7299
|
+
projectRoot: paths.projectRoot,
|
|
7300
|
+
projectName: path15.basename(paths.projectRoot),
|
|
7301
|
+
workingDir: opts.workingDir,
|
|
7302
|
+
clientType: "webui",
|
|
7303
|
+
pid: process.pid,
|
|
7304
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7305
|
+
agents: statusTracker.getAgents()
|
|
7306
|
+
});
|
|
7307
|
+
fleetNotifier.notify();
|
|
7308
|
+
} catch (err) {
|
|
7309
|
+
logger.debug?.(`WebUI session registry update failed: ${errorMessage2(err)}`);
|
|
7310
|
+
}
|
|
7311
|
+
};
|
|
7312
|
+
await register(activeSessionId);
|
|
7313
|
+
statusTracker.start();
|
|
7314
|
+
const recoveryLock = opts.manageRecoveryLock === false ? void 0 : new RecoveryLock({ dir: paths.projectSessions, sessionStore: opts.sessionStore });
|
|
7315
|
+
let ownsRecoveryLock = false;
|
|
7316
|
+
if (recoveryLock) {
|
|
7317
|
+
try {
|
|
7318
|
+
await recoveryLock.write(activeSessionId);
|
|
7319
|
+
ownsRecoveryLock = true;
|
|
7320
|
+
} catch {
|
|
7321
|
+
}
|
|
7322
|
+
}
|
|
7323
|
+
let stopHqBridges = () => {
|
|
7324
|
+
};
|
|
7325
|
+
let closeHqPublisher = () => {
|
|
7326
|
+
};
|
|
7327
|
+
let restartHqBridges = (_sessionId) => {
|
|
7328
|
+
};
|
|
7329
|
+
if (opts.enableHqTelemetry !== false) {
|
|
7330
|
+
try {
|
|
7331
|
+
const core = await import("@wrongstack/core");
|
|
7332
|
+
const publisher = core.createHqPublisherFromEnv({
|
|
7333
|
+
clientKind: "webui",
|
|
7334
|
+
projectRoot: paths.projectRoot,
|
|
7335
|
+
projectName: path15.basename(paths.projectRoot),
|
|
7336
|
+
appConfig: opts.config,
|
|
7337
|
+
socketFactory: (url) => new WebSocket2(url)
|
|
7338
|
+
});
|
|
7339
|
+
if (publisher) {
|
|
7340
|
+
publisher.connect();
|
|
7341
|
+
closeHqPublisher = () => publisher.close();
|
|
7342
|
+
restartHqBridges = (sessionId) => {
|
|
7343
|
+
stopHqBridges();
|
|
7344
|
+
const stops = [];
|
|
7345
|
+
const add = (start) => {
|
|
7346
|
+
try {
|
|
7347
|
+
stops.push(start());
|
|
7348
|
+
} catch {
|
|
7349
|
+
}
|
|
7350
|
+
};
|
|
7351
|
+
add(
|
|
7352
|
+
() => core.startSessionTelemetryBridge({
|
|
7353
|
+
publisher,
|
|
7354
|
+
events,
|
|
7355
|
+
sessionId,
|
|
7356
|
+
projectRoot: paths.projectRoot,
|
|
7357
|
+
projectName: path15.basename(paths.projectRoot),
|
|
7358
|
+
globalRoot: paths.globalRoot,
|
|
7359
|
+
initialAgents: statusTracker.getAgents(),
|
|
7360
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7361
|
+
})
|
|
7362
|
+
);
|
|
7363
|
+
add(
|
|
7364
|
+
() => core.startFleetTelemetryBridge({
|
|
7365
|
+
events,
|
|
7366
|
+
publisher,
|
|
7367
|
+
runId: sessionId,
|
|
7368
|
+
sessionId
|
|
7369
|
+
})
|
|
7370
|
+
);
|
|
7371
|
+
add(() => core.startBrainTelemetryBridge({ events, publisher, sessionId }));
|
|
7372
|
+
add(() => core.startWorktreeTelemetryBridge({ events, publisher, sessionId }));
|
|
7373
|
+
add(
|
|
7374
|
+
() => core.startToolTelemetryBridge({
|
|
7375
|
+
events,
|
|
7376
|
+
publisher,
|
|
7377
|
+
projectRoot: paths.projectRoot,
|
|
7378
|
+
sessionId
|
|
7379
|
+
})
|
|
7380
|
+
);
|
|
7381
|
+
add(() => core.startCostTelemetryBridge({ events, publisher, sessionId }));
|
|
7382
|
+
stopHqBridges = () => {
|
|
7383
|
+
for (const stop2 of stops.splice(0)) {
|
|
7384
|
+
try {
|
|
7385
|
+
stop2();
|
|
7386
|
+
} catch {
|
|
7387
|
+
}
|
|
7388
|
+
}
|
|
7389
|
+
};
|
|
7390
|
+
};
|
|
7391
|
+
restartHqBridges(activeSessionId);
|
|
7392
|
+
}
|
|
7393
|
+
} catch (err) {
|
|
7394
|
+
logger.debug?.(`WebUI HQ telemetry unavailable: ${errorMessage2(err)}`);
|
|
7395
|
+
}
|
|
7396
|
+
}
|
|
7397
|
+
const repointRecovery = async (sessionId) => {
|
|
7398
|
+
if (!recoveryLock) return;
|
|
7399
|
+
try {
|
|
7400
|
+
if (ownsRecoveryLock) await recoveryLock.clear();
|
|
7401
|
+
await recoveryLock.write(sessionId);
|
|
7402
|
+
ownsRecoveryLock = true;
|
|
7403
|
+
} catch {
|
|
7404
|
+
ownsRecoveryLock = false;
|
|
7405
|
+
}
|
|
7406
|
+
};
|
|
7407
|
+
const activate = async (sessionId) => {
|
|
7408
|
+
if (stopped || sessionId === activeSessionId) return;
|
|
7409
|
+
transition = transition.then(async () => {
|
|
7410
|
+
if (stopped || sessionId === activeSessionId) return;
|
|
7411
|
+
activeSessionId = sessionId;
|
|
7412
|
+
await register(sessionId);
|
|
7413
|
+
await repointRecovery(sessionId);
|
|
7414
|
+
try {
|
|
7415
|
+
restartHqBridges(sessionId);
|
|
7416
|
+
} catch (err) {
|
|
7417
|
+
logger.debug?.(`WebUI HQ session swap failed: ${errorMessage2(err)}`);
|
|
7418
|
+
}
|
|
7419
|
+
});
|
|
7420
|
+
await transition;
|
|
7421
|
+
};
|
|
7422
|
+
const stop = async () => {
|
|
7423
|
+
if (stopped) return;
|
|
7424
|
+
stopped = true;
|
|
7425
|
+
await transition.catch(() => void 0);
|
|
7426
|
+
statusTracker.stop();
|
|
7427
|
+
stopHqBridges();
|
|
7428
|
+
closeHqPublisher();
|
|
7429
|
+
fleetNotifier.dispose();
|
|
7430
|
+
try {
|
|
7431
|
+
await registry.markClosing();
|
|
7432
|
+
await registry.unregister();
|
|
7433
|
+
} catch {
|
|
7434
|
+
}
|
|
7435
|
+
if (recoveryLock && ownsRecoveryLock) {
|
|
7436
|
+
await recoveryLock.clear().catch(() => void 0);
|
|
7437
|
+
ownsRecoveryLock = false;
|
|
7438
|
+
}
|
|
7439
|
+
};
|
|
7440
|
+
return { statusTracker, activate, stop };
|
|
7441
|
+
}
|
|
7442
|
+
function errorMessage2(err) {
|
|
7443
|
+
return err instanceof Error ? err.message : String(err);
|
|
7444
|
+
}
|
|
7445
|
+
|
|
6782
7446
|
// src/server/pre-context-services.ts
|
|
6783
7447
|
var GITHUB_PROVIDERS_OVERLAY_URL = "https://raw.githubusercontent.com/WrongStack/WrongStack/main/packages/cli/data/providers.json";
|
|
6784
7448
|
async function createPreContextServices(input) {
|
|
6785
|
-
const { config, wpaths, logger, opts, projectRoot, workingDir, needsProvider } = input;
|
|
7449
|
+
const { config, wpaths, logger, opts, vault, projectRoot, workingDir, needsProvider } = input;
|
|
6786
7450
|
const modelsRegistry = opts.services?.modelsRegistry ?? new DefaultModelsRegistry({
|
|
6787
7451
|
cacheFile: wpaths.modelsCache,
|
|
6788
7452
|
ttlSeconds: 0,
|
|
@@ -6794,20 +7458,22 @@ async function createPreContextServices(input) {
|
|
|
6794
7458
|
await modelsRegistry.refresh();
|
|
6795
7459
|
logger.info("models.dev catalog refreshed");
|
|
6796
7460
|
} catch (err) {
|
|
6797
|
-
logger.warn(`models.dev refresh failed (${
|
|
7461
|
+
logger.warn(`models.dev refresh failed (${toErrorMessage5(err)}); using cached catalog`);
|
|
6798
7462
|
}
|
|
6799
7463
|
}
|
|
6800
7464
|
try {
|
|
6801
7465
|
await discoverAndMergeWebuiProviders({
|
|
6802
7466
|
config,
|
|
6803
7467
|
registry: modelsRegistry,
|
|
6804
|
-
cacheDir:
|
|
7468
|
+
cacheDir: path16.dirname(wpaths.modelsCache),
|
|
6805
7469
|
logger
|
|
6806
7470
|
});
|
|
6807
7471
|
} catch (err) {
|
|
6808
|
-
logger.debug(`provider auto-discovery skipped: ${
|
|
7472
|
+
logger.debug(`provider auto-discovery skipped: ${toErrorMessage5(err)}`);
|
|
6809
7473
|
}
|
|
6810
|
-
const
|
|
7474
|
+
const events = opts.services?.events ?? new EventBus();
|
|
7475
|
+
events.setLogger(logger);
|
|
7476
|
+
const container = createDefaultContainer({ config, wpaths, logger, modelsRegistry, events });
|
|
6811
7477
|
const configStore = opts.services?.configStore ?? container.resolve(TOKENS.ConfigStore);
|
|
6812
7478
|
const providerRegistry = new ProviderRegistry();
|
|
6813
7479
|
try {
|
|
@@ -6815,22 +7481,23 @@ async function createPreContextServices(input) {
|
|
|
6815
7481
|
for (const f of factories) providerRegistry.register(f);
|
|
6816
7482
|
console.log("[WebUI] Provider registry loaded:", providerRegistry.list().length, "providers");
|
|
6817
7483
|
} catch (err) {
|
|
6818
|
-
console.warn(JSON.stringify({ level: "warn", event: "webui.provider_registry_load_failed", message:
|
|
7484
|
+
console.warn(JSON.stringify({ level: "warn", event: "webui.provider_registry_load_failed", message: toErrorMessage5(err), timestamp: (/* @__PURE__ */ new Date()).toISOString() }));
|
|
6819
7485
|
}
|
|
6820
7486
|
const toolRegistry = opts.services?.toolRegistry ?? (() => {
|
|
6821
7487
|
const r = new ToolRegistry();
|
|
6822
7488
|
r.registerAllOrThrow([...builtinToolsPack.tools ?? []], builtinToolsPack.name);
|
|
6823
7489
|
return r;
|
|
6824
7490
|
})();
|
|
6825
|
-
const memoryStore =
|
|
7491
|
+
const memoryStore = container.resolve(TOKENS.MemoryStore);
|
|
6826
7492
|
if (config.features.memory) {
|
|
6827
7493
|
toolRegistry.register(rememberTool(memoryStore));
|
|
6828
7494
|
toolRegistry.register(forgetTool(memoryStore));
|
|
6829
7495
|
toolRegistry.register(searchMemoryTool(memoryStore));
|
|
6830
7496
|
toolRegistry.register(relatedMemoryTool(memoryStore));
|
|
7497
|
+
if (isSuperMemoryService(memoryStore)) {
|
|
7498
|
+
for (const tool of createSuperMemoryTools(memoryStore)) toolRegistry.register(tool);
|
|
7499
|
+
}
|
|
6831
7500
|
}
|
|
6832
|
-
const events = opts.services?.events ?? new EventBus();
|
|
6833
|
-
events.setLogger(logger);
|
|
6834
7501
|
toolRegistry.register(makeMailboxTool({ projectDir: wpaths.projectDir, events }));
|
|
6835
7502
|
toolRegistry.register(makeMailSendTool({ projectDir: wpaths.projectDir, events }));
|
|
6836
7503
|
toolRegistry.register(makeMailInboxTool({ projectDir: wpaths.projectDir, events }));
|
|
@@ -6839,11 +7506,25 @@ async function createPreContextServices(input) {
|
|
|
6839
7506
|
applyToolResultRenderModes(toolRegistry, config.tools?.resultRenderMode);
|
|
6840
7507
|
configureExecPolicy(config.tools?.exec ?? {});
|
|
6841
7508
|
configureDangerBypass(config.tools?.exec?.danger ?? {});
|
|
7509
|
+
configureChildEnvGitIdentity(config.git?.identity ?? null);
|
|
6842
7510
|
console.log("[WebUI] Tool registry loaded:", toolRegistry.list().length, "tools");
|
|
6843
|
-
const
|
|
7511
|
+
const mcpTokenStore = new MCPVaultTokenStore(
|
|
7512
|
+
path16.join(wpaths.projectDir, "mcp-auth.json"),
|
|
7513
|
+
vault
|
|
7514
|
+
);
|
|
7515
|
+
const mcpAuthorizationManager = new MCPAuthorizationManager({ store: mcpTokenStore });
|
|
7516
|
+
const mcpRegistry = new MCPRegistry({
|
|
7517
|
+
toolRegistry,
|
|
7518
|
+
events,
|
|
7519
|
+
log: logger,
|
|
7520
|
+
cacheDir: wpaths.cacheDir,
|
|
7521
|
+
authorizationProviderFactory: createVaultBackedMcpAuthorizationProviderFactory({
|
|
7522
|
+
store: mcpTokenStore
|
|
7523
|
+
}),
|
|
7524
|
+
authorizationManager: mcpAuthorizationManager
|
|
7525
|
+
});
|
|
6844
7526
|
if (config.features.mcp && config.mcpServers) {
|
|
6845
7527
|
for (const [name2, cfg] of Object.entries(config.mcpServers)) {
|
|
6846
|
-
if (cfg.enabled === false) continue;
|
|
6847
7528
|
void mcpRegistry.start({ ...cfg, name: name2 }).catch((err) => {
|
|
6848
7529
|
logger.warn(`MCP server "${name2}" failed to start at boot`, err);
|
|
6849
7530
|
});
|
|
@@ -6858,7 +7539,7 @@ async function createPreContextServices(input) {
|
|
|
6858
7539
|
// surface via the SessionRegistry.
|
|
6859
7540
|
isSessionInUse: async (sessionId) => {
|
|
6860
7541
|
try {
|
|
6861
|
-
const registry =
|
|
7542
|
+
const registry = getSessionRegistry2(wpaths.globalRoot);
|
|
6862
7543
|
const live = await registry.listByProject(wpaths.projectSlug);
|
|
6863
7544
|
const hit = live.find((e) => e.sessionId === sessionId);
|
|
6864
7545
|
if (hit) {
|
|
@@ -6883,120 +7564,22 @@ async function createPreContextServices(input) {
|
|
|
6883
7564
|
await input.touchProject(projectRoot, workingDir);
|
|
6884
7565
|
} catch {
|
|
6885
7566
|
}
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
7567
|
+
const sessionIdentity = await createStandaloneSessionIdentityLifecycle({
|
|
7568
|
+
config,
|
|
7569
|
+
events,
|
|
7570
|
+
logger,
|
|
7571
|
+
paths: {
|
|
7572
|
+
globalRoot: wpaths.globalRoot,
|
|
6892
7573
|
projectRoot,
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
registry,
|
|
6903
|
-
sessionId: () => session.id,
|
|
6904
|
-
onUpdate: () => fleetNotifier.notify()
|
|
6905
|
-
});
|
|
6906
|
-
statusTracker.start();
|
|
6907
|
-
let stopHqSessionBridge;
|
|
6908
|
-
let hqTelemetryPublisher;
|
|
6909
|
-
const stopHqAuxBridges = [];
|
|
6910
|
-
try {
|
|
6911
|
-
const {
|
|
6912
|
-
createHqPublisherFromEnv,
|
|
6913
|
-
startSessionTelemetryBridge,
|
|
6914
|
-
startFleetTelemetryBridge,
|
|
6915
|
-
startBrainTelemetryBridge,
|
|
6916
|
-
startWorktreeTelemetryBridge,
|
|
6917
|
-
startToolTelemetryBridge,
|
|
6918
|
-
startCostTelemetryBridge
|
|
6919
|
-
} = await import('@wrongstack/core');
|
|
6920
|
-
const hqTelemetry = createHqPublisherFromEnv({
|
|
6921
|
-
clientKind: "webui",
|
|
6922
|
-
projectRoot,
|
|
6923
|
-
projectName: path5.basename(projectRoot),
|
|
6924
|
-
appConfig: config,
|
|
6925
|
-
socketFactory: (url) => new WebSocket(url)
|
|
6926
|
-
});
|
|
6927
|
-
if (hqTelemetry) {
|
|
6928
|
-
hqTelemetry.connect();
|
|
6929
|
-
hqTelemetryPublisher = hqTelemetry;
|
|
6930
|
-
stopHqSessionBridge = startSessionTelemetryBridge({
|
|
6931
|
-
publisher: hqTelemetry,
|
|
6932
|
-
events,
|
|
6933
|
-
sessionId: session.id,
|
|
6934
|
-
projectRoot,
|
|
6935
|
-
projectName: path5.basename(projectRoot),
|
|
6936
|
-
globalRoot: wpaths.globalRoot,
|
|
6937
|
-
initialAgents: statusTracker?.getAgents(),
|
|
6938
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6939
|
-
});
|
|
6940
|
-
try {
|
|
6941
|
-
stopHqAuxBridges.push(
|
|
6942
|
-
startFleetTelemetryBridge({ events, publisher: hqTelemetry, runId: session.id, sessionId: session.id })
|
|
6943
|
-
);
|
|
6944
|
-
} catch {
|
|
6945
|
-
}
|
|
6946
|
-
try {
|
|
6947
|
-
stopHqAuxBridges.push(
|
|
6948
|
-
startBrainTelemetryBridge({ events, publisher: hqTelemetry, sessionId: session.id })
|
|
6949
|
-
);
|
|
6950
|
-
} catch {
|
|
6951
|
-
}
|
|
6952
|
-
try {
|
|
6953
|
-
stopHqAuxBridges.push(
|
|
6954
|
-
startWorktreeTelemetryBridge({ events, publisher: hqTelemetry, sessionId: session.id })
|
|
6955
|
-
);
|
|
6956
|
-
} catch {
|
|
6957
|
-
}
|
|
6958
|
-
try {
|
|
6959
|
-
stopHqAuxBridges.push(
|
|
6960
|
-
startToolTelemetryBridge({ events, publisher: hqTelemetry, projectRoot, sessionId: session.id })
|
|
6961
|
-
);
|
|
6962
|
-
} catch {
|
|
6963
|
-
}
|
|
6964
|
-
try {
|
|
6965
|
-
stopHqAuxBridges.push(
|
|
6966
|
-
startCostTelemetryBridge({ events, publisher: hqTelemetry, sessionId: session.id })
|
|
6967
|
-
);
|
|
6968
|
-
} catch {
|
|
6969
|
-
}
|
|
6970
|
-
}
|
|
6971
|
-
} catch {
|
|
6972
|
-
}
|
|
6973
|
-
const stopTracking = async () => {
|
|
6974
|
-
try {
|
|
6975
|
-
fleetNotifier.dispose();
|
|
6976
|
-
await registry.markClosing();
|
|
6977
|
-
statusTracker?.stop();
|
|
6978
|
-
stopHqSessionBridge?.();
|
|
6979
|
-
for (const stop of stopHqAuxBridges) {
|
|
6980
|
-
try {
|
|
6981
|
-
stop();
|
|
6982
|
-
} catch {
|
|
6983
|
-
}
|
|
6984
|
-
}
|
|
6985
|
-
hqTelemetryPublisher?.close();
|
|
6986
|
-
} catch {
|
|
6987
|
-
}
|
|
6988
|
-
};
|
|
6989
|
-
process.once("beforeExit", () => {
|
|
6990
|
-
void stopTracking();
|
|
6991
|
-
});
|
|
6992
|
-
process.once("SIGINT", () => {
|
|
6993
|
-
void stopTracking();
|
|
6994
|
-
});
|
|
6995
|
-
process.once("SIGTERM", () => {
|
|
6996
|
-
void stopTracking();
|
|
6997
|
-
});
|
|
6998
|
-
} catch {
|
|
6999
|
-
}
|
|
7574
|
+
projectSlug: wpaths.projectSlug,
|
|
7575
|
+
projectSessions: wpaths.projectSessions
|
|
7576
|
+
},
|
|
7577
|
+
workingDir,
|
|
7578
|
+
initialSessionId: session.id,
|
|
7579
|
+
sessionStore,
|
|
7580
|
+
manageRecoveryLock: !opts.services?.session
|
|
7581
|
+
});
|
|
7582
|
+
const statusTracker = sessionIdentity.statusTracker;
|
|
7000
7583
|
let context;
|
|
7001
7584
|
const tokenCounter = new DefaultTokenCounter({
|
|
7002
7585
|
registry: modelsRegistry,
|
|
@@ -7021,7 +7604,7 @@ async function createPreContextServices(input) {
|
|
|
7021
7604
|
const modelCapabilitiesRef = { current: modelCapabilities };
|
|
7022
7605
|
const skillLoader = config.features.skills ? new DefaultSkillLoader({ paths: wpaths }) : void 0;
|
|
7023
7606
|
const skillInstaller = config.features.skills ? new SkillInstaller({
|
|
7024
|
-
manifestPath:
|
|
7607
|
+
manifestPath: path16.join(wpaths.globalRoot, "installed-skills.json"),
|
|
7025
7608
|
projectSkillsDir: wpaths.inProjectSkills,
|
|
7026
7609
|
globalSkillsDir: wpaths.globalSkills,
|
|
7027
7610
|
projectHash: wpaths.projectHash,
|
|
@@ -7030,8 +7613,8 @@ async function createPreContextServices(input) {
|
|
|
7030
7613
|
const promptsEnabled = config.features.prompts !== false;
|
|
7031
7614
|
const bundledPromptsDir = promptsEnabled ? (() => {
|
|
7032
7615
|
try {
|
|
7033
|
-
const req =
|
|
7034
|
-
return
|
|
7616
|
+
const req = createRequire2(import.meta.url);
|
|
7617
|
+
return path16.join(path16.dirname(req.resolve("@wrongstack/core/package.json")), "data", "prompts");
|
|
7035
7618
|
} catch {
|
|
7036
7619
|
return void 0;
|
|
7037
7620
|
}
|
|
@@ -7083,6 +7666,14 @@ async function createPreContextServices(input) {
|
|
|
7083
7666
|
const initialContextPolicy = resolveContextWindowPolicy(config.context);
|
|
7084
7667
|
context.meta["contextWindowMode"] = initialContextPolicy.id;
|
|
7085
7668
|
context.meta["contextWindowPolicy"] = initialContextPolicy;
|
|
7669
|
+
context.state.setMeta(
|
|
7670
|
+
"plan.path",
|
|
7671
|
+
sessionScopedPath(wpaths.projectSessions, session.id, ".plan.json")
|
|
7672
|
+
);
|
|
7673
|
+
context.state.setMeta(
|
|
7674
|
+
"task.path",
|
|
7675
|
+
sessionScopedPath(wpaths.projectSessions, session.id, ".tasks.json")
|
|
7676
|
+
);
|
|
7086
7677
|
seedContextMeta(config, context);
|
|
7087
7678
|
return {
|
|
7088
7679
|
modelsRegistry,
|
|
@@ -7099,6 +7690,7 @@ async function createPreContextServices(input) {
|
|
|
7099
7690
|
session,
|
|
7100
7691
|
sessionStartedAt,
|
|
7101
7692
|
statusTracker,
|
|
7693
|
+
sessionIdentity,
|
|
7102
7694
|
tokenCounter,
|
|
7103
7695
|
modeStore,
|
|
7104
7696
|
modeId,
|
|
@@ -7112,8 +7704,33 @@ async function createPreContextServices(input) {
|
|
|
7112
7704
|
needsSetup
|
|
7113
7705
|
};
|
|
7114
7706
|
}
|
|
7707
|
+
function isSuperMemoryService(memoryStore) {
|
|
7708
|
+
const value = memoryStore;
|
|
7709
|
+
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";
|
|
7710
|
+
}
|
|
7711
|
+
|
|
7712
|
+
// src/server/start-webui.ts
|
|
7713
|
+
import * as path22 from "node:path";
|
|
7714
|
+
import {
|
|
7715
|
+
createDefaultPipelines,
|
|
7716
|
+
createSessionEventBridge,
|
|
7717
|
+
DEFAULT_CONTEXT_WINDOW_MODE_ID as DEFAULT_CONTEXT_WINDOW_MODE_ID2,
|
|
7718
|
+
expectDefined as expectDefined3,
|
|
7719
|
+
resolveSessionLoggingConfig,
|
|
7720
|
+
watchProviderConfig
|
|
7721
|
+
} from "@wrongstack/core";
|
|
7722
|
+
import { toErrorMessage as toErrorMessage10 } from "@wrongstack/core/utils";
|
|
7723
|
+
import { makeProviderFromConfig as makeProviderFromConfig3 } from "@wrongstack/providers";
|
|
7724
|
+
import {
|
|
7725
|
+
ensureSessionShell
|
|
7726
|
+
} from "@wrongstack/tools";
|
|
7727
|
+
|
|
7728
|
+
// src/server/boot.ts
|
|
7729
|
+
import {
|
|
7730
|
+
bootConfig as coreBootConfig
|
|
7731
|
+
} from "@wrongstack/core";
|
|
7115
7732
|
async function bootConfig() {
|
|
7116
|
-
const { config, vault, globalConfigPath, projectRoot, wpaths, logger } = await
|
|
7733
|
+
const { config, vault, globalConfigPath, projectRoot, wpaths, logger } = await coreBootConfig({
|
|
7117
7734
|
appLabel: "WebUI"
|
|
7118
7735
|
});
|
|
7119
7736
|
return { config, vault, globalConfigPath, projectRoot, wpaths, logger };
|
|
@@ -7121,14 +7738,46 @@ async function bootConfig() {
|
|
|
7121
7738
|
function patchConfig(config, updates) {
|
|
7122
7739
|
return Object.freeze({ ...config, ...updates });
|
|
7123
7740
|
}
|
|
7741
|
+
|
|
7742
|
+
// src/server/backend-services.ts
|
|
7743
|
+
import { join as join13 } from "node:path";
|
|
7744
|
+
import {
|
|
7745
|
+
Agent,
|
|
7746
|
+
AutoCompactionMiddleware as AutoCompactionMiddlewareCtor,
|
|
7747
|
+
BrainMonitor,
|
|
7748
|
+
CollaborationBus as CollaborationBusCtor,
|
|
7749
|
+
DEFAULT_TOOLS_CONFIG,
|
|
7750
|
+
createStrategyCompactor,
|
|
7751
|
+
collabInjectMiddleware,
|
|
7752
|
+
collabPauseMiddleware,
|
|
7753
|
+
estimateRequestTokensCalibrated,
|
|
7754
|
+
installDesignStudioMiddleware,
|
|
7755
|
+
ObservableBrainArbiter as ObservableBrainArbiterCtor,
|
|
7756
|
+
resolveContextWindowPolicy as resolveContextWindowPolicy2,
|
|
7757
|
+
BrainDecisionLedger,
|
|
7758
|
+
createBrainRuntime,
|
|
7759
|
+
resolveBrainConfigDefaults,
|
|
7760
|
+
EscalationRoutingBrainArbiter,
|
|
7761
|
+
GlobalMailbox as GlobalMailbox2,
|
|
7762
|
+
mailboxSessionTag,
|
|
7763
|
+
SessionMemoryConsolidator,
|
|
7764
|
+
TOKENS as TOKENS2,
|
|
7765
|
+
ToolExecutor
|
|
7766
|
+
} from "@wrongstack/core";
|
|
7767
|
+
|
|
7768
|
+
// src/server/collaboration-ws-handler.ts
|
|
7769
|
+
import { randomUUID } from "node:crypto";
|
|
7770
|
+
import { toErrorMessage as toErrorMessage6 } from "@wrongstack/core/utils";
|
|
7771
|
+
var REPLAY_LIMIT = 50;
|
|
7124
7772
|
var PAUSE_TIMEOUT_MS = 6e4;
|
|
7125
7773
|
var CollaborationWebSocketHandler = class {
|
|
7126
|
-
constructor(events, logger, reader, annotations, bus) {
|
|
7774
|
+
constructor(events, logger, reader, annotations, bus, options = {}) {
|
|
7127
7775
|
this.events = events;
|
|
7128
7776
|
this.logger = logger;
|
|
7129
7777
|
this.reader = reader;
|
|
7130
7778
|
this.annotations = annotations;
|
|
7131
7779
|
this.bus = bus;
|
|
7780
|
+
this.options = options;
|
|
7132
7781
|
this.subscribe();
|
|
7133
7782
|
this.bus?.onInjectionConsumed((info) => this.broadcastInjectionConsumed(info));
|
|
7134
7783
|
}
|
|
@@ -7137,6 +7786,7 @@ var CollaborationWebSocketHandler = class {
|
|
|
7137
7786
|
reader;
|
|
7138
7787
|
annotations;
|
|
7139
7788
|
bus;
|
|
7789
|
+
options;
|
|
7140
7790
|
clients = /* @__PURE__ */ new Set();
|
|
7141
7791
|
/** sessionId → participants currently watching it. */
|
|
7142
7792
|
bySession = /* @__PURE__ */ new Map();
|
|
@@ -7164,11 +7814,16 @@ var CollaborationWebSocketHandler = class {
|
|
|
7164
7814
|
handleMessage(ws, msg) {
|
|
7165
7815
|
if (msg.type === "collab.join") {
|
|
7166
7816
|
const payload = msg.payload;
|
|
7167
|
-
if (
|
|
7817
|
+
if (typeof payload?.sessionId !== "string" || payload.sessionId.trim().length === 0) {
|
|
7168
7818
|
this.send(ws, this.errorMessage("collab.join requires sessionId"));
|
|
7169
7819
|
return true;
|
|
7170
7820
|
}
|
|
7171
|
-
|
|
7821
|
+
const role = payload.role ?? "observer";
|
|
7822
|
+
if (role !== "observer" && role !== "annotator" && role !== "controller") {
|
|
7823
|
+
this.send(ws, this.errorMessage(`unknown collaboration role '${String(role)}'`));
|
|
7824
|
+
return true;
|
|
7825
|
+
}
|
|
7826
|
+
this.join(ws, payload.sessionId, role);
|
|
7172
7827
|
return true;
|
|
7173
7828
|
}
|
|
7174
7829
|
if (msg.type === "collab.leave") {
|
|
@@ -7203,6 +7858,20 @@ var CollaborationWebSocketHandler = class {
|
|
|
7203
7858
|
}
|
|
7204
7859
|
// ── Join / leave flow ──────────────────────────────────────────────────
|
|
7205
7860
|
join(ws, sessionId, role) {
|
|
7861
|
+
const activeSessionId = this.options.getActiveSessionId?.();
|
|
7862
|
+
if (activeSessionId !== void 0 && sessionId !== activeSessionId) {
|
|
7863
|
+
this.send(
|
|
7864
|
+
ws,
|
|
7865
|
+
this.errorMessage(
|
|
7866
|
+
`collab.join sessionId mismatch (active: ${activeSessionId})`
|
|
7867
|
+
)
|
|
7868
|
+
);
|
|
7869
|
+
return;
|
|
7870
|
+
}
|
|
7871
|
+
if (this.findParticipant(ws)) {
|
|
7872
|
+
this.send(ws, this.errorMessage("collab.join requires leaving the current session first"));
|
|
7873
|
+
return;
|
|
7874
|
+
}
|
|
7206
7875
|
if (role === "controller" && !this.bus) {
|
|
7207
7876
|
this.send(
|
|
7208
7877
|
ws,
|
|
@@ -7221,6 +7890,13 @@ var CollaborationWebSocketHandler = class {
|
|
|
7221
7890
|
);
|
|
7222
7891
|
return;
|
|
7223
7892
|
}
|
|
7893
|
+
if (role !== "observer" && this.options.authorizeRole?.({ ws, sessionId, requestedRole: role }) !== true) {
|
|
7894
|
+
this.send(
|
|
7895
|
+
ws,
|
|
7896
|
+
this.errorMessage(`role '${role}' requires explicit server authorization`)
|
|
7897
|
+
);
|
|
7898
|
+
return;
|
|
7899
|
+
}
|
|
7224
7900
|
const participant = {
|
|
7225
7901
|
participantId: randomUUID(),
|
|
7226
7902
|
ws,
|
|
@@ -7248,7 +7924,7 @@ var CollaborationWebSocketHandler = class {
|
|
|
7248
7924
|
if (this.reader) {
|
|
7249
7925
|
this.replayHistory(ws, sessionId).catch((err) => {
|
|
7250
7926
|
this.logger.debug?.(
|
|
7251
|
-
`collab: replay failed for ${sessionId}: ${
|
|
7927
|
+
`collab: replay failed for ${sessionId}: ${toErrorMessage6(err)}`
|
|
7252
7928
|
);
|
|
7253
7929
|
});
|
|
7254
7930
|
}
|
|
@@ -7366,7 +8042,7 @@ var CollaborationWebSocketHandler = class {
|
|
|
7366
8042
|
this.send(
|
|
7367
8043
|
ws,
|
|
7368
8044
|
this.errorMessage(
|
|
7369
|
-
`annotation rejected: ${
|
|
8045
|
+
`annotation rejected: ${toErrorMessage6(err)}`
|
|
7370
8046
|
)
|
|
7371
8047
|
);
|
|
7372
8048
|
}
|
|
@@ -7433,7 +8109,7 @@ var CollaborationWebSocketHandler = class {
|
|
|
7433
8109
|
this.send(
|
|
7434
8110
|
ws,
|
|
7435
8111
|
this.errorMessage(
|
|
7436
|
-
`resolve failed: ${
|
|
8112
|
+
`resolve failed: ${toErrorMessage6(err)}`
|
|
7437
8113
|
)
|
|
7438
8114
|
);
|
|
7439
8115
|
}
|
|
@@ -7474,17 +8150,13 @@ var CollaborationWebSocketHandler = class {
|
|
|
7474
8150
|
type: "collab.event",
|
|
7475
8151
|
payload: { kind, payload, at: (/* @__PURE__ */ new Date()).toISOString() }
|
|
7476
8152
|
};
|
|
7477
|
-
const
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
|
|
7483
|
-
|
|
7484
|
-
`collab broadcast failed: ${toErrorMessage(err)}`
|
|
7485
|
-
);
|
|
7486
|
-
}
|
|
7487
|
-
}
|
|
8153
|
+
const activeSessionId = this.options.getActiveSessionId?.();
|
|
8154
|
+
if (activeSessionId !== void 0) {
|
|
8155
|
+
this.broadcast(activeSessionId, msg);
|
|
8156
|
+
return;
|
|
8157
|
+
}
|
|
8158
|
+
for (const sessionId of this.bySession.keys()) {
|
|
8159
|
+
this.broadcast(sessionId, msg);
|
|
7488
8160
|
}
|
|
7489
8161
|
}
|
|
7490
8162
|
/**
|
|
@@ -7508,11 +8180,11 @@ var CollaborationWebSocketHandler = class {
|
|
|
7508
8180
|
}
|
|
7509
8181
|
} catch (err) {
|
|
7510
8182
|
this.logger.debug?.(
|
|
7511
|
-
`collab: session reader rejected ${sessionId}: ${
|
|
8183
|
+
`collab: session reader rejected ${sessionId}: ${toErrorMessage6(err)}`
|
|
7512
8184
|
);
|
|
7513
8185
|
return;
|
|
7514
8186
|
}
|
|
7515
|
-
const tail2 = all.slice(-
|
|
8187
|
+
const tail2 = all.slice(-REPLAY_LIMIT);
|
|
7516
8188
|
if (tail2.length === 0) return;
|
|
7517
8189
|
for (const raw of tail2) {
|
|
7518
8190
|
const ev = raw;
|
|
@@ -7589,7 +8261,7 @@ var CollaborationWebSocketHandler = class {
|
|
|
7589
8261
|
if (p.ws.readyState === 1) p.ws.send(data);
|
|
7590
8262
|
} catch (err) {
|
|
7591
8263
|
this.logger.debug?.(
|
|
7592
|
-
`collab broadcast failed: ${
|
|
8264
|
+
`collab broadcast failed: ${toErrorMessage6(err)}`
|
|
7593
8265
|
);
|
|
7594
8266
|
}
|
|
7595
8267
|
}
|
|
@@ -7808,7 +8480,8 @@ var CollaborationWebSocketHandler = class {
|
|
|
7808
8480
|
* Bus callback: a queued injection was spliced into a real tool call. Re-emit
|
|
7809
8481
|
* `collab.injection.granted` with phase `'consumed'` and the now-known tool
|
|
7810
8482
|
* name. The injection carries no sessionId, so resolve it from the author's
|
|
7811
|
-
* current
|
|
8483
|
+
* current participant. If the author has left, fail closed rather than leak
|
|
8484
|
+
* the payload to unrelated sessions.
|
|
7812
8485
|
*/
|
|
7813
8486
|
broadcastInjectionConsumed(info) {
|
|
7814
8487
|
let sessionId = null;
|
|
@@ -7837,10 +8510,23 @@ var CollaborationWebSocketHandler = class {
|
|
|
7837
8510
|
if (sessionId) {
|
|
7838
8511
|
this.broadcast(sessionId, message(sessionId));
|
|
7839
8512
|
} else {
|
|
7840
|
-
|
|
8513
|
+
this.logger.debug?.(
|
|
8514
|
+
`collab: consumed injection ${info.toolUseId} has no live author; broadcast suppressed`
|
|
8515
|
+
);
|
|
7841
8516
|
}
|
|
7842
8517
|
}
|
|
7843
8518
|
};
|
|
8519
|
+
|
|
8520
|
+
// src/server/codebase-indexing.ts
|
|
8521
|
+
import * as fs12 from "node:fs";
|
|
8522
|
+
import * as path17 from "node:path";
|
|
8523
|
+
import {
|
|
8524
|
+
cancelPendingReindexes,
|
|
8525
|
+
enqueueReindex,
|
|
8526
|
+
isIndexableFile,
|
|
8527
|
+
runStartupIndex,
|
|
8528
|
+
shutdownCodebaseIndexHost
|
|
8529
|
+
} from "@wrongstack/tools";
|
|
7844
8530
|
var IGNORE_DIRS = /* @__PURE__ */ new Set([
|
|
7845
8531
|
"node_modules",
|
|
7846
8532
|
".git",
|
|
@@ -7886,7 +8572,7 @@ function setupWebUICodebaseIndexing(deps2) {
|
|
|
7886
8572
|
if (!filename) return;
|
|
7887
8573
|
const rel = filename.toString();
|
|
7888
8574
|
if (isIgnored(rel)) return;
|
|
7889
|
-
const abs =
|
|
8575
|
+
const abs = path17.resolve(deps2.projectRoot, rel);
|
|
7890
8576
|
enqueueFile(abs);
|
|
7891
8577
|
});
|
|
7892
8578
|
watcher.on("error", (err) => deps2.logger.debug(`webui codebase index watcher error: ${err}`));
|
|
@@ -7899,7 +8585,7 @@ function setupWebUICodebaseIndexing(deps2) {
|
|
|
7899
8585
|
}
|
|
7900
8586
|
function enqueueFile(filePath) {
|
|
7901
8587
|
if (!idx.onEdit && !idx.watchExternal) return;
|
|
7902
|
-
const abs =
|
|
8588
|
+
const abs = path17.isAbsolute(filePath) ? path17.normalize(filePath) : path17.resolve(deps2.projectRoot, filePath);
|
|
7903
8589
|
if (!isInside2(deps2.projectRoot, abs) || !isIndexableFile(abs)) return;
|
|
7904
8590
|
enqueueReindex({
|
|
7905
8591
|
projectRoot: deps2.projectRoot,
|
|
@@ -7936,15 +8622,23 @@ function isIgnored(rel) {
|
|
|
7936
8622
|
return rel.split(/[/\\]/).some((seg) => IGNORE_DIRS.has(seg));
|
|
7937
8623
|
}
|
|
7938
8624
|
function isInside2(root, target) {
|
|
7939
|
-
const normalizedRoot =
|
|
7940
|
-
const normalizedTarget =
|
|
7941
|
-
return normalizedTarget === normalizedRoot || normalizedTarget.startsWith(normalizedRoot +
|
|
8625
|
+
const normalizedRoot = path17.resolve(root);
|
|
8626
|
+
const normalizedTarget = path17.resolve(target);
|
|
8627
|
+
return normalizedTarget === normalizedRoot || normalizedTarget.startsWith(normalizedRoot + path17.sep);
|
|
7942
8628
|
}
|
|
8629
|
+
|
|
8630
|
+
// src/server/discover-mailbox-bridge.ts
|
|
8631
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
8632
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
8633
|
+
import { existsSync } from "node:fs";
|
|
8634
|
+
import { dirname as dirname8, join as join12 } from "node:path";
|
|
8635
|
+
import { resolveProjectDir, wstackGlobalRoot as wstackGlobalRoot2 } from "@wrongstack/core";
|
|
8636
|
+
import { readLiveLock } from "@wrongstack/core/coordination";
|
|
7943
8637
|
var MAILBOX_BRIDGE_BOOT_TIMEOUT_MS = 5e3;
|
|
7944
8638
|
async function discoverMailboxBridgeForWebui(params) {
|
|
7945
8639
|
const mode = params.config?.features?.mailboxBridge ?? "auto";
|
|
7946
8640
|
if (mode === "off") return;
|
|
7947
|
-
const projectDir = resolveProjectDir(params.projectRoot,
|
|
8641
|
+
const projectDir = resolveProjectDir(params.projectRoot, wstackGlobalRoot2());
|
|
7948
8642
|
let result = await readLiveLock(projectDir);
|
|
7949
8643
|
let spawnedPid = null;
|
|
7950
8644
|
if (result.kind !== "live") {
|
|
@@ -8010,7 +8704,7 @@ async function waitForLiveMailboxBridge(projectDir, timeoutMs) {
|
|
|
8010
8704
|
function spawnMailboxBridge(projectRoot, logger) {
|
|
8011
8705
|
const invocation = mailboxServeInvocation(projectRoot);
|
|
8012
8706
|
try {
|
|
8013
|
-
const child =
|
|
8707
|
+
const child = spawn3(invocation.command, invocation.args, {
|
|
8014
8708
|
cwd: projectRoot,
|
|
8015
8709
|
detached: process.platform !== "win32" && !invocation.useShell,
|
|
8016
8710
|
env: process.env,
|
|
@@ -8041,7 +8735,7 @@ function mailboxServeInvocation(projectRoot) {
|
|
|
8041
8735
|
return { command: process.execPath, args: [explicitCliEntry, "mailbox", "serve"] };
|
|
8042
8736
|
}
|
|
8043
8737
|
try {
|
|
8044
|
-
const require2 =
|
|
8738
|
+
const require2 = createRequire3(import.meta.url);
|
|
8045
8739
|
const cliEntry = require2.resolve("@wrongstack/cli");
|
|
8046
8740
|
return { command: process.execPath, args: [cliEntry, "mailbox", "serve"] };
|
|
8047
8741
|
} catch {
|
|
@@ -8065,9 +8759,9 @@ function mailboxServeInvocation(projectRoot) {
|
|
|
8065
8759
|
function findWorkspaceCliEntry(projectRoot) {
|
|
8066
8760
|
let dir = projectRoot;
|
|
8067
8761
|
for (let i = 0; i < 6; i++) {
|
|
8068
|
-
const candidate =
|
|
8762
|
+
const candidate = join12(dir, "packages", "cli", "dist", "index.js");
|
|
8069
8763
|
if (existsSync(candidate)) return candidate;
|
|
8070
|
-
const parent =
|
|
8764
|
+
const parent = dirname8(dir);
|
|
8071
8765
|
if (parent === dir) return null;
|
|
8072
8766
|
dir = parent;
|
|
8073
8767
|
}
|
|
@@ -8076,10 +8770,15 @@ function findWorkspaceCliEntry(projectRoot) {
|
|
|
8076
8770
|
function sleep(ms) {
|
|
8077
8771
|
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
8078
8772
|
}
|
|
8773
|
+
|
|
8774
|
+
// src/server/terminal-ws-handler.ts
|
|
8775
|
+
import { spawn as spawnChild } from "node:child_process";
|
|
8776
|
+
import { createRequire as createRequire4 } from "node:module";
|
|
8777
|
+
import { toErrorMessage as toErrorMessage7 } from "@wrongstack/core/utils";
|
|
8079
8778
|
var MAX_SESSIONS_PER_CLIENT = 8;
|
|
8080
8779
|
var DEFAULT_COLS = 80;
|
|
8081
8780
|
var DEFAULT_ROWS = 24;
|
|
8082
|
-
var requireFromHere =
|
|
8781
|
+
var requireFromHere = createRequire4(import.meta.url);
|
|
8083
8782
|
var cachedNodePty;
|
|
8084
8783
|
var TerminalWebSocketHandler = class {
|
|
8085
8784
|
constructor(getCwd, logger, loadNodePty = defaultLoadNodePty, killProcessTree = defaultKillProcessTree) {
|
|
@@ -8157,8 +8856,8 @@ var TerminalWebSocketHandler = class {
|
|
|
8157
8856
|
...windowsPtyOptions()
|
|
8158
8857
|
});
|
|
8159
8858
|
} catch (err) {
|
|
8160
|
-
const msg = `Integrated terminal failed to start: ${
|
|
8161
|
-
this.logger.warn?.(`terminal spawn failed: ${
|
|
8859
|
+
const msg = `Integrated terminal failed to start: ${toErrorMessage7(err)}`;
|
|
8860
|
+
this.logger.warn?.(`terminal spawn failed: ${toErrorMessage7(err)}`);
|
|
8162
8861
|
this.send(ws, { type: "terminal.output", payload: { id: payload.id, data: `${msg}\r
|
|
8163
8862
|
` } });
|
|
8164
8863
|
this.send(ws, { type: "terminal.exit", payload: { id: payload.id, exitCode: -1 } });
|
|
@@ -8211,7 +8910,7 @@ var TerminalWebSocketHandler = class {
|
|
|
8211
8910
|
try {
|
|
8212
8911
|
pty.kill();
|
|
8213
8912
|
} catch (err) {
|
|
8214
|
-
this.logger.warn?.(`${reason} failed: ${
|
|
8913
|
+
this.logger.warn?.(`${reason} failed: ${toErrorMessage7(err)}`);
|
|
8215
8914
|
}
|
|
8216
8915
|
}
|
|
8217
8916
|
killWindowsProcessTree(pty, reason) {
|
|
@@ -8223,7 +8922,7 @@ var TerminalWebSocketHandler = class {
|
|
|
8223
8922
|
} catch {
|
|
8224
8923
|
}
|
|
8225
8924
|
} catch (err) {
|
|
8226
|
-
this.logger.warn?.(`${reason} taskkill failed: ${
|
|
8925
|
+
this.logger.warn?.(`${reason} taskkill failed: ${toErrorMessage7(err)}`);
|
|
8227
8926
|
}
|
|
8228
8927
|
}
|
|
8229
8928
|
send(ws, msg) {
|
|
@@ -8254,7 +8953,7 @@ function windowsPtyOptions() {
|
|
|
8254
8953
|
return { useConptyDll: true };
|
|
8255
8954
|
}
|
|
8256
8955
|
function defaultKillProcessTree(pid) {
|
|
8257
|
-
const child =
|
|
8956
|
+
const child = spawnChild("taskkill", ["/PID", String(pid), "/T", "/F"], {
|
|
8258
8957
|
stdio: "ignore",
|
|
8259
8958
|
windowsHide: true
|
|
8260
8959
|
});
|
|
@@ -8267,6 +8966,14 @@ function clampDim(value, fallback) {
|
|
|
8267
8966
|
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
8268
8967
|
return Math.max(1, Math.min(1e3, Math.floor(value)));
|
|
8269
8968
|
}
|
|
8969
|
+
|
|
8970
|
+
// src/server/backend-services.ts
|
|
8971
|
+
import { makeLightSubagentFactory } from "@wrongstack/runtime";
|
|
8972
|
+
import {
|
|
8973
|
+
createSuperMemoryToolCallMiddleware,
|
|
8974
|
+
createSuperMemoryTurnMiddleware
|
|
8975
|
+
} from "@wrongstack/super-memory";
|
|
8976
|
+
import { toErrorMessage as toErrorMessage8 } from "@wrongstack/core/utils";
|
|
8270
8977
|
async function createAgentServices(input) {
|
|
8271
8978
|
const {
|
|
8272
8979
|
config,
|
|
@@ -8281,15 +8988,37 @@ async function createAgentServices(input) {
|
|
|
8281
8988
|
providerRegistry,
|
|
8282
8989
|
modelsRegistry,
|
|
8283
8990
|
events,
|
|
8991
|
+
memoryStore,
|
|
8284
8992
|
modelCapabilitiesRef
|
|
8285
8993
|
} = input;
|
|
8286
|
-
const collabBus = new
|
|
8994
|
+
const collabBus = new CollaborationBusCtor();
|
|
8287
8995
|
const pipelines = input.pipelines;
|
|
8288
8996
|
const collabInject = collabInjectMiddleware(collabBus, { logger });
|
|
8289
8997
|
pipelines.toolCall.prepend(collabInject);
|
|
8290
8998
|
const collabPause = collabPauseMiddleware(collabBus, { logger });
|
|
8291
8999
|
pipelines.toolCall.prepend(collabPause);
|
|
8292
9000
|
installDesignStudioMiddleware({ pipelines, ctx: context });
|
|
9001
|
+
if (config.features.memory !== false && config.superMemory?.enabled !== false && isSuperMemoryRetriever(memoryStore)) {
|
|
9002
|
+
if (config.superMemory?.inject?.toolResults !== false) {
|
|
9003
|
+
pipelines.toolCall.use(createSuperMemoryToolCallMiddleware({
|
|
9004
|
+
memory: memoryStore,
|
|
9005
|
+
maxHintsPerTool: config.superMemory?.inject?.maxHintsPerTool,
|
|
9006
|
+
maxCharsPerTool: config.superMemory?.inject?.maxCharsPerTool,
|
|
9007
|
+
minScore: config.superMemory?.inject?.minScore,
|
|
9008
|
+
repeatCooldownMs: config.superMemory?.inject?.repeatCooldownMs,
|
|
9009
|
+
verifyOnMutation: config.superMemory?.hygiene?.autoOnFileChange,
|
|
9010
|
+
triggers: config.superMemory?.inject?.triggers
|
|
9011
|
+
}));
|
|
9012
|
+
}
|
|
9013
|
+
if (config.superMemory?.inject?.turnContext !== false) {
|
|
9014
|
+
pipelines.request.use(createSuperMemoryTurnMiddleware({
|
|
9015
|
+
memory: memoryStore,
|
|
9016
|
+
maxMemories: config.superMemory?.inject?.maxTurnMemories,
|
|
9017
|
+
maxChars: config.superMemory?.inject?.maxCharsPerTurn,
|
|
9018
|
+
minScore: config.superMemory?.inject?.minScore
|
|
9019
|
+
}));
|
|
9020
|
+
}
|
|
9021
|
+
}
|
|
8293
9022
|
const codebaseIndexing = setupWebUICodebaseIndexing({
|
|
8294
9023
|
config,
|
|
8295
9024
|
context,
|
|
@@ -8303,7 +9032,7 @@ async function createAgentServices(input) {
|
|
|
8303
9032
|
summarizerModel: config.context?.summarizerModel,
|
|
8304
9033
|
llmSelector: config.context?.llmSelector
|
|
8305
9034
|
});
|
|
8306
|
-
const initialContextPolicy =
|
|
9035
|
+
const initialContextPolicy = resolveContextWindowPolicy2(config.context);
|
|
8307
9036
|
let autoCompactor;
|
|
8308
9037
|
if (config.context?.autoCompact !== false) {
|
|
8309
9038
|
let effectiveMaxContext = config.context?.effectiveMaxContext ?? 0;
|
|
@@ -8320,7 +9049,7 @@ async function createAgentServices(input) {
|
|
|
8320
9049
|
}
|
|
8321
9050
|
}
|
|
8322
9051
|
if (!effectiveMaxContext) effectiveMaxContext = provider.capabilities.maxContext;
|
|
8323
|
-
autoCompactor = new
|
|
9052
|
+
autoCompactor = new AutoCompactionMiddlewareCtor(
|
|
8324
9053
|
compactor,
|
|
8325
9054
|
effectiveMaxContext,
|
|
8326
9055
|
(ctx) => estimateRequestTokensCalibrated(
|
|
@@ -8348,7 +9077,7 @@ async function createAgentServices(input) {
|
|
|
8348
9077
|
const updateAutoCompactionMaxContext = async (newProvider, providerId = newProvider.id, providerCfg) => {
|
|
8349
9078
|
await modelsRegistry.refresh().catch((err) => {
|
|
8350
9079
|
logger.warn(
|
|
8351
|
-
`models.dev refresh failed for ${providerId}/${context.model}: ${
|
|
9080
|
+
`models.dev refresh failed for ${providerId}/${context.model}: ${toErrorMessage8(err)}; using cached catalog`
|
|
8352
9081
|
);
|
|
8353
9082
|
});
|
|
8354
9083
|
const currentConfig = input.config;
|
|
@@ -8385,9 +9114,9 @@ async function createAgentServices(input) {
|
|
|
8385
9114
|
maxContext: newMaxContext
|
|
8386
9115
|
});
|
|
8387
9116
|
};
|
|
8388
|
-
const secretScrubber = container.resolve(
|
|
8389
|
-
const renderer = container.has(
|
|
8390
|
-
const permissionPolicy = container.resolve(
|
|
9117
|
+
const secretScrubber = container.resolve(TOKENS2.SecretScrubber);
|
|
9118
|
+
const renderer = container.has(TOKENS2.Renderer) ? container.resolve(TOKENS2.Renderer) : void 0;
|
|
9119
|
+
const permissionPolicy = container.resolve(TOKENS2.PermissionPolicy);
|
|
8391
9120
|
const toolExecutor = new ToolExecutor(toolRegistry, {
|
|
8392
9121
|
permissionPolicy,
|
|
8393
9122
|
secretScrubber,
|
|
@@ -8398,7 +9127,7 @@ async function createAgentServices(input) {
|
|
|
8398
9127
|
perIterationOutputCapBytes: config.tools?.perIterationOutputCapBytes ?? DEFAULT_TOOLS_CONFIG.perIterationOutputCapBytes,
|
|
8399
9128
|
tracer: void 0
|
|
8400
9129
|
});
|
|
8401
|
-
const webuiLogger = container.resolve(
|
|
9130
|
+
const webuiLogger = container.resolve(TOKENS2.Logger);
|
|
8402
9131
|
void discoverMailboxBridgeForWebui({
|
|
8403
9132
|
projectRoot,
|
|
8404
9133
|
config,
|
|
@@ -8424,25 +9153,66 @@ async function createAgentServices(input) {
|
|
|
8424
9153
|
confirmAwaiter: void 0,
|
|
8425
9154
|
toolExecutor
|
|
8426
9155
|
});
|
|
9156
|
+
if (config.features.memory && config.features.memoryConsolidation !== false) {
|
|
9157
|
+
agent.extensions.register(new SessionMemoryConsolidator({ memoryStore }));
|
|
9158
|
+
}
|
|
8427
9159
|
console.log("[WebUI] Agent initialized");
|
|
8428
|
-
const
|
|
8429
|
-
|
|
8430
|
-
|
|
8431
|
-
|
|
8432
|
-
|
|
8433
|
-
|
|
8434
|
-
|
|
8435
|
-
|
|
9160
|
+
const brainCfg = resolveBrainConfigDefaults(config.brain, {
|
|
9161
|
+
fallbackModels: config.fallbackModels
|
|
9162
|
+
});
|
|
9163
|
+
const brainLedgerPath = join13(wpaths.projectDir, "brain-ledger.jsonl");
|
|
9164
|
+
let brainLedgerEnabled = brainCfg.ledger?.enabled !== false;
|
|
9165
|
+
let brainLedger;
|
|
9166
|
+
const startBrainLedger = () => {
|
|
9167
|
+
if (brainLedger) return;
|
|
9168
|
+
brainLedger = new BrainDecisionLedger({ events, filePath: brainLedgerPath });
|
|
9169
|
+
void brainLedger.start();
|
|
8436
9170
|
};
|
|
8437
|
-
|
|
8438
|
-
|
|
8439
|
-
|
|
8440
|
-
|
|
8441
|
-
|
|
8442
|
-
|
|
9171
|
+
if (brainLedgerEnabled) startBrainLedger();
|
|
9172
|
+
const brainRuntime = createBrainRuntime({
|
|
9173
|
+
initialConfig: brainCfg,
|
|
9174
|
+
defaultProviderId: config.provider,
|
|
9175
|
+
sessionProvider: () => provider,
|
|
9176
|
+
sessionModel: () => context.model,
|
|
9177
|
+
resolveProvider: (providerId) => {
|
|
9178
|
+
const savedCfg = config.providers?.[providerId] ?? {};
|
|
9179
|
+
return providerRegistry.create({
|
|
9180
|
+
...savedCfg,
|
|
9181
|
+
apiKey: savedCfg.apiKey ?? config.apiKey,
|
|
9182
|
+
baseUrl: savedCfg.baseUrl ?? config.baseUrl,
|
|
9183
|
+
type: providerId
|
|
9184
|
+
});
|
|
9185
|
+
},
|
|
9186
|
+
ledger: {
|
|
9187
|
+
getPath: () => brainLedgerEnabled ? brainLedgerPath : void 0,
|
|
9188
|
+
isEnabled: () => brainLedgerEnabled,
|
|
9189
|
+
setEnabled: (on) => {
|
|
9190
|
+
brainLedgerEnabled = on;
|
|
9191
|
+
if (on) {
|
|
9192
|
+
startBrainLedger();
|
|
9193
|
+
} else {
|
|
9194
|
+
void brainLedger?.stop();
|
|
9195
|
+
brainLedger = void 0;
|
|
9196
|
+
}
|
|
9197
|
+
},
|
|
9198
|
+
failureStreakFor: (request) => brainLedger?.failureStreakFor(request) ?? 0,
|
|
9199
|
+
getDecisionDigest: (request) => brainLedger?.digestFor(request)
|
|
9200
|
+
},
|
|
9201
|
+
persist: input.persistBrainConfig
|
|
9202
|
+
});
|
|
9203
|
+
const brainSettings = {
|
|
9204
|
+
get maxAutoRisk() {
|
|
9205
|
+
return brainRuntime.getMaxAutoRisk();
|
|
9206
|
+
},
|
|
9207
|
+
set maxAutoRisk(level) {
|
|
9208
|
+
void brainRuntime.apply({ maxAutoRisk: level }).persisted;
|
|
9209
|
+
}
|
|
9210
|
+
};
|
|
9211
|
+
const brain = new ObservableBrainArbiterCtor(
|
|
9212
|
+
new EscalationRoutingBrainArbiter(brainRuntime.arbiter, void 0, () => "headless"),
|
|
8443
9213
|
events
|
|
8444
9214
|
);
|
|
8445
|
-
container.bind(
|
|
9215
|
+
container.bind(TOKENS2.BrainArbiter, () => brain);
|
|
8446
9216
|
const brainLog = [];
|
|
8447
9217
|
const pushBrainLog = (entry) => {
|
|
8448
9218
|
brainLog.push(entry);
|
|
@@ -8475,10 +9245,16 @@ async function createAgentServices(input) {
|
|
|
8475
9245
|
outcome: e.decision.type === "deny" ? e.decision.reason : ""
|
|
8476
9246
|
})
|
|
8477
9247
|
);
|
|
8478
|
-
const brainMailbox = new
|
|
9248
|
+
const brainMailbox = new GlobalMailbox2(wpaths.projectDir, events);
|
|
8479
9249
|
const brainMonitor = new BrainMonitor({
|
|
8480
9250
|
events,
|
|
8481
9251
|
brain,
|
|
9252
|
+
toolFailureStreak: brainCfg.monitor?.toolFailureStreak,
|
|
9253
|
+
errorStormCount: brainCfg.monitor?.errorStormCount,
|
|
9254
|
+
stallMs: brainCfg.monitor?.stallMs,
|
|
9255
|
+
fileChurnThreshold: brainCfg.monitor?.fileChurnThreshold,
|
|
9256
|
+
fileChurnWindowMs: brainCfg.monitor?.fileChurnWindowMs,
|
|
9257
|
+
cooldownMs: brainCfg.monitor?.cooldownMs,
|
|
8482
9258
|
sessionId: () => context.session?.id,
|
|
8483
9259
|
intervene: async ({ subject, body }) => {
|
|
8484
9260
|
const tag = mailboxSessionTag(input.sessionGetter().id);
|
|
@@ -8543,7 +9319,10 @@ async function createAgentServices(input) {
|
|
|
8543
9319
|
logger,
|
|
8544
9320
|
input.sessionReader,
|
|
8545
9321
|
input.annotationsStore,
|
|
8546
|
-
collabBus
|
|
9322
|
+
collabBus,
|
|
9323
|
+
{
|
|
9324
|
+
getActiveSessionId: () => context.session.id
|
|
9325
|
+
}
|
|
8547
9326
|
);
|
|
8548
9327
|
return {
|
|
8549
9328
|
collabBus,
|
|
@@ -8555,8 +9334,13 @@ async function createAgentServices(input) {
|
|
|
8555
9334
|
pipelines,
|
|
8556
9335
|
brain,
|
|
8557
9336
|
brainSettings,
|
|
9337
|
+
brainRuntime,
|
|
8558
9338
|
brainLog,
|
|
8559
9339
|
brainMonitor,
|
|
9340
|
+
// Getter: ledger toggles swap the instance, shutdown must stop the LIVE one.
|
|
9341
|
+
get brainLedger() {
|
|
9342
|
+
return brainLedger;
|
|
9343
|
+
},
|
|
8560
9344
|
codebaseIndexing,
|
|
8561
9345
|
autoPhaseHandler,
|
|
8562
9346
|
specsHandler,
|
|
@@ -8568,14 +9352,14 @@ async function createAgentServices(input) {
|
|
|
8568
9352
|
updateAutoCompactionMaxContext
|
|
8569
9353
|
};
|
|
8570
9354
|
}
|
|
9355
|
+
function isSuperMemoryRetriever(memoryStore) {
|
|
9356
|
+
const value = memoryStore;
|
|
9357
|
+
return typeof value["retrieveForPath"] === "function" && typeof value["searchSuper"] === "function";
|
|
9358
|
+
}
|
|
8571
9359
|
|
|
8572
9360
|
// src/server/pending-confirms.ts
|
|
8573
|
-
function isDestructivePendingConfirm(confirm) {
|
|
8574
|
-
return confirm.riskTier === "destructive" || confirm.decisionSource === "yolo_destructive";
|
|
8575
|
-
}
|
|
8576
9361
|
function resolveYoloEligiblePendingConfirms(pendingConfirms) {
|
|
8577
9362
|
for (const [id, confirm] of pendingConfirms) {
|
|
8578
|
-
if (isDestructivePendingConfirm(confirm)) continue;
|
|
8579
9363
|
pendingConfirms.delete(id);
|
|
8580
9364
|
confirm.resolve("yes");
|
|
8581
9365
|
}
|
|
@@ -8731,6 +9515,20 @@ function createConnectionHandler(opts) {
|
|
|
8731
9515
|
};
|
|
8732
9516
|
}
|
|
8733
9517
|
|
|
9518
|
+
// src/server/message-dispatcher.ts
|
|
9519
|
+
import path18 from "node:path";
|
|
9520
|
+
import {
|
|
9521
|
+
buildUserContentBlocks,
|
|
9522
|
+
IncomingImageError,
|
|
9523
|
+
parseIncomingImages
|
|
9524
|
+
} from "@wrongstack/core/utils";
|
|
9525
|
+
import {
|
|
9526
|
+
createToolVisionAdapters,
|
|
9527
|
+
ImageInputUnsupportedError,
|
|
9528
|
+
routeImagesForModel,
|
|
9529
|
+
VisionUrlBlockedError
|
|
9530
|
+
} from "@wrongstack/runtime/vision";
|
|
9531
|
+
|
|
8734
9532
|
// src/server/autophase-routes.ts
|
|
8735
9533
|
async function handleAutoPhaseRoute(_ws, msg, handlers) {
|
|
8736
9534
|
if (!msg.type.startsWith("autophase.")) return false;
|
|
@@ -8750,14 +9548,23 @@ async function handleBrainRoute(ws, msg, handlers) {
|
|
|
8750
9548
|
case "brain.ask":
|
|
8751
9549
|
await handlers.ask(ws, msg);
|
|
8752
9550
|
return true;
|
|
9551
|
+
case "brain.config.get":
|
|
9552
|
+
await handlers.configGet(ws, msg);
|
|
9553
|
+
return true;
|
|
9554
|
+
case "brain.config.set":
|
|
9555
|
+
await handlers.configSet(ws, msg);
|
|
9556
|
+
return true;
|
|
8753
9557
|
default:
|
|
8754
9558
|
return false;
|
|
8755
9559
|
}
|
|
8756
9560
|
}
|
|
9561
|
+
|
|
9562
|
+
// src/server/goal-handlers.ts
|
|
9563
|
+
import { resolveWstackPaths } from "@wrongstack/core/utils";
|
|
8757
9564
|
async function handleGoalGet(projectRoot, broadcast2) {
|
|
8758
9565
|
try {
|
|
8759
9566
|
const goalPath = resolveWstackPaths({ projectRoot }).projectGoal;
|
|
8760
|
-
const { readFile: readFile11 } = await import(
|
|
9567
|
+
const { readFile: readFile11 } = await import("node:fs/promises");
|
|
8761
9568
|
const raw = await readFile11(goalPath, "utf8");
|
|
8762
9569
|
const goal = JSON.parse(raw);
|
|
8763
9570
|
broadcast2({ type: "goal.updated", payload: goal });
|
|
@@ -8811,7 +9618,7 @@ async function handleTasksGet(ctx, ws) {
|
|
|
8811
9618
|
const taskPath = ctx.context.meta["task.path"];
|
|
8812
9619
|
if (typeof taskPath === "string" && taskPath) {
|
|
8813
9620
|
try {
|
|
8814
|
-
const { loadTasks } = await import(
|
|
9621
|
+
const { loadTasks } = await import("@wrongstack/core");
|
|
8815
9622
|
const file = await loadTasks(taskPath);
|
|
8816
9623
|
ctx.send(ws, { type: "tasks.updated", payload: sessionPayload(ctx, { tasks: file?.tasks ?? [] }) });
|
|
8817
9624
|
} catch {
|
|
@@ -8831,7 +9638,7 @@ async function handleTaskUpdate(ctx, ws, payload) {
|
|
|
8831
9638
|
return;
|
|
8832
9639
|
}
|
|
8833
9640
|
try {
|
|
8834
|
-
const { loadTasks, saveTasks } = await import(
|
|
9641
|
+
const { loadTasks, saveTasks } = await import("@wrongstack/core");
|
|
8835
9642
|
const file = await loadTasks(taskPath);
|
|
8836
9643
|
if (!file) {
|
|
8837
9644
|
sendResult2(ws, ctx, false, "No task file found.");
|
|
@@ -8860,7 +9667,7 @@ async function handlePlanGet(ctx, ws) {
|
|
|
8860
9667
|
const sessionId = ctx.context.session?.id ?? "";
|
|
8861
9668
|
if (typeof planPath === "string" && planPath) {
|
|
8862
9669
|
try {
|
|
8863
|
-
const { loadPlan } = await import(
|
|
9670
|
+
const { loadPlan } = await import("@wrongstack/core");
|
|
8864
9671
|
const plan = await loadPlan(planPath);
|
|
8865
9672
|
ctx.send(ws, {
|
|
8866
9673
|
type: "plan.updated",
|
|
@@ -8901,7 +9708,7 @@ async function handlePlanTemplateUse(ctx, ws, template) {
|
|
|
8901
9708
|
return;
|
|
8902
9709
|
}
|
|
8903
9710
|
try {
|
|
8904
|
-
const { getPlanTemplate, loadPlan, savePlan, emptyPlan, addPlanItem } = await import(
|
|
9711
|
+
const { getPlanTemplate, loadPlan, savePlan, emptyPlan, addPlanItem } = await import("@wrongstack/core");
|
|
8905
9712
|
const tpl = getPlanTemplate(template);
|
|
8906
9713
|
if (!tpl) {
|
|
8907
9714
|
sendResult2(ws, ctx, false, `Unknown template "${template}".`);
|
|
@@ -8926,7 +9733,7 @@ async function handlePlanItemUpdate(ctx, ws, payload) {
|
|
|
8926
9733
|
return;
|
|
8927
9734
|
}
|
|
8928
9735
|
try {
|
|
8929
|
-
const { mutatePlan, setPlanItemStatus } = await import(
|
|
9736
|
+
const { mutatePlan, setPlanItemStatus } = await import("@wrongstack/core");
|
|
8930
9737
|
let changed = false;
|
|
8931
9738
|
const plan = await mutatePlan(planPath, sessionId, async (p) => {
|
|
8932
9739
|
const before = p.updatedAt;
|
|
@@ -8993,6 +9800,45 @@ async function handleWorklistMessage(ctx, ws, msg) {
|
|
|
8993
9800
|
return;
|
|
8994
9801
|
}
|
|
8995
9802
|
}
|
|
9803
|
+
|
|
9804
|
+
// src/server/kanban-routes.ts
|
|
9805
|
+
import {
|
|
9806
|
+
deserializeTaskGraph,
|
|
9807
|
+
serializeTaskGraph
|
|
9808
|
+
} from "@wrongstack/core";
|
|
9809
|
+
import {
|
|
9810
|
+
addColumn,
|
|
9811
|
+
addGoalMetricToTask,
|
|
9812
|
+
addTask,
|
|
9813
|
+
assignTask,
|
|
9814
|
+
claimReadyTask,
|
|
9815
|
+
copyTaskToBoard,
|
|
9816
|
+
createBoard,
|
|
9817
|
+
duplicateBoard,
|
|
9818
|
+
exportBoardToTaskGraph,
|
|
9819
|
+
generateBoardFromDescription,
|
|
9820
|
+
getBoard,
|
|
9821
|
+
getKanbanOrchestrationSnapshot,
|
|
9822
|
+
getKanbanQueueHealth,
|
|
9823
|
+
getTask,
|
|
9824
|
+
getTaskChain,
|
|
9825
|
+
listReadyTasks,
|
|
9826
|
+
listBoards,
|
|
9827
|
+
mergeTasks,
|
|
9828
|
+
moveTask,
|
|
9829
|
+
parseLinesIntoTasks,
|
|
9830
|
+
removeBoard,
|
|
9831
|
+
removeColumn,
|
|
9832
|
+
removeTask,
|
|
9833
|
+
releaseTaskClaim,
|
|
9834
|
+
setTaskChain,
|
|
9835
|
+
splitTask,
|
|
9836
|
+
syncBoardFromTaskGraph,
|
|
9837
|
+
transferTaskToBoard,
|
|
9838
|
+
updateBoard,
|
|
9839
|
+
updateGoalMetricOnTask,
|
|
9840
|
+
updateTask
|
|
9841
|
+
} from "@wrongstack/kanban";
|
|
8996
9842
|
function ok(ws, type, data) {
|
|
8997
9843
|
send(ws, { type, payload: { success: true, data: data ?? null } });
|
|
8998
9844
|
}
|
|
@@ -9438,6 +10284,9 @@ async function handleKanbanRoute(ws, msg, ctx) {
|
|
|
9438
10284
|
"Kanban agent dispatch is only available from the CLI-hosted WebUI runtime."
|
|
9439
10285
|
);
|
|
9440
10286
|
return true;
|
|
10287
|
+
case "kanban.capabilities":
|
|
10288
|
+
ok(ws, type, { dispatchSupported: false });
|
|
10289
|
+
return true;
|
|
9441
10290
|
case "kanban.task.remove": {
|
|
9442
10291
|
const boardId = payload?.boardId;
|
|
9443
10292
|
const taskId = payload?.taskId;
|
|
@@ -9517,6 +10366,9 @@ async function handleMailboxRoute(ws, msg, handlers) {
|
|
|
9517
10366
|
case "mailbox.purge":
|
|
9518
10367
|
await handlers.purge(ws, msg);
|
|
9519
10368
|
return true;
|
|
10369
|
+
case "mailbox.compact":
|
|
10370
|
+
await handlers.compact(ws, msg);
|
|
10371
|
+
return true;
|
|
9520
10372
|
default:
|
|
9521
10373
|
return false;
|
|
9522
10374
|
}
|
|
@@ -9555,6 +10407,18 @@ async function handleMcpRoute(ws, msg, handlers) {
|
|
|
9555
10407
|
case "mcp.discover":
|
|
9556
10408
|
await handlers.discover(ws, msg);
|
|
9557
10409
|
return true;
|
|
10410
|
+
case "mcp.resources":
|
|
10411
|
+
await handlers.resources(ws, msg);
|
|
10412
|
+
return true;
|
|
10413
|
+
case "mcp.prompts":
|
|
10414
|
+
await handlers.prompts(ws, msg);
|
|
10415
|
+
return true;
|
|
10416
|
+
case "mcp.resource.read":
|
|
10417
|
+
await handlers.resourceRead(ws, msg);
|
|
10418
|
+
return true;
|
|
10419
|
+
case "mcp.prompt.get":
|
|
10420
|
+
await handlers.promptGet(ws, msg);
|
|
10421
|
+
return true;
|
|
9558
10422
|
default:
|
|
9559
10423
|
return false;
|
|
9560
10424
|
}
|
|
@@ -9593,7 +10457,7 @@ async function handlePrefsRoute(ws, msg, handlers) {
|
|
|
9593
10457
|
// src/server/process-handlers.ts
|
|
9594
10458
|
async function handleProcessList(ws) {
|
|
9595
10459
|
try {
|
|
9596
|
-
const { getProcessRegistry } = await import(
|
|
10460
|
+
const { getProcessRegistry } = await import("@wrongstack/tools");
|
|
9597
10461
|
const procs = getProcessRegistry().list();
|
|
9598
10462
|
send(ws, {
|
|
9599
10463
|
type: "process.list",
|
|
@@ -9620,7 +10484,7 @@ async function handleProcessKill(ws, payload) {
|
|
|
9620
10484
|
}
|
|
9621
10485
|
const { pid } = parsed.value;
|
|
9622
10486
|
try {
|
|
9623
|
-
const { getProcessRegistry } = await import(
|
|
10487
|
+
const { getProcessRegistry } = await import("@wrongstack/tools");
|
|
9624
10488
|
const proc = getProcessRegistry().get(pid);
|
|
9625
10489
|
if (proc?.protected) {
|
|
9626
10490
|
sendResult(ws, false, `Cannot kill protected process (PID ${pid})`);
|
|
@@ -9634,7 +10498,7 @@ async function handleProcessKill(ws, payload) {
|
|
|
9634
10498
|
}
|
|
9635
10499
|
async function handleProcessKillAll(ws) {
|
|
9636
10500
|
try {
|
|
9637
|
-
const { getProcessRegistry } = await import(
|
|
10501
|
+
const { getProcessRegistry } = await import("@wrongstack/tools");
|
|
9638
10502
|
getProcessRegistry().killAll();
|
|
9639
10503
|
sendResult(ws, true, "All processes killed");
|
|
9640
10504
|
} catch (err) {
|
|
@@ -9944,7 +10808,7 @@ function createMessageDispatcher(opts) {
|
|
|
9944
10808
|
skillLoader: deps2.skillLoader,
|
|
9945
10809
|
skillInstaller: deps2.skillInstaller,
|
|
9946
10810
|
projectRoot,
|
|
9947
|
-
projectSkillsDir:
|
|
10811
|
+
projectSkillsDir: path18.join(projectRoot, ".wrongstack", "skills"),
|
|
9948
10812
|
globalSkillsDir: deps2.wpaths.globalSkills
|
|
9949
10813
|
};
|
|
9950
10814
|
}
|
|
@@ -10017,7 +10881,8 @@ function createMessageDispatcher(opts) {
|
|
|
10017
10881
|
}
|
|
10018
10882
|
case "user_message": {
|
|
10019
10883
|
if (!ensureCurrentSession(ws, msg, "user_message")) return;
|
|
10020
|
-
const
|
|
10884
|
+
const userPayload = msg.payload;
|
|
10885
|
+
const content = userPayload.content;
|
|
10021
10886
|
if (runLock.get()) {
|
|
10022
10887
|
send(ws, {
|
|
10023
10888
|
type: "error",
|
|
@@ -10031,8 +10896,24 @@ function createMessageDispatcher(opts) {
|
|
|
10031
10896
|
const thisRun = new AbortController();
|
|
10032
10897
|
runLock.set(thisRun);
|
|
10033
10898
|
try {
|
|
10899
|
+
let input = content;
|
|
10900
|
+
const imageBlocks = parseIncomingImages(userPayload.images, userPayload.imageBase64);
|
|
10901
|
+
if (imageBlocks.length > 0) {
|
|
10902
|
+
const routed = await routeImagesForModel(
|
|
10903
|
+
buildUserContentBlocks(content, imageBlocks),
|
|
10904
|
+
{
|
|
10905
|
+
supportsVision: deps2.agent.ctx.provider.capabilities.vision,
|
|
10906
|
+
adapters: () => createToolVisionAdapters(deps2.agent.tools),
|
|
10907
|
+
ctx: deps2.agent.ctx,
|
|
10908
|
+
signal: thisRun.signal,
|
|
10909
|
+
providerId: deps2.agent.ctx.provider.id,
|
|
10910
|
+
model: deps2.agent.ctx.model
|
|
10911
|
+
}
|
|
10912
|
+
);
|
|
10913
|
+
input = routed.blocks;
|
|
10914
|
+
}
|
|
10034
10915
|
const maxIt = typeof deps2.context.meta["maxIterations"] === "number" ? deps2.context.meta["maxIterations"] : void 0;
|
|
10035
|
-
const result = await deps2.agent.run(
|
|
10916
|
+
const result = await deps2.agent.run(input, {
|
|
10036
10917
|
signal: thisRun.signal,
|
|
10037
10918
|
maxIterations: maxIt
|
|
10038
10919
|
});
|
|
@@ -10050,13 +10931,24 @@ function createMessageDispatcher(opts) {
|
|
|
10050
10931
|
})
|
|
10051
10932
|
});
|
|
10052
10933
|
} catch (err) {
|
|
10053
|
-
|
|
10054
|
-
|
|
10055
|
-
|
|
10056
|
-
|
|
10057
|
-
|
|
10058
|
-
|
|
10059
|
-
|
|
10934
|
+
if (err instanceof IncomingImageError || err instanceof ImageInputUnsupportedError || err instanceof VisionUrlBlockedError) {
|
|
10935
|
+
send(ws, {
|
|
10936
|
+
type: "error",
|
|
10937
|
+
payload: sessionPayload2({
|
|
10938
|
+
phase: "user_message",
|
|
10939
|
+
...err instanceof ImageInputUnsupportedError ? { code: "vision_unsupported" } : {},
|
|
10940
|
+
message: err.message
|
|
10941
|
+
})
|
|
10942
|
+
});
|
|
10943
|
+
} else {
|
|
10944
|
+
send(ws, {
|
|
10945
|
+
type: "error",
|
|
10946
|
+
payload: sessionPayload2({
|
|
10947
|
+
phase: "agent.run",
|
|
10948
|
+
message: errMessage(err)
|
|
10949
|
+
})
|
|
10950
|
+
});
|
|
10951
|
+
}
|
|
10060
10952
|
} finally {
|
|
10061
10953
|
if (runLock.get() === thisRun) {
|
|
10062
10954
|
runLock.set(null);
|
|
@@ -10104,7 +10996,7 @@ function createMessageDispatcher(opts) {
|
|
|
10104
10996
|
break;
|
|
10105
10997
|
}
|
|
10106
10998
|
case "tool.disable": {
|
|
10107
|
-
const name2 =
|
|
10999
|
+
const name2 = msg.payload?.name;
|
|
10108
11000
|
if (!name2) {
|
|
10109
11001
|
send(ws, { type: "error", payload: { message: "tool.disable requires a name" } });
|
|
10110
11002
|
break;
|
|
@@ -10119,7 +11011,7 @@ function createMessageDispatcher(opts) {
|
|
|
10119
11011
|
break;
|
|
10120
11012
|
}
|
|
10121
11013
|
case "tool.enable": {
|
|
10122
|
-
const name2 =
|
|
11014
|
+
const name2 = msg.payload?.name;
|
|
10123
11015
|
if (!name2) {
|
|
10124
11016
|
send(ws, { type: "error", payload: { message: "tool.enable requires a name" } });
|
|
10125
11017
|
break;
|
|
@@ -10163,6 +11055,14 @@ function createMessageDispatcher(opts) {
|
|
|
10163
11055
|
throw new Error("handleMcpRoute did not claim mcp.restart \u2014 check chain order");
|
|
10164
11056
|
case "mcp.discover":
|
|
10165
11057
|
throw new Error("handleMcpRoute did not claim mcp.discover \u2014 check chain order");
|
|
11058
|
+
case "mcp.resources":
|
|
11059
|
+
throw new Error("handleMcpRoute did not claim mcp.resources \u2014 check chain order");
|
|
11060
|
+
case "mcp.prompts":
|
|
11061
|
+
throw new Error("handleMcpRoute did not claim mcp.prompts \u2014 check chain order");
|
|
11062
|
+
case "mcp.resource.read":
|
|
11063
|
+
throw new Error("handleMcpRoute did not claim mcp.resource.read \u2014 check chain order");
|
|
11064
|
+
case "mcp.prompt.get":
|
|
11065
|
+
throw new Error("handleMcpRoute did not claim mcp.prompt.get \u2014 check chain order");
|
|
10166
11066
|
// Skills — full request→response cycle lives in skills-handlers.ts.
|
|
10167
11067
|
case "skills.list":
|
|
10168
11068
|
await handleSkillsList(ws, makeSkillsContext());
|
|
@@ -10399,6 +11299,7 @@ function createMessageDispatcher(opts) {
|
|
|
10399
11299
|
break;
|
|
10400
11300
|
}
|
|
10401
11301
|
case "prefs.update": {
|
|
11302
|
+
void ws;
|
|
10402
11303
|
throw new Error("handlePrefsRoute did not claim prefs.update \u2014 check chain order");
|
|
10403
11304
|
}
|
|
10404
11305
|
case "prefs.get": {
|
|
@@ -10412,6 +11313,11 @@ function createMessageDispatcher(opts) {
|
|
|
10412
11313
|
}
|
|
10413
11314
|
};
|
|
10414
11315
|
}
|
|
11316
|
+
|
|
11317
|
+
// src/server/pref-helpers.ts
|
|
11318
|
+
import { atomicWrite as atomicWrite6 } from "@wrongstack/core/utils";
|
|
11319
|
+
import { decryptConfigSecrets as decryptConfigSecrets2, encryptConfigSecrets as encryptConfigSecrets2 } from "@wrongstack/core/security";
|
|
11320
|
+
import * as fs13 from "node:fs/promises";
|
|
10415
11321
|
var PREF_KEYS = [
|
|
10416
11322
|
"autonomy",
|
|
10417
11323
|
"autonomyDelayMs",
|
|
@@ -10457,7 +11363,19 @@ var PREF_KEYS = [
|
|
|
10457
11363
|
"favoriteModels",
|
|
10458
11364
|
"favoriteModelsOnly",
|
|
10459
11365
|
"modelMatrix",
|
|
10460
|
-
"fallbackAuto"
|
|
11366
|
+
"fallbackAuto",
|
|
11367
|
+
// Refiner + TUI visual prefs (parity with the CLI's embedded server —
|
|
11368
|
+
// these were browser-editable there but rejected as unknown keys here).
|
|
11369
|
+
"refinerProvider",
|
|
11370
|
+
"refinerModel",
|
|
11371
|
+
"thinkingWord",
|
|
11372
|
+
"statuslineMode",
|
|
11373
|
+
"animationStyle",
|
|
11374
|
+
// Safety / system prefs (parity with /settings breaker, fs-access, debug-stream).
|
|
11375
|
+
"breakerEnabled",
|
|
11376
|
+
"breakerAutoKillResetMs",
|
|
11377
|
+
"fsAccess",
|
|
11378
|
+
"debugStream"
|
|
10461
11379
|
];
|
|
10462
11380
|
function prefSnapshot(contextMeta) {
|
|
10463
11381
|
const snapshot = {};
|
|
@@ -10471,7 +11389,7 @@ async function updateGlobalConfig(deps2, holder, mutate, errorLabel) {
|
|
|
10471
11389
|
const write = async () => {
|
|
10472
11390
|
let raw;
|
|
10473
11391
|
try {
|
|
10474
|
-
raw = await
|
|
11392
|
+
raw = await fs13.readFile(globalConfigPath, "utf8");
|
|
10475
11393
|
} catch {
|
|
10476
11394
|
raw = "{}";
|
|
10477
11395
|
}
|
|
@@ -10482,10 +11400,10 @@ async function updateGlobalConfig(deps2, holder, mutate, errorLabel) {
|
|
|
10482
11400
|
logger.warn(`${errorLabel}: refusing to overwrite corrupt config at ${globalConfigPath}`);
|
|
10483
11401
|
return;
|
|
10484
11402
|
}
|
|
10485
|
-
const decrypted =
|
|
11403
|
+
const decrypted = decryptConfigSecrets2(parsed, vault);
|
|
10486
11404
|
mutate(decrypted);
|
|
10487
|
-
const encrypted =
|
|
10488
|
-
await
|
|
11405
|
+
const encrypted = encryptConfigSecrets2(decrypted, vault);
|
|
11406
|
+
await atomicWrite6(globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
10489
11407
|
};
|
|
10490
11408
|
const next = holder.lock.then(write);
|
|
10491
11409
|
holder.lock = next.then(
|
|
@@ -10528,6 +11446,16 @@ async function persistPrefsToConfig(deps2, holder, payload) {
|
|
|
10528
11446
|
setAutonomy("enhanceDelayMs", payload["enhanceDelayMs"]);
|
|
10529
11447
|
if (typeof payload["enhanceLanguage"] === "string")
|
|
10530
11448
|
setAutonomy("enhanceLanguage", payload["enhanceLanguage"]);
|
|
11449
|
+
if (typeof payload["refinerProvider"] === "string")
|
|
11450
|
+
setAutonomy("refinerProvider", payload["refinerProvider"]);
|
|
11451
|
+
if (typeof payload["refinerModel"] === "string")
|
|
11452
|
+
setAutonomy("refinerModel", payload["refinerModel"]);
|
|
11453
|
+
if (typeof payload["thinkingWord"] === "string")
|
|
11454
|
+
setAutonomy("thinkingWord", payload["thinkingWord"]);
|
|
11455
|
+
if (typeof payload["statuslineMode"] === "string")
|
|
11456
|
+
setAutonomy("statuslineMode", payload["statuslineMode"]);
|
|
11457
|
+
if (typeof payload["animationStyle"] === "string")
|
|
11458
|
+
setAutonomy("animationStyle", payload["animationStyle"]);
|
|
10531
11459
|
if (autonomyTouched) decrypted.autonomy = autonomyCfg;
|
|
10532
11460
|
if (typeof payload["nextPrediction"] === "boolean")
|
|
10533
11461
|
decrypted.nextPrediction = payload["nextPrediction"];
|
|
@@ -10645,15 +11573,38 @@ async function persistPrefsToConfig(deps2, holder, payload) {
|
|
|
10645
11573
|
}
|
|
10646
11574
|
decrypted.modelRuntime = mr;
|
|
10647
11575
|
}
|
|
11576
|
+
if (typeof payload["breakerEnabled"] === "boolean" || typeof payload["breakerAutoKillResetMs"] === "number") {
|
|
11577
|
+
const cb = decrypted.circuitBreaker ?? {};
|
|
11578
|
+
if (typeof payload["breakerEnabled"] === "boolean") cb.enabled = payload["breakerEnabled"];
|
|
11579
|
+
if (typeof payload["breakerAutoKillResetMs"] === "number")
|
|
11580
|
+
cb.autoKillResetMs = payload["breakerAutoKillResetMs"];
|
|
11581
|
+
decrypted.circuitBreaker = cb;
|
|
11582
|
+
}
|
|
11583
|
+
if (payload["fsAccess"] === "unrestricted" || payload["fsAccess"] === "project") {
|
|
11584
|
+
const restrict = payload["fsAccess"] === "project";
|
|
11585
|
+
const toolsCfg = decrypted.tools ?? {};
|
|
11586
|
+
toolsCfg.restrictToProjectRoot = restrict;
|
|
11587
|
+
decrypted.tools = toolsCfg;
|
|
11588
|
+
const featsCfg = decrypted.features ?? {};
|
|
11589
|
+
featsCfg.allowOutsideProjectRoot = !restrict;
|
|
11590
|
+
decrypted.features = featsCfg;
|
|
11591
|
+
}
|
|
11592
|
+
if (typeof payload["debugStream"] === "boolean")
|
|
11593
|
+
decrypted.debugStream = payload["debugStream"];
|
|
10648
11594
|
}, "prefs");
|
|
10649
11595
|
}
|
|
11596
|
+
|
|
11597
|
+
// src/server/projects-manifest.ts
|
|
11598
|
+
import * as fs14 from "node:fs/promises";
|
|
11599
|
+
import * as path19 from "node:path";
|
|
11600
|
+
import { projectSlug } from "@wrongstack/core";
|
|
10650
11601
|
function projectsJsonPath(globalConfigPath) {
|
|
10651
|
-
const base =
|
|
10652
|
-
return
|
|
11602
|
+
const base = path19.dirname(globalConfigPath);
|
|
11603
|
+
return path19.join(base, "projects.json");
|
|
10653
11604
|
}
|
|
10654
11605
|
async function loadManifest(globalConfigPath) {
|
|
10655
11606
|
try {
|
|
10656
|
-
const raw = await
|
|
11607
|
+
const raw = await fs14.readFile(projectsJsonPath(globalConfigPath), "utf8");
|
|
10657
11608
|
const parsed = JSON.parse(raw);
|
|
10658
11609
|
return { projects: parsed.projects ?? [] };
|
|
10659
11610
|
} catch {
|
|
@@ -10662,18 +11613,26 @@ async function loadManifest(globalConfigPath) {
|
|
|
10662
11613
|
}
|
|
10663
11614
|
async function saveManifest(manifest, globalConfigPath) {
|
|
10664
11615
|
const file = projectsJsonPath(globalConfigPath);
|
|
10665
|
-
await
|
|
10666
|
-
await
|
|
11616
|
+
await fs14.mkdir(path19.dirname(file), { recursive: true });
|
|
11617
|
+
await fs14.writeFile(file, JSON.stringify(manifest, null, 2), "utf8");
|
|
10667
11618
|
}
|
|
10668
11619
|
function generateProjectSlug(rootPath) {
|
|
10669
11620
|
return projectSlug(rootPath);
|
|
10670
11621
|
}
|
|
10671
11622
|
async function ensureProjectDataDir(slug, globalConfigPath) {
|
|
10672
|
-
const base =
|
|
10673
|
-
const dir =
|
|
10674
|
-
await
|
|
11623
|
+
const base = path19.dirname(globalConfigPath);
|
|
11624
|
+
const dir = path19.join(base, "projects", slug);
|
|
11625
|
+
await fs14.mkdir(dir, { recursive: true });
|
|
10675
11626
|
return dir;
|
|
10676
11627
|
}
|
|
11628
|
+
|
|
11629
|
+
// src/server/provider-handlers.ts
|
|
11630
|
+
import { DefaultSecretScrubber } from "@wrongstack/core";
|
|
11631
|
+
import {
|
|
11632
|
+
beginOAuthLogin
|
|
11633
|
+
} from "@wrongstack/providers/oauth";
|
|
11634
|
+
import { probeLocalLlm } from "@wrongstack/runtime/probe";
|
|
11635
|
+
import { toErrorMessage as toErrorMessage9 } from "@wrongstack/core/utils";
|
|
10677
11636
|
function projectSavedProviders(providers) {
|
|
10678
11637
|
return Object.entries(providers).map(([id, cfg]) => {
|
|
10679
11638
|
const keys = normalizeKeys(cfg);
|
|
@@ -10704,7 +11663,7 @@ function createProviderHandlers(deps2) {
|
|
|
10704
11663
|
}
|
|
10705
11664
|
async function saveConfigProviders(providers) {
|
|
10706
11665
|
const next = configWriteLock.then(() => saveProviders(globalConfigPath, vault, providers)).catch((err) => {
|
|
10707
|
-
const msg =
|
|
11666
|
+
const msg = toErrorMessage9(err);
|
|
10708
11667
|
console.error(JSON.stringify({
|
|
10709
11668
|
level: "error",
|
|
10710
11669
|
event: "webui.provider_save_failed",
|
|
@@ -10976,10 +11935,23 @@ function createProviderHandlers(deps2) {
|
|
|
10976
11935
|
loadConfigProviders
|
|
10977
11936
|
};
|
|
10978
11937
|
}
|
|
11938
|
+
|
|
11939
|
+
// src/server/routes.ts
|
|
11940
|
+
import path21 from "node:path";
|
|
11941
|
+
import {
|
|
11942
|
+
enhanceUserPrompt,
|
|
11943
|
+
gatedEnhancerReasoning,
|
|
11944
|
+
recentTextTurns,
|
|
11945
|
+
resolveProviderModelList
|
|
11946
|
+
} from "@wrongstack/core";
|
|
11947
|
+
import { makeProviderFromConfig as makeProviderFromConfig2 } from "@wrongstack/providers";
|
|
11948
|
+
|
|
11949
|
+
// src/server/mailbox-handlers.ts
|
|
11950
|
+
import { GlobalMailbox as GlobalMailbox3, resolveProjectDir as resolveProjectDir2 } from "@wrongstack/core";
|
|
10979
11951
|
async function handleMailboxMessages(ws, deps2, payload) {
|
|
10980
11952
|
try {
|
|
10981
|
-
const dir =
|
|
10982
|
-
const mb = new
|
|
11953
|
+
const dir = resolveProjectDir2(deps2.projectRoot, deps2.globalRoot);
|
|
11954
|
+
const mb = new GlobalMailbox3(dir);
|
|
10983
11955
|
const limit = payload?.limit ?? 30;
|
|
10984
11956
|
const unreadForAgent = payload?.unreadOnly === true && payload.agentId !== void 0;
|
|
10985
11957
|
const messages = await mb.query({
|
|
@@ -11019,8 +11991,8 @@ async function handleMailboxMessages(ws, deps2, payload) {
|
|
|
11019
11991
|
}
|
|
11020
11992
|
async function handleMailboxAgents(ws, deps2, payload) {
|
|
11021
11993
|
try {
|
|
11022
|
-
const dir =
|
|
11023
|
-
const mb = new
|
|
11994
|
+
const dir = resolveProjectDir2(deps2.projectRoot, deps2.globalRoot);
|
|
11995
|
+
const mb = new GlobalMailbox3(dir);
|
|
11024
11996
|
const agents = payload?.onlineOnly ? await mb.getOnlineAgents() : await mb.getAgentStatuses();
|
|
11025
11997
|
send(ws, {
|
|
11026
11998
|
type: "mailbox.agents",
|
|
@@ -11048,8 +12020,8 @@ async function handleMailboxAgents(ws, deps2, payload) {
|
|
|
11048
12020
|
}
|
|
11049
12021
|
async function handleMailboxClear(ws, deps2) {
|
|
11050
12022
|
try {
|
|
11051
|
-
const dir =
|
|
11052
|
-
const mb = new
|
|
12023
|
+
const dir = resolveProjectDir2(deps2.projectRoot, deps2.globalRoot);
|
|
12024
|
+
const mb = new GlobalMailbox3(dir);
|
|
11053
12025
|
await mb.clearAll();
|
|
11054
12026
|
send(ws, { type: "mailbox.cleared", payload: {} });
|
|
11055
12027
|
} catch (err) {
|
|
@@ -11058,14 +12030,31 @@ async function handleMailboxClear(ws, deps2) {
|
|
|
11058
12030
|
}
|
|
11059
12031
|
async function handleMailboxPurge(ws, deps2, opts) {
|
|
11060
12032
|
try {
|
|
11061
|
-
const dir =
|
|
11062
|
-
const mb = new
|
|
12033
|
+
const dir = resolveProjectDir2(deps2.projectRoot, deps2.globalRoot);
|
|
12034
|
+
const mb = new GlobalMailbox3(dir);
|
|
11063
12035
|
const result = await mb.purgeStale(opts);
|
|
11064
12036
|
send(ws, { type: "mailbox.purged", payload: result });
|
|
11065
12037
|
} catch (err) {
|
|
11066
12038
|
send(ws, { type: "mailbox.purged", payload: { error: errMessage(err) } });
|
|
11067
12039
|
}
|
|
11068
12040
|
}
|
|
12041
|
+
async function handleMailboxCompact(ws, deps2, opts) {
|
|
12042
|
+
try {
|
|
12043
|
+
const dir = resolveProjectDir2(deps2.projectRoot, deps2.globalRoot);
|
|
12044
|
+
const mb = new GlobalMailbox3(dir);
|
|
12045
|
+
const result = await mb.autoCompact(opts);
|
|
12046
|
+
send(ws, { type: "mailbox.compacted", payload: result });
|
|
12047
|
+
} catch (err) {
|
|
12048
|
+
send(ws, { type: "mailbox.compacted", payload: { error: errMessage(err) } });
|
|
12049
|
+
}
|
|
12050
|
+
}
|
|
12051
|
+
|
|
12052
|
+
// src/server/mode-handlers.ts
|
|
12053
|
+
import {
|
|
12054
|
+
DefaultSystemPromptBuilder as DefaultSystemPromptBuilder2,
|
|
12055
|
+
resolveWstackPaths as resolveWstackPaths2,
|
|
12056
|
+
ToolValidationError as ToolValidationError5
|
|
12057
|
+
} from "@wrongstack/core";
|
|
11069
12058
|
function createModeHandlers(ctx) {
|
|
11070
12059
|
return {
|
|
11071
12060
|
listModes: async (ws) => {
|
|
@@ -11108,14 +12097,14 @@ function createModeHandlers(ctx) {
|
|
|
11108
12097
|
} else {
|
|
11109
12098
|
const found = await ctx.modeStore.getMode(id);
|
|
11110
12099
|
if (!found) {
|
|
11111
|
-
throw new
|
|
12100
|
+
throw new ToolValidationError5({ message: `Unknown mode "${id}"`, field: "modeId" });
|
|
11112
12101
|
}
|
|
11113
12102
|
await ctx.modeStore.setActiveMode(id);
|
|
11114
12103
|
}
|
|
11115
12104
|
ctx.setModeId(id);
|
|
11116
12105
|
const modePrompt = id === "default" ? "" : (await ctx.modeStore.getMode(id))?.prompt ?? "";
|
|
11117
|
-
const paths =
|
|
11118
|
-
const freshBuilder = new
|
|
12106
|
+
const paths = resolveWstackPaths2({ projectRoot: ctx.projectRoot, globalRoot: ctx.globalRoot });
|
|
12107
|
+
const freshBuilder = new DefaultSystemPromptBuilder2({
|
|
11119
12108
|
memoryStore: ctx.memoryStore,
|
|
11120
12109
|
skillLoader: ctx.skillLoader,
|
|
11121
12110
|
modeStore: ctx.modeStore,
|
|
@@ -11145,6 +12134,9 @@ function createModeHandlers(ctx) {
|
|
|
11145
12134
|
}
|
|
11146
12135
|
};
|
|
11147
12136
|
}
|
|
12137
|
+
|
|
12138
|
+
// src/server/project-handlers.ts
|
|
12139
|
+
import * as path20 from "node:path";
|
|
11148
12140
|
function createProjectHandlers(ctx) {
|
|
11149
12141
|
return {
|
|
11150
12142
|
listProjects: async (ws) => {
|
|
@@ -11170,7 +12162,7 @@ function createProjectHandlers(ctx) {
|
|
|
11170
12162
|
selectProject: async (ws, msg) => {
|
|
11171
12163
|
const payload = msg.payload;
|
|
11172
12164
|
const root = typeof payload?.root === "string" ? payload.root : "";
|
|
11173
|
-
const name2 = typeof payload?.name === "string" ? payload.name : root ?
|
|
12165
|
+
const name2 = typeof payload?.name === "string" ? payload.name : root ? path20.basename(root) : "";
|
|
11174
12166
|
send(ws, {
|
|
11175
12167
|
type: "projects.selected",
|
|
11176
12168
|
payload: {
|
|
@@ -11203,6 +12195,14 @@ function createProjectHandlers(ctx) {
|
|
|
11203
12195
|
}
|
|
11204
12196
|
};
|
|
11205
12197
|
}
|
|
12198
|
+
|
|
12199
|
+
// src/server/session-handlers.ts
|
|
12200
|
+
import {
|
|
12201
|
+
DEFAULT_CONTEXT_WINDOW_MODE_ID,
|
|
12202
|
+
repairToolUseAdjacency,
|
|
12203
|
+
resolveContextWindowPolicy as resolveContextWindowPolicy3
|
|
12204
|
+
} from "@wrongstack/core";
|
|
12205
|
+
import { sessionScopedPath as sessionScopedPath2 } from "@wrongstack/core/utils";
|
|
11206
12206
|
function createSessionHandlers(ctx) {
|
|
11207
12207
|
const currentSessionId = () => ctx.getSession().id;
|
|
11208
12208
|
const sessionPayload2 = (payload) => {
|
|
@@ -11228,33 +12228,46 @@ function createSessionHandlers(ctx) {
|
|
|
11228
12228
|
});
|
|
11229
12229
|
return false;
|
|
11230
12230
|
};
|
|
12231
|
+
const finalizeSession = async (writer) => {
|
|
12232
|
+
await writer.append({
|
|
12233
|
+
type: "session_end",
|
|
12234
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12235
|
+
usage: ctx.tokenCounter.total()
|
|
12236
|
+
}).catch(() => void 0);
|
|
12237
|
+
await writer.close().catch(() => void 0);
|
|
12238
|
+
};
|
|
12239
|
+
const activateSession = async (next, messages, usage) => {
|
|
12240
|
+
const current = ctx.getSession();
|
|
12241
|
+
if (current !== next) await finalizeSession(current);
|
|
12242
|
+
ctx.setSession(next);
|
|
12243
|
+
ctx.context.session = next;
|
|
12244
|
+
ctx.context.state.replaceMessages(messages);
|
|
12245
|
+
ctx.context.state.replaceTodos([]);
|
|
12246
|
+
ctx.context.readFiles.clear();
|
|
12247
|
+
ctx.context.fileMtimes.clear();
|
|
12248
|
+
ctx.context.state.setMeta(
|
|
12249
|
+
"plan.path",
|
|
12250
|
+
sessionScopedPath2(ctx.sessionsDir, next.id, ".plan.json")
|
|
12251
|
+
);
|
|
12252
|
+
ctx.context.state.setMeta(
|
|
12253
|
+
"task.path",
|
|
12254
|
+
sessionScopedPath2(ctx.sessionsDir, next.id, ".tasks.json")
|
|
12255
|
+
);
|
|
12256
|
+
ctx.tokenCounter.reset();
|
|
12257
|
+
if (usage) ctx.tokenCounter.account(usage, ctx.config.model);
|
|
12258
|
+
ctx.setSessionStartedAt(Date.now());
|
|
12259
|
+
await ctx.onSessionSwapped(next.id);
|
|
12260
|
+
};
|
|
11231
12261
|
return {
|
|
11232
12262
|
newSession: async (ws, msg) => {
|
|
11233
12263
|
if (!ensureCurrentSession(ws, msg, "session.new")) return;
|
|
11234
|
-
const session = ctx.getSession();
|
|
11235
|
-
try {
|
|
11236
|
-
await session.append({
|
|
11237
|
-
type: "session_end",
|
|
11238
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11239
|
-
usage: ctx.tokenCounter.total()
|
|
11240
|
-
});
|
|
11241
|
-
await session.close();
|
|
11242
|
-
} catch {
|
|
11243
|
-
}
|
|
11244
12264
|
const next = await ctx.getSessionStore().create({
|
|
11245
12265
|
id: "",
|
|
11246
12266
|
title: "",
|
|
11247
12267
|
model: ctx.config.model,
|
|
11248
12268
|
provider: ctx.config.provider
|
|
11249
12269
|
});
|
|
11250
|
-
|
|
11251
|
-
ctx.context.session = next;
|
|
11252
|
-
ctx.context.state.replaceMessages([]);
|
|
11253
|
-
ctx.context.state.replaceTodos([]);
|
|
11254
|
-
ctx.context.readFiles.clear();
|
|
11255
|
-
ctx.context.fileMtimes.clear();
|
|
11256
|
-
ctx.tokenCounter.reset();
|
|
11257
|
-
ctx.setSessionStartedAt(Date.now());
|
|
12270
|
+
await activateSession(next, []);
|
|
11258
12271
|
broadcast(ctx.clients, { type: "session.start", payload: await ctx.sessionStartPayload() });
|
|
11259
12272
|
},
|
|
11260
12273
|
clearContext: async (ws, msg) => {
|
|
@@ -11356,7 +12369,7 @@ function createSessionHandlers(ctx) {
|
|
|
11356
12369
|
return;
|
|
11357
12370
|
}
|
|
11358
12371
|
const { id } = parsed.value;
|
|
11359
|
-
let policy =
|
|
12372
|
+
let policy = resolveContextWindowPolicy3({}, id);
|
|
11360
12373
|
if (policy.id !== id) {
|
|
11361
12374
|
const customModes = ctx.customModeStore.list().filter((m) => m.custom === true);
|
|
11362
12375
|
const custom = customModes.find((m) => m.id === id);
|
|
@@ -11428,7 +12441,7 @@ function createSessionHandlers(ctx) {
|
|
|
11428
12441
|
const { id } = parsed.value;
|
|
11429
12442
|
if (String(ctx.context.meta["contextWindowMode"] ?? "") === id) {
|
|
11430
12443
|
ctx.context.meta["contextWindowMode"] = DEFAULT_CONTEXT_WINDOW_MODE_ID;
|
|
11431
|
-
ctx.context.meta["contextWindowPolicy"] =
|
|
12444
|
+
ctx.context.meta["contextWindowPolicy"] = resolveContextWindowPolicy3({}, DEFAULT_CONTEXT_WINDOW_MODE_ID);
|
|
11432
12445
|
}
|
|
11433
12446
|
const result = ctx.customModeStore.remove(id);
|
|
11434
12447
|
sendResult(ws, result.ok, result.error ?? `Mode "${id}" deleted`);
|
|
@@ -11510,23 +12523,7 @@ function createSessionHandlers(ctx) {
|
|
|
11510
12523
|
return;
|
|
11511
12524
|
}
|
|
11512
12525
|
const resumed = await ctx.getSessionStore().resume(id);
|
|
11513
|
-
|
|
11514
|
-
await current.append({
|
|
11515
|
-
type: "session_end",
|
|
11516
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11517
|
-
usage: ctx.tokenCounter.total()
|
|
11518
|
-
});
|
|
11519
|
-
await current.close();
|
|
11520
|
-
} catch {
|
|
11521
|
-
}
|
|
11522
|
-
ctx.setSession(resumed.writer);
|
|
11523
|
-
ctx.context.session = resumed.writer;
|
|
11524
|
-
ctx.context.state.replaceMessages(resumed.data.messages);
|
|
11525
|
-
ctx.context.readFiles.clear();
|
|
11526
|
-
ctx.context.fileMtimes.clear();
|
|
11527
|
-
ctx.tokenCounter.reset();
|
|
11528
|
-
ctx.tokenCounter.account(resumed.data.usage, ctx.config.model);
|
|
11529
|
-
ctx.setSessionStartedAt(Date.now());
|
|
12526
|
+
await activateSession(resumed.writer, resumed.data.messages, resumed.data.usage);
|
|
11530
12527
|
broadcast(ctx.clients, {
|
|
11531
12528
|
type: "session.start",
|
|
11532
12529
|
payload: {
|
|
@@ -11548,12 +12545,9 @@ function createSessionHandlers(ctx) {
|
|
|
11548
12545
|
listCheckpoints: async (ws, msg) => {
|
|
11549
12546
|
if (!ensureCurrentSession(ws, msg, "session.checkpoints")) return;
|
|
11550
12547
|
try {
|
|
11551
|
-
const { DefaultSessionRewinder } = await import(
|
|
12548
|
+
const { DefaultSessionRewinder } = await import("@wrongstack/core");
|
|
11552
12549
|
const projectRoot = ctx.getProjectRoot();
|
|
11553
|
-
const rewinder = new DefaultSessionRewinder(
|
|
11554
|
-
path5.join(projectRoot, ".wrongstack", "sessions"),
|
|
11555
|
-
projectRoot
|
|
11556
|
-
);
|
|
12550
|
+
const rewinder = new DefaultSessionRewinder(ctx.sessionsDir, projectRoot);
|
|
11557
12551
|
const checkpoints = await rewinder.listCheckpoints(ctx.getSession().id);
|
|
11558
12552
|
send(ws, { type: "session.checkpoints", payload: sessionPayload2({ checkpoints }) });
|
|
11559
12553
|
} catch {
|
|
@@ -11564,12 +12558,9 @@ function createSessionHandlers(ctx) {
|
|
|
11564
12558
|
if (!ensureCurrentSession(ws, msg, "session.rewind")) return;
|
|
11565
12559
|
const { checkpointIndex } = msg.payload;
|
|
11566
12560
|
try {
|
|
11567
|
-
const { DefaultSessionRewinder } = await import(
|
|
12561
|
+
const { DefaultSessionRewinder } = await import("@wrongstack/core");
|
|
11568
12562
|
const projectRoot = ctx.getProjectRoot();
|
|
11569
|
-
const rewinder = new DefaultSessionRewinder(
|
|
11570
|
-
path5.join(projectRoot, ".wrongstack", "sessions"),
|
|
11571
|
-
projectRoot
|
|
11572
|
-
);
|
|
12563
|
+
const rewinder = new DefaultSessionRewinder(ctx.sessionsDir, projectRoot);
|
|
11573
12564
|
await rewinder.rewindToCheckpoint(ctx.getSession().id, checkpointIndex);
|
|
11574
12565
|
await ctx.context.session.truncateToCheckpoint(checkpointIndex);
|
|
11575
12566
|
sendResult(ws, true, `Rewound to checkpoint ${checkpointIndex}`);
|
|
@@ -11652,11 +12643,13 @@ function buildRoutes(state, deps2, cb) {
|
|
|
11652
12643
|
const saved = await providerHandlers.loadConfigProviders();
|
|
11653
12644
|
const cfg = saved[providerId];
|
|
11654
12645
|
const provider = await resolveProviderCatalogForModels(deps2.modelsRegistry, providerId, cfg);
|
|
12646
|
+
const siblingId = SIBLING_CATALOG[providerId];
|
|
12647
|
+
const siblingCatalog = siblingId && siblingId !== providerId ? await deps2.modelsRegistry.getProvider(siblingId).catch(() => void 0) : void 0;
|
|
11655
12648
|
const models = await enrichProviderModelDescriptors(
|
|
11656
12649
|
deps2.modelsRegistry,
|
|
11657
12650
|
providerId,
|
|
11658
12651
|
cfg,
|
|
11659
|
-
resolveProviderModelList(cfg?.models, provider, cfg?.type ?? providerId)
|
|
12652
|
+
resolveProviderModelList(cfg?.models, provider, cfg?.type ?? providerId, siblingCatalog)
|
|
11660
12653
|
);
|
|
11661
12654
|
send(ws, {
|
|
11662
12655
|
type: "provider.models",
|
|
@@ -11682,7 +12675,7 @@ function buildRoutes(state, deps2, cb) {
|
|
|
11682
12675
|
const providerCfg = newCfg.providers?.[newProvider] ?? {
|
|
11683
12676
|
type: newProvider
|
|
11684
12677
|
};
|
|
11685
|
-
const newProv = deps2.providerRegistry.has(newProvider) ? deps2.providerRegistry.create({ ...providerCfg, type: newProvider }) :
|
|
12678
|
+
const newProv = deps2.providerRegistry.has(newProvider) ? deps2.providerRegistry.create({ ...providerCfg, type: newProvider }) : makeProviderFromConfig2(newProvider, providerCfg);
|
|
11686
12679
|
deps2.context.provider = newProv;
|
|
11687
12680
|
await cb.updateAutoCompactionMaxContext(newProv, newProvider, providerCfg);
|
|
11688
12681
|
await cb.updateGlobalConfig((config) => {
|
|
@@ -11783,8 +12776,10 @@ function buildRoutes(state, deps2, cb) {
|
|
|
11783
12776
|
getProjectRoot: state.getProjectRoot,
|
|
11784
12777
|
getSession: state.getSession,
|
|
11785
12778
|
getSessionStore: state.getSessionStore,
|
|
12779
|
+
sessionsDir: deps2.wpaths.projectSessions,
|
|
11786
12780
|
setSession: state.setSession,
|
|
11787
12781
|
setSessionStartedAt: state.setSessionStartedAt,
|
|
12782
|
+
onSessionSwapped: cb.onSessionSwapped,
|
|
11788
12783
|
sessionStartPayload: cb.sessionStartPayload
|
|
11789
12784
|
});
|
|
11790
12785
|
const projectRoutes = createProjectHandlers({
|
|
@@ -11879,6 +12874,17 @@ function buildRoutes(state, deps2, cb) {
|
|
|
11879
12874
|
deps2.pipelines.contextWindow.remove("AutoCompaction", { optional: true });
|
|
11880
12875
|
}
|
|
11881
12876
|
}
|
|
12877
|
+
if (typeof payload["breakerEnabled"] === "boolean" || typeof payload["breakerAutoKillResetMs"] === "number") {
|
|
12878
|
+
const { getProcessRegistry } = await import("@wrongstack/tools");
|
|
12879
|
+
getProcessRegistry().setBreakerConfig({
|
|
12880
|
+
...typeof payload["breakerEnabled"] === "boolean" ? { enabled: payload["breakerEnabled"] } : {},
|
|
12881
|
+
...typeof payload["breakerAutoKillResetMs"] === "number" ? { autoKillResetMs: payload["breakerAutoKillResetMs"] } : {}
|
|
12882
|
+
});
|
|
12883
|
+
}
|
|
12884
|
+
if (typeof payload["debugStream"] === "boolean") {
|
|
12885
|
+
const { setDebugStreamEnabled } = await import("@wrongstack/providers");
|
|
12886
|
+
setDebugStreamEnabled(payload["debugStream"]);
|
|
12887
|
+
}
|
|
11882
12888
|
if (typeof payload["logLevel"] === "string") {
|
|
11883
12889
|
const valid = ["debug", "info", "warn", "error"];
|
|
11884
12890
|
if (valid.includes(payload["logLevel"])) {
|
|
@@ -11925,7 +12931,7 @@ function buildRoutes(state, deps2, cb) {
|
|
|
11925
12931
|
}
|
|
11926
12932
|
return handleMailboxMessages(
|
|
11927
12933
|
ws,
|
|
11928
|
-
{ projectRoot: state.getProjectRoot(), globalRoot:
|
|
12934
|
+
{ projectRoot: state.getProjectRoot(), globalRoot: path21.dirname(deps2.globalConfigPath) },
|
|
11929
12935
|
parsed.value
|
|
11930
12936
|
);
|
|
11931
12937
|
},
|
|
@@ -11937,13 +12943,13 @@ function buildRoutes(state, deps2, cb) {
|
|
|
11937
12943
|
}
|
|
11938
12944
|
return handleMailboxAgents(
|
|
11939
12945
|
ws,
|
|
11940
|
-
{ projectRoot: state.getProjectRoot(), globalRoot:
|
|
12946
|
+
{ projectRoot: state.getProjectRoot(), globalRoot: path21.dirname(deps2.globalConfigPath) },
|
|
11941
12947
|
parsed.value
|
|
11942
12948
|
);
|
|
11943
12949
|
},
|
|
11944
12950
|
clear: (ws) => handleMailboxClear(ws, {
|
|
11945
12951
|
projectRoot: state.getProjectRoot(),
|
|
11946
|
-
globalRoot:
|
|
12952
|
+
globalRoot: path21.dirname(deps2.globalConfigPath)
|
|
11947
12953
|
}),
|
|
11948
12954
|
purge: (ws, msg) => {
|
|
11949
12955
|
const parsed = validateMailboxPurgePayload(msg.payload);
|
|
@@ -11953,9 +12959,16 @@ function buildRoutes(state, deps2, cb) {
|
|
|
11953
12959
|
}
|
|
11954
12960
|
return handleMailboxPurge(
|
|
11955
12961
|
ws,
|
|
11956
|
-
{ projectRoot: state.getProjectRoot(), globalRoot:
|
|
12962
|
+
{ projectRoot: state.getProjectRoot(), globalRoot: path21.dirname(deps2.globalConfigPath) },
|
|
11957
12963
|
parsed.value
|
|
11958
12964
|
);
|
|
12965
|
+
},
|
|
12966
|
+
compact: (ws, msg) => {
|
|
12967
|
+
return handleMailboxCompact(
|
|
12968
|
+
ws,
|
|
12969
|
+
{ projectRoot: state.getProjectRoot(), globalRoot: path21.dirname(deps2.globalConfigPath) },
|
|
12970
|
+
msg.payload ?? {}
|
|
12971
|
+
);
|
|
11959
12972
|
}
|
|
11960
12973
|
};
|
|
11961
12974
|
const mcpRoutes = {
|
|
@@ -11968,14 +12981,29 @@ function buildRoutes(state, deps2, cb) {
|
|
|
11968
12981
|
sleep: (ws, msg) => handleMcpSleep(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
11969
12982
|
wake: (ws, msg) => handleMcpWake(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
11970
12983
|
restart: (ws, msg) => handleMcpRestart(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
11971
|
-
discover: (ws, msg) => handleMcpDiscover(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry)
|
|
12984
|
+
discover: (ws, msg) => handleMcpDiscover(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
12985
|
+
resources: (ws, msg) => handleMcpResources(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
12986
|
+
prompts: (ws, msg) => handleMcpPrompts(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
12987
|
+
resourceRead: (ws, msg) => handleMcpResourceRead(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry),
|
|
12988
|
+
promptGet: (ws, msg) => handleMcpPromptGet(ws, msg, deps2.globalConfigPath, deps2.mcpRegistry)
|
|
12989
|
+
};
|
|
12990
|
+
const sendBrainStatus = (ws) => {
|
|
12991
|
+
const snapshot = deps2.brainRuntime.getSnapshot();
|
|
12992
|
+
send(ws, {
|
|
12993
|
+
type: "brain.status",
|
|
12994
|
+
payload: {
|
|
12995
|
+
maxAutoRisk: deps2.brainSettings.maxAutoRisk,
|
|
12996
|
+
log: deps2.brainLog,
|
|
12997
|
+
mode: snapshot.mode,
|
|
12998
|
+
poolLabels: snapshot.poolLabels,
|
|
12999
|
+
councilLabels: snapshot.councilLabels,
|
|
13000
|
+
ledgerPath: snapshot.ledger.path
|
|
13001
|
+
}
|
|
13002
|
+
});
|
|
11972
13003
|
};
|
|
11973
13004
|
const brainRoutes = {
|
|
11974
13005
|
status: (ws) => {
|
|
11975
|
-
|
|
11976
|
-
type: "brain.status",
|
|
11977
|
-
payload: { maxAutoRisk: deps2.brainSettings.maxAutoRisk, log: deps2.brainLog }
|
|
11978
|
-
});
|
|
13006
|
+
sendBrainStatus(ws);
|
|
11979
13007
|
},
|
|
11980
13008
|
risk: (ws, msg) => {
|
|
11981
13009
|
const parsed = validateBrainRiskPayload(msg.payload);
|
|
@@ -11985,11 +13013,43 @@ function buildRoutes(state, deps2, cb) {
|
|
|
11985
13013
|
}
|
|
11986
13014
|
const { level } = parsed.value;
|
|
11987
13015
|
deps2.brainSettings.maxAutoRisk = level;
|
|
13016
|
+
sendBrainStatus(ws);
|
|
13017
|
+
},
|
|
13018
|
+
configGet: (ws) => {
|
|
11988
13019
|
send(ws, {
|
|
11989
|
-
type: "brain.
|
|
11990
|
-
payload: {
|
|
13020
|
+
type: "brain.config",
|
|
13021
|
+
payload: { config: deps2.brainRuntime.getSnapshot(), persisted: true }
|
|
11991
13022
|
});
|
|
11992
13023
|
},
|
|
13024
|
+
configSet: async (ws, msg) => {
|
|
13025
|
+
const parsed = validateBrainConfigSetPayload(msg.payload);
|
|
13026
|
+
if (!parsed.ok) {
|
|
13027
|
+
sendResult(ws, false, parsed.message);
|
|
13028
|
+
return;
|
|
13029
|
+
}
|
|
13030
|
+
try {
|
|
13031
|
+
const { persisted } = deps2.brainRuntime.apply(parsed.value.patch);
|
|
13032
|
+
const result = await persisted;
|
|
13033
|
+
send(ws, {
|
|
13034
|
+
type: "brain.config",
|
|
13035
|
+
payload: {
|
|
13036
|
+
config: deps2.brainRuntime.getSnapshot(),
|
|
13037
|
+
persisted: result.ok,
|
|
13038
|
+
...result.ok ? {} : { error: result.error ?? "Persist failed." }
|
|
13039
|
+
}
|
|
13040
|
+
});
|
|
13041
|
+
sendBrainStatus(ws);
|
|
13042
|
+
} catch (err) {
|
|
13043
|
+
send(ws, {
|
|
13044
|
+
type: "brain.config",
|
|
13045
|
+
payload: {
|
|
13046
|
+
config: deps2.brainRuntime.getSnapshot(),
|
|
13047
|
+
persisted: false,
|
|
13048
|
+
error: `Invalid Brain setting: ${errMessage(err)}`
|
|
13049
|
+
}
|
|
13050
|
+
});
|
|
13051
|
+
}
|
|
13052
|
+
},
|
|
11993
13053
|
ask: async (ws, msg) => {
|
|
11994
13054
|
const parsed = validateBrainAskPayload(msg.payload);
|
|
11995
13055
|
if (!parsed.ok) {
|
|
@@ -12061,7 +13121,7 @@ async function startWebUI(opts = {}) {
|
|
|
12061
13121
|
const updateGlobalConfig2 = async (mutate, errorLabel) => updateGlobalConfig(prefHelperDeps, configWriteLock, mutate, errorLabel);
|
|
12062
13122
|
console.log("[WebUI] Config loaded:", config.provider ?? "(none)", "/", config.model ?? "(none)");
|
|
12063
13123
|
if (!config.provider && config.providers && typeof config.providers === "object" && config.providers !== null && !Array.isArray(config.providers) && Object.keys(config.providers).length > 0) {
|
|
12064
|
-
const firstKey =
|
|
13124
|
+
const firstKey = expectDefined3(Object.keys(config.providers)[0]);
|
|
12065
13125
|
config = patchConfig(config, { provider: firstKey });
|
|
12066
13126
|
console.log("[WebUI] No active provider \u2014 auto-selected:", firstKey);
|
|
12067
13127
|
}
|
|
@@ -12097,7 +13157,8 @@ async function startWebUI(opts = {}) {
|
|
|
12097
13157
|
promptsCtx,
|
|
12098
13158
|
modelCapabilitiesRef,
|
|
12099
13159
|
provider,
|
|
12100
|
-
context
|
|
13160
|
+
context,
|
|
13161
|
+
sessionIdentity
|
|
12101
13162
|
} = preContext;
|
|
12102
13163
|
let sessionStore = preContext.sessionStore;
|
|
12103
13164
|
let session = preContext.session;
|
|
@@ -12130,7 +13191,12 @@ async function startWebUI(opts = {}) {
|
|
|
12130
13191
|
modelCapabilitiesRef,
|
|
12131
13192
|
sessionGetter: () => session,
|
|
12132
13193
|
sessionReader,
|
|
12133
|
-
annotationsStore
|
|
13194
|
+
annotationsStore,
|
|
13195
|
+
// Brain settings persist to the GLOBAL config only (config.brain is on
|
|
13196
|
+
// the in-project deny list), serialized behind the shared write lock.
|
|
13197
|
+
persistBrainConfig: (brainConfig) => updateGlobalConfig2((decrypted) => {
|
|
13198
|
+
decrypted["brain"] = brainConfig;
|
|
13199
|
+
}, "brain.config")
|
|
12134
13200
|
});
|
|
12135
13201
|
const {
|
|
12136
13202
|
compactor,
|
|
@@ -12140,6 +13206,7 @@ async function startWebUI(opts = {}) {
|
|
|
12140
13206
|
pipelines,
|
|
12141
13207
|
brain,
|
|
12142
13208
|
brainSettings,
|
|
13209
|
+
brainRuntime,
|
|
12143
13210
|
brainLog,
|
|
12144
13211
|
brainMonitor,
|
|
12145
13212
|
codebaseIndexing,
|
|
@@ -12161,7 +13228,7 @@ async function startWebUI(opts = {}) {
|
|
|
12161
13228
|
getProjectRoot: () => projectRoot,
|
|
12162
13229
|
getWorkingDir: () => workingDir,
|
|
12163
13230
|
getModeId: () => modeId,
|
|
12164
|
-
getContextMode: () => String(context.meta["contextWindowMode"] ??
|
|
13231
|
+
getContextMode: () => String(context.meta["contextWindowMode"] ?? DEFAULT_CONTEXT_WINDOW_MODE_ID2),
|
|
12165
13232
|
getNeedsSetup: () => needsSetup,
|
|
12166
13233
|
modelsRegistry
|
|
12167
13234
|
});
|
|
@@ -12204,21 +13271,21 @@ async function startWebUI(opts = {}) {
|
|
|
12204
13271
|
wpaths
|
|
12205
13272
|
}, watcherMetricsRef);
|
|
12206
13273
|
async function touchProjectEntry(root, workDir) {
|
|
12207
|
-
const resolved =
|
|
13274
|
+
const resolved = path22.resolve(root);
|
|
12208
13275
|
const manifest = await loadManifest(globalConfigPath);
|
|
12209
13276
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
12210
|
-
const existing = manifest.projects.find((p) =>
|
|
13277
|
+
const existing = manifest.projects.find((p) => path22.resolve(p.root) === resolved);
|
|
12211
13278
|
if (existing) {
|
|
12212
13279
|
existing.lastSeen = now;
|
|
12213
|
-
if (workDir) existing.lastWorkingDir =
|
|
13280
|
+
if (workDir) existing.lastWorkingDir = path22.resolve(workDir);
|
|
12214
13281
|
} else {
|
|
12215
13282
|
manifest.projects.push({
|
|
12216
|
-
name:
|
|
13283
|
+
name: path22.basename(resolved),
|
|
12217
13284
|
root: resolved,
|
|
12218
13285
|
slug: generateProjectSlug(resolved),
|
|
12219
13286
|
createdAt: now,
|
|
12220
13287
|
lastSeen: now,
|
|
12221
|
-
lastWorkingDir: workDir ?
|
|
13288
|
+
lastWorkingDir: workDir ? path22.resolve(workDir) : void 0
|
|
12222
13289
|
});
|
|
12223
13290
|
}
|
|
12224
13291
|
await saveManifest(manifest, globalConfigPath);
|
|
@@ -12310,10 +13377,12 @@ async function startWebUI(opts = {}) {
|
|
|
12310
13377
|
terminalHandler,
|
|
12311
13378
|
brain,
|
|
12312
13379
|
brainSettings,
|
|
13380
|
+
brainRuntime,
|
|
12313
13381
|
brainLog
|
|
12314
13382
|
};
|
|
12315
13383
|
const cb = {
|
|
12316
13384
|
sessionStartPayload,
|
|
13385
|
+
onSessionSwapped: (sessionId) => sessionIdentity.activate(sessionId),
|
|
12317
13386
|
updateAutoCompactionMaxContext,
|
|
12318
13387
|
updateGlobalConfig: updateGlobalConfig2,
|
|
12319
13388
|
persistPrefsToConfig: persistPrefsToConfig2,
|
|
@@ -12363,13 +13432,13 @@ async function startWebUI(opts = {}) {
|
|
|
12363
13432
|
...snapshot.apiKey !== void 0 ? { apiKey: snapshot.apiKey } : {},
|
|
12364
13433
|
...snapshot.baseUrl !== void 0 ? { baseUrl: snapshot.baseUrl } : {}
|
|
12365
13434
|
};
|
|
12366
|
-
const newProv = deps2.providerRegistry.has(activeId) ? deps2.providerRegistry.create({ ...providerCfg, type: activeId }) :
|
|
13435
|
+
const newProv = deps2.providerRegistry.has(activeId) ? deps2.providerRegistry.create({ ...providerCfg, type: activeId }) : makeProviderFromConfig3(activeId, { ...providerCfg, type: activeId });
|
|
12367
13436
|
deps2.context.provider = newProv;
|
|
12368
13437
|
void updateAutoCompactionMaxContext(newProv).catch(() => void 0);
|
|
12369
13438
|
console.log(`[WebUI] Provider credentials reloaded from config.json (${activeId})`);
|
|
12370
13439
|
} catch (err) {
|
|
12371
13440
|
console.warn(
|
|
12372
|
-
`[WebUI] Credential hot-reload failed for ${activeId}: ${
|
|
13441
|
+
`[WebUI] Credential hot-reload failed for ${activeId}: ${toErrorMessage10(err)}`
|
|
12373
13442
|
);
|
|
12374
13443
|
}
|
|
12375
13444
|
},
|
|
@@ -12436,20 +13505,36 @@ async function startWebUI(opts = {}) {
|
|
|
12436
13505
|
},
|
|
12437
13506
|
clients: () => clients.keys(),
|
|
12438
13507
|
servers: [httpServer, wssPrimary, ...wssSecondary ? [wssSecondary] : []],
|
|
12439
|
-
onShutdown: () => {
|
|
13508
|
+
onShutdown: async () => {
|
|
12440
13509
|
credentialWatcherClose?.();
|
|
12441
13510
|
brainMonitor.stop();
|
|
12442
|
-
|
|
13511
|
+
await agentServices.brainLedger?.stop().catch(() => {
|
|
13512
|
+
});
|
|
13513
|
+
await mcpRegistry.stopAll().catch(() => void 0);
|
|
13514
|
+
await sessionIdentity.stop();
|
|
12443
13515
|
eventArming.getDispose()?.();
|
|
12444
13516
|
if (eternalSubscription) {
|
|
12445
13517
|
eternalSubscription.dispose();
|
|
12446
13518
|
eternalSubscription = null;
|
|
12447
13519
|
}
|
|
12448
13520
|
codebaseIndexing.dispose();
|
|
12449
|
-
|
|
13521
|
+
if (config.superMemory?.enabled !== false && config.superMemory?.hygiene?.autoAfterSession !== false) {
|
|
13522
|
+
const candidate = memoryStore;
|
|
13523
|
+
await candidate.hygiene?.({
|
|
13524
|
+
retentionDays: config.superMemory?.hygiene?.retentionDays,
|
|
13525
|
+
archiveLowConfidenceAfterDays: config.superMemory?.hygiene?.archiveLowConfidenceAfterDays
|
|
13526
|
+
}).catch((err) => logger.warn(`super-memory session hygiene failed: ${toErrorMessage10(err)}`));
|
|
13527
|
+
}
|
|
13528
|
+
await unregisterInstance(process.pid, path22.dirname(globalConfigPath));
|
|
12450
13529
|
}
|
|
12451
13530
|
});
|
|
12452
13531
|
}
|
|
13532
|
+
|
|
13533
|
+
// src/server/provider-store.ts
|
|
13534
|
+
import { expectDefined as expectDefined4 } from "@wrongstack/core";
|
|
13535
|
+
import * as fs15 from "node:fs/promises";
|
|
13536
|
+
import { decryptConfigSecrets as decryptConfigSecrets3, encryptConfigSecrets as encryptConfigSecrets3 } from "@wrongstack/core/security";
|
|
13537
|
+
import { atomicWrite as atomicWrite7 } from "@wrongstack/core";
|
|
12453
13538
|
function createConfigWriteLock() {
|
|
12454
13539
|
let lock = Promise.resolve();
|
|
12455
13540
|
return {
|
|
@@ -12471,10 +13556,10 @@ function createProviderStore(deps2) {
|
|
|
12471
13556
|
const configWriteLock = createConfigWriteLock();
|
|
12472
13557
|
async function loadSavedProviders2() {
|
|
12473
13558
|
try {
|
|
12474
|
-
const raw = await
|
|
13559
|
+
const raw = await fs15.readFile(globalConfigPath, "utf8");
|
|
12475
13560
|
const parsed = JSON.parse(raw);
|
|
12476
13561
|
if (!parsed.providers) return {};
|
|
12477
|
-
return
|
|
13562
|
+
return decryptConfigSecrets3(parsed.providers, vault);
|
|
12478
13563
|
} catch {
|
|
12479
13564
|
return {};
|
|
12480
13565
|
}
|
|
@@ -12485,14 +13570,14 @@ function createProviderStore(deps2) {
|
|
|
12485
13570
|
await prev;
|
|
12486
13571
|
let parsed;
|
|
12487
13572
|
try {
|
|
12488
|
-
const raw = await
|
|
13573
|
+
const raw = await fs15.readFile(globalConfigPath, "utf8");
|
|
12489
13574
|
parsed = JSON.parse(raw);
|
|
12490
13575
|
} catch {
|
|
12491
13576
|
parsed = {};
|
|
12492
13577
|
}
|
|
12493
13578
|
parsed["providers"] = providers;
|
|
12494
|
-
const encrypted =
|
|
12495
|
-
await
|
|
13579
|
+
const encrypted = encryptConfigSecrets3(parsed, vault);
|
|
13580
|
+
await atomicWrite7(globalConfigPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
12496
13581
|
} finally {
|
|
12497
13582
|
release();
|
|
12498
13583
|
}
|
|
@@ -12514,7 +13599,7 @@ function createProviderStore(deps2) {
|
|
|
12514
13599
|
return;
|
|
12515
13600
|
}
|
|
12516
13601
|
cfg.apiKeys = keys;
|
|
12517
|
-
const active = keys.find((k) => k.label === cfg.activeKey) ??
|
|
13602
|
+
const active = keys.find((k) => k.label === cfg.activeKey) ?? expectDefined4(keys[0]);
|
|
12518
13603
|
delete cfg.apiKey;
|
|
12519
13604
|
if (!cfg.activeKey || !keys.some((k) => k.label === cfg.activeKey)) {
|
|
12520
13605
|
cfg.activeKey = active.label;
|
|
@@ -12533,7 +13618,190 @@ function createProviderStore(deps2) {
|
|
|
12533
13618
|
maskedKey: maskedKey2
|
|
12534
13619
|
};
|
|
12535
13620
|
}
|
|
12536
|
-
|
|
12537
|
-
|
|
13621
|
+
export {
|
|
13622
|
+
AutoPhaseWebSocketHandler,
|
|
13623
|
+
CollaborationWebSocketHandler,
|
|
13624
|
+
SKIP_DIRS,
|
|
13625
|
+
SddBoardWebSocketHandler,
|
|
13626
|
+
SddWizardWebSocketHandler,
|
|
13627
|
+
SpecsWebSocketHandler,
|
|
13628
|
+
TerminalWebSocketHandler,
|
|
13629
|
+
WorktreeWebSocketHandler,
|
|
13630
|
+
addProvider,
|
|
13631
|
+
bootConfig,
|
|
13632
|
+
broadcast,
|
|
13633
|
+
browserOpenCommand,
|
|
13634
|
+
buildCspHeader,
|
|
13635
|
+
buildSddWizardDeps,
|
|
13636
|
+
buildWebUIAccessUrl,
|
|
13637
|
+
clearAnalyticsBuffer,
|
|
13638
|
+
computeUsageCost,
|
|
13639
|
+
createConfigWriteLock,
|
|
13640
|
+
createCustomModeStore,
|
|
13641
|
+
createEternalSubscription,
|
|
13642
|
+
createHttpServer,
|
|
13643
|
+
createProviderConfigIO,
|
|
13644
|
+
createProviderHandlers,
|
|
13645
|
+
createProviderStore,
|
|
13646
|
+
createShutdown,
|
|
13647
|
+
createToolLspCompletionSource,
|
|
13648
|
+
createZipBuffer,
|
|
13649
|
+
decodeSessionId,
|
|
13650
|
+
defaultBaseDir,
|
|
13651
|
+
deleteKey,
|
|
13652
|
+
ensureProjectDataDir,
|
|
13653
|
+
envFlag,
|
|
13654
|
+
errMessage,
|
|
13655
|
+
estimateContextBreakdown,
|
|
13656
|
+
estimateTokens,
|
|
13657
|
+
extractToken,
|
|
13658
|
+
extractTokenFromCookie,
|
|
13659
|
+
findFreePort,
|
|
13660
|
+
formatInstances,
|
|
13661
|
+
generateAuthToken,
|
|
13662
|
+
getAnalyticsBuffer,
|
|
13663
|
+
getCostRates,
|
|
13664
|
+
handleApiAnalyticsGet,
|
|
13665
|
+
handleApiAnalyticsPost,
|
|
13666
|
+
handleApiAnalyticsSummary,
|
|
13667
|
+
handleAutoPhaseRoute,
|
|
13668
|
+
handleBrainRoute,
|
|
13669
|
+
handleCompletionRequest,
|
|
13670
|
+
handleDesignList,
|
|
13671
|
+
handleDesignMaterialize,
|
|
13672
|
+
handleDesignSet,
|
|
13673
|
+
handleDesignState,
|
|
13674
|
+
handleDesignUse,
|
|
13675
|
+
handleDesignVerify,
|
|
13676
|
+
handleFilesList,
|
|
13677
|
+
handleFilesRead,
|
|
13678
|
+
handleFilesTree,
|
|
13679
|
+
handleFilesWrite,
|
|
13680
|
+
handleGitChanges,
|
|
13681
|
+
handleGitDiff,
|
|
13682
|
+
handleGitInfo,
|
|
13683
|
+
handleGoalGet,
|
|
13684
|
+
handleMailboxMessages,
|
|
13685
|
+
handleMailboxRoute,
|
|
13686
|
+
handleMcpAdd,
|
|
13687
|
+
handleMcpDisable,
|
|
13688
|
+
handleMcpDiscover,
|
|
13689
|
+
handleMcpEnable,
|
|
13690
|
+
handleMcpList,
|
|
13691
|
+
handleMcpPromptGet,
|
|
13692
|
+
handleMcpPrompts,
|
|
13693
|
+
handleMcpRemove,
|
|
13694
|
+
handleMcpResourceRead,
|
|
13695
|
+
handleMcpResources,
|
|
13696
|
+
handleMcpRestart,
|
|
13697
|
+
handleMcpRoute,
|
|
13698
|
+
handleMcpSleep,
|
|
13699
|
+
handleMcpUpdate,
|
|
13700
|
+
handleMcpWake,
|
|
13701
|
+
handleMemoryForget,
|
|
13702
|
+
handleMemoryList,
|
|
13703
|
+
handleMemoryRemember,
|
|
13704
|
+
handleModeRoute,
|
|
13705
|
+
handlePrefsRoute,
|
|
13706
|
+
handleProcessKill,
|
|
13707
|
+
handleProcessKillAll,
|
|
13708
|
+
handleProcessList,
|
|
13709
|
+
handleProjectRoute,
|
|
13710
|
+
handlePromptsContent,
|
|
13711
|
+
handlePromptsCreate,
|
|
13712
|
+
handlePromptsFavorite,
|
|
13713
|
+
handlePromptsList,
|
|
13714
|
+
handlePromptsRecent,
|
|
13715
|
+
handlePromptsSearch,
|
|
13716
|
+
handlePromptsUsed,
|
|
13717
|
+
handleProviderRoute,
|
|
13718
|
+
handleSddBoardRoute,
|
|
13719
|
+
handleSddWizardRoute,
|
|
13720
|
+
handleSessionRoute,
|
|
13721
|
+
handleShellGitRoute,
|
|
13722
|
+
handleShellOpen,
|
|
13723
|
+
handleSkillsContent,
|
|
13724
|
+
handleSkillsCreate,
|
|
13725
|
+
handleSkillsEdit,
|
|
13726
|
+
handleSkillsExport,
|
|
13727
|
+
handleSkillsInstall,
|
|
13728
|
+
handleSkillsList,
|
|
13729
|
+
handleSkillsUninstall,
|
|
13730
|
+
handleSkillsUpdate,
|
|
13731
|
+
handleSpecsRoute,
|
|
13732
|
+
handleWorklistMessage,
|
|
13733
|
+
hostForBrowserUrl,
|
|
13734
|
+
hostHeaderOk,
|
|
13735
|
+
injectWsConfig,
|
|
13736
|
+
injectWsPort,
|
|
13737
|
+
isHiddenEntry,
|
|
13738
|
+
isInsideDist,
|
|
13739
|
+
isLoopbackBind,
|
|
13740
|
+
isLoopbackHostname,
|
|
13741
|
+
isPathInside,
|
|
13742
|
+
isPidAlive,
|
|
13743
|
+
isPortFree,
|
|
13744
|
+
isWildcardBind,
|
|
13745
|
+
listInstances,
|
|
13746
|
+
loadManifest,
|
|
13747
|
+
loadSavedProviders,
|
|
13748
|
+
maskedKey,
|
|
13749
|
+
messagePreview,
|
|
13750
|
+
messageTokens,
|
|
13751
|
+
normalizeKeys,
|
|
13752
|
+
openBrowser,
|
|
13753
|
+
patchConfig,
|
|
13754
|
+
persistPrefsToConfig,
|
|
13755
|
+
projectSavedProviders,
|
|
13756
|
+
projectsJsonPath,
|
|
13757
|
+
rankFiles,
|
|
13758
|
+
readZipEntries,
|
|
13759
|
+
registerInstance,
|
|
13760
|
+
registerShutdownHandlers,
|
|
13761
|
+
registryPath,
|
|
13762
|
+
removeProvider,
|
|
13763
|
+
resolveAllPendingConfirms,
|
|
13764
|
+
resolveAuthToken,
|
|
13765
|
+
resolveProviderCatalogForModels,
|
|
13766
|
+
resolveProviderModelMetadata,
|
|
13767
|
+
resolveWorkingDirInsideProject,
|
|
13768
|
+
resolveYoloEligiblePendingConfirms,
|
|
13769
|
+
saveManifest,
|
|
13770
|
+
saveProviders,
|
|
13771
|
+
send,
|
|
13772
|
+
sendResult,
|
|
13773
|
+
setActiveKey,
|
|
13774
|
+
setupEvents,
|
|
13775
|
+
setupWebUICodebaseIndexing,
|
|
13776
|
+
startWebUI,
|
|
13777
|
+
statusProjectHashFromWatchFilename,
|
|
13778
|
+
stringifyContent,
|
|
13779
|
+
tokenMatches,
|
|
13780
|
+
unregisterInstance,
|
|
13781
|
+
upsertKey,
|
|
13782
|
+
validateAutonomySwitchPayload,
|
|
13783
|
+
validateBrainAskPayload,
|
|
13784
|
+
validateBrainRiskPayload,
|
|
13785
|
+
validateContextModeCreatePayload,
|
|
13786
|
+
validateContextModeDeletePayload,
|
|
13787
|
+
validateContextModeSwitchPayload,
|
|
13788
|
+
validateContextModeUpdatePayload,
|
|
13789
|
+
validateGitDiffPayload,
|
|
13790
|
+
validateMailboxAgentsPayload,
|
|
13791
|
+
validateMailboxMessagesPayload,
|
|
13792
|
+
validateMailboxPurgePayload,
|
|
13793
|
+
validateModeSwitchPayload,
|
|
13794
|
+
validateModelSwitchPayload,
|
|
13795
|
+
validatePlanTemplateUsePayload,
|
|
13796
|
+
validatePrefsUpdatePayload,
|
|
13797
|
+
validateProcessKillPayload,
|
|
13798
|
+
validateProjectsAddPayload,
|
|
13799
|
+
validateProjectsSelectPayload,
|
|
13800
|
+
validateShellOpenPayload,
|
|
13801
|
+
validateSkillsCreatePayload,
|
|
13802
|
+
validateSkillsEditPayload,
|
|
13803
|
+
validateWorkingDirSetPayload,
|
|
13804
|
+
verifyClient,
|
|
13805
|
+
writeKeysBack
|
|
13806
|
+
};
|
|
12538
13807
|
//# sourceMappingURL=index.js.map
|
|
12539
|
-
//# sourceMappingURL=index.js.map
|