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