@wrongstack/cli 0.257.2 → 0.264.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.js +1343 -434
- package/dist/index.js.map +1 -1
- package/package.json +13 -12
package/dist/index.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import * as fsp5 from 'fs/promises';
|
|
3
|
-
import * as
|
|
3
|
+
import * as path39 from 'path';
|
|
4
4
|
import { join } from 'path';
|
|
5
|
-
import { color, writeErr, loadPlugins, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, expectDefined, DefaultTaskStore, TaskTracker, renderTaskGraph, withFileLock, DefaultSecretScrubber, resolveProjectDir, GlobalMailbox, TOKENS, ToolRegistry, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, SlashCommandRegistry, attachDepWatcherBridge, SessionMemoryConsolidator, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, createTieredBrainArbiter, DefaultBrainArbiter, BrainMonitor, mailboxSessionTag, createDelegateTool, FLEET_ROSTER, createMcpControlTool, startTechStackConsumer, startPackageOutdatedWatcher, recordFileAction, createAutonomyBrain, DefaultPluginAPI, SpecVersioning, wstackGlobalRoot, DEFAULT_CONTEXT_WINDOW_MODE_ID, recentTextTurns, enhanceUserPrompt, projectSlug, DefaultSystemPromptBuilder, mutateTasks, loadTasks, resolveContextWindowPolicy, repairToolUseAdjacency, mutatePlan, setPlanItemStatus, getPlanTemplate, loadPlan, emptyPlan, addPlanItem, savePlan, DefaultLogger, DefaultModelsRegistry, isStdinTTY, DefaultPathResolver, EventBus, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, mergeCustomModelDefs, makeAutonomyPromptContributor, createContextManagerTool, makeMailboxTool, makeMailSendTool, makeMailInboxTool, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, DEFAULT_SESSION_PRUNE_DAYS, RecoveryLock, DefaultAttachmentStore,
|
|
5
|
+
import { color, writeErr, loadPlugins, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, expectDefined, DefaultTaskStore, TaskTracker, renderTaskGraph, withFileLock, DefaultSecretScrubber, resolveProjectDir, GlobalMailbox, TOKENS, ToolRegistry, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, SlashCommandRegistry, attachDepWatcherBridge, SessionMemoryConsolidator, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, createTieredBrainArbiter, DefaultBrainArbiter, BrainMonitor, mailboxSessionTag, createDelegateTool, FLEET_ROSTER, createMcpControlTool, startTechStackConsumer, startPackageOutdatedWatcher, recordFileAction, createAutonomyBrain, DefaultPluginAPI, SpecVersioning, wstackGlobalRoot, DEFAULT_CONTEXT_WINDOW_MODE_ID, recentTextTurns, enhanceUserPrompt, projectSlug, DefaultSystemPromptBuilder, mutateTasks, loadTasks, resolveContextWindowPolicy, repairToolUseAdjacency, mutatePlan, setPlanItemStatus, getPlanTemplate, loadPlan, emptyPlan, addPlanItem, savePlan, DefaultLogger, DefaultModelsRegistry, isStdinTTY, DefaultPathResolver, EventBus, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, mergeCustomModelDefs, makeAutonomyPromptContributor, createContextManagerTool, makeMailboxTool, makeMailSendTool, makeMailInboxTool, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, DEFAULT_SESSION_PRUNE_DAYS, RecoveryLock, DefaultAttachmentStore, Context, QueueStore, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, createDefaultPipelines, resolveAuditLevel, AutoCompactionMiddleware, estimateRequestTokensCalibrated, Agent, FleetManager, makeDirectorSessionFactory, Director, makeFleetEmitTool, makeFleetStatusTool, resolveModelMatrix, DEFAULT_SUBAGENT_BASELINE, AutoApprovePermissionPolicy, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, makeLLMClassifier, writeOut, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1, CHIMERA_REVIEW_PROMPT, AutonomousCoordinator, noOpVault, decryptConfigSecrets, encryptConfigSecrets, atomicWrite, setQueuedMessagesSnapshot, DefaultSessionRewinder, bootConfig as bootConfig$1, setOutputLineGuard, setRawMode, DefaultSessionReader, resolveWstackPaths, ToolAuditLog, DefaultSessionStore as DefaultSessionStore$1, ProviderRegistry, StreamHangError, ProviderError, makeAgentSubagentRunner, NULL_FLEET_BUS, buildChildEnv, formatContextWindowModeList, getContextWindowMode, AGENT_CATALOG, dispatchAgent, formatTodosList, formatTaskList, formatTaskProgress, formatPlan, SessionRecovery, loadGoal, goalFilePath, summarizeUsage, saveGoal, formatGoal, emptyGoal, buildGoalPreamble, pendingBtwCount, setBtwNote, MATRIX_PHASE_KEYS, matrixKeyKind, phaseForRole, onResize, ERROR_CODES, FsError, ConfigError, InputBuilder, truncate, estimateMessageTokens, AGENTS_BY_PHASE, validateAgainstSchema, resolveMailboxIdentity, isSecretField as isSecretField$1 } from '@wrongstack/core';
|
|
6
6
|
import { DefaultSecretVault, encryptConfigSecrets as encryptConfigSecrets$1, decryptConfigSecrets as decryptConfigSecrets$1, isSecretField } from '@wrongstack/core/security';
|
|
7
7
|
import * as crypto3 from 'crypto';
|
|
8
|
-
import { createHash, randomUUID } from 'crypto';
|
|
8
|
+
import { createHash, randomBytes, randomUUID } from 'crypto';
|
|
9
9
|
import { createRequire } from 'module';
|
|
10
10
|
import * as os from 'os';
|
|
11
11
|
import os__default from 'os';
|
|
12
12
|
import { findFreePort, AutoPhaseWebSocketHandler, handleShellOpen, handleMemoryForget, handleMemoryRemember, handleMemoryList, handleFilesWrite, handleFilesRead, handleFilesTree, handleFilesList, createEternalSubscription, createHttpServer, registerInstance, openBrowser, unregisterInstance, estimateTokens as estimateTokens$1, stringifyContent, messagePreview, messageTokens, createCustomModeStore } from '@wrongstack/webui/server';
|
|
13
13
|
import { makeProviderFromConfig, capabilitiesFor, buildProviderFactoriesFromRegistry } from '@wrongstack/providers';
|
|
14
|
+
import { toErrorMessage } from '@wrongstack/core/utils';
|
|
14
15
|
import { getProcessRegistry, builtinToolsPack, TIER1_TOOLS, rememberTool, forgetTool, searchMemoryTool, relatedMemoryTool, runStartupIndex, isIndexableFile, enqueueReindex, cancelPendingReindexes, shutdownCodebaseIndexHost, resetIndexCircuitBreaker } from '@wrongstack/tools';
|
|
15
16
|
import { DefaultSessionStore } from '@wrongstack/core/storage';
|
|
17
|
+
import { probeLocalLlm } from '@wrongstack/runtime/probe';
|
|
16
18
|
import { WebSocketServer, WebSocket } from 'ws';
|
|
17
19
|
import { spawn, execFile, execFileSync } from 'child_process';
|
|
18
20
|
import { MCPRegistry, MCPServer, serveHttp, serveStdio } from '@wrongstack/mcp';
|
|
@@ -21,10 +23,11 @@ import { writeFileSync, existsSync, readFileSync } from 'fs';
|
|
|
21
23
|
import { fileURLToPath } from 'url';
|
|
22
24
|
import { createDefaultContainer, routeImagesForModel, readClipboardImage } from '@wrongstack/runtime';
|
|
23
25
|
import * as readline from 'readline';
|
|
26
|
+
import { ACP_AGENT_COMMANDS, makeACPSubagentRunner, runEnsemble, renderEnsembleText, EnsembleRegistry, makeACPSubagentRunnerWithStop, findAgentDescriptor } from '@wrongstack/acp';
|
|
24
27
|
import { parseNextSteps } from '@wrongstack/tui';
|
|
25
|
-
import { ACP_AGENT_COMMANDS, makeACPSubagentRunner, makeACPSubagentRunnerWithStop } from '@wrongstack/acp';
|
|
26
28
|
import { WrongStackACPServer } from '@wrongstack/acp/agent';
|
|
27
|
-
import {
|
|
29
|
+
import { SubagentBudget } from '@wrongstack/core/coordination';
|
|
30
|
+
import { loadBenchConfig, reportHeaderLine, readSummary, renderMarkdownReport, createPolyglotSuite, createSwebenchSuite, runBenchmark, writeJsonArtifacts, collectCellPredictions, writePredictionsJsonl, gradePolyglot, gradeSwebench } from '@wrongstack/bench';
|
|
28
31
|
import { allServers } from '@wrongstack/core/infrastructure';
|
|
29
32
|
import { ToolExecutor } from '@wrongstack/core/execution';
|
|
30
33
|
import { createToolVisionAdapters } from '@wrongstack/runtime/vision';
|
|
@@ -70,10 +73,10 @@ async function detectPackageManager2(root, declared) {
|
|
|
70
73
|
const name = declared.split("@")[0];
|
|
71
74
|
if (name) return name;
|
|
72
75
|
}
|
|
73
|
-
if (await pathExists(
|
|
74
|
-
if (await pathExists(
|
|
75
|
-
if (await pathExists(
|
|
76
|
-
if (await pathExists(
|
|
76
|
+
if (await pathExists(path39.join(root, "pnpm-lock.yaml"))) return "pnpm";
|
|
77
|
+
if (await pathExists(path39.join(root, "bun.lockb"))) return "bun";
|
|
78
|
+
if (await pathExists(path39.join(root, "bun.lock"))) return "bun";
|
|
79
|
+
if (await pathExists(path39.join(root, "yarn.lock"))) return "yarn";
|
|
77
80
|
return "npm";
|
|
78
81
|
}
|
|
79
82
|
function hasUsableScript(scripts, name) {
|
|
@@ -94,7 +97,7 @@ function parseMakeTargets(makefile) {
|
|
|
94
97
|
async function detectProjectFacts(root) {
|
|
95
98
|
const facts = { hints: [] };
|
|
96
99
|
try {
|
|
97
|
-
const pkg = JSON.parse(await fsp5.readFile(
|
|
100
|
+
const pkg = JSON.parse(await fsp5.readFile(path39.join(root, "package.json"), "utf8"));
|
|
98
101
|
const scripts = pkg.scripts ?? {};
|
|
99
102
|
const pm = await detectPackageManager2(root, pkg.packageManager);
|
|
100
103
|
if (hasUsableScript(scripts, "build")) facts.build = `${pm} run build`;
|
|
@@ -108,14 +111,14 @@ async function detectProjectFacts(root) {
|
|
|
108
111
|
} catch {
|
|
109
112
|
}
|
|
110
113
|
try {
|
|
111
|
-
if (!await pathExists(
|
|
114
|
+
if (!await pathExists(path39.join(root, "pyproject.toml"))) throw new Error("not python");
|
|
112
115
|
facts.test ??= "pytest";
|
|
113
116
|
facts.lint ??= "ruff check .";
|
|
114
117
|
facts.hints.push("pyproject.toml");
|
|
115
118
|
} catch {
|
|
116
119
|
}
|
|
117
120
|
try {
|
|
118
|
-
if (!await pathExists(
|
|
121
|
+
if (!await pathExists(path39.join(root, "go.mod"))) throw new Error("not go");
|
|
119
122
|
facts.build ??= "go build ./...";
|
|
120
123
|
facts.test ??= "go test ./...";
|
|
121
124
|
facts.run ??= "go run .";
|
|
@@ -123,7 +126,7 @@ async function detectProjectFacts(root) {
|
|
|
123
126
|
} catch {
|
|
124
127
|
}
|
|
125
128
|
try {
|
|
126
|
-
if (!await pathExists(
|
|
129
|
+
if (!await pathExists(path39.join(root, "Cargo.toml"))) throw new Error("not rust");
|
|
127
130
|
facts.build ??= "cargo build";
|
|
128
131
|
facts.test ??= "cargo test";
|
|
129
132
|
facts.lint ??= "cargo clippy";
|
|
@@ -132,7 +135,7 @@ async function detectProjectFacts(root) {
|
|
|
132
135
|
} catch {
|
|
133
136
|
}
|
|
134
137
|
try {
|
|
135
|
-
const makefile = await fsp5.readFile(
|
|
138
|
+
const makefile = await fsp5.readFile(path39.join(root, "Makefile"), "utf8");
|
|
136
139
|
const targets = parseMakeTargets(makefile);
|
|
137
140
|
facts.build ??= targets.has("build") ? "make build" : "make";
|
|
138
141
|
if (targets.has("test")) facts.test ??= "make test";
|
|
@@ -371,26 +374,26 @@ function fmtDuration(ms) {
|
|
|
371
374
|
const remMin = m - h * 60;
|
|
372
375
|
return `${h}h${remMin}m`;
|
|
373
376
|
}
|
|
374
|
-
function fmtTaskResultLine(r,
|
|
377
|
+
function fmtTaskResultLine(r, color74) {
|
|
375
378
|
const stats = `${r.iterations}it ${r.toolCalls}tc ${fmtDuration(r.durationMs)}`;
|
|
376
379
|
const errMsg = typeof r.error === "string" ? r.error : r.error?.message;
|
|
377
380
|
const errKind = typeof r.error === "object" ? r.error?.kind : void 0;
|
|
378
381
|
const errTail = errMsg ? ` \u2014 ${errMsg.replace(/\s+/g, " ").slice(0, 80)}${errMsg.length > 80 ? "\u2026" : ""}` : "";
|
|
379
|
-
const errKindChip = errKind ?
|
|
380
|
-
const errSnip = errMsg || errKind ? `${errKindChip}${
|
|
382
|
+
const errKindChip = errKind ? color74.dim(` [${errKind}]`) : "";
|
|
383
|
+
const errSnip = errMsg || errKind ? `${errKindChip}${color74.dim(errTail)}` : "";
|
|
381
384
|
switch (r.status) {
|
|
382
385
|
case "success":
|
|
383
|
-
return { mark:
|
|
386
|
+
return { mark: color74.green("\u2713"), stats, tail: "" };
|
|
384
387
|
case "timeout":
|
|
385
388
|
return {
|
|
386
|
-
mark:
|
|
387
|
-
stats: `${
|
|
389
|
+
mark: color74.yellow("\u23F1"),
|
|
390
|
+
stats: `${color74.yellow("timeout")} ${stats}`,
|
|
388
391
|
tail: errSnip
|
|
389
392
|
};
|
|
390
393
|
case "stopped":
|
|
391
|
-
return { mark:
|
|
394
|
+
return { mark: color74.dim("\u2298"), stats: `${color74.dim("stopped")} ${stats}`, tail: errSnip };
|
|
392
395
|
case "failed":
|
|
393
|
-
return { mark:
|
|
396
|
+
return { mark: color74.red("\u2717"), stats: `${color74.red("failed")} ${stats}`, tail: errSnip };
|
|
394
397
|
}
|
|
395
398
|
}
|
|
396
399
|
var init_utils = __esm({
|
|
@@ -675,7 +678,7 @@ async function findSpec(store, idOrTitle) {
|
|
|
675
678
|
async function gatherProjectContext2(projectRoot) {
|
|
676
679
|
const parts = [];
|
|
677
680
|
try {
|
|
678
|
-
const pkgPath =
|
|
681
|
+
const pkgPath = path39.join(projectRoot, "package.json");
|
|
679
682
|
const pkgRaw = await fsp5.readFile(pkgPath, "utf8");
|
|
680
683
|
const pkg = JSON.parse(pkgRaw);
|
|
681
684
|
parts.push(`Project: ${String(pkg.name ?? "unknown")}`);
|
|
@@ -693,13 +696,13 @@ async function gatherProjectContext2(projectRoot) {
|
|
|
693
696
|
} catch {
|
|
694
697
|
}
|
|
695
698
|
try {
|
|
696
|
-
const tsconfigPath =
|
|
699
|
+
const tsconfigPath = path39.join(projectRoot, "tsconfig.json");
|
|
697
700
|
await fsp5.access(tsconfigPath);
|
|
698
701
|
parts.push("Language: TypeScript");
|
|
699
702
|
} catch {
|
|
700
703
|
}
|
|
701
704
|
try {
|
|
702
|
-
const srcDir =
|
|
705
|
+
const srcDir = path39.join(projectRoot, "src");
|
|
703
706
|
const entries = await fsp5.readdir(srcDir, { withFileTypes: true });
|
|
704
707
|
const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
705
708
|
if (dirs.length > 0) parts.push(`Source structure: src/${dirs.join(", src/")}`);
|
|
@@ -2124,12 +2127,12 @@ __export(project_utils_exports, {
|
|
|
2124
2127
|
touchProjectInManifest: () => touchProjectInManifest
|
|
2125
2128
|
});
|
|
2126
2129
|
function projectsJsonPath(globalConfigPath) {
|
|
2127
|
-
const base = globalConfigPath ?
|
|
2128
|
-
return
|
|
2130
|
+
const base = globalConfigPath ? path39.dirname(globalConfigPath) : wstackGlobalRoot();
|
|
2131
|
+
return path39.join(base, "projects.json");
|
|
2129
2132
|
}
|
|
2130
2133
|
function projectsDataDir(globalConfigPath) {
|
|
2131
|
-
const base = globalConfigPath ?
|
|
2132
|
-
return
|
|
2134
|
+
const base = globalConfigPath ? path39.dirname(globalConfigPath) : wstackGlobalRoot();
|
|
2135
|
+
return path39.join(base, "projects");
|
|
2133
2136
|
}
|
|
2134
2137
|
async function loadManifest(globalConfigPath) {
|
|
2135
2138
|
const file = projectsJsonPath(globalConfigPath);
|
|
@@ -2143,12 +2146,12 @@ async function loadManifest(globalConfigPath) {
|
|
|
2143
2146
|
}
|
|
2144
2147
|
async function saveManifest(manifest, globalConfigPath) {
|
|
2145
2148
|
const file = projectsJsonPath(globalConfigPath);
|
|
2146
|
-
await fsp5.mkdir(
|
|
2149
|
+
await fsp5.mkdir(path39.dirname(file), { recursive: true });
|
|
2147
2150
|
await fsp5.writeFile(file, JSON.stringify(manifest, null, 2), "utf8");
|
|
2148
2151
|
}
|
|
2149
2152
|
function generateSlug(root) {
|
|
2150
|
-
const base =
|
|
2151
|
-
const hash = createHash("sha256").update(
|
|
2153
|
+
const base = path39.basename(root).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
|
|
2154
|
+
const hash = createHash("sha256").update(path39.resolve(root)).digest("hex").slice(0, 6);
|
|
2152
2155
|
return `${base}-${hash}`;
|
|
2153
2156
|
}
|
|
2154
2157
|
function findProject(manifest, query) {
|
|
@@ -2163,29 +2166,29 @@ function findProject(manifest, query) {
|
|
|
2163
2166
|
return found;
|
|
2164
2167
|
}
|
|
2165
2168
|
async function ensureProjectDataDir(slug, globalConfigPath) {
|
|
2166
|
-
const dir =
|
|
2169
|
+
const dir = path39.join(projectsDataDir(globalConfigPath), slug);
|
|
2167
2170
|
await fsp5.mkdir(dir, { recursive: true });
|
|
2168
2171
|
return dir;
|
|
2169
2172
|
}
|
|
2170
2173
|
async function touchProjectInManifest(opts) {
|
|
2171
|
-
const root =
|
|
2174
|
+
const root = path39.resolve(opts.projectRoot);
|
|
2172
2175
|
const file = projectsJsonPath(opts.globalConfigPath);
|
|
2173
2176
|
let entry;
|
|
2174
2177
|
await withFileLock(file, async () => {
|
|
2175
2178
|
const manifest = await loadManifest(opts.globalConfigPath);
|
|
2176
2179
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2177
|
-
entry = manifest.projects.find((p) =>
|
|
2180
|
+
entry = manifest.projects.find((p) => path39.resolve(p.root) === root);
|
|
2178
2181
|
if (entry) {
|
|
2179
2182
|
entry.lastSeen = now;
|
|
2180
|
-
if (opts.workingDir) entry.lastWorkingDir =
|
|
2183
|
+
if (opts.workingDir) entry.lastWorkingDir = path39.resolve(opts.workingDir);
|
|
2181
2184
|
} else {
|
|
2182
2185
|
entry = {
|
|
2183
|
-
name: opts.name ??
|
|
2186
|
+
name: opts.name ?? path39.basename(root),
|
|
2184
2187
|
root,
|
|
2185
2188
|
slug: generateSlug(root),
|
|
2186
2189
|
createdAt: now,
|
|
2187
2190
|
lastSeen: now,
|
|
2188
|
-
lastWorkingDir: opts.workingDir ?
|
|
2191
|
+
lastWorkingDir: opts.workingDir ? path39.resolve(opts.workingDir) : void 0
|
|
2189
2192
|
};
|
|
2190
2193
|
manifest.projects.push(entry);
|
|
2191
2194
|
}
|
|
@@ -2379,7 +2382,7 @@ async function runProjectPicker(opts) {
|
|
|
2379
2382
|
const reservedBottom = 3;
|
|
2380
2383
|
const headerHeight = reservedTop + reservedBottom;
|
|
2381
2384
|
const baseVisibleHeight = Math.max(5, terminalHeight() - headerHeight);
|
|
2382
|
-
return new Promise((
|
|
2385
|
+
return new Promise((resolve11) => {
|
|
2383
2386
|
const wasRaw = stdin.isRaw;
|
|
2384
2387
|
const wasPaused = stdin.isPaused();
|
|
2385
2388
|
let filter = "";
|
|
@@ -2503,7 +2506,7 @@ async function runProjectPicker(opts) {
|
|
|
2503
2506
|
cleanup();
|
|
2504
2507
|
out.write(CURSOR_SHOW);
|
|
2505
2508
|
out.write("\n");
|
|
2506
|
-
|
|
2509
|
+
resolve11(void 0);
|
|
2507
2510
|
return;
|
|
2508
2511
|
}
|
|
2509
2512
|
if (ch === ESC) {
|
|
@@ -2516,7 +2519,7 @@ async function runProjectPicker(opts) {
|
|
|
2516
2519
|
cleanup();
|
|
2517
2520
|
out.write(CURSOR_SHOW);
|
|
2518
2521
|
out.write("\n");
|
|
2519
|
-
|
|
2522
|
+
resolve11(void 0);
|
|
2520
2523
|
return;
|
|
2521
2524
|
}
|
|
2522
2525
|
if (ch === BS || ch === "\b") {
|
|
@@ -2533,22 +2536,22 @@ async function runProjectPicker(opts) {
|
|
|
2533
2536
|
out.write(CURSOR_SHOW);
|
|
2534
2537
|
out.write("\n");
|
|
2535
2538
|
if (!item || item.key === "__divider__") {
|
|
2536
|
-
|
|
2539
|
+
resolve11(void 0);
|
|
2537
2540
|
return;
|
|
2538
2541
|
}
|
|
2539
2542
|
if (item.key === "quit") {
|
|
2540
|
-
|
|
2543
|
+
resolve11(void 0);
|
|
2541
2544
|
return;
|
|
2542
2545
|
}
|
|
2543
2546
|
if (item.key === "new-session") {
|
|
2544
|
-
|
|
2547
|
+
resolve11({ kind: "action", key: "new-session", action: "new-session" });
|
|
2545
2548
|
return;
|
|
2546
2549
|
}
|
|
2547
2550
|
if (item.key === "prev-sessions") {
|
|
2548
|
-
|
|
2551
|
+
resolve11({ kind: "action", key: "prev-sessions", action: "prev-sessions" });
|
|
2549
2552
|
return;
|
|
2550
2553
|
}
|
|
2551
|
-
|
|
2554
|
+
resolve11({ kind: "project", key: item.key });
|
|
2552
2555
|
return;
|
|
2553
2556
|
}
|
|
2554
2557
|
if (filter.length === 0) {
|
|
@@ -2556,7 +2559,7 @@ async function runProjectPicker(opts) {
|
|
|
2556
2559
|
cleanup();
|
|
2557
2560
|
out.write(CURSOR_SHOW);
|
|
2558
2561
|
out.write("\n");
|
|
2559
|
-
|
|
2562
|
+
resolve11(void 0);
|
|
2560
2563
|
return;
|
|
2561
2564
|
}
|
|
2562
2565
|
if (ch === "j") {
|
|
@@ -2585,7 +2588,7 @@ async function runProjectPicker(opts) {
|
|
|
2585
2588
|
try {
|
|
2586
2589
|
stdin.setRawMode(true);
|
|
2587
2590
|
} catch {
|
|
2588
|
-
|
|
2591
|
+
resolve11(void 0);
|
|
2589
2592
|
return;
|
|
2590
2593
|
}
|
|
2591
2594
|
stdin.resume();
|
|
@@ -2596,7 +2599,7 @@ async function runProjectPicker(opts) {
|
|
|
2596
2599
|
stdin.once("close", () => {
|
|
2597
2600
|
cleanup();
|
|
2598
2601
|
out.write(CURSOR_SHOW);
|
|
2599
|
-
|
|
2602
|
+
resolve11(void 0);
|
|
2600
2603
|
});
|
|
2601
2604
|
});
|
|
2602
2605
|
}
|
|
@@ -2631,7 +2634,7 @@ __export(update_check_exports, {
|
|
|
2631
2634
|
getUpdateNotification: () => getUpdateNotification
|
|
2632
2635
|
});
|
|
2633
2636
|
function cachePath(homeFn = defaultHomeDir2) {
|
|
2634
|
-
return
|
|
2637
|
+
return path39.join(homeFn(), ".wrongstack", "update-cache.json");
|
|
2635
2638
|
}
|
|
2636
2639
|
function currentVersion() {
|
|
2637
2640
|
const req2 = createRequire(import.meta.url);
|
|
@@ -2668,7 +2671,7 @@ async function readCache(homeFn = defaultHomeDir2) {
|
|
|
2668
2671
|
}
|
|
2669
2672
|
async function writeCache(entry, homeFn = defaultHomeDir2) {
|
|
2670
2673
|
try {
|
|
2671
|
-
const dir =
|
|
2674
|
+
const dir = path39.dirname(cachePath(homeFn));
|
|
2672
2675
|
await fsp5.mkdir(dir, { recursive: true });
|
|
2673
2676
|
await fsp5.writeFile(cachePath(homeFn), JSON.stringify(entry, null, 2), "utf8");
|
|
2674
2677
|
} catch {
|
|
@@ -2754,7 +2757,7 @@ function registerWebuiInstance(p, deps = {}) {
|
|
|
2754
2757
|
wsPort: p.wsPort,
|
|
2755
2758
|
host: p.host,
|
|
2756
2759
|
projectRoot: p.projectRoot,
|
|
2757
|
-
projectName:
|
|
2760
|
+
projectName: path39.basename(p.projectRoot) || p.projectRoot,
|
|
2758
2761
|
startedAt: p.startedAt,
|
|
2759
2762
|
url: `http://${p.host}:${p.httpPort}`
|
|
2760
2763
|
},
|
|
@@ -2818,7 +2821,7 @@ var init_lifecycle = __esm({
|
|
|
2818
2821
|
}
|
|
2819
2822
|
});
|
|
2820
2823
|
function getVault(globalConfigPath) {
|
|
2821
|
-
const keyFile =
|
|
2824
|
+
const keyFile = path39.join(path39.dirname(globalConfigPath ?? ""), ".key");
|
|
2822
2825
|
return new DefaultSecretVault({ keyFile });
|
|
2823
2826
|
}
|
|
2824
2827
|
async function loadSavedProviders(globalConfigPath) {
|
|
@@ -2852,7 +2855,7 @@ function resolveDistDir() {
|
|
|
2852
2855
|
try {
|
|
2853
2856
|
const requireFromHere = createRequire(import.meta.url);
|
|
2854
2857
|
const serverEntry = requireFromHere.resolve("@wrongstack/webui/server");
|
|
2855
|
-
return
|
|
2858
|
+
return path39.resolve(path39.dirname(serverEntry), "..");
|
|
2856
2859
|
} catch {
|
|
2857
2860
|
return null;
|
|
2858
2861
|
}
|
|
@@ -2907,7 +2910,7 @@ async function handleModesList(ctx, ws) {
|
|
|
2907
2910
|
payload: {
|
|
2908
2911
|
modes: [],
|
|
2909
2912
|
activeId: "default",
|
|
2910
|
-
error:
|
|
2913
|
+
error: toErrorMessage(err)
|
|
2911
2914
|
}
|
|
2912
2915
|
});
|
|
2913
2916
|
}
|
|
@@ -2930,7 +2933,7 @@ async function handleModeSwitch(ctx, ws, id) {
|
|
|
2930
2933
|
const payload = await ctx.buildSessionStart({ mode: id });
|
|
2931
2934
|
ctx.broadcast({ type: "session.start", payload });
|
|
2932
2935
|
} catch (err) {
|
|
2933
|
-
sendResult(ctx, ws, false,
|
|
2936
|
+
sendResult(ctx, ws, false, toErrorMessage(err));
|
|
2934
2937
|
}
|
|
2935
2938
|
}
|
|
2936
2939
|
async function handleModelSwitch(ctx, ws, payload) {
|
|
@@ -2949,7 +2952,7 @@ async function handleModelSwitch(ctx, ws, payload) {
|
|
|
2949
2952
|
ctx,
|
|
2950
2953
|
ws,
|
|
2951
2954
|
false,
|
|
2952
|
-
`Switch failed: ${
|
|
2955
|
+
`Switch failed: ${toErrorMessage(err)}`
|
|
2953
2956
|
);
|
|
2954
2957
|
}
|
|
2955
2958
|
}
|
|
@@ -2998,7 +3001,7 @@ async function handleModelRefine(ctx, ws, text) {
|
|
|
2998
3001
|
payload: {
|
|
2999
3002
|
refined: text,
|
|
3000
3003
|
english: text,
|
|
3001
|
-
error:
|
|
3004
|
+
error: toErrorMessage(err)
|
|
3002
3005
|
}
|
|
3003
3006
|
});
|
|
3004
3007
|
}
|
|
@@ -3008,8 +3011,6 @@ var init_agent_config = __esm({
|
|
|
3008
3011
|
init_provider_config();
|
|
3009
3012
|
}
|
|
3010
3013
|
});
|
|
3011
|
-
|
|
3012
|
-
// src/webui-server/ws-handlers/brain.ts
|
|
3013
3014
|
function sendResult2(ctx, ws, success, message) {
|
|
3014
3015
|
ctx.send(ws, { type: "key.operation_result", payload: { success, message } });
|
|
3015
3016
|
}
|
|
@@ -3063,7 +3064,7 @@ async function handleBrainAsk(ctx, ws, question) {
|
|
|
3063
3064
|
ctx,
|
|
3064
3065
|
ws,
|
|
3065
3066
|
false,
|
|
3066
|
-
`Brain consultation failed: ${
|
|
3067
|
+
`Brain consultation failed: ${toErrorMessage(err)}`
|
|
3067
3068
|
);
|
|
3068
3069
|
}
|
|
3069
3070
|
}
|
|
@@ -3071,8 +3072,6 @@ var init_brain = __esm({
|
|
|
3071
3072
|
"src/webui-server/ws-handlers/brain.ts"() {
|
|
3072
3073
|
}
|
|
3073
3074
|
});
|
|
3074
|
-
|
|
3075
|
-
// src/webui-server/ws-handlers/connection.ts
|
|
3076
3075
|
async function handleUserMessage(ctx, ws, content) {
|
|
3077
3076
|
if (ctx.abortControllers.has(ws)) {
|
|
3078
3077
|
ctx.send(ws, {
|
|
@@ -3105,7 +3104,7 @@ async function handleUserMessage(ctx, ws, content) {
|
|
|
3105
3104
|
type: "error",
|
|
3106
3105
|
payload: {
|
|
3107
3106
|
phase: "agent.run",
|
|
3108
|
-
message:
|
|
3107
|
+
message: toErrorMessage(err)
|
|
3109
3108
|
}
|
|
3110
3109
|
});
|
|
3111
3110
|
} finally {
|
|
@@ -3124,10 +3123,10 @@ function handlePing(ctx, ws) {
|
|
|
3124
3123
|
ctx.send(ws, { type: "pong", payload: {} });
|
|
3125
3124
|
}
|
|
3126
3125
|
function handleToolConfirmResult(ctx, id, decision) {
|
|
3127
|
-
const
|
|
3128
|
-
if (
|
|
3126
|
+
const resolve11 = ctx.pendingConfirms.get(id);
|
|
3127
|
+
if (resolve11) {
|
|
3129
3128
|
ctx.pendingConfirms.delete(id);
|
|
3130
|
-
|
|
3129
|
+
resolve11(decision);
|
|
3131
3130
|
}
|
|
3132
3131
|
}
|
|
3133
3132
|
var init_connection = __esm({
|
|
@@ -3354,8 +3353,6 @@ var init_cost_helpers = __esm({
|
|
|
3354
3353
|
"src/webui-server/cost-helpers.ts"() {
|
|
3355
3354
|
}
|
|
3356
3355
|
});
|
|
3357
|
-
|
|
3358
|
-
// src/webui-server/ws-handlers/introspection.ts
|
|
3359
3356
|
async function handleSkillsList(ctx, ws) {
|
|
3360
3357
|
if (!ctx.skillLoader) {
|
|
3361
3358
|
ctx.send(ws, { type: "skills.list", payload: { skills: [], enabled: false } });
|
|
@@ -3386,7 +3383,7 @@ async function handleSkillsList(ctx, ws) {
|
|
|
3386
3383
|
payload: {
|
|
3387
3384
|
skills: [],
|
|
3388
3385
|
enabled: true,
|
|
3389
|
-
error:
|
|
3386
|
+
error: toErrorMessage(err)
|
|
3390
3387
|
}
|
|
3391
3388
|
});
|
|
3392
3389
|
}
|
|
@@ -3543,8 +3540,8 @@ function sendResult6(ctx, ws, success, message) {
|
|
|
3543
3540
|
ctx.send(ws, { type: "key.operation_result", payload: { success, message } });
|
|
3544
3541
|
}
|
|
3545
3542
|
async function handleProjectsList(ctx, ws) {
|
|
3546
|
-
const projectsBase = ctx.opts.globalConfigPath ?
|
|
3547
|
-
const manifestPath =
|
|
3543
|
+
const projectsBase = ctx.opts.globalConfigPath ? path39.resolve(path39.dirname(ctx.opts.globalConfigPath)) : wstackGlobalRoot();
|
|
3544
|
+
const manifestPath = path39.join(projectsBase, "projects.json");
|
|
3548
3545
|
try {
|
|
3549
3546
|
const raw = await fsp5.readFile(manifestPath, "utf8");
|
|
3550
3547
|
const manifest = JSON.parse(raw);
|
|
@@ -3557,22 +3554,22 @@ async function handleProjectsSelect(ctx, ws, payload) {
|
|
|
3557
3554
|
const { opts } = ctx;
|
|
3558
3555
|
const { root, name: projectName } = payload;
|
|
3559
3556
|
try {
|
|
3560
|
-
const resolved =
|
|
3557
|
+
const resolved = path39.resolve(root);
|
|
3561
3558
|
const stat7 = await fsp5.stat(resolved).catch(() => null);
|
|
3562
3559
|
if (!stat7?.isDirectory()) {
|
|
3563
3560
|
ctx.send(ws, {
|
|
3564
3561
|
type: "projects.selected",
|
|
3565
3562
|
payload: {
|
|
3566
3563
|
root,
|
|
3567
|
-
name: projectName ??
|
|
3564
|
+
name: projectName ?? path39.basename(root),
|
|
3568
3565
|
message: `Cannot switch: not a directory: ${resolved}`
|
|
3569
3566
|
}
|
|
3570
3567
|
});
|
|
3571
3568
|
return;
|
|
3572
3569
|
}
|
|
3573
3570
|
const manifest = await loadManifest(opts.globalConfigPath);
|
|
3574
|
-
const entry = manifest.projects.find((p) =>
|
|
3575
|
-
const displayName = projectName?.trim() || entry?.name ||
|
|
3571
|
+
const entry = manifest.projects.find((p) => path39.resolve(p.root) === resolved);
|
|
3572
|
+
const displayName = projectName?.trim() || entry?.name || path39.basename(resolved);
|
|
3576
3573
|
if (entry) {
|
|
3577
3574
|
entry.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
|
|
3578
3575
|
} else {
|
|
@@ -3618,8 +3615,8 @@ async function handleProjectsSelect(ctx, ws, payload) {
|
|
|
3618
3615
|
});
|
|
3619
3616
|
} catch {
|
|
3620
3617
|
}
|
|
3621
|
-
const globalRoot = opts.globalConfigPath ?
|
|
3622
|
-
const newSessionsDir =
|
|
3618
|
+
const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : wstackGlobalRoot();
|
|
3619
|
+
const newSessionsDir = path39.join(resolveProjectDir(resolved, globalRoot), "sessions");
|
|
3623
3620
|
await fsp5.mkdir(newSessionsDir, { recursive: true });
|
|
3624
3621
|
const newStore = new DefaultSessionStore({ dir: newSessionsDir });
|
|
3625
3622
|
opts.sessionStore = newStore;
|
|
@@ -3643,17 +3640,17 @@ async function handleProjectsSelect(ctx, ws, payload) {
|
|
|
3643
3640
|
const switchedP = await ctx.buildSessionStart({ reset: true, clearedSessionId: oldSessionId });
|
|
3644
3641
|
ctx.broadcast({ type: "session.start", payload: switchedP });
|
|
3645
3642
|
} catch (err) {
|
|
3646
|
-
sendResult6(ctx, ws, false,
|
|
3643
|
+
sendResult6(ctx, ws, false, toErrorMessage(err));
|
|
3647
3644
|
}
|
|
3648
3645
|
}
|
|
3649
3646
|
async function handleProjectsAdd(ctx, ws, payload) {
|
|
3650
3647
|
const { root: addRoot, name: addName } = payload;
|
|
3651
3648
|
try {
|
|
3652
|
-
const resolved =
|
|
3649
|
+
const resolved = path39.resolve(addRoot);
|
|
3653
3650
|
const stat7 = await fsp5.stat(resolved).catch(() => null);
|
|
3654
3651
|
if (!stat7?.isDirectory()) throw new Error(`Not a directory: ${resolved}`);
|
|
3655
3652
|
const manifest = await loadManifest(ctx.opts.globalConfigPath);
|
|
3656
|
-
const existing = manifest.projects.find((p) =>
|
|
3653
|
+
const existing = manifest.projects.find((p) => path39.resolve(p.root) === resolved);
|
|
3657
3654
|
if (existing) {
|
|
3658
3655
|
ctx.send(ws, {
|
|
3659
3656
|
type: "projects.added",
|
|
@@ -3666,7 +3663,7 @@ async function handleProjectsAdd(ctx, ws, payload) {
|
|
|
3666
3663
|
});
|
|
3667
3664
|
return;
|
|
3668
3665
|
}
|
|
3669
|
-
const name = addName?.trim() ||
|
|
3666
|
+
const name = addName?.trim() || path39.basename(resolved);
|
|
3670
3667
|
const slug = projectSlug(resolved);
|
|
3671
3668
|
await ensureProjectDataDir(slug, ctx.opts.globalConfigPath);
|
|
3672
3669
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -3680,10 +3677,10 @@ async function handleProjectsAdd(ctx, ws, payload) {
|
|
|
3680
3677
|
ctx.send(ws, {
|
|
3681
3678
|
type: "projects.added",
|
|
3682
3679
|
payload: {
|
|
3683
|
-
name:
|
|
3680
|
+
name: path39.basename(addRoot),
|
|
3684
3681
|
root: addRoot,
|
|
3685
3682
|
slug: "",
|
|
3686
|
-
message:
|
|
3683
|
+
message: toErrorMessage(err)
|
|
3687
3684
|
}
|
|
3688
3685
|
});
|
|
3689
3686
|
}
|
|
@@ -3691,8 +3688,8 @@ async function handleProjectsAdd(ctx, ws, payload) {
|
|
|
3691
3688
|
async function handleWorkingDirSet(ctx, ws, newPath) {
|
|
3692
3689
|
try {
|
|
3693
3690
|
const wdRoot = ctx.opts.projectRoot ?? ctx.opts.agent.ctx.projectRoot;
|
|
3694
|
-
const resolved =
|
|
3695
|
-
if (!resolved.startsWith(wdRoot +
|
|
3691
|
+
const resolved = path39.resolve(wdRoot, newPath);
|
|
3692
|
+
if (!resolved.startsWith(wdRoot + path39.sep) && resolved !== wdRoot) {
|
|
3696
3693
|
sendResult6(ctx, ws, false, `Path must stay inside the project root: ${wdRoot}`);
|
|
3697
3694
|
return;
|
|
3698
3695
|
}
|
|
@@ -3705,7 +3702,7 @@ async function handleWorkingDirSet(ctx, ws, newPath) {
|
|
|
3705
3702
|
ctx.broadcast({ type: "working_dir.changed", payload: { cwd: resolved, projectRoot: wdRoot } });
|
|
3706
3703
|
sendResult6(ctx, ws, true, `Working directory set to ${resolved}`);
|
|
3707
3704
|
} catch (err) {
|
|
3708
|
-
sendResult6(ctx, ws, false,
|
|
3705
|
+
sendResult6(ctx, ws, false, toErrorMessage(err));
|
|
3709
3706
|
}
|
|
3710
3707
|
}
|
|
3711
3708
|
var init_projects = __esm({
|
|
@@ -3713,11 +3710,27 @@ var init_projects = __esm({
|
|
|
3713
3710
|
init_project_utils();
|
|
3714
3711
|
}
|
|
3715
3712
|
});
|
|
3716
|
-
|
|
3717
|
-
// src/webui-server/ws-handlers/providers.ts
|
|
3718
3713
|
function sendResult7(ctx, ws, success, message) {
|
|
3719
3714
|
ctx.send(ws, { type: "key.operation_result", payload: { success, message } });
|
|
3720
3715
|
}
|
|
3716
|
+
function broadcastSaved(ctx, providers) {
|
|
3717
|
+
ctx.broadcast({
|
|
3718
|
+
type: "providers.saved",
|
|
3719
|
+
payload: {
|
|
3720
|
+
providers: Object.entries(providers).map(([id, cfg]) => ({
|
|
3721
|
+
id,
|
|
3722
|
+
family: cfg.family,
|
|
3723
|
+
baseUrl: cfg.baseUrl,
|
|
3724
|
+
apiKeys: normalizeKeys(cfg).map((k) => ({
|
|
3725
|
+
label: k.label,
|
|
3726
|
+
maskedKey: maskedKey(k.apiKey),
|
|
3727
|
+
isActive: k.label === cfg.activeKey,
|
|
3728
|
+
createdAt: k.createdAt
|
|
3729
|
+
}))
|
|
3730
|
+
}))
|
|
3731
|
+
}
|
|
3732
|
+
});
|
|
3733
|
+
}
|
|
3721
3734
|
async function handleProvidersList(ctx, ws) {
|
|
3722
3735
|
if (!ctx.modelsRegistry) {
|
|
3723
3736
|
sendResult7(ctx, ws, false, "Models registry not available");
|
|
@@ -3742,7 +3755,7 @@ async function handleProvidersList(ctx, ws) {
|
|
|
3742
3755
|
}
|
|
3743
3756
|
});
|
|
3744
3757
|
} catch (err) {
|
|
3745
|
-
sendResult7(ctx, ws, false,
|
|
3758
|
+
sendResult7(ctx, ws, false, toErrorMessage(err));
|
|
3746
3759
|
}
|
|
3747
3760
|
}
|
|
3748
3761
|
async function handleProviderModels(ctx, ws, providerId) {
|
|
@@ -3777,7 +3790,7 @@ async function handleProviderModels(ctx, ws, providerId) {
|
|
|
3777
3790
|
}
|
|
3778
3791
|
});
|
|
3779
3792
|
} catch (err) {
|
|
3780
|
-
sendResult7(ctx, ws, false,
|
|
3793
|
+
sendResult7(ctx, ws, false, toErrorMessage(err));
|
|
3781
3794
|
}
|
|
3782
3795
|
}
|
|
3783
3796
|
async function handleProvidersSaved(ctx, ws) {
|
|
@@ -3800,7 +3813,7 @@ async function handleProvidersSaved(ctx, ws) {
|
|
|
3800
3813
|
}
|
|
3801
3814
|
});
|
|
3802
3815
|
} catch (err) {
|
|
3803
|
-
sendResult7(ctx, ws, false,
|
|
3816
|
+
sendResult7(ctx, ws, false, toErrorMessage(err));
|
|
3804
3817
|
}
|
|
3805
3818
|
}
|
|
3806
3819
|
async function handleKeyUpsert(ctx, ws, providerId, label, apiKey) {
|
|
@@ -3820,7 +3833,7 @@ async function handleKeyUpsert(ctx, ws, providerId, label, apiKey) {
|
|
|
3820
3833
|
await ctx.providerStore.save(providers);
|
|
3821
3834
|
sendResult7(ctx, ws, true, `Key "${label}" saved for ${providerId}`);
|
|
3822
3835
|
} catch (err) {
|
|
3823
|
-
sendResult7(ctx, ws, false,
|
|
3836
|
+
sendResult7(ctx, ws, false, toErrorMessage(err));
|
|
3824
3837
|
}
|
|
3825
3838
|
}
|
|
3826
3839
|
async function handleKeyDelete(ctx, ws, providerId, label) {
|
|
@@ -3844,7 +3857,7 @@ async function handleKeyDelete(ctx, ws, providerId, label) {
|
|
|
3844
3857
|
await ctx.providerStore.save(providers);
|
|
3845
3858
|
sendResult7(ctx, ws, true, `Key "${label}" deleted from ${providerId}`);
|
|
3846
3859
|
} catch (err) {
|
|
3847
|
-
sendResult7(ctx, ws, false,
|
|
3860
|
+
sendResult7(ctx, ws, false, toErrorMessage(err));
|
|
3848
3861
|
}
|
|
3849
3862
|
}
|
|
3850
3863
|
async function handleKeySetActive(ctx, ws, providerId, label) {
|
|
@@ -3861,7 +3874,7 @@ async function handleKeySetActive(ctx, ws, providerId, label) {
|
|
|
3861
3874
|
await ctx.providerStore.save(providers);
|
|
3862
3875
|
sendResult7(ctx, ws, true, `Active key for ${providerId} set to "${label}"`);
|
|
3863
3876
|
} catch (err) {
|
|
3864
|
-
sendResult7(ctx, ws, false,
|
|
3877
|
+
sendResult7(ctx, ws, false, toErrorMessage(err));
|
|
3865
3878
|
}
|
|
3866
3879
|
}
|
|
3867
3880
|
async function handleProviderAdd(ctx, ws, payload) {
|
|
@@ -3906,7 +3919,7 @@ async function handleProviderAdd(ctx, ws, payload) {
|
|
|
3906
3919
|
}
|
|
3907
3920
|
});
|
|
3908
3921
|
} catch (err) {
|
|
3909
|
-
sendResult7(ctx, ws, false,
|
|
3922
|
+
sendResult7(ctx, ws, false, toErrorMessage(err));
|
|
3910
3923
|
}
|
|
3911
3924
|
}
|
|
3912
3925
|
async function handleProviderRemove(ctx, ws, providerId) {
|
|
@@ -3920,12 +3933,92 @@ async function handleProviderRemove(ctx, ws, providerId) {
|
|
|
3920
3933
|
await ctx.providerStore.save(providers);
|
|
3921
3934
|
sendResult7(ctx, ws, true, `Provider "${providerId}" removed`);
|
|
3922
3935
|
} catch (err) {
|
|
3923
|
-
sendResult7(ctx, ws, false,
|
|
3936
|
+
sendResult7(ctx, ws, false, toErrorMessage(err));
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
async function handleProviderClearModels(ctx, ws, providerId) {
|
|
3940
|
+
try {
|
|
3941
|
+
const providers = await ctx.providerStore.load();
|
|
3942
|
+
const cfg = providers[providerId];
|
|
3943
|
+
if (!cfg) {
|
|
3944
|
+
sendResult7(ctx, ws, false, `Unknown provider "${providerId}"`);
|
|
3945
|
+
return;
|
|
3946
|
+
}
|
|
3947
|
+
delete cfg.models;
|
|
3948
|
+
await ctx.providerStore.save(providers);
|
|
3949
|
+
sendResult7(ctx, ws, true, `Cleared model allowlist for ${providerId}`);
|
|
3950
|
+
broadcastSaved(ctx, providers);
|
|
3951
|
+
} catch (err) {
|
|
3952
|
+
sendResult7(ctx, ws, false, toErrorMessage(err));
|
|
3924
3953
|
}
|
|
3925
3954
|
}
|
|
3955
|
+
async function handleProviderUndoClear(ctx, ws, providerId, previousModels) {
|
|
3956
|
+
try {
|
|
3957
|
+
const providers = await ctx.providerStore.load();
|
|
3958
|
+
const cfg = providers[providerId];
|
|
3959
|
+
if (!cfg) {
|
|
3960
|
+
sendResult7(ctx, ws, false, `Unknown provider "${providerId}"`);
|
|
3961
|
+
return;
|
|
3962
|
+
}
|
|
3963
|
+
cfg.models = [...previousModels];
|
|
3964
|
+
await ctx.providerStore.save(providers);
|
|
3965
|
+
sendResult7(ctx, ws, true, `Restored ${previousModels.length} model(s) for ${providerId}`);
|
|
3966
|
+
broadcastSaved(ctx, providers);
|
|
3967
|
+
} catch (err) {
|
|
3968
|
+
sendResult7(ctx, ws, false, toErrorMessage(err));
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3971
|
+
async function handleProviderUpdate(ctx, ws, payload) {
|
|
3972
|
+
try {
|
|
3973
|
+
const providers = await ctx.providerStore.load();
|
|
3974
|
+
const cfg = providers[payload.id];
|
|
3975
|
+
if (!cfg) {
|
|
3976
|
+
sendResult7(ctx, ws, false, `Unknown provider "${payload.id}"`);
|
|
3977
|
+
return;
|
|
3978
|
+
}
|
|
3979
|
+
if (payload.family !== void 0) cfg.family = payload.family;
|
|
3980
|
+
if (payload.baseUrl !== void 0) cfg.baseUrl = payload.baseUrl;
|
|
3981
|
+
if (payload.envVars !== void 0) cfg.envVars = payload.envVars;
|
|
3982
|
+
if (payload.models !== void 0) cfg.models = payload.models;
|
|
3983
|
+
await ctx.providerStore.save(providers);
|
|
3984
|
+
sendResult7(ctx, ws, true, `Updated ${payload.id}`);
|
|
3985
|
+
broadcastSaved(ctx, providers);
|
|
3986
|
+
} catch (err) {
|
|
3987
|
+
sendResult7(ctx, ws, false, toErrorMessage(err));
|
|
3988
|
+
}
|
|
3989
|
+
}
|
|
3990
|
+
async function handleProviderProbe(ctx, ws, providerId, timeoutMs) {
|
|
3991
|
+
const reply = (payload) => ctx.send(ws, { type: "provider.probe", payload: { providerId, ...payload } });
|
|
3992
|
+
try {
|
|
3993
|
+
const providers = await ctx.providerStore.load();
|
|
3994
|
+
const cfg = providers[providerId];
|
|
3995
|
+
if (!cfg) {
|
|
3996
|
+
reply({ ok: false, status: "no_provider" });
|
|
3997
|
+
return;
|
|
3998
|
+
}
|
|
3999
|
+
if (!cfg.baseUrl) {
|
|
4000
|
+
reply({ ok: false, status: "no_base_url" });
|
|
4001
|
+
return;
|
|
4002
|
+
}
|
|
4003
|
+
const keys = normalizeKeys(cfg);
|
|
4004
|
+
const active = keys.find((k) => k.label === cfg.activeKey) ?? keys[0];
|
|
4005
|
+
const result = await probeLocalLlm({
|
|
4006
|
+
baseUrl: cfg.baseUrl,
|
|
4007
|
+
apiKey: active?.apiKey,
|
|
4008
|
+
noAuth: false,
|
|
4009
|
+
scrubber: probeScrubber,
|
|
4010
|
+
...timeoutMs !== void 0 ? { timeoutMs } : {}
|
|
4011
|
+
});
|
|
4012
|
+
reply(result);
|
|
4013
|
+
} catch (err) {
|
|
4014
|
+
reply({ ok: false, status: "unreachable", detail: toErrorMessage(err) });
|
|
4015
|
+
}
|
|
4016
|
+
}
|
|
4017
|
+
var probeScrubber;
|
|
3926
4018
|
var init_providers = __esm({
|
|
3927
4019
|
"src/webui-server/ws-handlers/providers.ts"() {
|
|
3928
4020
|
init_provider_config();
|
|
4021
|
+
probeScrubber = new DefaultSecretScrubber();
|
|
3929
4022
|
}
|
|
3930
4023
|
});
|
|
3931
4024
|
function sendResult8(ctx, ws, success, message) {
|
|
@@ -3933,13 +4026,13 @@ function sendResult8(ctx, ws, success, message) {
|
|
|
3933
4026
|
}
|
|
3934
4027
|
function storeFor(opts) {
|
|
3935
4028
|
return opts.sessionStore ?? new DefaultSessionStore({
|
|
3936
|
-
dir:
|
|
4029
|
+
dir: path39.join(opts.projectRoot ?? opts.agent.ctx.projectRoot, ".wrongstack", "sessions")
|
|
3937
4030
|
});
|
|
3938
4031
|
}
|
|
3939
4032
|
async function handleGoalGet(ctx, _ws) {
|
|
3940
4033
|
const projectRoot = ctx.opts.projectRoot ?? ctx.opts.agent.ctx.projectRoot;
|
|
3941
4034
|
try {
|
|
3942
|
-
const goalPath =
|
|
4035
|
+
const goalPath = path39.join(projectRoot, ".wrongstack", "goal.json");
|
|
3943
4036
|
const raw = await fsp5.readFile(goalPath, "utf8");
|
|
3944
4037
|
ctx.broadcast({ type: "goal.updated", payload: JSON.parse(raw) });
|
|
3945
4038
|
} catch {
|
|
@@ -3967,7 +4060,7 @@ async function handleSessionsList(ctx, ws, limit) {
|
|
|
3967
4060
|
} catch (err) {
|
|
3968
4061
|
ctx.send(ws, {
|
|
3969
4062
|
type: "sessions.list",
|
|
3970
|
-
payload: { sessions: [], error:
|
|
4063
|
+
payload: { sessions: [], error: toErrorMessage(err) }
|
|
3971
4064
|
});
|
|
3972
4065
|
}
|
|
3973
4066
|
}
|
|
@@ -3999,7 +4092,7 @@ async function handleSessionNew(ctx, _ws) {
|
|
|
3999
4092
|
JSON.stringify({
|
|
4000
4093
|
level: "warn",
|
|
4001
4094
|
event: "webui.session_new_store_failed",
|
|
4002
|
-
message:
|
|
4095
|
+
message: toErrorMessage(err),
|
|
4003
4096
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4004
4097
|
})
|
|
4005
4098
|
);
|
|
@@ -4015,7 +4108,7 @@ async function handleSessionNew(ctx, _ws) {
|
|
|
4015
4108
|
function rewinderFor(opts) {
|
|
4016
4109
|
const projectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot;
|
|
4017
4110
|
return new DefaultSessionRewinder(
|
|
4018
|
-
opts.sessionsDir ??
|
|
4111
|
+
opts.sessionsDir ?? path39.join(projectRoot, ".wrongstack", "sessions"),
|
|
4019
4112
|
projectRoot
|
|
4020
4113
|
);
|
|
4021
4114
|
}
|
|
@@ -4039,7 +4132,7 @@ async function handleSessionRewind(ctx, ws, checkpointIndex) {
|
|
|
4039
4132
|
const payload = await ctx.buildSessionStart({ reset: true });
|
|
4040
4133
|
ctx.broadcast({ type: "session.start", payload });
|
|
4041
4134
|
} catch (err) {
|
|
4042
|
-
sendResult8(ctx, ws, false,
|
|
4135
|
+
sendResult8(ctx, ws, false, toErrorMessage(err));
|
|
4043
4136
|
}
|
|
4044
4137
|
}
|
|
4045
4138
|
async function handleSessionDelete(ctx, ws, id) {
|
|
@@ -4051,7 +4144,7 @@ async function handleSessionDelete(ctx, ws, id) {
|
|
|
4051
4144
|
await storeFor(ctx.opts).delete(id);
|
|
4052
4145
|
sendResult8(ctx, ws, true, `Session ${id} deleted`);
|
|
4053
4146
|
} catch (err) {
|
|
4054
|
-
sendResult8(ctx, ws, false,
|
|
4147
|
+
sendResult8(ctx, ws, false, toErrorMessage(err));
|
|
4055
4148
|
}
|
|
4056
4149
|
}
|
|
4057
4150
|
function handleSessionSave(ctx, ws) {
|
|
@@ -4094,7 +4187,7 @@ async function handleSessionResume(ctx, ws, id) {
|
|
|
4094
4187
|
ctx.broadcast({ type: "session.start", payload });
|
|
4095
4188
|
sendResult8(ctx, ws, true, `Resumed session ${id}`);
|
|
4096
4189
|
} catch (err) {
|
|
4097
|
-
sendResult8(ctx, ws, false,
|
|
4190
|
+
sendResult8(ctx, ws, false, toErrorMessage(err));
|
|
4098
4191
|
}
|
|
4099
4192
|
}
|
|
4100
4193
|
var init_sessions = __esm({
|
|
@@ -4349,14 +4442,14 @@ async function runWebUI(opts) {
|
|
|
4349
4442
|
let customModeStoreP = null;
|
|
4350
4443
|
const getCustomModeStore = () => {
|
|
4351
4444
|
customModeStoreP ??= (async () => {
|
|
4352
|
-
const dir = opts.globalConfigPath ?
|
|
4445
|
+
const dir = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : wstackGlobalRoot();
|
|
4353
4446
|
const store = createCustomModeStore(dir);
|
|
4354
4447
|
await store.load();
|
|
4355
4448
|
return store;
|
|
4356
4449
|
})();
|
|
4357
4450
|
return customModeStoreP;
|
|
4358
4451
|
};
|
|
4359
|
-
const autoPhaseStoreDir = opts.projectRoot ?
|
|
4452
|
+
const autoPhaseStoreDir = opts.projectRoot ? path39.join(opts.projectRoot, ".wrongstack", "autophase") : path39.join(os.tmpdir(), ".wrongstack", "autophase");
|
|
4360
4453
|
const autoPhaseHandler = new AutoPhaseWebSocketHandler(
|
|
4361
4454
|
opts.agent,
|
|
4362
4455
|
opts.agent.ctx,
|
|
@@ -4447,7 +4540,7 @@ async function runWebUI(opts) {
|
|
|
4447
4540
|
return;
|
|
4448
4541
|
}
|
|
4449
4542
|
const vault = new DefaultSecretVault({
|
|
4450
|
-
keyFile:
|
|
4543
|
+
keyFile: path39.join(path39.dirname(configPath2), ".key")
|
|
4451
4544
|
});
|
|
4452
4545
|
const decrypted = decryptConfigSecrets$1(parsed, vault);
|
|
4453
4546
|
const autonomyCfg = decrypted.autonomy ?? {};
|
|
@@ -4570,7 +4663,7 @@ async function runWebUI(opts) {
|
|
|
4570
4663
|
model: opts.agent.ctx.model,
|
|
4571
4664
|
provider: opts.agent.ctx.provider.id,
|
|
4572
4665
|
mode: opts.modeId ?? "default",
|
|
4573
|
-
projectName: opts.projectRoot ?
|
|
4666
|
+
projectName: opts.projectRoot ? path39.basename(opts.projectRoot) : void 0,
|
|
4574
4667
|
// Frontend reads `projectRoot` from session.start (ws-handlers setEnv) —
|
|
4575
4668
|
// omitting it left the store's projectRoot empty after a project switch.
|
|
4576
4669
|
projectRoot: opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "",
|
|
@@ -4596,7 +4689,7 @@ async function runWebUI(opts) {
|
|
|
4596
4689
|
const projectDir = resolveProjectDir(opts.projectRoot, wstackGlobalRoot());
|
|
4597
4690
|
const mailbox = new GlobalMailbox(projectDir, opts.events);
|
|
4598
4691
|
webuiClientId = `webui@${crypto3.randomUUID().slice(0, 8)}`;
|
|
4599
|
-
const projectName = opts.projectRoot ?
|
|
4692
|
+
const projectName = opts.projectRoot ? path39.basename(opts.projectRoot) : "unknown";
|
|
4600
4693
|
await mailbox.registerClient({
|
|
4601
4694
|
clientId: webuiClientId,
|
|
4602
4695
|
sessionId: opts.projectRoot,
|
|
@@ -4627,7 +4720,7 @@ async function runWebUI(opts) {
|
|
|
4627
4720
|
host,
|
|
4628
4721
|
httpPort,
|
|
4629
4722
|
wsPort,
|
|
4630
|
-
globalRoot:
|
|
4723
|
+
globalRoot: path39.dirname(opts.globalConfigPath ?? "")
|
|
4631
4724
|
});
|
|
4632
4725
|
if (httpServer) {
|
|
4633
4726
|
announceWebuiReady({
|
|
@@ -4642,7 +4735,7 @@ async function runWebUI(opts) {
|
|
|
4642
4735
|
`[WebUI] Frontend not served (run \`pnpm --filter @wrongstack/webui build\`). WS bridge still active on ws://${host}:${wsPort}.`
|
|
4643
4736
|
);
|
|
4644
4737
|
}
|
|
4645
|
-
const registryBaseDir = opts.globalConfigPath ?
|
|
4738
|
+
const registryBaseDir = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : void 0;
|
|
4646
4739
|
if (opts.projectRoot) {
|
|
4647
4740
|
registerWebuiInstance({
|
|
4648
4741
|
pid: process.pid,
|
|
@@ -4999,12 +5092,12 @@ async function runWebUI(opts) {
|
|
|
4999
5092
|
broadcast,
|
|
5000
5093
|
log: (m) => console.log(m)
|
|
5001
5094
|
};
|
|
5002
|
-
return new Promise((
|
|
5095
|
+
return new Promise((resolve11) => {
|
|
5003
5096
|
wss.on("listening", () => {
|
|
5004
5097
|
console.log(`[WebUI] WebSocket server running on ws://${host}:${port}`);
|
|
5005
5098
|
setupEvents();
|
|
5006
5099
|
opts.onListening?.({ httpPort, wsPort, host });
|
|
5007
|
-
const globalRoot = opts.globalConfigPath ?
|
|
5100
|
+
const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : void 0;
|
|
5008
5101
|
if (globalRoot) {
|
|
5009
5102
|
const statusInterval = setInterval(async () => {
|
|
5010
5103
|
try {
|
|
@@ -5123,8 +5216,8 @@ async function runWebUI(opts) {
|
|
|
5123
5216
|
clients.delete(ws);
|
|
5124
5217
|
abortControllers.delete(ws);
|
|
5125
5218
|
if (clients.size === 0 && pendingConfirms.size > 0) {
|
|
5126
|
-
for (const [id,
|
|
5127
|
-
|
|
5219
|
+
for (const [id, resolve12] of pendingConfirms) {
|
|
5220
|
+
resolve12("no");
|
|
5128
5221
|
pendingConfirms.delete(id);
|
|
5129
5222
|
}
|
|
5130
5223
|
}
|
|
@@ -5163,7 +5256,7 @@ async function runWebUI(opts) {
|
|
|
5163
5256
|
wss,
|
|
5164
5257
|
pid: process.pid,
|
|
5165
5258
|
registryBaseDir,
|
|
5166
|
-
onStopped:
|
|
5259
|
+
onStopped: resolve11
|
|
5167
5260
|
});
|
|
5168
5261
|
registerWebuiSignalHandlers(signalShutdown);
|
|
5169
5262
|
});
|
|
@@ -5226,6 +5319,31 @@ async function runWebUI(opts) {
|
|
|
5226
5319
|
await handleProviderRemove(wsHandlerCtx, ws, m.payload.providerId);
|
|
5227
5320
|
break;
|
|
5228
5321
|
}
|
|
5322
|
+
case "provider.clear_models": {
|
|
5323
|
+
const m = msg;
|
|
5324
|
+
await handleProviderClearModels(wsHandlerCtx, ws, m.payload.providerId);
|
|
5325
|
+
break;
|
|
5326
|
+
}
|
|
5327
|
+
case "provider.undo_clear": {
|
|
5328
|
+
const m = msg;
|
|
5329
|
+
await handleProviderUndoClear(
|
|
5330
|
+
wsHandlerCtx,
|
|
5331
|
+
ws,
|
|
5332
|
+
m.payload.providerId,
|
|
5333
|
+
m.payload.previousModels
|
|
5334
|
+
);
|
|
5335
|
+
break;
|
|
5336
|
+
}
|
|
5337
|
+
case "provider.update": {
|
|
5338
|
+
const m = msg;
|
|
5339
|
+
await handleProviderUpdate(wsHandlerCtx, ws, m.payload);
|
|
5340
|
+
break;
|
|
5341
|
+
}
|
|
5342
|
+
case "provider.probe": {
|
|
5343
|
+
const m = msg;
|
|
5344
|
+
await handleProviderProbe(wsHandlerCtx, ws, m.payload.providerId, m.payload.timeoutMs);
|
|
5345
|
+
break;
|
|
5346
|
+
}
|
|
5229
5347
|
case "todos.get": {
|
|
5230
5348
|
handleTodosGet(worklistCtx, ws);
|
|
5231
5349
|
break;
|
|
@@ -5500,11 +5618,16 @@ async function runWebUI(opts) {
|
|
|
5500
5618
|
break;
|
|
5501
5619
|
}
|
|
5502
5620
|
// Collaboration messages — the CLI webui-server doesn't run a
|
|
5503
|
-
// full collab hub; silently acknowledge and ignore.
|
|
5621
|
+
// full collab hub; silently acknowledge and ignore. request_pause /
|
|
5622
|
+
// resume are included so the CollabPanel's pause/resume buttons don't
|
|
5623
|
+
// trip the "Unhandled message type" warning (the standalone webui
|
|
5624
|
+
// server is the one that wires the real CollaborationWebSocketHandler).
|
|
5504
5625
|
case "collab.join":
|
|
5505
5626
|
case "collab.leave":
|
|
5506
5627
|
case "collab.annotate":
|
|
5507
5628
|
case "collab.resolve":
|
|
5629
|
+
case "collab.request_pause":
|
|
5630
|
+
case "collab.resume":
|
|
5508
5631
|
break;
|
|
5509
5632
|
case "projects.list": {
|
|
5510
5633
|
await handleProjectsList(projectsCtx, ws);
|
|
@@ -5553,7 +5676,7 @@ async function runWebUI(opts) {
|
|
|
5553
5676
|
// ── Mailbox operations — project-level inter-agent messaging ────
|
|
5554
5677
|
case "mailbox.messages": {
|
|
5555
5678
|
const projectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "";
|
|
5556
|
-
const globalRoot = opts.globalConfigPath ?
|
|
5679
|
+
const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : "";
|
|
5557
5680
|
if (!projectRoot || !globalRoot) {
|
|
5558
5681
|
send(ws, {
|
|
5559
5682
|
type: "mailbox.messages",
|
|
@@ -5602,7 +5725,7 @@ async function runWebUI(opts) {
|
|
|
5602
5725
|
}
|
|
5603
5726
|
case "mailbox.agents": {
|
|
5604
5727
|
const projectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "";
|
|
5605
|
-
const globalRoot = opts.globalConfigPath ?
|
|
5728
|
+
const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : "";
|
|
5606
5729
|
if (!projectRoot || !globalRoot) {
|
|
5607
5730
|
send(ws, {
|
|
5608
5731
|
type: "mailbox.agents",
|
|
@@ -5645,7 +5768,7 @@ async function runWebUI(opts) {
|
|
|
5645
5768
|
}
|
|
5646
5769
|
case "mailbox.clear": {
|
|
5647
5770
|
const projectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "";
|
|
5648
|
-
const globalRoot = opts.globalConfigPath ?
|
|
5771
|
+
const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : "";
|
|
5649
5772
|
if (!projectRoot || !globalRoot) {
|
|
5650
5773
|
send(ws, { type: "mailbox.cleared", payload: { error: "No project root available" } });
|
|
5651
5774
|
break;
|
|
@@ -5663,6 +5786,57 @@ async function runWebUI(opts) {
|
|
|
5663
5786
|
}
|
|
5664
5787
|
break;
|
|
5665
5788
|
}
|
|
5789
|
+
case "mailbox.purge": {
|
|
5790
|
+
const projectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "";
|
|
5791
|
+
const globalRoot = opts.globalConfigPath ? path39.dirname(opts.globalConfigPath) : "";
|
|
5792
|
+
if (!projectRoot || !globalRoot) {
|
|
5793
|
+
send(ws, { type: "mailbox.purged", payload: { error: "No project root available" } });
|
|
5794
|
+
break;
|
|
5795
|
+
}
|
|
5796
|
+
try {
|
|
5797
|
+
const mbDir = resolveProjectDir(projectRoot, globalRoot);
|
|
5798
|
+
const mb = new GlobalMailbox(mbDir);
|
|
5799
|
+
const payload = msg;
|
|
5800
|
+
const result = await mb.purgeStale(payload.payload);
|
|
5801
|
+
send(ws, { type: "mailbox.purged", payload: result });
|
|
5802
|
+
} catch (err) {
|
|
5803
|
+
send(ws, {
|
|
5804
|
+
type: "mailbox.purged",
|
|
5805
|
+
payload: { error: err instanceof Error ? err.message : String(err) }
|
|
5806
|
+
});
|
|
5807
|
+
}
|
|
5808
|
+
break;
|
|
5809
|
+
}
|
|
5810
|
+
case "git.info": {
|
|
5811
|
+
const projectRoot = opts.projectRoot ?? opts.agent.ctx.projectRoot ?? "";
|
|
5812
|
+
const cwd = projectRoot || void 0;
|
|
5813
|
+
const { execFile: ef } = await import('child_process');
|
|
5814
|
+
const git = (args) => new Promise((resolve11) => {
|
|
5815
|
+
ef("git", args, { cwd, timeout: 3e3 }, (err, stdout) => {
|
|
5816
|
+
resolve11(err ? "" : stdout.trim());
|
|
5817
|
+
});
|
|
5818
|
+
});
|
|
5819
|
+
const [branchRaw, diffRaw, statusRaw, upstreamRaw] = await Promise.all([
|
|
5820
|
+
git(["branch", "--show-current"]),
|
|
5821
|
+
git(["diff", "--stat"]),
|
|
5822
|
+
git(["status", "--porcelain"]),
|
|
5823
|
+
git(["rev-list", "--left-right", "--count", "@{upstream}...HEAD"])
|
|
5824
|
+
]);
|
|
5825
|
+
const branch = branchRaw || "(detached)";
|
|
5826
|
+
const addMatch = /(\d+)\s+insertion/i.exec(diffRaw);
|
|
5827
|
+
const delMatch = /(\d+)\s+deletion/i.exec(diffRaw);
|
|
5828
|
+
const added = addMatch ? Number(addMatch[1]) : 0;
|
|
5829
|
+
const deleted = delMatch ? Number(delMatch[1]) : 0;
|
|
5830
|
+
const untracked = statusRaw.split("\n").filter((l) => l.startsWith("??")).length;
|
|
5831
|
+
const [behindRaw, aheadRaw] = (upstreamRaw || "0 0").split(" ");
|
|
5832
|
+
const behind = Number(behindRaw) || 0;
|
|
5833
|
+
const ahead = Number(aheadRaw) || 0;
|
|
5834
|
+
send(ws, {
|
|
5835
|
+
type: "git.info",
|
|
5836
|
+
payload: { branch, added, deleted, untracked, ahead, behind }
|
|
5837
|
+
});
|
|
5838
|
+
break;
|
|
5839
|
+
}
|
|
5666
5840
|
default: {
|
|
5667
5841
|
const msgType = msg.type;
|
|
5668
5842
|
if (msgType.startsWith("autophase.")) {
|
|
@@ -5715,7 +5889,7 @@ var init_webui_server = __esm({
|
|
|
5715
5889
|
var WORKTREE_PHASE_CONCURRENCY = 4;
|
|
5716
5890
|
var MAX_CMD_OUTPUT = 2e5;
|
|
5717
5891
|
function gitText(args, cwd) {
|
|
5718
|
-
return new Promise((
|
|
5892
|
+
return new Promise((resolve11, reject) => {
|
|
5719
5893
|
let child;
|
|
5720
5894
|
try {
|
|
5721
5895
|
child = spawn("git", args, {
|
|
@@ -5735,8 +5909,8 @@ function gitText(args, cwd) {
|
|
|
5735
5909
|
};
|
|
5736
5910
|
child.stdout?.on("data", emit);
|
|
5737
5911
|
child.stderr?.on("data", emit);
|
|
5738
|
-
child.on("error", () =>
|
|
5739
|
-
child.on("close", (code) =>
|
|
5912
|
+
child.on("error", () => resolve11({ code: 1, out: chunks.join("") }));
|
|
5913
|
+
child.on("close", (code) => resolve11({ code: code ?? 1, out: chunks.join("").trim() }));
|
|
5740
5914
|
});
|
|
5741
5915
|
}
|
|
5742
5916
|
async function isGitRepo(cwd) {
|
|
@@ -5772,7 +5946,7 @@ function runCmd(cmd, args, cwd, shell = false) {
|
|
|
5772
5946
|
});
|
|
5773
5947
|
}
|
|
5774
5948
|
}
|
|
5775
|
-
return new Promise((
|
|
5949
|
+
return new Promise((resolve11, reject) => {
|
|
5776
5950
|
const chunks = [];
|
|
5777
5951
|
let child;
|
|
5778
5952
|
try {
|
|
@@ -5795,11 +5969,11 @@ function runCmd(cmd, args, cwd, shell = false) {
|
|
|
5795
5969
|
};
|
|
5796
5970
|
child.stdout?.on("data", append);
|
|
5797
5971
|
child.stderr?.on("data", append);
|
|
5798
|
-
child.on("error", (e) =>
|
|
5972
|
+
child.on("error", (e) => resolve11({ code: 1, out: `${chunks.join("")}${String(e)}` }));
|
|
5799
5973
|
child.on("close", (code) => {
|
|
5800
5974
|
let out = chunks.join("");
|
|
5801
5975
|
if (out.length > MAX_CMD_OUTPUT) out = out.slice(-MAX_CMD_OUTPUT);
|
|
5802
|
-
|
|
5976
|
+
resolve11({ code: code ?? 1, out: out.trim() });
|
|
5803
5977
|
});
|
|
5804
5978
|
});
|
|
5805
5979
|
}
|
|
@@ -6275,7 +6449,7 @@ var ReadlineInputReader = class {
|
|
|
6275
6449
|
history = [];
|
|
6276
6450
|
pending = false;
|
|
6277
6451
|
constructor(opts = {}) {
|
|
6278
|
-
this.historyFile = opts.historyFile ??
|
|
6452
|
+
this.historyFile = opts.historyFile ?? path39.join(wstackGlobalRoot(), "history");
|
|
6279
6453
|
}
|
|
6280
6454
|
async loadHistory() {
|
|
6281
6455
|
try {
|
|
@@ -6287,7 +6461,7 @@ var ReadlineInputReader = class {
|
|
|
6287
6461
|
}
|
|
6288
6462
|
async saveHistory() {
|
|
6289
6463
|
try {
|
|
6290
|
-
await fsp5.mkdir(
|
|
6464
|
+
await fsp5.mkdir(path39.dirname(this.historyFile), { recursive: true });
|
|
6291
6465
|
await fsp5.writeFile(this.historyFile, this.history.slice(-1e3).join("\n"));
|
|
6292
6466
|
} catch {
|
|
6293
6467
|
}
|
|
@@ -6306,31 +6480,31 @@ var ReadlineInputReader = class {
|
|
|
6306
6480
|
async readLine(prompt) {
|
|
6307
6481
|
if (this.history.length === 0) await this.loadHistory();
|
|
6308
6482
|
while (this.pending) {
|
|
6309
|
-
await new Promise((
|
|
6483
|
+
await new Promise((resolve11) => setTimeout(resolve11, 50));
|
|
6310
6484
|
}
|
|
6311
6485
|
this.pending = true;
|
|
6312
6486
|
try {
|
|
6313
6487
|
if (this.rl) {
|
|
6314
6488
|
const old = this.rl;
|
|
6315
6489
|
this.rl = void 0;
|
|
6316
|
-
await new Promise((
|
|
6490
|
+
await new Promise((resolve11) => {
|
|
6317
6491
|
if (old.closed) {
|
|
6318
|
-
|
|
6492
|
+
resolve11();
|
|
6319
6493
|
} else {
|
|
6320
|
-
old.once("close",
|
|
6494
|
+
old.once("close", resolve11);
|
|
6321
6495
|
old.close();
|
|
6322
6496
|
}
|
|
6323
6497
|
});
|
|
6324
6498
|
}
|
|
6325
6499
|
const fresh = this.ensure();
|
|
6326
6500
|
this.installPromptGuard(fresh);
|
|
6327
|
-
return new Promise((
|
|
6501
|
+
return new Promise((resolve11) => {
|
|
6328
6502
|
let settled = false;
|
|
6329
6503
|
const settle = (line) => {
|
|
6330
6504
|
if (settled) return;
|
|
6331
6505
|
settled = true;
|
|
6332
6506
|
setOutputLineGuard(null);
|
|
6333
|
-
|
|
6507
|
+
resolve11(line);
|
|
6334
6508
|
};
|
|
6335
6509
|
fresh.question(prompt ?? "> ", (line) => {
|
|
6336
6510
|
if (line.trim()) {
|
|
@@ -6383,7 +6557,7 @@ var ReadlineInputReader = class {
|
|
|
6383
6557
|
async readKey(prompt, options) {
|
|
6384
6558
|
setOutputLineGuard(null);
|
|
6385
6559
|
writeOut(prompt);
|
|
6386
|
-
return new Promise((
|
|
6560
|
+
return new Promise((resolve11) => {
|
|
6387
6561
|
const stdin = process.stdin;
|
|
6388
6562
|
const wasRaw = stdin.isRaw;
|
|
6389
6563
|
const wasPaused = stdin.isPaused();
|
|
@@ -6394,7 +6568,7 @@ var ReadlineInputReader = class {
|
|
|
6394
6568
|
if (key === "") {
|
|
6395
6569
|
cleanup();
|
|
6396
6570
|
writeOut("\n");
|
|
6397
|
-
|
|
6571
|
+
resolve11("");
|
|
6398
6572
|
return;
|
|
6399
6573
|
}
|
|
6400
6574
|
const opt = options.find(
|
|
@@ -6404,12 +6578,12 @@ var ReadlineInputReader = class {
|
|
|
6404
6578
|
cleanup();
|
|
6405
6579
|
writeOut(`${opt.key}
|
|
6406
6580
|
`);
|
|
6407
|
-
|
|
6581
|
+
resolve11(opt.value);
|
|
6408
6582
|
}
|
|
6409
6583
|
};
|
|
6410
6584
|
const onClose = () => {
|
|
6411
6585
|
cleanup();
|
|
6412
|
-
|
|
6586
|
+
resolve11("");
|
|
6413
6587
|
};
|
|
6414
6588
|
const cleanup = () => {
|
|
6415
6589
|
stdin.off("data", onData);
|
|
@@ -6438,7 +6612,7 @@ var ReadlineInputReader = class {
|
|
|
6438
6612
|
this.rl?.close();
|
|
6439
6613
|
this.rl = void 0;
|
|
6440
6614
|
writeOut(prompt);
|
|
6441
|
-
return new Promise((
|
|
6615
|
+
return new Promise((resolve11) => {
|
|
6442
6616
|
let buf = "";
|
|
6443
6617
|
const wasRaw = stdin.isRaw;
|
|
6444
6618
|
setRawMode(stdin, true);
|
|
@@ -6456,7 +6630,7 @@ var ReadlineInputReader = class {
|
|
|
6456
6630
|
cleanup();
|
|
6457
6631
|
writeOut(` ${dim(`[${buf.length} chars]`)}
|
|
6458
6632
|
`);
|
|
6459
|
-
|
|
6633
|
+
resolve11(buf);
|
|
6460
6634
|
return;
|
|
6461
6635
|
}
|
|
6462
6636
|
if (ch === "") {
|
|
@@ -6619,7 +6793,7 @@ function pickGroupIndex(opts) {
|
|
|
6619
6793
|
if (Number.isFinite(parsed)) current = wrap(parsed);
|
|
6620
6794
|
} catch {
|
|
6621
6795
|
}
|
|
6622
|
-
fs2.mkdirSync(
|
|
6796
|
+
fs2.mkdirSync(path39.dirname(opts.cursorFile), { recursive: true });
|
|
6623
6797
|
fs2.writeFileSync(opts.cursorFile, String(wrap(current + 1)));
|
|
6624
6798
|
return current;
|
|
6625
6799
|
} catch {
|
|
@@ -6672,11 +6846,11 @@ function assertSafeToDelete(filename, parentDir) {
|
|
|
6672
6846
|
throw new FsError({
|
|
6673
6847
|
message: `Refusing to delete protected file: ${filename}`,
|
|
6674
6848
|
code: ERROR_CODES.FS_DELETE_FAILED,
|
|
6675
|
-
path:
|
|
6849
|
+
path: path39.join(parentDir, filename),
|
|
6676
6850
|
context: { reason: "protected_basename" }
|
|
6677
6851
|
});
|
|
6678
6852
|
}
|
|
6679
|
-
if (filename !==
|
|
6853
|
+
if (filename !== path39.basename(filename)) {
|
|
6680
6854
|
throw new FsError({
|
|
6681
6855
|
message: `Refusing to delete path with traversal: ${filename}`,
|
|
6682
6856
|
code: ERROR_CODES.FS_DELETE_FAILED,
|
|
@@ -6688,11 +6862,11 @@ function assertSafeToDelete(filename, parentDir) {
|
|
|
6688
6862
|
throw new FsError({
|
|
6689
6863
|
message: `Refusing to delete unknown file: ${filename}`,
|
|
6690
6864
|
code: ERROR_CODES.FS_DELETE_FAILED,
|
|
6691
|
-
path:
|
|
6865
|
+
path: path39.join(parentDir, filename),
|
|
6692
6866
|
context: { reason: "unknown_file_pattern" }
|
|
6693
6867
|
});
|
|
6694
6868
|
}
|
|
6695
|
-
const resolvedParent =
|
|
6869
|
+
const resolvedParent = path39.resolve(parentDir);
|
|
6696
6870
|
if (!resolvedParent.endsWith(".wrongstack")) {
|
|
6697
6871
|
throw new FsError({
|
|
6698
6872
|
message: `Unexpected parent directory for bak prune: ${resolvedParent}`,
|
|
@@ -6703,8 +6877,8 @@ function assertSafeToDelete(filename, parentDir) {
|
|
|
6703
6877
|
}
|
|
6704
6878
|
}
|
|
6705
6879
|
async function safeDelete(filePath) {
|
|
6706
|
-
const dir =
|
|
6707
|
-
const filename =
|
|
6880
|
+
const dir = path39.dirname(filePath);
|
|
6881
|
+
const filename = path39.basename(filePath);
|
|
6708
6882
|
try {
|
|
6709
6883
|
assertSafeToDelete(filename, dir);
|
|
6710
6884
|
await fsp5.unlink(filePath);
|
|
@@ -6749,16 +6923,16 @@ function diffSummary(oldCfg, newCfg) {
|
|
|
6749
6923
|
}
|
|
6750
6924
|
var defaultHomeDir = () => os__default.homedir();
|
|
6751
6925
|
function historyDir(homeFn = defaultHomeDir) {
|
|
6752
|
-
return
|
|
6926
|
+
return path39.join(homeFn(), ".wrongstack", "config.history", "entries");
|
|
6753
6927
|
}
|
|
6754
6928
|
function historyIndexPath(homeFn = defaultHomeDir) {
|
|
6755
|
-
return
|
|
6929
|
+
return path39.join(homeFn(), ".wrongstack", "config.history", "index.json");
|
|
6756
6930
|
}
|
|
6757
6931
|
function configPath(homeFn = defaultHomeDir) {
|
|
6758
|
-
return
|
|
6932
|
+
return path39.join(homeFn(), ".wrongstack", "config.json");
|
|
6759
6933
|
}
|
|
6760
6934
|
function backupLastPath(homeFn = defaultHomeDir) {
|
|
6761
|
-
return
|
|
6935
|
+
return path39.join(homeFn(), ".wrongstack", "config.json.last");
|
|
6762
6936
|
}
|
|
6763
6937
|
function entryId(ts) {
|
|
6764
6938
|
return ts.replace(/[:.]/g, "-").slice(0, 19);
|
|
@@ -6768,7 +6942,7 @@ async function ensureHistoryDir(homeFn = defaultHomeDir) {
|
|
|
6768
6942
|
await fsp5.mkdir(historyDir(homeFn), { recursive: true });
|
|
6769
6943
|
} catch (err) {
|
|
6770
6944
|
throw new FsError({
|
|
6771
|
-
message:
|
|
6945
|
+
message: toErrorMessage(err),
|
|
6772
6946
|
code: ERROR_CODES.FS_MKDIR_FAILED,
|
|
6773
6947
|
path: historyDir(homeFn),
|
|
6774
6948
|
cause: err
|
|
@@ -6789,7 +6963,7 @@ async function writeIndex(idx, homeFn = defaultHomeDir) {
|
|
|
6789
6963
|
await atomicWrite(historyIndexPath(homeFn), JSON.stringify(idx, null, 2));
|
|
6790
6964
|
} catch (err) {
|
|
6791
6965
|
throw new FsError({
|
|
6792
|
-
message:
|
|
6966
|
+
message: toErrorMessage(err),
|
|
6793
6967
|
code: ERROR_CODES.FS_ATOMIC_WRITE_FAILED,
|
|
6794
6968
|
path: historyIndexPath(homeFn),
|
|
6795
6969
|
cause: err
|
|
@@ -6810,30 +6984,30 @@ async function backupCurrent(homeFn = defaultHomeDir) {
|
|
|
6810
6984
|
await atomicWrite(last, content);
|
|
6811
6985
|
} catch (err) {
|
|
6812
6986
|
writeErr(
|
|
6813
|
-
`[config-history] .last backup failed: ${
|
|
6987
|
+
`[config-history] .last backup failed: ${toErrorMessage(err)}`
|
|
6814
6988
|
);
|
|
6815
6989
|
}
|
|
6816
6990
|
}
|
|
6817
6991
|
if (content !== void 0) {
|
|
6818
6992
|
try {
|
|
6819
|
-
const bakPath =
|
|
6993
|
+
const bakPath = path39.join(homeFn(), ".wrongstack", `config.json.${ts}.bak`);
|
|
6820
6994
|
await atomicWrite(bakPath, content);
|
|
6821
6995
|
} catch (err) {
|
|
6822
6996
|
writeErr(
|
|
6823
|
-
`[config-history] timestamped backup failed: ${
|
|
6997
|
+
`[config-history] timestamped backup failed: ${toErrorMessage(err)}`
|
|
6824
6998
|
);
|
|
6825
6999
|
}
|
|
6826
7000
|
}
|
|
6827
7001
|
try {
|
|
6828
|
-
const dir =
|
|
7002
|
+
const dir = path39.join(homeFn(), ".wrongstack");
|
|
6829
7003
|
const files = await fsp5.readdir(dir);
|
|
6830
7004
|
const baks = files.filter((f) => f.startsWith("config.json.") && f.endsWith(".bak")).sort().reverse();
|
|
6831
7005
|
for (const f of baks.slice(10)) {
|
|
6832
|
-
await safeDelete(
|
|
7006
|
+
await safeDelete(path39.join(dir, f));
|
|
6833
7007
|
}
|
|
6834
7008
|
} catch (err) {
|
|
6835
7009
|
writeErr(
|
|
6836
|
-
`[config-history] backup prune failed: ${
|
|
7010
|
+
`[config-history] backup prune failed: ${toErrorMessage(err)}`
|
|
6837
7011
|
);
|
|
6838
7012
|
}
|
|
6839
7013
|
}
|
|
@@ -6850,15 +7024,15 @@ async function appendHistory(oldCfg, newCfg, description, homeFn = defaultHomeDi
|
|
|
6850
7024
|
};
|
|
6851
7025
|
try {
|
|
6852
7026
|
await fsp5.writeFile(
|
|
6853
|
-
|
|
7027
|
+
path39.join(historyDir(homeFn), `${id}.json`),
|
|
6854
7028
|
JSON.stringify(entry, null, 2),
|
|
6855
7029
|
"utf8"
|
|
6856
7030
|
);
|
|
6857
7031
|
} catch (err) {
|
|
6858
7032
|
throw new FsError({
|
|
6859
|
-
message:
|
|
7033
|
+
message: toErrorMessage(err),
|
|
6860
7034
|
code: ERROR_CODES.FS_WRITE_FAILED,
|
|
6861
|
-
path:
|
|
7035
|
+
path: path39.join(historyDir(homeFn), `${id}.json`),
|
|
6862
7036
|
cause: err
|
|
6863
7037
|
});
|
|
6864
7038
|
}
|
|
@@ -6873,7 +7047,7 @@ async function listHistory(homeFn = defaultHomeDir) {
|
|
|
6873
7047
|
}
|
|
6874
7048
|
async function getHistoryEntry(id, homeFn = defaultHomeDir) {
|
|
6875
7049
|
try {
|
|
6876
|
-
const raw = await fsp5.readFile(
|
|
7050
|
+
const raw = await fsp5.readFile(path39.join(historyDir(homeFn), `${id}.json`), "utf8");
|
|
6877
7051
|
return JSON.parse(raw);
|
|
6878
7052
|
} catch {
|
|
6879
7053
|
return null;
|
|
@@ -6984,16 +7158,14 @@ async function buildPickableProviders(modelsRegistry, config) {
|
|
|
6984
7158
|
}
|
|
6985
7159
|
return out;
|
|
6986
7160
|
}
|
|
6987
|
-
|
|
6988
|
-
// src/picker.ts
|
|
6989
7161
|
var theme = { primary: color.amber };
|
|
6990
7162
|
async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => process.env.HOME ?? os__default.homedir()) {
|
|
6991
7163
|
try {
|
|
6992
7164
|
const { atomicWrite: atomicWrite17 } = await import('@wrongstack/core');
|
|
6993
|
-
const
|
|
7165
|
+
const fs39 = await import('fs/promises');
|
|
6994
7166
|
let existing = {};
|
|
6995
7167
|
try {
|
|
6996
|
-
const raw = await
|
|
7168
|
+
const raw = await fs39.readFile(configPath2, "utf8");
|
|
6997
7169
|
existing = JSON.parse(raw);
|
|
6998
7170
|
} catch {
|
|
6999
7171
|
}
|
|
@@ -7007,7 +7179,7 @@ async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => p
|
|
|
7007
7179
|
JSON.stringify({
|
|
7008
7180
|
level: "warn",
|
|
7009
7181
|
event: "picker.backup_failed",
|
|
7010
|
-
message:
|
|
7182
|
+
message: toErrorMessage(err),
|
|
7011
7183
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7012
7184
|
})
|
|
7013
7185
|
);
|
|
@@ -7028,7 +7200,7 @@ async function saveToGlobalConfig(configPath2, provider, model, homeFn = () => p
|
|
|
7028
7200
|
JSON.stringify({
|
|
7029
7201
|
level: "warn",
|
|
7030
7202
|
event: "picker.save_failed",
|
|
7031
|
-
message:
|
|
7203
|
+
message: toErrorMessage(err),
|
|
7032
7204
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
7033
7205
|
})
|
|
7034
7206
|
);
|
|
@@ -7486,7 +7658,7 @@ function buildAutonomyCommand(opts) {
|
|
|
7486
7658
|
const current = opts.onAutonomy();
|
|
7487
7659
|
const lines = [`Autonomy mode: ${MODE_LABELS[current] ?? current}`];
|
|
7488
7660
|
try {
|
|
7489
|
-
const goal = await loadGoal(goalFilePath(opts.projectRoot));
|
|
7661
|
+
const goal = await loadGoal(goalFilePath(opts.projectRoot), opts.events);
|
|
7490
7662
|
if (goal) {
|
|
7491
7663
|
const u = summarizeUsage(goal);
|
|
7492
7664
|
lines.push(
|
|
@@ -7532,7 +7704,7 @@ function buildAutonomyCommand(opts) {
|
|
|
7532
7704
|
opts.onAutonomy("off");
|
|
7533
7705
|
let summaryLine = "";
|
|
7534
7706
|
try {
|
|
7535
|
-
const goal = await loadGoal(goalFilePath(opts.projectRoot));
|
|
7707
|
+
const goal = await loadGoal(goalFilePath(opts.projectRoot), opts.events);
|
|
7536
7708
|
if (goal) {
|
|
7537
7709
|
const u = summarizeUsage(goal);
|
|
7538
7710
|
if (u.iterationsWithUsage > 0) {
|
|
@@ -7572,7 +7744,7 @@ function buildAutonomyCommand(opts) {
|
|
|
7572
7744
|
if (newMode === "eternal" || newMode === "eternal-parallel") {
|
|
7573
7745
|
const wantKeep = modifiers.includes("--keep") || modifiers.includes("keep");
|
|
7574
7746
|
const wantNew = modifiers.includes("--new") || modifiers.includes("new");
|
|
7575
|
-
const goal = await loadGoal(goalFilePath(opts.projectRoot));
|
|
7747
|
+
const goal = await loadGoal(goalFilePath(opts.projectRoot), opts.events);
|
|
7576
7748
|
if (!goal) {
|
|
7577
7749
|
const msg3 = `${color.red("Eternal/parallel mode requires a goal.")} Run \`/goal set <mission>\` first.`;
|
|
7578
7750
|
opts.renderer.writeWarning(msg3);
|
|
@@ -7675,7 +7847,7 @@ function formatPhaseList(graph) {
|
|
|
7675
7847
|
}
|
|
7676
7848
|
async function gatherProjectContext(projectRoot) {
|
|
7677
7849
|
try {
|
|
7678
|
-
const raw = await fsp5.readFile(
|
|
7850
|
+
const raw = await fsp5.readFile(path39.join(projectRoot, "package.json"), "utf8");
|
|
7679
7851
|
const pkg = JSON.parse(raw);
|
|
7680
7852
|
const parts = [
|
|
7681
7853
|
`Project: ${String(pkg.name ?? "unknown")}`,
|
|
@@ -8026,7 +8198,7 @@ function buildCodebaseReindexCommand(opts) {
|
|
|
8026
8198
|
${color.yellow(` ${r.errors.length} file(s) had errors`)}` : "");
|
|
8027
8199
|
return { message: summary };
|
|
8028
8200
|
} catch (err) {
|
|
8029
|
-
const msg = `${color.red("Codebase reindex failed:")} ${
|
|
8201
|
+
const msg = `${color.red("Codebase reindex failed:")} ${toErrorMessage(err)}`;
|
|
8030
8202
|
return { message: msg };
|
|
8031
8203
|
}
|
|
8032
8204
|
}
|
|
@@ -8113,7 +8285,7 @@ async function historyCommand(opts, sessionId, args) {
|
|
|
8113
8285
|
} catch (err) {
|
|
8114
8286
|
return {
|
|
8115
8287
|
message: color.yellow(
|
|
8116
|
-
`Failed to read session: ${
|
|
8288
|
+
`Failed to read session: ${toErrorMessage(err)}`
|
|
8117
8289
|
)
|
|
8118
8290
|
};
|
|
8119
8291
|
}
|
|
@@ -8146,7 +8318,9 @@ async function annotationsCommand(opts, sessionId) {
|
|
|
8146
8318
|
)
|
|
8147
8319
|
};
|
|
8148
8320
|
}
|
|
8149
|
-
const
|
|
8321
|
+
const annotationsOpts = { dir: storeDir, events: opts.events };
|
|
8322
|
+
if (opts.context?.traceId !== void 0) annotationsOpts.traceId = opts.context.traceId;
|
|
8323
|
+
const annotations = new AnnotationsStore(annotationsOpts);
|
|
8150
8324
|
const open = await annotations.listOpen(sessionId);
|
|
8151
8325
|
if (open.length === 0) {
|
|
8152
8326
|
return {
|
|
@@ -8613,7 +8787,7 @@ async function spawnAgent(opts, role, _task, _name, header) {
|
|
|
8613
8787
|
opts.renderer.write(msg);
|
|
8614
8788
|
return { message: msg };
|
|
8615
8789
|
} catch (err) {
|
|
8616
|
-
const msg = `${color.red("\u2717 Spawn failed")}: ${
|
|
8790
|
+
const msg = `${color.red("\u2717 Spawn failed")}: ${toErrorMessage(err)}`;
|
|
8617
8791
|
opts.renderer.writeWarning(msg);
|
|
8618
8792
|
return { message: msg };
|
|
8619
8793
|
}
|
|
@@ -8664,7 +8838,7 @@ The following characters are not allowed: ; & | < > ^ $ , ( ) { } [ ] ! # % ' "
|
|
|
8664
8838
|
}
|
|
8665
8839
|
}
|
|
8666
8840
|
function runCommand(cmd, cwd, timeout) {
|
|
8667
|
-
return new Promise((
|
|
8841
|
+
return new Promise((resolve11) => {
|
|
8668
8842
|
validateCommand(cmd);
|
|
8669
8843
|
const opts = {
|
|
8670
8844
|
cwd,
|
|
@@ -8677,7 +8851,7 @@ function runCommand(cmd, cwd, timeout) {
|
|
|
8677
8851
|
shell: process.platform === "win32" ? true : false
|
|
8678
8852
|
};
|
|
8679
8853
|
execFile(cmd, [], opts, (error, stdout, stderr) => {
|
|
8680
|
-
|
|
8854
|
+
resolve11({
|
|
8681
8855
|
stdout,
|
|
8682
8856
|
stderr,
|
|
8683
8857
|
exitCode: typeof error?.code === "number" ? error.code : 0,
|
|
@@ -9099,17 +9273,17 @@ function diagnoseConfig(cfg, plugins = []) {
|
|
|
9099
9273
|
function scanPlaintextSecrets(node, prefix, findings) {
|
|
9100
9274
|
if (!isPlainObject(node)) return;
|
|
9101
9275
|
for (const [key, value] of Object.entries(node)) {
|
|
9102
|
-
const
|
|
9276
|
+
const path40 = prefix ? `${prefix}.${key}` : key;
|
|
9103
9277
|
if (typeof value === "string") {
|
|
9104
9278
|
if (value.length > 0 && isSecretField$1(key) && !value.startsWith(ENC_PREFIX)) {
|
|
9105
9279
|
findings.push({
|
|
9106
|
-
path:
|
|
9280
|
+
path: path40,
|
|
9107
9281
|
problem: "looks like a plaintext secret (not vault-encrypted) \u2014 it will be encrypted on next boot",
|
|
9108
9282
|
severity: "warning"
|
|
9109
9283
|
});
|
|
9110
9284
|
}
|
|
9111
9285
|
} else if (isPlainObject(value)) {
|
|
9112
|
-
scanPlaintextSecrets(value,
|
|
9286
|
+
scanPlaintextSecrets(value, path40, findings);
|
|
9113
9287
|
}
|
|
9114
9288
|
}
|
|
9115
9289
|
}
|
|
@@ -9130,7 +9304,7 @@ function resolvePersistPath(deps) {
|
|
|
9130
9304
|
return deps.globalConfigPath;
|
|
9131
9305
|
}
|
|
9132
9306
|
async function ensureProjectDir(filePath) {
|
|
9133
|
-
const dir =
|
|
9307
|
+
const dir = path39.dirname(filePath);
|
|
9134
9308
|
try {
|
|
9135
9309
|
await fsp5.mkdir(dir, { recursive: true });
|
|
9136
9310
|
} catch {
|
|
@@ -9334,8 +9508,8 @@ function buildDoctorCommand(opts) {
|
|
|
9334
9508
|
return ` ${icon} ${color.cyan(f.path)} \u2014 ${f.problem}${fix}`;
|
|
9335
9509
|
}
|
|
9336
9510
|
async function findParsableBackup(file) {
|
|
9337
|
-
const dir =
|
|
9338
|
-
const base =
|
|
9511
|
+
const dir = path39.dirname(file);
|
|
9512
|
+
const base = path39.basename(file);
|
|
9339
9513
|
const candidates = [`${base}.last`];
|
|
9340
9514
|
try {
|
|
9341
9515
|
const siblings = await fsp5.readdir(dir);
|
|
@@ -9346,7 +9520,7 @@ function buildDoctorCommand(opts) {
|
|
|
9346
9520
|
}
|
|
9347
9521
|
for (const name of candidates) {
|
|
9348
9522
|
try {
|
|
9349
|
-
const raw = await fsp5.readFile(
|
|
9523
|
+
const raw = await fsp5.readFile(path39.join(dir, name), "utf8");
|
|
9350
9524
|
JSON.parse(raw);
|
|
9351
9525
|
return { name, raw };
|
|
9352
9526
|
} catch {
|
|
@@ -9412,7 +9586,7 @@ function buildDoctorCommand(opts) {
|
|
|
9412
9586
|
parsed = JSON.parse(raw);
|
|
9413
9587
|
} catch (err) {
|
|
9414
9588
|
errorCount++;
|
|
9415
|
-
const msg =
|
|
9589
|
+
const msg = toErrorMessage(err);
|
|
9416
9590
|
lines.push(` ${color.red("\u2717")} invalid JSON \u2014 ${msg}`);
|
|
9417
9591
|
if (!applyFixes) {
|
|
9418
9592
|
fixableCount++;
|
|
@@ -9463,7 +9637,7 @@ function buildDoctorCommand(opts) {
|
|
|
9463
9637
|
await atomicWrite(target.file, JSON.stringify(report.fixed, null, 2));
|
|
9464
9638
|
if (!target.isProject) {
|
|
9465
9639
|
try {
|
|
9466
|
-
const homeFn = () =>
|
|
9640
|
+
const homeFn = () => path39.dirname(path39.dirname(target.file));
|
|
9467
9641
|
await appendHistory(parsed, report.fixed, "config doctor auto-fix", homeFn);
|
|
9468
9642
|
} catch {
|
|
9469
9643
|
}
|
|
@@ -9551,6 +9725,65 @@ function buildEnhanceCommand(opts) {
|
|
|
9551
9725
|
}
|
|
9552
9726
|
};
|
|
9553
9727
|
}
|
|
9728
|
+
function buildEnsembleCommand(_opts) {
|
|
9729
|
+
return {
|
|
9730
|
+
name: "ensemble",
|
|
9731
|
+
category: "Agent",
|
|
9732
|
+
description: "Fan a task out to multiple ACP agents in parallel (claude-code, gemini-cli, codex-cli, etc.).",
|
|
9733
|
+
argsHint: "<agent-ids-csv> <task description>",
|
|
9734
|
+
help: [
|
|
9735
|
+
"Fan a single task out to multiple ACP-supporting agents in parallel.",
|
|
9736
|
+
"",
|
|
9737
|
+
"Usage:",
|
|
9738
|
+
" /ensemble <agent-ids-csv> <task description>",
|
|
9739
|
+
"",
|
|
9740
|
+
"Examples:",
|
|
9741
|
+
' /ensemble claude-code,gemini-cli "review this diff"',
|
|
9742
|
+
' /ensemble claude-code,codex-cli "refactor auth/session.ts"',
|
|
9743
|
+
' /ensemble claude-code,gemini-cli,codex-cli "explain the v1 protocol"',
|
|
9744
|
+
"",
|
|
9745
|
+
"Each agent runs in its own process. Agents not installed on the host",
|
|
9746
|
+
"are skipped with a warning. The command waits for all agents to finish",
|
|
9747
|
+
"and reports per-agent outcomes (success / failed / skipped / cancelled).",
|
|
9748
|
+
"",
|
|
9749
|
+
"Use /acp list (or wstack acp list) to see which agents are detected."
|
|
9750
|
+
].join("\n"),
|
|
9751
|
+
async run(args) {
|
|
9752
|
+
const trimmed = args.trim();
|
|
9753
|
+
if (!trimmed) {
|
|
9754
|
+
return {
|
|
9755
|
+
message: 'Usage: /ensemble <agent-ids-csv> <task description>\n\nExamples:\n /ensemble claude-code,gemini-cli "review this diff"\n /ensemble claude-code,codex-cli "refactor auth/session.ts"\n\nRun `wstack acp list` to see which agents are detected on this host.'
|
|
9756
|
+
};
|
|
9757
|
+
}
|
|
9758
|
+
const spaceIdx = trimmed.search(/\s/);
|
|
9759
|
+
if (spaceIdx === -1) {
|
|
9760
|
+
return {
|
|
9761
|
+
message: 'Task description is required.\n\nUsage: /ensemble <agent-ids-csv> <task description>\nExample: /ensemble claude-code,gemini-cli "explain this code"'
|
|
9762
|
+
};
|
|
9763
|
+
}
|
|
9764
|
+
const agentIds = trimmed.slice(0, spaceIdx);
|
|
9765
|
+
const task = stripSurroundingQuotes(trimmed.slice(spaceIdx + 1).trim());
|
|
9766
|
+
if (!task) {
|
|
9767
|
+
return { message: "Task description is required." };
|
|
9768
|
+
}
|
|
9769
|
+
try {
|
|
9770
|
+
const result = await runEnsemble({ agentIds, task });
|
|
9771
|
+
return { message: renderEnsembleText(result) };
|
|
9772
|
+
} catch (err) {
|
|
9773
|
+
return { message: `Ensemble failed: ${toErrorMessage(err)}` };
|
|
9774
|
+
}
|
|
9775
|
+
}
|
|
9776
|
+
};
|
|
9777
|
+
}
|
|
9778
|
+
function stripSurroundingQuotes(s) {
|
|
9779
|
+
if (s.length < 2) return s;
|
|
9780
|
+
const first = s[0];
|
|
9781
|
+
const last = s[s.length - 1];
|
|
9782
|
+
if ((first === '"' || first === "'") && first === last) {
|
|
9783
|
+
return s.slice(1, -1);
|
|
9784
|
+
}
|
|
9785
|
+
return s;
|
|
9786
|
+
}
|
|
9554
9787
|
function parseModelRef(ref) {
|
|
9555
9788
|
const trimmed = ref.trim();
|
|
9556
9789
|
const slash = trimmed.indexOf("/");
|
|
@@ -9851,7 +10084,7 @@ function buildFallbackCommand(opts) {
|
|
|
9851
10084
|
};
|
|
9852
10085
|
} catch (err) {
|
|
9853
10086
|
return {
|
|
9854
|
-
message: `${color.red("fallback error")}: ${
|
|
10087
|
+
message: `${color.red("fallback error")}: ${toErrorMessage(err)}`
|
|
9855
10088
|
};
|
|
9856
10089
|
}
|
|
9857
10090
|
}
|
|
@@ -11068,7 +11301,7 @@ async function handleSpawn(opts, subargs) {
|
|
|
11068
11301
|
const id = await opts.onFleetSpawn(role);
|
|
11069
11302
|
spawned.push(id);
|
|
11070
11303
|
} catch (err) {
|
|
11071
|
-
const warnMsg = `${color.red("\u2717 Spawn failed")} for slot ${i + 1}: ${
|
|
11304
|
+
const warnMsg = `${color.red("\u2717 Spawn failed")} for slot ${i + 1}: ${toErrorMessage(err)}`;
|
|
11072
11305
|
opts.renderer.writeWarning(warnMsg);
|
|
11073
11306
|
}
|
|
11074
11307
|
}
|
|
@@ -11122,7 +11355,7 @@ async function handleDispatch(opts, subargs) {
|
|
|
11122
11355
|
lines.push(` ${color.green("\u2713 spawned")} ${color.bold(decision.role)} as ${color.dim(id)}`);
|
|
11123
11356
|
} catch (err) {
|
|
11124
11357
|
lines.push(
|
|
11125
|
-
` ${color.amber("\u26A0 spawn failed:")} ${
|
|
11358
|
+
` ${color.amber("\u26A0 spawn failed:")} ${toErrorMessage(err)}`
|
|
11126
11359
|
);
|
|
11127
11360
|
}
|
|
11128
11361
|
} else {
|
|
@@ -11324,7 +11557,7 @@ function buildGoalCommand(opts) {
|
|
|
11324
11557
|
case "":
|
|
11325
11558
|
case "show":
|
|
11326
11559
|
case "status": {
|
|
11327
|
-
const current = await loadGoal(goalPath);
|
|
11560
|
+
const current = await loadGoal(goalPath, opts.events);
|
|
11328
11561
|
if (!current) {
|
|
11329
11562
|
const msg2 = "No goal set. Use `/goal set <mission text>` to create one.";
|
|
11330
11563
|
opts.renderer.write(msg2);
|
|
@@ -11349,7 +11582,7 @@ function buildGoalCommand(opts) {
|
|
|
11349
11582
|
if (!refined) {
|
|
11350
11583
|
refined = refineGoalHeuristic(setText);
|
|
11351
11584
|
}
|
|
11352
|
-
const existing = await loadGoal(goalPath);
|
|
11585
|
+
const existing = await loadGoal(goalPath, opts.events);
|
|
11353
11586
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
11354
11587
|
const next = existing ? {
|
|
11355
11588
|
...existing,
|
|
@@ -11366,7 +11599,7 @@ function buildGoalCommand(opts) {
|
|
|
11366
11599
|
refinedGoal: refined.refinedGoal,
|
|
11367
11600
|
deliverables: refined.deliverables
|
|
11368
11601
|
};
|
|
11369
|
-
await saveGoal(goalPath, next);
|
|
11602
|
+
await saveGoal(goalPath, next, opts.events);
|
|
11370
11603
|
const lines = [];
|
|
11371
11604
|
lines.push(`\u{1F3AF} ${color.green("Goal locked:")} ${color.bold(refined.refinedGoal)}`);
|
|
11372
11605
|
if (refined.refinedGoal !== setText) {
|
|
@@ -11393,7 +11626,7 @@ function buildGoalCommand(opts) {
|
|
|
11393
11626
|
};
|
|
11394
11627
|
}
|
|
11395
11628
|
case "refine": {
|
|
11396
|
-
const current = await loadGoal(goalPath);
|
|
11629
|
+
const current = await loadGoal(goalPath, opts.events);
|
|
11397
11630
|
if (!current) {
|
|
11398
11631
|
const msg2 = "No goal set to refine. Use /goal set <text> first.";
|
|
11399
11632
|
opts.renderer.writeWarning(msg2);
|
|
@@ -11412,7 +11645,7 @@ function buildGoalCommand(opts) {
|
|
|
11412
11645
|
refinedGoal: refined.refinedGoal,
|
|
11413
11646
|
deliverables: refined.deliverables
|
|
11414
11647
|
};
|
|
11415
|
-
await saveGoal(goalPath, updated);
|
|
11648
|
+
await saveGoal(goalPath, updated, opts.events);
|
|
11416
11649
|
const msg = `${color.green("\u2713")} Goal re-refined with ${refined.deliverables.length} deliverables.`;
|
|
11417
11650
|
opts.renderer.write(msg);
|
|
11418
11651
|
return { message: `${msg}
|
|
@@ -11421,14 +11654,14 @@ ${formatGoal(updated)}` };
|
|
|
11421
11654
|
}
|
|
11422
11655
|
case "clear":
|
|
11423
11656
|
case "reset": {
|
|
11424
|
-
const current = await loadGoal(goalPath);
|
|
11657
|
+
const current = await loadGoal(goalPath, opts.events);
|
|
11425
11658
|
if (!current) {
|
|
11426
11659
|
const msg2 = "No goal to clear.";
|
|
11427
11660
|
opts.renderer.write(msg2);
|
|
11428
11661
|
return { message: msg2 };
|
|
11429
11662
|
}
|
|
11430
11663
|
const abandoned = { ...current, goalState: "abandoned" };
|
|
11431
|
-
await saveGoal(goalPath, abandoned);
|
|
11664
|
+
await saveGoal(goalPath, abandoned, opts.events);
|
|
11432
11665
|
const { unlink: unlink4 } = await import('fs/promises');
|
|
11433
11666
|
try {
|
|
11434
11667
|
await unlink4(goalPath);
|
|
@@ -11442,7 +11675,7 @@ ${formatGoal(updated)}` };
|
|
|
11442
11675
|
}
|
|
11443
11676
|
case "journal":
|
|
11444
11677
|
case "log": {
|
|
11445
|
-
const current = await loadGoal(goalPath);
|
|
11678
|
+
const current = await loadGoal(goalPath, opts.events);
|
|
11446
11679
|
if (!current) {
|
|
11447
11680
|
const msg2 = "No goal set.";
|
|
11448
11681
|
opts.renderer.write(msg2);
|
|
@@ -11467,7 +11700,7 @@ ${lines.join("\n")}`;
|
|
|
11467
11700
|
return { message: msg };
|
|
11468
11701
|
}
|
|
11469
11702
|
case "pause": {
|
|
11470
|
-
const current = await loadGoal(goalPath);
|
|
11703
|
+
const current = await loadGoal(goalPath, opts.events);
|
|
11471
11704
|
if (!current) {
|
|
11472
11705
|
const msg2 = "No goal set \u2014 nothing to pause.";
|
|
11473
11706
|
opts.renderer.writeWarning(msg2);
|
|
@@ -11479,13 +11712,13 @@ ${lines.join("\n")}`;
|
|
|
11479
11712
|
return { message: msg2 };
|
|
11480
11713
|
}
|
|
11481
11714
|
const paused = { ...current, goalState: "paused" };
|
|
11482
|
-
await saveGoal(goalPath, paused);
|
|
11715
|
+
await saveGoal(goalPath, paused, opts.events);
|
|
11483
11716
|
const msg = `${color.cyan("Goal paused.")} Current iteration will finish, then the loop stops. Use /goal resume to continue.`;
|
|
11484
11717
|
opts.renderer.write(msg);
|
|
11485
11718
|
return { message: msg };
|
|
11486
11719
|
}
|
|
11487
11720
|
case "resume": {
|
|
11488
|
-
const current = await loadGoal(goalPath);
|
|
11721
|
+
const current = await loadGoal(goalPath, opts.events);
|
|
11489
11722
|
if (!current) {
|
|
11490
11723
|
const msg2 = "No goal set \u2014 cannot resume.";
|
|
11491
11724
|
opts.renderer.writeWarning(msg2);
|
|
@@ -11497,7 +11730,7 @@ ${lines.join("\n")}`;
|
|
|
11497
11730
|
return { message: msg2 };
|
|
11498
11731
|
}
|
|
11499
11732
|
const resumed = { ...current, goalState: "active" };
|
|
11500
|
-
await saveGoal(goalPath, resumed);
|
|
11733
|
+
await saveGoal(goalPath, resumed, opts.events);
|
|
11501
11734
|
const msg = `${color.green("Goal resumed.")} Loop will continue from the next iteration.`;
|
|
11502
11735
|
opts.renderer.write(msg);
|
|
11503
11736
|
return { message: msg };
|
|
@@ -11584,8 +11817,8 @@ function buildInitCommand(opts) {
|
|
|
11584
11817
|
description: "Create or update .wrongstack/AGENTS.md project context for the system prompt.",
|
|
11585
11818
|
async run(_args, ctx) {
|
|
11586
11819
|
const root = ctx?.projectRoot ?? opts.projectRoot ?? process.cwd();
|
|
11587
|
-
const dir =
|
|
11588
|
-
const file =
|
|
11820
|
+
const dir = path39.join(root, ".wrongstack");
|
|
11821
|
+
const file = path39.join(dir, "AGENTS.md");
|
|
11589
11822
|
const isFirstInit = !await fileExists(file);
|
|
11590
11823
|
const detected = await detectProjectFacts(root);
|
|
11591
11824
|
const body = renderAgentsTemplate(detected);
|
|
@@ -11593,7 +11826,7 @@ function buildInitCommand(opts) {
|
|
|
11593
11826
|
await fsp5.writeFile(file, body, "utf8");
|
|
11594
11827
|
let nodePkg = false;
|
|
11595
11828
|
try {
|
|
11596
|
-
await fsp5.access(
|
|
11829
|
+
await fsp5.access(path39.join(root, "package.json"));
|
|
11597
11830
|
nodePkg = true;
|
|
11598
11831
|
} catch {
|
|
11599
11832
|
}
|
|
@@ -11755,6 +11988,7 @@ function buildMailboxCommand(opts) {
|
|
|
11755
11988
|
" /mailbox broadcast <message> Message every agent on the project.",
|
|
11756
11989
|
" /mailbox history [n] Last n messages on the project (default 20).",
|
|
11757
11990
|
" /mailbox clear Delete all messages from the mailbox.",
|
|
11991
|
+
" /mailbox purge Remove stale/orphaned messages (completed >1d, incomplete >7d).",
|
|
11758
11992
|
"",
|
|
11759
11993
|
"Examples:",
|
|
11760
11994
|
" /mailbox broadcast pausing deploys, hold off on main",
|
|
@@ -11863,8 +12097,17 @@ function buildMailboxCommand(opts) {
|
|
|
11863
12097
|
await mb.clearAll();
|
|
11864
12098
|
return { message: color.green("\u2713 All messages deleted from the mailbox.") };
|
|
11865
12099
|
}
|
|
12100
|
+
if (sub === "purge") {
|
|
12101
|
+
const result = await mb.purgeStale();
|
|
12102
|
+
if (result.totalPurged === 0) {
|
|
12103
|
+
return { message: color.green("\u2713 No stale messages found. Mailbox is clean.") };
|
|
12104
|
+
}
|
|
12105
|
+
return {
|
|
12106
|
+
message: `\u2713 Purged ${result.totalPurged} message(s): ${result.completedPurged} completed, ${result.incompletePurged} incomplete. ${result.remaining} message(s) remain.`
|
|
12107
|
+
};
|
|
12108
|
+
}
|
|
11866
12109
|
return {
|
|
11867
|
-
message: `Unknown subcommand "${sub}". Use: /mailbox [agents|online|send|broadcast|history|clear]`
|
|
12110
|
+
message: `Unknown subcommand "${sub}". Use: /mailbox [agents|online|send|broadcast|history|clear purge]`
|
|
11868
12111
|
};
|
|
11869
12112
|
}
|
|
11870
12113
|
};
|
|
@@ -12277,9 +12520,9 @@ function stateBadge(state) {
|
|
|
12277
12520
|
return color.dim(state);
|
|
12278
12521
|
}
|
|
12279
12522
|
}
|
|
12280
|
-
async function readConfig(
|
|
12523
|
+
async function readConfig(path40) {
|
|
12281
12524
|
try {
|
|
12282
|
-
return JSON.parse(await fsp5.readFile(
|
|
12525
|
+
return JSON.parse(await fsp5.readFile(path40, "utf8"));
|
|
12283
12526
|
} catch {
|
|
12284
12527
|
return {};
|
|
12285
12528
|
}
|
|
@@ -12287,11 +12530,11 @@ async function readConfig(path39) {
|
|
|
12287
12530
|
function isMcpServerRecord(value) {
|
|
12288
12531
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
12289
12532
|
}
|
|
12290
|
-
async function writeConfig(
|
|
12533
|
+
async function writeConfig(path40, cfg) {
|
|
12291
12534
|
const raw = JSON.stringify(cfg, null, 2);
|
|
12292
|
-
const tmp =
|
|
12535
|
+
const tmp = path40 + ".tmp";
|
|
12293
12536
|
await fsp5.writeFile(tmp, raw, "utf8");
|
|
12294
|
-
await fsp5.rename(tmp,
|
|
12537
|
+
await fsp5.rename(tmp, path40);
|
|
12295
12538
|
}
|
|
12296
12539
|
|
|
12297
12540
|
// src/slash-commands/mcp.ts
|
|
@@ -12481,7 +12724,7 @@ async function runCompact(opts) {
|
|
|
12481
12724
|
responseText = response.content.filter((b) => b.type === "text").map((b) => b.text).join("").trim();
|
|
12482
12725
|
} catch (err) {
|
|
12483
12726
|
return {
|
|
12484
|
-
message: `LLM call failed: ${
|
|
12727
|
+
message: `LLM call failed: ${toErrorMessage(err)}`
|
|
12485
12728
|
};
|
|
12486
12729
|
}
|
|
12487
12730
|
if (!responseText) {
|
|
@@ -12497,7 +12740,7 @@ ${responseText.slice(0, 500)}` };
|
|
|
12497
12740
|
parsed = JSON.parse(jsonMatch[0]);
|
|
12498
12741
|
} catch (err) {
|
|
12499
12742
|
return {
|
|
12500
|
-
message: `Failed to parse LLM response: ${
|
|
12743
|
+
message: `Failed to parse LLM response: ${toErrorMessage(err)}
|
|
12501
12744
|
|
|
12502
12745
|
Raw response:
|
|
12503
12746
|
${responseText.slice(0, 500)}`
|
|
@@ -12555,7 +12798,7 @@ ${responseText.slice(0, 500)}`
|
|
|
12555
12798
|
}
|
|
12556
12799
|
} catch (err) {
|
|
12557
12800
|
errors.push(
|
|
12558
|
-
`${op.action} failed for ${op.targets.join(", ")}: ${
|
|
12801
|
+
`${op.action} failed for ${op.targets.join(", ")}: ${toErrorMessage(err)}`
|
|
12559
12802
|
);
|
|
12560
12803
|
}
|
|
12561
12804
|
}
|
|
@@ -13192,7 +13435,7 @@ function buildModelsCommand(opts) {
|
|
|
13192
13435
|
};
|
|
13193
13436
|
} catch (err) {
|
|
13194
13437
|
return {
|
|
13195
|
-
message: `${color.red("models error")}: ${
|
|
13438
|
+
message: `${color.red("models error")}: ${toErrorMessage(err)}`
|
|
13196
13439
|
};
|
|
13197
13440
|
}
|
|
13198
13441
|
}
|
|
@@ -13876,7 +14119,7 @@ async function killSession(sessionId) {
|
|
|
13876
14119
|
} catch (err) {
|
|
13877
14120
|
return {
|
|
13878
14121
|
message: color.red(
|
|
13879
|
-
`Failed to kill session: ${
|
|
14122
|
+
`Failed to kill session: ${toErrorMessage(err)}`
|
|
13880
14123
|
)
|
|
13881
14124
|
};
|
|
13882
14125
|
}
|
|
@@ -14247,7 +14490,7 @@ function buildSetModelCommand(opts) {
|
|
|
14247
14490
|
};
|
|
14248
14491
|
} catch (err) {
|
|
14249
14492
|
return {
|
|
14250
|
-
message: `${color.red("setmodel error")}: ${
|
|
14493
|
+
message: `${color.red("setmodel error")}: ${toErrorMessage(err)}`
|
|
14251
14494
|
};
|
|
14252
14495
|
}
|
|
14253
14496
|
}
|
|
@@ -14350,7 +14593,7 @@ function buildSuggestCommand(opts) {
|
|
|
14350
14593
|
opts.onSuggestions?.(suggestions);
|
|
14351
14594
|
return { message: formatSuggestions(suggestions) };
|
|
14352
14595
|
} catch (err) {
|
|
14353
|
-
const msg = `Suggestion generation failed: ${
|
|
14596
|
+
const msg = `Suggestion generation failed: ${toErrorMessage(err)}`;
|
|
14354
14597
|
opts.renderer.writeWarning(msg);
|
|
14355
14598
|
return { message: msg };
|
|
14356
14599
|
}
|
|
@@ -14590,7 +14833,7 @@ async function listProjectsCommand(opts, ctx) {
|
|
|
14590
14833
|
return { message: lines.join("\n") };
|
|
14591
14834
|
}
|
|
14592
14835
|
async function addProjectCommand(opts, ctx, targetPath, displayName) {
|
|
14593
|
-
const resolved =
|
|
14836
|
+
const resolved = path39.resolve(ctx?.projectRoot ?? ctx?.cwd ?? process.cwd(), targetPath);
|
|
14594
14837
|
try {
|
|
14595
14838
|
await fsp5.access(resolved);
|
|
14596
14839
|
} catch {
|
|
@@ -14607,7 +14850,7 @@ async function addProjectCommand(opts, ctx, targetPath, displayName) {
|
|
|
14607
14850
|
message: color.yellow(`Project already registered: "${existing.name}" (${existing.slug})`)
|
|
14608
14851
|
};
|
|
14609
14852
|
}
|
|
14610
|
-
const name = displayName?.trim() ||
|
|
14853
|
+
const name = displayName?.trim() || path39.basename(resolved);
|
|
14611
14854
|
const slug = generateSlug(resolved);
|
|
14612
14855
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
14613
14856
|
await ensureProjectDataDir(slug, opts.paths?.globalConfig);
|
|
@@ -14660,7 +14903,7 @@ async function removeProjectCommand(opts, _ctx, slugOrName) {
|
|
|
14660
14903
|
};
|
|
14661
14904
|
}
|
|
14662
14905
|
async function switchProjectCommand(opts, ctx, target, displayName) {
|
|
14663
|
-
const resolved =
|
|
14906
|
+
const resolved = path39.resolve(ctx?.projectRoot ?? ctx?.cwd ?? process.cwd(), target);
|
|
14664
14907
|
try {
|
|
14665
14908
|
await fsp5.access(resolved);
|
|
14666
14909
|
} catch {
|
|
@@ -14674,8 +14917,8 @@ async function switchProjectCommand(opts, ctx, target, displayName) {
|
|
|
14674
14917
|
try {
|
|
14675
14918
|
const req2 = createRequire(import.meta.url);
|
|
14676
14919
|
const pkgPath = req2.resolve("@wrongstack/cli/package.json");
|
|
14677
|
-
const pkgDir =
|
|
14678
|
-
cliPath =
|
|
14920
|
+
const pkgDir = path39.dirname(pkgPath);
|
|
14921
|
+
cliPath = path39.join(pkgDir, "dist", "index.js");
|
|
14679
14922
|
await fsp5.access(cliPath);
|
|
14680
14923
|
} catch {
|
|
14681
14924
|
cliPath = process.argv[1] ?? "";
|
|
@@ -14692,13 +14935,13 @@ async function switchProjectCommand(opts, ctx, target, displayName) {
|
|
|
14692
14935
|
if (existing) {
|
|
14693
14936
|
existing.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
|
|
14694
14937
|
} else {
|
|
14695
|
-
const name = displayName?.trim() ||
|
|
14938
|
+
const name = displayName?.trim() || path39.basename(resolved);
|
|
14696
14939
|
const slug = generateSlug(resolved);
|
|
14697
14940
|
manifest.projects.push({ name, root: resolved, slug, lastSeen: (/* @__PURE__ */ new Date()).toISOString() });
|
|
14698
14941
|
await ensureProjectDataDir(slug, opts.paths?.globalConfig);
|
|
14699
14942
|
}
|
|
14700
14943
|
await saveManifest(manifest, opts.paths?.globalConfig);
|
|
14701
|
-
const targetName = displayName?.trim() ||
|
|
14944
|
+
const targetName = displayName?.trim() || path39.basename(resolved);
|
|
14702
14945
|
const canSwitch = await confirmProjectSwitch(opts, targetName);
|
|
14703
14946
|
if (!canSwitch) return { message: "" };
|
|
14704
14947
|
const nodeExe = process.execPath;
|
|
@@ -14814,8 +15057,8 @@ async function spawnInProject(opts, _ctx, root, projectName) {
|
|
|
14814
15057
|
try {
|
|
14815
15058
|
const req2 = createRequire(import.meta.url);
|
|
14816
15059
|
const pkgPath = req2.resolve("@wrongstack/cli/package.json");
|
|
14817
|
-
const pkgDir =
|
|
14818
|
-
cliPath =
|
|
15060
|
+
const pkgDir = path39.dirname(pkgPath);
|
|
15061
|
+
cliPath = path39.join(pkgDir, "dist", "index.js");
|
|
14819
15062
|
await fsp5.access(cliPath);
|
|
14820
15063
|
} catch {
|
|
14821
15064
|
cliPath = process.argv[1] ?? "";
|
|
@@ -14832,7 +15075,7 @@ async function spawnInProject(opts, _ctx, root, projectName) {
|
|
|
14832
15075
|
if (existing) {
|
|
14833
15076
|
existing.lastSeen = (/* @__PURE__ */ new Date()).toISOString();
|
|
14834
15077
|
} else {
|
|
14835
|
-
const name = projectName ||
|
|
15078
|
+
const name = projectName || path39.basename(root);
|
|
14836
15079
|
const slug = generateSlug(root);
|
|
14837
15080
|
manifest.projects.push({ name, root, slug, lastSeen: (/* @__PURE__ */ new Date()).toISOString() });
|
|
14838
15081
|
await ensureProjectDataDir(slug, opts.paths?.globalConfig);
|
|
@@ -14864,8 +15107,8 @@ async function handleNewSession(_opts, _ctx) {
|
|
|
14864
15107
|
try {
|
|
14865
15108
|
const req2 = createRequire(import.meta.url);
|
|
14866
15109
|
const pkgPath = req2.resolve("@wrongstack/cli/package.json");
|
|
14867
|
-
const pkgDir =
|
|
14868
|
-
cliPath =
|
|
15110
|
+
const pkgDir = path39.dirname(pkgPath);
|
|
15111
|
+
cliPath = path39.join(pkgDir, "dist", "index.js");
|
|
14869
15112
|
await fsp5.access(cliPath);
|
|
14870
15113
|
} catch {
|
|
14871
15114
|
cliPath = process.argv[1] ?? "";
|
|
@@ -14923,7 +15166,7 @@ async function handlePrevSessions(opts, _ctx) {
|
|
|
14923
15166
|
return { message: lines.join("\n") };
|
|
14924
15167
|
}
|
|
14925
15168
|
async function runGit(args, cwd) {
|
|
14926
|
-
return new Promise((
|
|
15169
|
+
return new Promise((resolve11) => {
|
|
14927
15170
|
const child = spawn("git", args, {
|
|
14928
15171
|
cwd,
|
|
14929
15172
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -14934,8 +15177,8 @@ async function runGit(args, cwd) {
|
|
|
14934
15177
|
child.stdout?.on("data", (d) => {
|
|
14935
15178
|
stdout += d;
|
|
14936
15179
|
});
|
|
14937
|
-
child.on("error", () =>
|
|
14938
|
-
child.on("close", (code) =>
|
|
15180
|
+
child.on("error", () => resolve11({ stdout, code: 1 }));
|
|
15181
|
+
child.on("close", (code) => resolve11({ stdout, code: code ?? 0 }));
|
|
14939
15182
|
});
|
|
14940
15183
|
}
|
|
14941
15184
|
async function getChangedFiles(cwd) {
|
|
@@ -14976,7 +15219,7 @@ function buildReviewCommand(opts) {
|
|
|
14976
15219
|
for (const f of allChanged) {
|
|
14977
15220
|
if (f.path.startsWith(".wrongstack/")) continue;
|
|
14978
15221
|
try {
|
|
14979
|
-
await fsp5.access(
|
|
15222
|
+
await fsp5.access(path39.join(cwd, f.path));
|
|
14980
15223
|
existing.push(f);
|
|
14981
15224
|
} catch {
|
|
14982
15225
|
}
|
|
@@ -14987,7 +15230,7 @@ function buildReviewCommand(opts) {
|
|
|
14987
15230
|
const filesWithContent = [];
|
|
14988
15231
|
for (const f of existing.slice(0, 30)) {
|
|
14989
15232
|
try {
|
|
14990
|
-
const content = await fsp5.readFile(
|
|
15233
|
+
const content = await fsp5.readFile(path39.join(cwd, f.path), "utf8");
|
|
14991
15234
|
filesWithContent.push({ ...f, content });
|
|
14992
15235
|
} catch {
|
|
14993
15236
|
}
|
|
@@ -15229,14 +15472,12 @@ function buildSettingsCommand(opts) {
|
|
|
15229
15472
|
};
|
|
15230
15473
|
} catch (err) {
|
|
15231
15474
|
return {
|
|
15232
|
-
message: `${color.red("Settings error")}: ${
|
|
15475
|
+
message: `${color.red("Settings error")}: ${toErrorMessage(err)}`
|
|
15233
15476
|
};
|
|
15234
15477
|
}
|
|
15235
15478
|
}
|
|
15236
15479
|
};
|
|
15237
15480
|
}
|
|
15238
|
-
|
|
15239
|
-
// src/slash-commands/spawn-agents.ts
|
|
15240
15481
|
function buildSpawnCommand(opts) {
|
|
15241
15482
|
return {
|
|
15242
15483
|
name: "spawn",
|
|
@@ -15273,7 +15514,7 @@ function buildSpawnCommand(opts) {
|
|
|
15273
15514
|
const summary = Object.keys(parsed).length > 0 ? await opts.onSpawn(description, parsed) : await opts.onSpawn(description);
|
|
15274
15515
|
return { message: summary };
|
|
15275
15516
|
} catch (err) {
|
|
15276
|
-
return { message: `Spawn failed: ${
|
|
15517
|
+
return { message: `Spawn failed: ${toErrorMessage(err)}` };
|
|
15277
15518
|
}
|
|
15278
15519
|
}
|
|
15279
15520
|
};
|
|
@@ -15360,7 +15601,7 @@ var DEFAULTS = {
|
|
|
15360
15601
|
working_dir: true
|
|
15361
15602
|
};
|
|
15362
15603
|
function resolveConfigPath() {
|
|
15363
|
-
return process.env[CONFIG_ENV] ??
|
|
15604
|
+
return process.env[CONFIG_ENV] ?? path39.join(process.env.HOME ?? "", ".wrongstack", "statusline.json");
|
|
15364
15605
|
}
|
|
15365
15606
|
async function loadStatuslineConfig() {
|
|
15366
15607
|
const p = resolveConfigPath();
|
|
@@ -15374,11 +15615,11 @@ async function loadStatuslineConfig() {
|
|
|
15374
15615
|
async function saveStatuslineConfig(cfg) {
|
|
15375
15616
|
const p = resolveConfigPath();
|
|
15376
15617
|
try {
|
|
15377
|
-
await fsp5.mkdir(
|
|
15618
|
+
await fsp5.mkdir(path39.dirname(p), { recursive: true });
|
|
15378
15619
|
await atomicWrite(p, JSON.stringify(cfg, null, 2));
|
|
15379
15620
|
} catch (err) {
|
|
15380
15621
|
throw new FsError({
|
|
15381
|
-
message:
|
|
15622
|
+
message: toErrorMessage(err),
|
|
15382
15623
|
code: err instanceof Error && err.message.includes("mkdir") ? ERROR_CODES.FS_MKDIR_FAILED : ERROR_CODES.FS_ATOMIC_WRITE_FAILED,
|
|
15383
15624
|
path: p,
|
|
15384
15625
|
cause: err
|
|
@@ -15613,7 +15854,7 @@ ${formatPlan(updated)}`;
|
|
|
15613
15854
|
const priority = validatePriority(parts[2] ?? "") ?? "medium";
|
|
15614
15855
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
15615
15856
|
file.tasks.push({
|
|
15616
|
-
id: `task_${
|
|
15857
|
+
id: `task_${randomUUID()}`,
|
|
15617
15858
|
title,
|
|
15618
15859
|
type,
|
|
15619
15860
|
priority,
|
|
@@ -15736,7 +15977,7 @@ ${formatTaskProgress(file.tasks)}`;
|
|
|
15736
15977
|
];
|
|
15737
15978
|
if (found.item.description) {
|
|
15738
15979
|
todos.push({
|
|
15739
|
-
id: `todo_${
|
|
15980
|
+
id: `todo_${randomUUID()}`,
|
|
15740
15981
|
content: found.item.description.slice(0, 200),
|
|
15741
15982
|
status: "pending",
|
|
15742
15983
|
promotedFromTask: found.item.id
|
|
@@ -15786,13 +16027,13 @@ ${formatTaskProgress(file.tasks)}`;
|
|
|
15786
16027
|
}
|
|
15787
16028
|
async function discoverPackageFiles(projectRoot) {
|
|
15788
16029
|
const files = [];
|
|
15789
|
-
const rootPkg =
|
|
16030
|
+
const rootPkg = path39.join(projectRoot, "package.json");
|
|
15790
16031
|
try {
|
|
15791
16032
|
await fsp5.access(rootPkg);
|
|
15792
16033
|
files.push(rootPkg);
|
|
15793
16034
|
} catch {
|
|
15794
16035
|
}
|
|
15795
|
-
const workspaceFile =
|
|
16036
|
+
const workspaceFile = path39.join(projectRoot, "pnpm-workspace.yaml");
|
|
15796
16037
|
try {
|
|
15797
16038
|
await fsp5.access(workspaceFile);
|
|
15798
16039
|
const content = await fsp5.readFile(workspaceFile, "utf8");
|
|
@@ -15802,12 +16043,12 @@ async function discoverPackageFiles(projectRoot) {
|
|
|
15802
16043
|
const globs = rawGlobs.split(/[\s,]+/).filter(Boolean).map((g) => g.replace(/['"]/g, ""));
|
|
15803
16044
|
for (const g of globs) {
|
|
15804
16045
|
const dirPrefix = g.replace(/\/?\*$/, "").replace(/\/\*$/, "");
|
|
15805
|
-
const dir =
|
|
16046
|
+
const dir = path39.join(projectRoot, dirPrefix);
|
|
15806
16047
|
try {
|
|
15807
16048
|
const entries = await fsp5.readdir(dir, { withFileTypes: true });
|
|
15808
16049
|
for (const e of entries) {
|
|
15809
16050
|
if (!e.isDirectory()) continue;
|
|
15810
|
-
const subPkg =
|
|
16051
|
+
const subPkg = path39.join(dir, e.name, "package.json");
|
|
15811
16052
|
try {
|
|
15812
16053
|
await fsp5.access(subPkg);
|
|
15813
16054
|
files.push(subPkg);
|
|
@@ -15822,7 +16063,7 @@ async function discoverPackageFiles(projectRoot) {
|
|
|
15822
16063
|
return files;
|
|
15823
16064
|
}
|
|
15824
16065
|
function buildTechStackTask(opts) {
|
|
15825
|
-
const pkgList = opts.packageFiles.map((f) => ` - ${
|
|
16066
|
+
const pkgList = opts.packageFiles.map((f) => ` - ${path39.relative(opts.projectRoot, f)}`).join("\n");
|
|
15826
16067
|
const header = opts.isInit ? [
|
|
15827
16068
|
"## Tech Stack Audit \u2014 First-Time Project Init",
|
|
15828
16069
|
"",
|
|
@@ -15944,7 +16185,7 @@ function buildTechStackCommand(opts) {
|
|
|
15944
16185
|
}
|
|
15945
16186
|
} catch (err) {
|
|
15946
16187
|
discoveryNote = color.red(
|
|
15947
|
-
`Could not scan for package files: ${
|
|
16188
|
+
`Could not scan for package files: ${toErrorMessage(err)}`
|
|
15948
16189
|
);
|
|
15949
16190
|
}
|
|
15950
16191
|
const task = buildTechStackTask({
|
|
@@ -15972,7 +16213,7 @@ function buildTechStackCommand(opts) {
|
|
|
15972
16213
|
const summary = await opts.onSpawnAndWait(task, { name });
|
|
15973
16214
|
return { message: summary };
|
|
15974
16215
|
} catch (err) {
|
|
15975
|
-
const msg = `Tech stack scan failed: ${
|
|
16216
|
+
const msg = `Tech stack scan failed: ${toErrorMessage(err)}`;
|
|
15976
16217
|
opts.renderer.writeWarning(msg);
|
|
15977
16218
|
return { message: msg };
|
|
15978
16219
|
}
|
|
@@ -16123,6 +16364,163 @@ ${color.dim("No default chat set. You can add it later: /telegram-setup <token>
|
|
|
16123
16364
|
}
|
|
16124
16365
|
};
|
|
16125
16366
|
}
|
|
16367
|
+
init_helpers();
|
|
16368
|
+
var HELP2 = [
|
|
16369
|
+
"Usage:",
|
|
16370
|
+
" /telegram-settings Show current Telegram notification settings",
|
|
16371
|
+
" /telegram-settings session-end on|off Notify on session end",
|
|
16372
|
+
" /telegram-settings delegate on|off Notify when a delegated subagent finishes",
|
|
16373
|
+
" /telegram-settings long-tool <ms|off> Notify for tools slower than <ms> (0/off = disabled)",
|
|
16374
|
+
" /telegram-settings poll <seconds> Bot polling interval (1\u201360)",
|
|
16375
|
+
" /telegram-settings chat <chatId> Default chat for notifications",
|
|
16376
|
+
"",
|
|
16377
|
+
"Aliases: /tg-settings",
|
|
16378
|
+
"",
|
|
16379
|
+
"Settings apply immediately \u2014 no restart needed."
|
|
16380
|
+
].join("\n");
|
|
16381
|
+
function buildTelegramSettingsCommand(opts) {
|
|
16382
|
+
function currentView() {
|
|
16383
|
+
const config = opts.configStore.get();
|
|
16384
|
+
const tg = config.extensions?.telegram ?? {};
|
|
16385
|
+
const sessionEnd = tg.notifyOnSessionEnd === true;
|
|
16386
|
+
const delegate = tg.notifyOnDelegate !== false;
|
|
16387
|
+
const longToolMs = typeof tg.longToolThresholdMs === "number" ? tg.longToolThresholdMs : 3e4;
|
|
16388
|
+
const longTool = longToolMs > 0 ? `${longToolMs}ms` : "off";
|
|
16389
|
+
const poll = typeof tg.pollIntervalSec === "number" ? `${tg.pollIntervalSec}s` : "2s";
|
|
16390
|
+
const chat = tg.notifyChatId !== void 0 && tg.notifyChatId !== null ? String(tg.notifyChatId) : "not set";
|
|
16391
|
+
const hasToken = typeof tg.botToken === "string" && tg.botToken.length > 0;
|
|
16392
|
+
return [
|
|
16393
|
+
`${color.bold("Telegram")} ${color.dim("\u2014 Notification Settings")}`,
|
|
16394
|
+
"",
|
|
16395
|
+
` session end: ${sessionEnd ? color.cyan("on") : color.dim("off")} ${color.dim("change: /telegram-settings session-end on|off")}`,
|
|
16396
|
+
` delegate done: ${delegate ? color.cyan("on") : color.dim("off")} ${color.dim("change: /telegram-settings delegate on|off")}`,
|
|
16397
|
+
` long tool: ${color.cyan(longTool)} ${color.dim("change: /telegram-settings long-tool <ms|off>")}`,
|
|
16398
|
+
` poll interval: ${color.cyan(poll)} ${color.dim("change: /telegram-settings poll <seconds>")}`,
|
|
16399
|
+
` notify chat: ${color.cyan(chat)} ${color.dim("change: /telegram-settings chat <chatId>")}`,
|
|
16400
|
+
"",
|
|
16401
|
+
hasToken ? color.dim(" Bot token configured. Changes apply immediately.") : `${color.amber("\u26A0")} No bot token configured. Run: /telegram-setup <botToken> [chatId]`
|
|
16402
|
+
].join("\n");
|
|
16403
|
+
}
|
|
16404
|
+
return {
|
|
16405
|
+
name: "telegram-settings",
|
|
16406
|
+
category: "Config",
|
|
16407
|
+
aliases: ["tg-settings"],
|
|
16408
|
+
description: "Toggle which agent events are reported to Telegram.",
|
|
16409
|
+
argsHint: "[session-end|delegate|long-tool|poll|chat <value>]",
|
|
16410
|
+
help: HELP2,
|
|
16411
|
+
async run(args) {
|
|
16412
|
+
const { cmd: sub, rest } = parseSubcommand(args);
|
|
16413
|
+
if (sub === "help" || sub === "--help" || sub === "-h") {
|
|
16414
|
+
return { message: HELP2 };
|
|
16415
|
+
}
|
|
16416
|
+
if (!opts.configStore || !opts.paths?.globalConfig) {
|
|
16417
|
+
return { message: `${color.red("Error")} config store not available.` };
|
|
16418
|
+
}
|
|
16419
|
+
if (!sub) {
|
|
16420
|
+
return { message: currentView() };
|
|
16421
|
+
}
|
|
16422
|
+
const persistDeps = {
|
|
16423
|
+
configStore: opts.configStore,
|
|
16424
|
+
globalConfigPath: opts.paths.globalConfig,
|
|
16425
|
+
vault: noOpVault
|
|
16426
|
+
};
|
|
16427
|
+
try {
|
|
16428
|
+
if (sub === "session-end") {
|
|
16429
|
+
const raw = (rest[0] ?? "").toLowerCase();
|
|
16430
|
+
if (!["on", "off"].includes(raw)) {
|
|
16431
|
+
return { message: `${color.amber("Usage:")} /telegram-settings session-end on|off` };
|
|
16432
|
+
}
|
|
16433
|
+
const on = raw === "on";
|
|
16434
|
+
await persistTelegramConfig(persistDeps, (tg) => {
|
|
16435
|
+
tg.notifyOnSessionEnd = on;
|
|
16436
|
+
});
|
|
16437
|
+
return {
|
|
16438
|
+
message: `${color.green("\u2713")} session-end \u2192 ${on ? color.cyan("on") : color.dim("off")}`
|
|
16439
|
+
};
|
|
16440
|
+
}
|
|
16441
|
+
if (sub === "delegate") {
|
|
16442
|
+
const raw = (rest[0] ?? "").toLowerCase();
|
|
16443
|
+
if (!["on", "off"].includes(raw)) {
|
|
16444
|
+
return { message: `${color.amber("Usage:")} /telegram-settings delegate on|off` };
|
|
16445
|
+
}
|
|
16446
|
+
const on = raw === "on";
|
|
16447
|
+
await persistTelegramConfig(persistDeps, (tg) => {
|
|
16448
|
+
tg.notifyOnDelegate = on;
|
|
16449
|
+
});
|
|
16450
|
+
return {
|
|
16451
|
+
message: `${color.green("\u2713")} delegate \u2192 ${on ? color.cyan("on") : color.dim("off")}`
|
|
16452
|
+
};
|
|
16453
|
+
}
|
|
16454
|
+
if (sub === "long-tool") {
|
|
16455
|
+
const raw = rest[0];
|
|
16456
|
+
if (raw === void 0) {
|
|
16457
|
+
return {
|
|
16458
|
+
message: `${color.amber("Usage:")} /telegram-settings long-tool <ms|off> ${color.dim("(0 or off disables)")}`
|
|
16459
|
+
};
|
|
16460
|
+
}
|
|
16461
|
+
if (raw === "off") {
|
|
16462
|
+
await persistTelegramConfig(persistDeps, (tg) => {
|
|
16463
|
+
tg.longToolThresholdMs = 0;
|
|
16464
|
+
});
|
|
16465
|
+
return {
|
|
16466
|
+
message: `${color.green("\u2713")} long-tool \u2192 ${color.dim("off")}`
|
|
16467
|
+
};
|
|
16468
|
+
}
|
|
16469
|
+
const ms = Number.parseInt(raw, 10);
|
|
16470
|
+
if (Number.isNaN(ms) || ms < 0) {
|
|
16471
|
+
return {
|
|
16472
|
+
message: `${color.red("Invalid number")}: "${raw}". Enter milliseconds, e.g. /telegram-settings long-tool 15000`
|
|
16473
|
+
};
|
|
16474
|
+
}
|
|
16475
|
+
await persistTelegramConfig(persistDeps, (tg) => {
|
|
16476
|
+
tg.longToolThresholdMs = ms;
|
|
16477
|
+
});
|
|
16478
|
+
return {
|
|
16479
|
+
message: `${color.green("\u2713")} long-tool \u2192 ${color.cyan(`${ms}ms`)}`
|
|
16480
|
+
};
|
|
16481
|
+
}
|
|
16482
|
+
if (sub === "poll") {
|
|
16483
|
+
const raw = rest[0];
|
|
16484
|
+
if (raw === void 0) {
|
|
16485
|
+
return { message: `${color.amber("Usage:")} /telegram-settings poll <seconds> ${color.dim("(1\u201360)")}` };
|
|
16486
|
+
}
|
|
16487
|
+
const sec = Number.parseInt(raw, 10);
|
|
16488
|
+
if (Number.isNaN(sec) || sec < 1 || sec > 60) {
|
|
16489
|
+
return {
|
|
16490
|
+
message: `${color.red("Invalid value")}: "${raw}". Enter seconds between 1 and 60.`
|
|
16491
|
+
};
|
|
16492
|
+
}
|
|
16493
|
+
await persistTelegramConfig(persistDeps, (tg) => {
|
|
16494
|
+
tg.pollIntervalSec = sec;
|
|
16495
|
+
});
|
|
16496
|
+
return {
|
|
16497
|
+
message: `${color.green("\u2713")} poll \u2192 ${color.cyan(`${sec}s`)}`
|
|
16498
|
+
};
|
|
16499
|
+
}
|
|
16500
|
+
if (sub === "chat") {
|
|
16501
|
+
const raw = rest[0];
|
|
16502
|
+
if (!raw) {
|
|
16503
|
+
return { message: `${color.amber("Usage:")} /telegram-settings chat <chatId>` };
|
|
16504
|
+
}
|
|
16505
|
+
const chatId = /^\d+$/.test(raw) ? Number(raw) : raw;
|
|
16506
|
+
await persistTelegramConfig(persistDeps, (tg) => {
|
|
16507
|
+
tg.notifyChatId = chatId;
|
|
16508
|
+
});
|
|
16509
|
+
return {
|
|
16510
|
+
message: `${color.green("\u2713")} notify chat \u2192 ${color.cyan(raw)}`
|
|
16511
|
+
};
|
|
16512
|
+
}
|
|
16513
|
+
return {
|
|
16514
|
+
message: `${color.red("Unknown setting")} "${sub}". ${unknownSubcommand(sub, ["session-end", "delegate", "long-tool", "poll", "chat"], "telegram-settings")}`
|
|
16515
|
+
};
|
|
16516
|
+
} catch (err) {
|
|
16517
|
+
return {
|
|
16518
|
+
message: `${color.red("Settings error")}: ${toErrorMessage(err)}`
|
|
16519
|
+
};
|
|
16520
|
+
}
|
|
16521
|
+
}
|
|
16522
|
+
};
|
|
16523
|
+
}
|
|
16126
16524
|
|
|
16127
16525
|
// src/slash-commands/todos.ts
|
|
16128
16526
|
init_helpers();
|
|
@@ -16250,7 +16648,7 @@ function buildWorkingDirCommand(_opts) {
|
|
|
16250
16648
|
}
|
|
16251
16649
|
const trimmed = args.trim();
|
|
16252
16650
|
if (!trimmed) {
|
|
16253
|
-
const rel2 =
|
|
16651
|
+
const rel2 = path39.relative(ctx.projectRoot, ctx.workingDir) || ".";
|
|
16254
16652
|
return {
|
|
16255
16653
|
message: [
|
|
16256
16654
|
`Working directory: ${color.bold(ctx.workingDir)}`,
|
|
@@ -16259,10 +16657,10 @@ function buildWorkingDirCommand(_opts) {
|
|
|
16259
16657
|
].join("\n")
|
|
16260
16658
|
};
|
|
16261
16659
|
}
|
|
16262
|
-
const resolved =
|
|
16263
|
-
const root =
|
|
16264
|
-
const rel =
|
|
16265
|
-
if (rel.startsWith("..") ||
|
|
16660
|
+
const resolved = path39.isAbsolute(trimmed) ? path39.resolve(trimmed) : path39.resolve(ctx.projectRoot, trimmed);
|
|
16661
|
+
const root = path39.resolve(ctx.projectRoot);
|
|
16662
|
+
const rel = path39.relative(root, resolved);
|
|
16663
|
+
if (rel.startsWith("..") || path39.isAbsolute(rel)) {
|
|
16266
16664
|
return {
|
|
16267
16665
|
message: color.red(
|
|
16268
16666
|
`Directory "${trimmed}" is outside the project root.
|
|
@@ -16284,11 +16682,11 @@ function buildWorkingDirCommand(_opts) {
|
|
|
16284
16682
|
ctx.setWorkingDir(resolved);
|
|
16285
16683
|
} catch (err) {
|
|
16286
16684
|
return {
|
|
16287
|
-
message: color.red(
|
|
16685
|
+
message: color.red(toErrorMessage(err))
|
|
16288
16686
|
};
|
|
16289
16687
|
}
|
|
16290
|
-
const prevRel =
|
|
16291
|
-
const newRel =
|
|
16688
|
+
const prevRel = path39.relative(ctx.projectRoot, previous) || ".";
|
|
16689
|
+
const newRel = path39.relative(ctx.projectRoot, resolved) || ".";
|
|
16292
16690
|
return {
|
|
16293
16691
|
message: [
|
|
16294
16692
|
color.green(` \u2713 ${prevRel} \u2192 ${color.bold(newRel)}`),
|
|
@@ -16431,6 +16829,7 @@ function buildBuiltinSlashCommands(opts) {
|
|
|
16431
16829
|
buildDirectorCommand(opts),
|
|
16432
16830
|
buildFleetCommand(opts),
|
|
16433
16831
|
buildEnhanceCommand(opts),
|
|
16832
|
+
buildEnsembleCommand(),
|
|
16434
16833
|
buildMemoryCommand(opts),
|
|
16435
16834
|
buildTodosCommand(opts),
|
|
16436
16835
|
buildTasksCommand(),
|
|
@@ -16453,6 +16852,7 @@ function buildBuiltinSlashCommands(opts) {
|
|
|
16453
16852
|
buildWorktreeCommand(opts),
|
|
16454
16853
|
buildSettingsCommand(opts),
|
|
16455
16854
|
buildTelegramSetupCommand(opts),
|
|
16855
|
+
buildTelegramSettingsCommand(opts),
|
|
16456
16856
|
buildSetModelCommand(opts),
|
|
16457
16857
|
buildFallbackCommand(opts),
|
|
16458
16858
|
buildModelCapsCommand(opts),
|
|
@@ -16472,8 +16872,6 @@ function buildBuiltinSlashCommands(opts) {
|
|
|
16472
16872
|
})
|
|
16473
16873
|
];
|
|
16474
16874
|
}
|
|
16475
|
-
|
|
16476
|
-
// src/pre-launch.ts
|
|
16477
16875
|
var MANIFESTS = [
|
|
16478
16876
|
"package.json",
|
|
16479
16877
|
"pyproject.toml",
|
|
@@ -16488,13 +16886,13 @@ var MANIFESTS = [
|
|
|
16488
16886
|
];
|
|
16489
16887
|
async function detectProjectKind(projectRoot) {
|
|
16490
16888
|
try {
|
|
16491
|
-
await fsp5.access(
|
|
16889
|
+
await fsp5.access(path39.join(projectRoot, ".wrongstack", "AGENTS.md"));
|
|
16492
16890
|
return "initialized";
|
|
16493
16891
|
} catch {
|
|
16494
16892
|
}
|
|
16495
16893
|
for (const m of MANIFESTS) {
|
|
16496
16894
|
try {
|
|
16497
|
-
await fsp5.access(
|
|
16895
|
+
await fsp5.access(path39.join(projectRoot, m));
|
|
16498
16896
|
return "project";
|
|
16499
16897
|
} catch {
|
|
16500
16898
|
}
|
|
@@ -16502,8 +16900,8 @@ async function detectProjectKind(projectRoot) {
|
|
|
16502
16900
|
return "empty";
|
|
16503
16901
|
}
|
|
16504
16902
|
async function scaffoldAgentsMd(projectRoot) {
|
|
16505
|
-
const dir =
|
|
16506
|
-
const file =
|
|
16903
|
+
const dir = path39.join(projectRoot, ".wrongstack");
|
|
16904
|
+
const file = path39.join(dir, "AGENTS.md");
|
|
16507
16905
|
const facts = await detectProjectFacts(projectRoot);
|
|
16508
16906
|
const body = renderAgentsTemplate(facts);
|
|
16509
16907
|
await fsp5.mkdir(dir, { recursive: true });
|
|
@@ -16516,7 +16914,7 @@ async function runProjectCheck(opts) {
|
|
|
16516
16914
|
if (kind === "initialized") {
|
|
16517
16915
|
renderer.write(
|
|
16518
16916
|
`
|
|
16519
|
-
${color.green("\u2713")} Project initialized ${color.dim(`(${
|
|
16917
|
+
${color.green("\u2713")} Project initialized ${color.dim(`(${path39.join(projectRoot, ".wrongstack", "AGENTS.md")})`)}
|
|
16520
16918
|
`
|
|
16521
16919
|
);
|
|
16522
16920
|
return true;
|
|
@@ -16541,13 +16939,13 @@ async function runProjectCheck(opts) {
|
|
|
16541
16939
|
`);
|
|
16542
16940
|
} catch (err) {
|
|
16543
16941
|
renderer.writeError(
|
|
16544
|
-
`Failed to scaffold AGENTS.md: ${
|
|
16942
|
+
`Failed to scaffold AGENTS.md: ${toErrorMessage(err)}`
|
|
16545
16943
|
);
|
|
16546
16944
|
}
|
|
16547
16945
|
}
|
|
16548
16946
|
return true;
|
|
16549
16947
|
}
|
|
16550
|
-
const gitDir =
|
|
16948
|
+
const gitDir = path39.join(projectRoot, ".git");
|
|
16551
16949
|
let hasGit = false;
|
|
16552
16950
|
try {
|
|
16553
16951
|
await fsp5.access(gitDir);
|
|
@@ -16570,7 +16968,7 @@ async function runProjectCheck(opts) {
|
|
|
16570
16968
|
if (answer2 === "y" || answer2 === "yes") {
|
|
16571
16969
|
try {
|
|
16572
16970
|
const { spawn: spawn6 } = await import('child_process');
|
|
16573
|
-
await new Promise((
|
|
16971
|
+
await new Promise((resolve11, reject) => {
|
|
16574
16972
|
const child = spawn6("git", ["init"], {
|
|
16575
16973
|
cwd,
|
|
16576
16974
|
signal: AbortSignal.timeout(1e4),
|
|
@@ -16579,14 +16977,14 @@ async function runProjectCheck(opts) {
|
|
|
16579
16977
|
child.on("error", reject);
|
|
16580
16978
|
child.on(
|
|
16581
16979
|
"close",
|
|
16582
|
-
(code) => code === 0 ?
|
|
16980
|
+
(code) => code === 0 ? resolve11() : reject(new Error(`git init failed with ${code}`))
|
|
16583
16981
|
);
|
|
16584
16982
|
});
|
|
16585
16983
|
renderer.write(` ${color.green("\u2713")} Git repository initialized
|
|
16586
16984
|
`);
|
|
16587
16985
|
} catch (err) {
|
|
16588
16986
|
renderer.writeError(
|
|
16589
|
-
`git init failed: ${
|
|
16987
|
+
`git init failed: ${toErrorMessage(err)}
|
|
16590
16988
|
`
|
|
16591
16989
|
);
|
|
16592
16990
|
}
|
|
@@ -16782,11 +17180,11 @@ async function countProjectFiles(projectRoot, threshold) {
|
|
|
16782
17180
|
for (const e of entries) {
|
|
16783
17181
|
if (SKIP_DIRS.has(e.name)) continue;
|
|
16784
17182
|
if (count >= threshold) return;
|
|
16785
|
-
const full =
|
|
17183
|
+
const full = path39.join(dir, e.name);
|
|
16786
17184
|
if (e.isDirectory()) {
|
|
16787
17185
|
await walk(full);
|
|
16788
17186
|
} else if (e.isFile()) {
|
|
16789
|
-
if (INDEXABLE_EXTS.has(
|
|
17187
|
+
if (INDEXABLE_EXTS.has(path39.extname(e.name))) {
|
|
16790
17188
|
count++;
|
|
16791
17189
|
}
|
|
16792
17190
|
}
|
|
@@ -17127,14 +17525,14 @@ function summarize(value, name) {
|
|
|
17127
17525
|
if (typeof v === "object" && v !== null) {
|
|
17128
17526
|
const o = v;
|
|
17129
17527
|
if (name === "edit") {
|
|
17130
|
-
const
|
|
17528
|
+
const path40 = typeof o["path"] === "string" ? o["path"] : "";
|
|
17131
17529
|
const reps = typeof o["replacements"] === "number" ? o["replacements"] : 0;
|
|
17132
|
-
return `${
|
|
17530
|
+
return `${path40} ${reps} replacement${reps === 1 ? "" : "s"}`.trim();
|
|
17133
17531
|
}
|
|
17134
17532
|
if (name === "write") {
|
|
17135
|
-
const
|
|
17533
|
+
const path40 = typeof o["path"] === "string" ? o["path"] : "";
|
|
17136
17534
|
const bytes = typeof o["bytes"] === "number" ? o["bytes"] : void 0;
|
|
17137
|
-
return bytes !== void 0 ? `${
|
|
17535
|
+
return bytes !== void 0 ? `${path40} ${bytes}B` : path40;
|
|
17138
17536
|
}
|
|
17139
17537
|
if (typeof o["count"] === "number") {
|
|
17140
17538
|
return `${o["count"]} match${o["count"] === 1 ? "" : "es"}`;
|
|
@@ -17145,6 +17543,17 @@ function summarize(value, name) {
|
|
|
17145
17543
|
|
|
17146
17544
|
// src/boot.ts
|
|
17147
17545
|
init_project_utils();
|
|
17546
|
+
function resolveCmdFromCatalog(subagentId) {
|
|
17547
|
+
const desc = findAgentDescriptor(subagentId);
|
|
17548
|
+
if (!desc) return null;
|
|
17549
|
+
const out = {
|
|
17550
|
+
command: desc.acp.command,
|
|
17551
|
+
args: [...desc.acp.args ?? []],
|
|
17552
|
+
role: subagentId
|
|
17553
|
+
};
|
|
17554
|
+
if (desc.acp.env) out.env = desc.acp.env;
|
|
17555
|
+
return out;
|
|
17556
|
+
}
|
|
17148
17557
|
var acpCmd = async (args, deps) => {
|
|
17149
17558
|
const sub = args[0];
|
|
17150
17559
|
if (!sub || sub === "server" || sub === "serve") {
|
|
@@ -17159,6 +17568,9 @@ Usage:
|
|
|
17159
17568
|
wstack acp list List available ACP agents
|
|
17160
17569
|
wstack acp spawn <id> <task>
|
|
17161
17570
|
Spawn an ACP agent as a subagent and wait for result
|
|
17571
|
+
wstack acp parallel <agent-id-csv> <task>
|
|
17572
|
+
Fan a task out to multiple ACP agents in parallel
|
|
17573
|
+
and aggregate the results
|
|
17162
17574
|
wstack acp help Show this help
|
|
17163
17575
|
|
|
17164
17576
|
ACP Mode:
|
|
@@ -17168,9 +17580,17 @@ ACP Mode:
|
|
|
17168
17580
|
Press Ctrl+C to stop.
|
|
17169
17581
|
|
|
17170
17582
|
spawn:
|
|
17171
|
-
Spawns a named ACP agent (
|
|
17172
|
-
|
|
17583
|
+
Spawns a named ACP agent (claude-code, gemini-cli, codex-cli, copilot,
|
|
17584
|
+
cline, goose, openhands, qwen-code, kiro-cli, opencode, mistral-vibe,
|
|
17585
|
+
cursor) with the given task and waits for its result.
|
|
17173
17586
|
Example: wstack acp spawn cline "fix the login bug"
|
|
17587
|
+
|
|
17588
|
+
parallel:
|
|
17589
|
+
Runs the same task on a comma-separated list of ACP agents concurrently.
|
|
17590
|
+
Example: wstack acp parallel claude-code,gemini-cli,codex-cli "review this diff"
|
|
17591
|
+
Each agent's result is rendered under a clearly-marked header. Returns 0
|
|
17592
|
+
if at least one agent succeeds, 1 if all fail. Agents that aren't
|
|
17593
|
+
installed are skipped with a warning.
|
|
17174
17594
|
`);
|
|
17175
17595
|
return 0;
|
|
17176
17596
|
}
|
|
@@ -17180,20 +17600,20 @@ spawn:
|
|
|
17180
17600
|
if (sub === "spawn") {
|
|
17181
17601
|
return spawnACPAgent(args.slice(1), deps);
|
|
17182
17602
|
}
|
|
17603
|
+
if (sub === "parallel") {
|
|
17604
|
+
return parallelACPAgents(args.slice(1), deps);
|
|
17605
|
+
}
|
|
17183
17606
|
deps.renderer.writeError(`Unknown acp subcommand: ${sub}
|
|
17184
17607
|
`);
|
|
17185
17608
|
deps.renderer.write("Run `wstack acp help` for usage.\n");
|
|
17186
17609
|
return 1;
|
|
17187
17610
|
};
|
|
17188
17611
|
async function runACPServer(deps) {
|
|
17189
|
-
const toolRegistry = deps.toolRegistry;
|
|
17190
|
-
const tools = toolRegistry?.list() ?? [];
|
|
17191
17612
|
deps.renderer.writeInfo("Starting WrongStack ACP server...\n");
|
|
17192
|
-
deps.renderer.writeInfo(`Exposing ${tools.length} tool(s) via ACP protocol.
|
|
17193
|
-
`);
|
|
17194
17613
|
deps.renderer.writeInfo("Waiting for ACP client connection on stdin/stdout...\n");
|
|
17614
|
+
deps.renderer.writeInfo("(default runTurn is a no-op echo \u2014 wire makeACPServerAgentTurn for a real agent)\n");
|
|
17195
17615
|
deps.renderer.writeInfo("Press Ctrl+C to stop.\n");
|
|
17196
|
-
const server = new WrongStackACPServer({
|
|
17616
|
+
const server = new WrongStackACPServer({});
|
|
17197
17617
|
const shutdown = () => {
|
|
17198
17618
|
deps.renderer.writeWarning("\nShutting down ACP server...");
|
|
17199
17619
|
server.stop();
|
|
@@ -17212,16 +17632,25 @@ async function runACPServer(deps) {
|
|
|
17212
17632
|
}
|
|
17213
17633
|
return 0;
|
|
17214
17634
|
}
|
|
17215
|
-
function listACPAgents(deps) {
|
|
17216
|
-
|
|
17217
|
-
|
|
17218
|
-
|
|
17219
|
-
|
|
17220
|
-
|
|
17221
|
-
|
|
17635
|
+
async function listACPAgents(deps) {
|
|
17636
|
+
const registry = new EnsembleRegistry();
|
|
17637
|
+
const detected = await registry.list();
|
|
17638
|
+
deps.renderer.write("Detected ACP agents:\n\n");
|
|
17639
|
+
const installed = detected.filter((a) => a.installed);
|
|
17640
|
+
const missing = detected.filter((a) => !a.installed);
|
|
17641
|
+
for (const a of installed) {
|
|
17642
|
+
const ver = a.version ? ` (${a.version.split("\n")[0]})` : "";
|
|
17643
|
+
deps.renderer.write(` \u2713 ${a.id.padEnd(16)} ${a.displayName}${ver}
|
|
17222
17644
|
`);
|
|
17223
17645
|
}
|
|
17224
|
-
|
|
17646
|
+
for (const a of missing) {
|
|
17647
|
+
deps.renderer.write(` \u2717 ${a.id.padEnd(16)} ${a.displayName} (${a.reason ?? "not installed"})
|
|
17648
|
+
`);
|
|
17649
|
+
}
|
|
17650
|
+
deps.renderer.write(`
|
|
17651
|
+
${installed.length} of ${detected.length} agents available.
|
|
17652
|
+
`);
|
|
17653
|
+
deps.renderer.write("Use `wstack acp spawn <agent-id> <task>` to delegate a task.\n");
|
|
17225
17654
|
return 0;
|
|
17226
17655
|
}
|
|
17227
17656
|
async function spawnACPAgent(args, deps) {
|
|
@@ -17237,7 +17666,7 @@ async function spawnACPAgent(args, deps) {
|
|
|
17237
17666
|
deps.renderer.write("Task description is required.\n");
|
|
17238
17667
|
return 1;
|
|
17239
17668
|
}
|
|
17240
|
-
const cmd = ACP_AGENT_COMMANDS[subagentId];
|
|
17669
|
+
const cmd = ACP_AGENT_COMMANDS[subagentId] ?? resolveCmdFromCatalog(subagentId);
|
|
17241
17670
|
if (!cmd) {
|
|
17242
17671
|
deps.renderer.writeError(`Unknown ACP agent: ${subagentId}
|
|
17243
17672
|
`);
|
|
@@ -17291,10 +17720,11 @@ async function spawnACPAgent(args, deps) {
|
|
|
17291
17720
|
);
|
|
17292
17721
|
return 0;
|
|
17293
17722
|
} catch (err) {
|
|
17294
|
-
|
|
17295
|
-
|
|
17296
|
-
|
|
17297
|
-
|
|
17723
|
+
const e = err;
|
|
17724
|
+
const detail = e.kind ? `[${e.kind}] ` : "";
|
|
17725
|
+
const message = e.message ?? (err instanceof Error ? err.message : String(err));
|
|
17726
|
+
deps.renderer.writeError(`ACP agent error: ${detail}${message}
|
|
17727
|
+
`);
|
|
17298
17728
|
return 1;
|
|
17299
17729
|
} finally {
|
|
17300
17730
|
cleanup();
|
|
@@ -17302,6 +17732,94 @@ async function spawnACPAgent(args, deps) {
|
|
|
17302
17732
|
process.off("SIGTERM", cleanup);
|
|
17303
17733
|
}
|
|
17304
17734
|
}
|
|
17735
|
+
async function parallelACPAgents(args, deps) {
|
|
17736
|
+
const [csv, ...taskParts] = args;
|
|
17737
|
+
if (!csv) {
|
|
17738
|
+
deps.renderer.writeError("Usage: wstack acp parallel <agent-id-csv> <task>\n");
|
|
17739
|
+
deps.renderer.write('Example: wstack acp parallel claude-code,gemini-cli "review this diff"\n');
|
|
17740
|
+
return 1;
|
|
17741
|
+
}
|
|
17742
|
+
const task = taskParts.join(" ");
|
|
17743
|
+
if (!task) {
|
|
17744
|
+
deps.renderer.writeError("Usage: wstack acp parallel <agent-id-csv> <task>\n");
|
|
17745
|
+
deps.renderer.writeError("Task description is required.\n");
|
|
17746
|
+
return 1;
|
|
17747
|
+
}
|
|
17748
|
+
const ac = new AbortController();
|
|
17749
|
+
const onSignal = () => ac.abort();
|
|
17750
|
+
process.on("SIGINT", onSignal);
|
|
17751
|
+
process.on("SIGTERM", onSignal);
|
|
17752
|
+
try {
|
|
17753
|
+
const result = await runEnsemble({
|
|
17754
|
+
agentIds: csv,
|
|
17755
|
+
task,
|
|
17756
|
+
resolveCmd: resolveCmdFromCatalog,
|
|
17757
|
+
signal: ac.signal
|
|
17758
|
+
});
|
|
17759
|
+
const skipped = result.results.filter((r) => r.status === "skipped");
|
|
17760
|
+
if (skipped.length > 0) {
|
|
17761
|
+
deps.renderer.writeWarning(
|
|
17762
|
+
`Skipping ${skipped.length} agent(s) not installed: ${skipped.map((s) => `${s.agentId} (${s.reason ?? "not installed"})`).join(", ")}
|
|
17763
|
+
`
|
|
17764
|
+
);
|
|
17765
|
+
}
|
|
17766
|
+
if (result.summary.succeeded + result.summary.failed + result.summary.cancelled === 0) {
|
|
17767
|
+
deps.renderer.writeError("No installed agents to run.\n");
|
|
17768
|
+
deps.renderer.write("Run `wstack acp list` to see what is available.\n");
|
|
17769
|
+
return 1;
|
|
17770
|
+
}
|
|
17771
|
+
const fannedOut = result.results.filter((r) => r.status !== "skipped").map((r) => r.agentId).join(", ");
|
|
17772
|
+
deps.renderer.writeInfo(
|
|
17773
|
+
`Fanning out to ${result.summary.succeeded + result.summary.failed + result.summary.cancelled} agent(s): ${fannedOut}
|
|
17774
|
+
`
|
|
17775
|
+
);
|
|
17776
|
+
deps.renderer.writeInfo(`Task: ${result.task}
|
|
17777
|
+
|
|
17778
|
+
`);
|
|
17779
|
+
for (const r of result.results) {
|
|
17780
|
+
if (r.status === "skipped") continue;
|
|
17781
|
+
deps.renderer.write(`
|
|
17782
|
+
=== ${r.agentId} ===
|
|
17783
|
+
`);
|
|
17784
|
+
if (r.status === "success") {
|
|
17785
|
+
deps.renderer.write(r.result && r.result.length > 0 ? r.result : "(no result)");
|
|
17786
|
+
deps.renderer.write(
|
|
17787
|
+
`
|
|
17788
|
+
[${r.agentId}] success ${r.durationMs}ms iterations=${r.iterations} toolCalls=${r.toolCalls}
|
|
17789
|
+
`
|
|
17790
|
+
);
|
|
17791
|
+
} else if (r.status === "failed") {
|
|
17792
|
+
deps.renderer.writeError(
|
|
17793
|
+
`[${r.error?.kind ?? "unknown"}] ${r.error?.message ?? "failed"}
|
|
17794
|
+
`
|
|
17795
|
+
);
|
|
17796
|
+
deps.renderer.write(
|
|
17797
|
+
`[${r.agentId}] failed ${r.durationMs}ms
|
|
17798
|
+
`
|
|
17799
|
+
);
|
|
17800
|
+
} else {
|
|
17801
|
+
deps.renderer.writeError(
|
|
17802
|
+
`[${r.error?.kind ?? "aborted"}] ${r.error?.message ?? "cancelled"}
|
|
17803
|
+
`
|
|
17804
|
+
);
|
|
17805
|
+
deps.renderer.write(
|
|
17806
|
+
`[${r.agentId}] cancelled ${r.durationMs}ms
|
|
17807
|
+
`
|
|
17808
|
+
);
|
|
17809
|
+
}
|
|
17810
|
+
}
|
|
17811
|
+
const { succeeded, failed, cancelled, skipped: skip } = result.summary;
|
|
17812
|
+
deps.renderer.write(
|
|
17813
|
+
`
|
|
17814
|
+
Parallel summary: ${succeeded} succeeded, ${failed} failed, ${cancelled} cancelled, ${skip} skipped.
|
|
17815
|
+
`
|
|
17816
|
+
);
|
|
17817
|
+
return succeeded > 0 ? 0 : 1;
|
|
17818
|
+
} finally {
|
|
17819
|
+
process.off("SIGINT", onSignal);
|
|
17820
|
+
process.off("SIGTERM", onSignal);
|
|
17821
|
+
}
|
|
17822
|
+
}
|
|
17305
17823
|
var auditCmd = async (args, deps) => {
|
|
17306
17824
|
const wpaths = resolveWstackPaths({
|
|
17307
17825
|
projectRoot: deps.projectRoot,
|
|
@@ -17351,14 +17869,14 @@ var auditCmd = async (args, deps) => {
|
|
|
17351
17869
|
return verify.ok ? 0 : 1;
|
|
17352
17870
|
};
|
|
17353
17871
|
async function listAudits(log, dir, deps) {
|
|
17354
|
-
const
|
|
17355
|
-
const
|
|
17872
|
+
const fs39 = await import('fs/promises');
|
|
17873
|
+
const path40 = await import('path');
|
|
17356
17874
|
const out = [];
|
|
17357
17875
|
let foundRoot = true;
|
|
17358
17876
|
const scan = async (scanDir, prefix, depth) => {
|
|
17359
17877
|
let entries;
|
|
17360
17878
|
try {
|
|
17361
|
-
entries = await
|
|
17879
|
+
entries = await fs39.readdir(scanDir, { withFileTypes: true });
|
|
17362
17880
|
} catch {
|
|
17363
17881
|
if (depth === 0) foundRoot = false;
|
|
17364
17882
|
return;
|
|
@@ -17366,7 +17884,7 @@ async function listAudits(log, dir, deps) {
|
|
|
17366
17884
|
for (const entry of entries) {
|
|
17367
17885
|
if (entry.name.startsWith(".")) continue;
|
|
17368
17886
|
if (entry.isDirectory()) {
|
|
17369
|
-
if (depth === 0) await scan(
|
|
17887
|
+
if (depth === 0) await scan(path40.join(scanDir, entry.name), entry.name, depth + 1);
|
|
17370
17888
|
continue;
|
|
17371
17889
|
}
|
|
17372
17890
|
if (!entry.isFile() || !entry.name.endsWith(".audit.jsonl")) continue;
|
|
@@ -18089,6 +18607,35 @@ ${color.amber("?")} Pick: `)).trim().toLowerCase();
|
|
|
18089
18607
|
}
|
|
18090
18608
|
}
|
|
18091
18609
|
|
|
18610
|
+
// src/auth-menu/local.ts
|
|
18611
|
+
init_provider_config_utils();
|
|
18612
|
+
|
|
18613
|
+
// src/auth-menu/local.ts
|
|
18614
|
+
var LOCAL_LLM_PRESETS = [
|
|
18615
|
+
{
|
|
18616
|
+
id: "ollama",
|
|
18617
|
+
label: "Ollama",
|
|
18618
|
+
defaultBaseUrl: "http://localhost:11434/v1",
|
|
18619
|
+
noAuth: true,
|
|
18620
|
+
hint: "https://ollama.com \u2014 port 11434, no auth"
|
|
18621
|
+
},
|
|
18622
|
+
{
|
|
18623
|
+
id: "vllm",
|
|
18624
|
+
label: "vLLM",
|
|
18625
|
+
defaultBaseUrl: "http://localhost:8000/v1",
|
|
18626
|
+
noAuth: false,
|
|
18627
|
+
hint: "https://docs.vllm.ai \u2014 port 8000, optional Bearer"
|
|
18628
|
+
},
|
|
18629
|
+
{
|
|
18630
|
+
id: "lmstudio",
|
|
18631
|
+
label: "LM Studio",
|
|
18632
|
+
defaultBaseUrl: "http://localhost:1234/v1",
|
|
18633
|
+
noAuth: false,
|
|
18634
|
+
hint: "https://lmstudio.ai \u2014 port 1234, optional Bearer"
|
|
18635
|
+
}
|
|
18636
|
+
];
|
|
18637
|
+
new Map(LOCAL_LLM_PRESETS.map((p) => [p.id, p]));
|
|
18638
|
+
|
|
18092
18639
|
// src/subcommands/handlers/auth.ts
|
|
18093
18640
|
init_provider_config_utils();
|
|
18094
18641
|
var authCmd = async (args, deps) => {
|
|
@@ -18285,7 +18832,200 @@ try {
|
|
|
18285
18832
|
} catch {
|
|
18286
18833
|
}
|
|
18287
18834
|
|
|
18288
|
-
// src/subcommands/handlers/
|
|
18835
|
+
// src/subcommands/handlers/bench.ts
|
|
18836
|
+
var benchCmd = async (args, deps) => {
|
|
18837
|
+
const sub = args[0];
|
|
18838
|
+
const rest = args.slice(1);
|
|
18839
|
+
switch (sub) {
|
|
18840
|
+
case "run":
|
|
18841
|
+
return benchRun(rest, deps);
|
|
18842
|
+
case "report":
|
|
18843
|
+
return benchReport(rest, deps);
|
|
18844
|
+
case "list":
|
|
18845
|
+
return benchList(rest, deps);
|
|
18846
|
+
default:
|
|
18847
|
+
printUsage(deps);
|
|
18848
|
+
return sub === void 0 ? 0 : 1;
|
|
18849
|
+
}
|
|
18850
|
+
};
|
|
18851
|
+
function printUsage(deps) {
|
|
18852
|
+
deps.renderer.write(
|
|
18853
|
+
[
|
|
18854
|
+
color.bold("wstack bench") + " \u2014 model-independent agentic benchmarks",
|
|
18855
|
+
"",
|
|
18856
|
+
" run Run a suite across a model matrix and write a report",
|
|
18857
|
+
" report Re-render report.md from a finished run directory",
|
|
18858
|
+
" list Show available suites and configured model cells",
|
|
18859
|
+
"",
|
|
18860
|
+
color.dim("Examples:"),
|
|
18861
|
+
color.dim(
|
|
18862
|
+
" wstack bench run --suite polyglot --polyglot-dir ./polyglot --models bench.config.json --limit 5"
|
|
18863
|
+
),
|
|
18864
|
+
color.dim(" wstack bench report ./bench-results/2026-06-14T10-00-00"),
|
|
18865
|
+
""
|
|
18866
|
+
].join("\n") + "\n"
|
|
18867
|
+
);
|
|
18868
|
+
}
|
|
18869
|
+
function flagStr(deps, name) {
|
|
18870
|
+
const v = deps.flags?.[name];
|
|
18871
|
+
return typeof v === "string" ? v : void 0;
|
|
18872
|
+
}
|
|
18873
|
+
function flagBool(deps, name) {
|
|
18874
|
+
const v = deps.flags?.[name];
|
|
18875
|
+
return v === true || v === "true";
|
|
18876
|
+
}
|
|
18877
|
+
async function resolveWstackEntry() {
|
|
18878
|
+
try {
|
|
18879
|
+
const req2 = createRequire(import.meta.url);
|
|
18880
|
+
const pkgPath = req2.resolve("@wrongstack/cli/package.json");
|
|
18881
|
+
const entry = path39.join(path39.dirname(pkgPath), "dist", "index.js");
|
|
18882
|
+
await fsp5.access(entry);
|
|
18883
|
+
return entry;
|
|
18884
|
+
} catch {
|
|
18885
|
+
return process.argv[1] ?? "";
|
|
18886
|
+
}
|
|
18887
|
+
}
|
|
18888
|
+
async function benchRun(_args, deps) {
|
|
18889
|
+
const suiteId = flagStr(deps, "suite") ?? "polyglot";
|
|
18890
|
+
const modelsPath = flagStr(deps, "models") ?? "bench.config.json";
|
|
18891
|
+
const limitRaw = flagStr(deps, "limit");
|
|
18892
|
+
const limit = limitRaw ? Math.max(1, Number.parseInt(limitRaw, 10)) : void 0;
|
|
18893
|
+
const outBase = flagStr(deps, "out") ?? "bench-results";
|
|
18894
|
+
let config;
|
|
18895
|
+
try {
|
|
18896
|
+
config = await loadBenchConfig(path39.resolve(deps.cwd, modelsPath));
|
|
18897
|
+
} catch (err) {
|
|
18898
|
+
deps.renderer.writeError(toErrorMessage(err));
|
|
18899
|
+
return 1;
|
|
18900
|
+
}
|
|
18901
|
+
const concurrencyRaw = flagStr(deps, "concurrency");
|
|
18902
|
+
if (concurrencyRaw) {
|
|
18903
|
+
const c = Number.parseInt(concurrencyRaw, 10);
|
|
18904
|
+
if (c > 0) config.concurrency = c;
|
|
18905
|
+
}
|
|
18906
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
18907
|
+
const outDir = path39.resolve(deps.cwd, outBase, stamp);
|
|
18908
|
+
const predictionsDir = path39.join(outDir, "predictions");
|
|
18909
|
+
let suite;
|
|
18910
|
+
let grade;
|
|
18911
|
+
let isSwebench = false;
|
|
18912
|
+
if (suiteId === "polyglot") {
|
|
18913
|
+
const polyglotDir = flagStr(deps, "polyglot-dir");
|
|
18914
|
+
if (!polyglotDir) {
|
|
18915
|
+
deps.renderer.writeError("--polyglot-dir <path> is required for the polyglot suite.");
|
|
18916
|
+
return 1;
|
|
18917
|
+
}
|
|
18918
|
+
const languagesRaw = flagStr(deps, "languages");
|
|
18919
|
+
const languages = languagesRaw ? languagesRaw.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
18920
|
+
suite = createPolyglotSuite({ polyglotDir: path39.resolve(deps.cwd, polyglotDir), languages });
|
|
18921
|
+
grade = (a) => gradePolyglot(a);
|
|
18922
|
+
} else if (suiteId === "swebench") {
|
|
18923
|
+
isSwebench = true;
|
|
18924
|
+
const datasetDir = flagStr(deps, "dataset-dir");
|
|
18925
|
+
const docker = flagBool(deps, "docker");
|
|
18926
|
+
suite = createSwebenchSuite({
|
|
18927
|
+
datasetDir: datasetDir ? path39.resolve(deps.cwd, datasetDir) : void 0,
|
|
18928
|
+
docker
|
|
18929
|
+
});
|
|
18930
|
+
grade = (a) => gradeSwebench({ ...a, predictionsDir });
|
|
18931
|
+
} else {
|
|
18932
|
+
deps.renderer.writeError(`unknown suite "${suiteId}" (expected: polyglot | swebench)`);
|
|
18933
|
+
return 1;
|
|
18934
|
+
}
|
|
18935
|
+
const toolNames = deps.toolRegistry?.list().map((t) => t.name) ?? [];
|
|
18936
|
+
const wstackEntry = await resolveWstackEntry();
|
|
18937
|
+
deps.renderer.writeInfo(`Running ${suiteId} across ${config.cells.length} model(s)\u2026`);
|
|
18938
|
+
let report;
|
|
18939
|
+
try {
|
|
18940
|
+
report = await runBenchmark({
|
|
18941
|
+
suite,
|
|
18942
|
+
grade,
|
|
18943
|
+
config,
|
|
18944
|
+
cliVersion: CLI_VERSION,
|
|
18945
|
+
toolNames,
|
|
18946
|
+
nodeBin: process.execPath,
|
|
18947
|
+
wstackEntry,
|
|
18948
|
+
limit,
|
|
18949
|
+
onProgress: (msg) => deps.renderer.write(color.dim(msg) + "\n")
|
|
18950
|
+
});
|
|
18951
|
+
} catch (err) {
|
|
18952
|
+
deps.renderer.writeError(toErrorMessage(err));
|
|
18953
|
+
return 1;
|
|
18954
|
+
}
|
|
18955
|
+
await writeJsonArtifacts(outDir, report);
|
|
18956
|
+
const md = renderMarkdownReport(report);
|
|
18957
|
+
await fsp5.writeFile(path39.join(outDir, "report.md"), md, "utf8");
|
|
18958
|
+
deps.renderer.write("\n" + md + "\n");
|
|
18959
|
+
if (isSwebench) {
|
|
18960
|
+
for (const cell of config.cells) {
|
|
18961
|
+
const preds = await collectCellPredictions(predictionsDir, cell.label);
|
|
18962
|
+
if (preds.length === 0) continue;
|
|
18963
|
+
const file = await writePredictionsJsonl(outDir, cell.label, preds);
|
|
18964
|
+
deps.renderer.writeInfo(`Predictions for "${cell.label}" \u2192 ${file}`);
|
|
18965
|
+
}
|
|
18966
|
+
deps.renderer.writeInfo(
|
|
18967
|
+
"Grade with the official SWE-bench harness: python -m swebench.harness.run_evaluation --predictions_path <file> --run_id <id>"
|
|
18968
|
+
);
|
|
18969
|
+
}
|
|
18970
|
+
deps.renderer.writeInfo(`Report written to ${path39.join(outDir, "report.md")}`);
|
|
18971
|
+
return 0;
|
|
18972
|
+
}
|
|
18973
|
+
async function benchReport(args, deps) {
|
|
18974
|
+
const dir = args.find((a) => !a.startsWith("-"));
|
|
18975
|
+
if (!dir) {
|
|
18976
|
+
deps.renderer.writeError("Usage: wstack bench report <run-directory>");
|
|
18977
|
+
return 1;
|
|
18978
|
+
}
|
|
18979
|
+
const outDir = path39.resolve(deps.cwd, dir);
|
|
18980
|
+
let summary;
|
|
18981
|
+
try {
|
|
18982
|
+
summary = await readSummary(outDir);
|
|
18983
|
+
} catch (err) {
|
|
18984
|
+
deps.renderer.writeError(
|
|
18985
|
+
`cannot read summary.json in ${outDir}: ${toErrorMessage(err)}`
|
|
18986
|
+
);
|
|
18987
|
+
return 1;
|
|
18988
|
+
}
|
|
18989
|
+
const md = renderMarkdownReport(summary);
|
|
18990
|
+
await fsp5.writeFile(path39.join(outDir, "report.md"), md, "utf8");
|
|
18991
|
+
deps.renderer.write("\n" + md + "\n");
|
|
18992
|
+
return 0;
|
|
18993
|
+
}
|
|
18994
|
+
async function benchList(_args, deps) {
|
|
18995
|
+
deps.renderer.write(color.bold("Suites\n"));
|
|
18996
|
+
deps.renderer.write(
|
|
18997
|
+
" polyglot " + color.dim("Aider polyglot (edit accuracy) \u2014 Phase 1, Docker-free\n")
|
|
18998
|
+
);
|
|
18999
|
+
deps.renderer.write(
|
|
19000
|
+
" swebench " + color.dim("SWE-bench Verified (end-to-end) \u2014 Phase 2, Docker-gated\n")
|
|
19001
|
+
);
|
|
19002
|
+
const modelsPath = flagStr(deps, "models");
|
|
19003
|
+
if (modelsPath) {
|
|
19004
|
+
try {
|
|
19005
|
+
const config = await loadBenchConfig(path39.resolve(deps.cwd, modelsPath));
|
|
19006
|
+
deps.renderer.write("\n" + color.bold("Model cells\n"));
|
|
19007
|
+
for (const cell of config.cells) {
|
|
19008
|
+
deps.renderer.write(
|
|
19009
|
+
` ${cell.label.padEnd(16)} ${color.dim(`${cell.provider}/${cell.model}`)}
|
|
19010
|
+
`
|
|
19011
|
+
);
|
|
19012
|
+
}
|
|
19013
|
+
const fp = reportHeaderLine({
|
|
19014
|
+
cliVersion: CLI_VERSION,
|
|
19015
|
+
toolNames: deps.toolRegistry?.list().map((t) => t.name) ?? [],
|
|
19016
|
+
maxIterations: config.maxIterations,
|
|
19017
|
+
yolo: true,
|
|
19018
|
+
subsetId: "(computed at run time)",
|
|
19019
|
+
hash: "(computed at run time)"
|
|
19020
|
+
});
|
|
19021
|
+
deps.renderer.write("\n" + color.dim(`Harness: ${fp}`) + "\n");
|
|
19022
|
+
} catch (err) {
|
|
19023
|
+
deps.renderer.writeError(toErrorMessage(err));
|
|
19024
|
+
return 1;
|
|
19025
|
+
}
|
|
19026
|
+
}
|
|
19027
|
+
return 0;
|
|
19028
|
+
}
|
|
18289
19029
|
var diagCmd = async (_args, deps) => {
|
|
18290
19030
|
const cfg = deps.config;
|
|
18291
19031
|
const age = await deps.modelsRegistry.ageSeconds();
|
|
@@ -18364,7 +19104,7 @@ var doctorCmd = async (_args, deps) => {
|
|
|
18364
19104
|
checks.push({
|
|
18365
19105
|
name: "models cache",
|
|
18366
19106
|
status: "warn",
|
|
18367
|
-
detail: `read failed: ${
|
|
19107
|
+
detail: `read failed: ${toErrorMessage(err)}`
|
|
18368
19108
|
});
|
|
18369
19109
|
}
|
|
18370
19110
|
try {
|
|
@@ -18379,7 +19119,7 @@ var doctorCmd = async (_args, deps) => {
|
|
|
18379
19119
|
}
|
|
18380
19120
|
try {
|
|
18381
19121
|
await fsp5.mkdir(deps.paths.projectSessions, { recursive: true });
|
|
18382
|
-
const probe =
|
|
19122
|
+
const probe = path39.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
|
|
18383
19123
|
await fsp5.writeFile(probe, "");
|
|
18384
19124
|
await fsp5.unlink(probe);
|
|
18385
19125
|
checks.push({ name: "sessions writable", status: "ok", detail: deps.paths.projectSessions });
|
|
@@ -18387,7 +19127,7 @@ var doctorCmd = async (_args, deps) => {
|
|
|
18387
19127
|
checks.push({
|
|
18388
19128
|
name: "sessions writable",
|
|
18389
19129
|
status: "fail",
|
|
18390
|
-
detail: `cannot write to ${deps.paths.projectSessions}: ${
|
|
19130
|
+
detail: `cannot write to ${deps.paths.projectSessions}: ${toErrorMessage(err)}`
|
|
18391
19131
|
});
|
|
18392
19132
|
}
|
|
18393
19133
|
const mcpEntries = Object.entries(cfg.mcpServers ?? {});
|
|
@@ -18478,12 +19218,12 @@ var exportCmd = async (args, deps) => {
|
|
|
18478
19218
|
try {
|
|
18479
19219
|
rendered = await reader.export(sessionId, { format, includeTools, includeDiagnostics });
|
|
18480
19220
|
} catch (err) {
|
|
18481
|
-
deps.renderer.writeError(`Export failed: ${
|
|
19221
|
+
deps.renderer.writeError(`Export failed: ${toErrorMessage(err)}`);
|
|
18482
19222
|
return 1;
|
|
18483
19223
|
}
|
|
18484
19224
|
if (output) {
|
|
18485
|
-
await fsp5.mkdir(
|
|
18486
|
-
await fsp5.writeFile(
|
|
19225
|
+
await fsp5.mkdir(path39.dirname(path39.resolve(deps.cwd, output)), { recursive: true });
|
|
19226
|
+
await fsp5.writeFile(path39.resolve(deps.cwd, output), rendered, "utf8");
|
|
18487
19227
|
deps.renderer.write(`Wrote ${rendered.length} bytes to ${output}
|
|
18488
19228
|
`);
|
|
18489
19229
|
} else {
|
|
@@ -18559,8 +19299,8 @@ var initCmd = async (_args, deps) => {
|
|
|
18559
19299
|
const vault = new DefaultSecretVault({ keyFile: deps.paths.secretsKey });
|
|
18560
19300
|
const encrypted = encryptConfigSecrets$1(config, vault);
|
|
18561
19301
|
await atomicWrite(deps.paths.globalConfig, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
18562
|
-
await fsp5.mkdir(
|
|
18563
|
-
const agentsFile =
|
|
19302
|
+
await fsp5.mkdir(path39.join(deps.projectRoot, ".wrongstack"), { recursive: true });
|
|
19303
|
+
const agentsFile = path39.join(deps.projectRoot, ".wrongstack", "AGENTS.md");
|
|
18564
19304
|
const projectFacts = await detectProjectFacts(deps.projectRoot);
|
|
18565
19305
|
await atomicWrite(agentsFile, renderAgentsTemplate(projectFacts));
|
|
18566
19306
|
deps.renderer.writeInfo(`Wrote ${deps.paths.globalConfig}`);
|
|
@@ -18699,8 +19439,8 @@ async function serveMcpStdio(deps) {
|
|
|
18699
19439
|
log(
|
|
18700
19440
|
`wrongstack MCP server ready at ${handle2.url} \u2014 exposing ${allowed.length} tool(s) (${mode})${token ? " [token auth]" : ""}.`
|
|
18701
19441
|
);
|
|
18702
|
-
await new Promise((
|
|
18703
|
-
const stop = () =>
|
|
19442
|
+
await new Promise((resolve11) => {
|
|
19443
|
+
const stop = () => resolve11();
|
|
18704
19444
|
process.once("SIGINT", stop);
|
|
18705
19445
|
process.once("SIGTERM", stop);
|
|
18706
19446
|
});
|
|
@@ -19451,7 +20191,7 @@ var modeldiagCmd = async (args, deps) => {
|
|
|
19451
20191
|
` ${label} ${provColor(modelKey.padEnd(50))} ${scoreBar(c.score, 110).slice(0, 11)} ${color.amber(fmtMs(latency).padEnd(8))} ${color.dim(`in${usage?.input ?? "?"}/out${usage?.output ?? "?"}`.padEnd(12))} ${firstLineClean}`
|
|
19452
20192
|
);
|
|
19453
20193
|
} catch (err) {
|
|
19454
|
-
const errMsg =
|
|
20194
|
+
const errMsg = toErrorMessage(err);
|
|
19455
20195
|
writeLine(
|
|
19456
20196
|
` ${label} ${color.red(modelKey.padEnd(50))} ${scoreBar(c.score, 110).slice(0, 11)} ${color.red("FAILED")} ${color.dim(errMsg.slice(0, 40))}`
|
|
19457
20197
|
);
|
|
@@ -19923,7 +20663,7 @@ var usageCmd = async (_args, deps) => {
|
|
|
19923
20663
|
return 0;
|
|
19924
20664
|
};
|
|
19925
20665
|
var projectsCmd = async (_args, deps) => {
|
|
19926
|
-
const projectsRoot =
|
|
20666
|
+
const projectsRoot = path39.join(deps.paths.globalRoot, "projects");
|
|
19927
20667
|
try {
|
|
19928
20668
|
const entries = await fsp5.readdir(projectsRoot);
|
|
19929
20669
|
if (entries.length === 0) {
|
|
@@ -19933,7 +20673,7 @@ var projectsCmd = async (_args, deps) => {
|
|
|
19933
20673
|
for (const hash of entries) {
|
|
19934
20674
|
try {
|
|
19935
20675
|
const meta = JSON.parse(
|
|
19936
|
-
await fsp5.readFile(
|
|
20676
|
+
await fsp5.readFile(path39.join(projectsRoot, hash, "meta.json"), "utf8")
|
|
19937
20677
|
);
|
|
19938
20678
|
deps.renderer.write(
|
|
19939
20679
|
` ${color.dim(hash)} ${color.dim(meta.lastSeen ?? "")} ${meta.root ?? "?"}
|
|
@@ -20382,7 +21122,7 @@ function findSessionId(args) {
|
|
|
20382
21122
|
var rewindCmd = async (args, deps) => {
|
|
20383
21123
|
const flags = parseRewindFlags(args);
|
|
20384
21124
|
const wpaths = resolveWstackPaths({ projectRoot: deps.projectRoot });
|
|
20385
|
-
const sessionsDir =
|
|
21125
|
+
const sessionsDir = path39.join(wpaths.globalRoot, "sessions");
|
|
20386
21126
|
const rewind = new DefaultSessionRewinder(sessionsDir, deps.projectRoot);
|
|
20387
21127
|
let sessionId = findSessionId(args);
|
|
20388
21128
|
if (!sessionId) {
|
|
@@ -20495,7 +21235,7 @@ ${result.errors.length} error(s):
|
|
|
20495
21235
|
}
|
|
20496
21236
|
return 0;
|
|
20497
21237
|
} catch (err) {
|
|
20498
|
-
deps.renderer.writeError(
|
|
21238
|
+
deps.renderer.writeError(toErrorMessage(err));
|
|
20499
21239
|
return 1;
|
|
20500
21240
|
}
|
|
20501
21241
|
};
|
|
@@ -20527,7 +21267,7 @@ async function listFleetRuns(deps) {
|
|
|
20527
21267
|
}
|
|
20528
21268
|
const runs = [];
|
|
20529
21269
|
for (const id of entries) {
|
|
20530
|
-
const runDir =
|
|
21270
|
+
const runDir = path39.join(deps.paths.projectSessions, id);
|
|
20531
21271
|
let stat7;
|
|
20532
21272
|
try {
|
|
20533
21273
|
stat7 = await fsp5.stat(runDir);
|
|
@@ -20540,17 +21280,17 @@ async function listFleetRuns(deps) {
|
|
|
20540
21280
|
let subagentCount = 0;
|
|
20541
21281
|
let subagentsDir;
|
|
20542
21282
|
try {
|
|
20543
|
-
await fsp5.access(
|
|
21283
|
+
await fsp5.access(path39.join(runDir, "fleet.json"));
|
|
20544
21284
|
manifest = true;
|
|
20545
21285
|
} catch {
|
|
20546
21286
|
}
|
|
20547
21287
|
try {
|
|
20548
|
-
await fsp5.access(
|
|
21288
|
+
await fsp5.access(path39.join(runDir, "checkpoint.json"));
|
|
20549
21289
|
checkpoint = true;
|
|
20550
21290
|
} catch {
|
|
20551
21291
|
}
|
|
20552
21292
|
try {
|
|
20553
|
-
subagentsDir =
|
|
21293
|
+
subagentsDir = path39.join(runDir, "subagents");
|
|
20554
21294
|
const files = await fsp5.readdir(subagentsDir);
|
|
20555
21295
|
subagentCount = files.filter((f) => f.endsWith(".jsonl")).length;
|
|
20556
21296
|
} catch {
|
|
@@ -20577,7 +21317,7 @@ async function listFleetRuns(deps) {
|
|
|
20577
21317
|
return 0;
|
|
20578
21318
|
}
|
|
20579
21319
|
async function showFleetRun(runId, deps) {
|
|
20580
|
-
const runDir =
|
|
21320
|
+
const runDir = path39.join(deps.paths.projectSessions, runId);
|
|
20581
21321
|
let stat7;
|
|
20582
21322
|
try {
|
|
20583
21323
|
stat7 = await fsp5.stat(runDir);
|
|
@@ -20594,7 +21334,7 @@ async function showFleetRun(runId, deps) {
|
|
|
20594
21334
|
deps.renderer.write(color.bold(`
|
|
20595
21335
|
Fleet Run: ${runId}
|
|
20596
21336
|
`) + "\n");
|
|
20597
|
-
const manifestPath =
|
|
21337
|
+
const manifestPath = path39.join(runDir, "fleet.json");
|
|
20598
21338
|
let manifestData = null;
|
|
20599
21339
|
try {
|
|
20600
21340
|
manifestData = await fsp5.readFile(manifestPath, "utf8");
|
|
@@ -20612,7 +21352,7 @@ Fleet Run: ${runId}
|
|
|
20612
21352
|
deps.renderer.write(` ${color.dim("\u25CB")} fleet.json \u2014 not found
|
|
20613
21353
|
`);
|
|
20614
21354
|
}
|
|
20615
|
-
const checkpointPath =
|
|
21355
|
+
const checkpointPath = path39.join(runDir, "checkpoint.json");
|
|
20616
21356
|
let checkpointData = null;
|
|
20617
21357
|
try {
|
|
20618
21358
|
checkpointData = await fsp5.readFile(checkpointPath, "utf8");
|
|
@@ -20659,7 +21399,7 @@ Fleet Run: ${runId}
|
|
|
20659
21399
|
} catch {
|
|
20660
21400
|
}
|
|
20661
21401
|
}
|
|
20662
|
-
const subagentsDir =
|
|
21402
|
+
const subagentsDir = path39.join(runDir, "subagents");
|
|
20663
21403
|
let subagentFiles = [];
|
|
20664
21404
|
try {
|
|
20665
21405
|
subagentFiles = await fsp5.readdir(subagentsDir);
|
|
@@ -20671,7 +21411,7 @@ Fleet Run: ${runId}
|
|
|
20671
21411
|
Subagent transcripts (${subagentFiles.length}):
|
|
20672
21412
|
`);
|
|
20673
21413
|
for (const f of subagentFiles.sort()) {
|
|
20674
|
-
const filePath =
|
|
21414
|
+
const filePath = path39.join(subagentsDir, f);
|
|
20675
21415
|
let size;
|
|
20676
21416
|
try {
|
|
20677
21417
|
const s = await fsp5.stat(filePath);
|
|
@@ -20688,7 +21428,7 @@ Fleet Run: ${runId}
|
|
|
20688
21428
|
${color.dim("\u25CB")} No subagent transcripts
|
|
20689
21429
|
`);
|
|
20690
21430
|
}
|
|
20691
|
-
const sharedDir =
|
|
21431
|
+
const sharedDir = path39.join(runDir, "shared");
|
|
20692
21432
|
try {
|
|
20693
21433
|
const files = await fsp5.readdir(sharedDir);
|
|
20694
21434
|
deps.renderer.write(`
|
|
@@ -20872,7 +21612,7 @@ var updateCmd = async (args, deps) => {
|
|
|
20872
21612
|
deps.renderer.write(`Updating wrongstack from v${info.current} to v${info.latest}...
|
|
20873
21613
|
`);
|
|
20874
21614
|
try {
|
|
20875
|
-
const result = await new Promise((
|
|
21615
|
+
const result = await new Promise((resolve11, reject) => {
|
|
20876
21616
|
const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
20877
21617
|
const child = spawn(npmCommand, ["install", "-g", "wrongstack@latest"], {
|
|
20878
21618
|
cwd,
|
|
@@ -20885,7 +21625,7 @@ var updateCmd = async (args, deps) => {
|
|
|
20885
21625
|
_stderr += d;
|
|
20886
21626
|
});
|
|
20887
21627
|
child.on("error", reject);
|
|
20888
|
-
child.on("close", (code) =>
|
|
21628
|
+
child.on("close", (code) => resolve11({ code: code ?? 0 }));
|
|
20889
21629
|
});
|
|
20890
21630
|
if (result.code === 0) {
|
|
20891
21631
|
deps.renderer.write(
|
|
@@ -20996,7 +21736,8 @@ var subcommands = {
|
|
|
20996
21736
|
help: helpCmd,
|
|
20997
21737
|
projects: projectsCmd,
|
|
20998
21738
|
modeldiag: modeldiagCmd,
|
|
20999
|
-
quick: quickCmd
|
|
21739
|
+
quick: quickCmd,
|
|
21740
|
+
bench: benchCmd
|
|
21000
21741
|
};
|
|
21001
21742
|
|
|
21002
21743
|
// src/boot.ts
|
|
@@ -21014,7 +21755,7 @@ function resolveBundledSkillsDir() {
|
|
|
21014
21755
|
try {
|
|
21015
21756
|
const req2 = createRequire(import.meta.url);
|
|
21016
21757
|
const corePkg = req2.resolve("@wrongstack/core/package.json");
|
|
21017
|
-
return
|
|
21758
|
+
return path39.join(path39.dirname(corePkg), "skills");
|
|
21018
21759
|
} catch {
|
|
21019
21760
|
return void 0;
|
|
21020
21761
|
}
|
|
@@ -21033,7 +21774,7 @@ async function boot(argv) {
|
|
|
21033
21774
|
try {
|
|
21034
21775
|
bootResult = await bootConfig(flags);
|
|
21035
21776
|
} catch (err) {
|
|
21036
|
-
writeErr(`Config error: ${
|
|
21777
|
+
writeErr(`Config error: ${toErrorMessage(err)}
|
|
21037
21778
|
`);
|
|
21038
21779
|
return 2;
|
|
21039
21780
|
}
|
|
@@ -21086,7 +21827,7 @@ async function boot(argv) {
|
|
|
21086
21827
|
await modelsRegistry.refresh();
|
|
21087
21828
|
logger.info("models.dev catalog refreshed");
|
|
21088
21829
|
} catch (err) {
|
|
21089
|
-
const msg =
|
|
21830
|
+
const msg = toErrorMessage(err);
|
|
21090
21831
|
logger.warn(`models.dev refresh failed (${msg}); using cached catalog`);
|
|
21091
21832
|
}
|
|
21092
21833
|
}
|
|
@@ -21235,7 +21976,7 @@ async function boot(argv) {
|
|
|
21235
21976
|
const created = await registerProjectAtBoot({ projectRoot, cwd, wpaths });
|
|
21236
21977
|
if (created && isInteractiveTTY) {
|
|
21237
21978
|
renderer.write(
|
|
21238
|
-
color.dim(` \u2713 Registered "${
|
|
21979
|
+
color.dim(` \u2713 Registered "${path39.basename(projectRoot)}" in projects.json.
|
|
21239
21980
|
`)
|
|
21240
21981
|
);
|
|
21241
21982
|
}
|
|
@@ -21315,7 +22056,7 @@ async function boot(argv) {
|
|
|
21315
22056
|
} catch {
|
|
21316
22057
|
}
|
|
21317
22058
|
printLaunchHints(renderer, flags, {
|
|
21318
|
-
cursorFile:
|
|
22059
|
+
cursorFile: path39.join(wpaths.cacheDir, "hint-cursor")
|
|
21319
22060
|
});
|
|
21320
22061
|
} else {
|
|
21321
22062
|
const effectiveChoices = config.launch ? {
|
|
@@ -21353,7 +22094,7 @@ async function boot(argv) {
|
|
|
21353
22094
|
}
|
|
21354
22095
|
async function checkGitInCwd(opts) {
|
|
21355
22096
|
const { cwd, renderer, reader } = opts;
|
|
21356
|
-
const cwdGit =
|
|
22097
|
+
const cwdGit = path39.join(cwd, ".git");
|
|
21357
22098
|
let hasCwdGit = false;
|
|
21358
22099
|
try {
|
|
21359
22100
|
await fsp5.access(cwdGit);
|
|
@@ -21370,7 +22111,7 @@ async function checkGitInCwd(opts) {
|
|
|
21370
22111
|
if (answer === "y" || answer === "yes") {
|
|
21371
22112
|
try {
|
|
21372
22113
|
const { spawn: spawn6 } = await import('child_process');
|
|
21373
|
-
await new Promise((
|
|
22114
|
+
await new Promise((resolve11, reject) => {
|
|
21374
22115
|
const child = spawn6("git", ["init"], {
|
|
21375
22116
|
cwd,
|
|
21376
22117
|
signal: AbortSignal.timeout(1e4),
|
|
@@ -21379,7 +22120,7 @@ async function checkGitInCwd(opts) {
|
|
|
21379
22120
|
child.on("error", reject);
|
|
21380
22121
|
child.on(
|
|
21381
22122
|
"close",
|
|
21382
|
-
(code) => code === 0 ?
|
|
22123
|
+
(code) => code === 0 ? resolve11() : reject(new Error(`git init failed with ${code}`))
|
|
21383
22124
|
);
|
|
21384
22125
|
});
|
|
21385
22126
|
renderer.write(` ${color.green("\u2713")} Git repository initialized
|
|
@@ -21387,16 +22128,16 @@ async function checkGitInCwd(opts) {
|
|
|
21387
22128
|
hasCwdGit = true;
|
|
21388
22129
|
} catch (err) {
|
|
21389
22130
|
renderer.writeError(
|
|
21390
|
-
`git init failed: ${
|
|
22131
|
+
`git init failed: ${toErrorMessage(err)}
|
|
21391
22132
|
`
|
|
21392
22133
|
);
|
|
21393
22134
|
}
|
|
21394
22135
|
}
|
|
21395
22136
|
}
|
|
21396
|
-
const parentDir =
|
|
22137
|
+
const parentDir = path39.dirname(cwd);
|
|
21397
22138
|
if (parentDir !== cwd) {
|
|
21398
22139
|
try {
|
|
21399
|
-
await fsp5.access(
|
|
22140
|
+
await fsp5.access(path39.join(parentDir, ".git"));
|
|
21400
22141
|
renderer.write(
|
|
21401
22142
|
` ${color.dim("\u2139")} A ${color.bold(".git")} repo exists in the parent directory: ${color.dim(parentDir)}
|
|
21402
22143
|
`
|
|
@@ -21408,7 +22149,7 @@ async function checkGitInCwd(opts) {
|
|
|
21408
22149
|
async function registerProjectAtBoot(opts) {
|
|
21409
22150
|
const { projectRoot, cwd, wpaths } = opts;
|
|
21410
22151
|
const manifest = await loadManifest(wpaths.globalConfig);
|
|
21411
|
-
const existed = manifest.projects.some((p) =>
|
|
22152
|
+
const existed = manifest.projects.some((p) => path39.resolve(p.root) === path39.resolve(projectRoot));
|
|
21412
22153
|
await touchProjectInManifest({
|
|
21413
22154
|
projectRoot,
|
|
21414
22155
|
globalConfigPath: wpaths.globalConfig,
|
|
@@ -21609,7 +22350,7 @@ function resolveBundledSkillsDir2() {
|
|
|
21609
22350
|
try {
|
|
21610
22351
|
const req2 = createRequire(import.meta.url);
|
|
21611
22352
|
const corePkg = req2.resolve("@wrongstack/core/package.json");
|
|
21612
|
-
return
|
|
22353
|
+
return path39.join(path39.dirname(corePkg), "skills");
|
|
21613
22354
|
} catch {
|
|
21614
22355
|
return void 0;
|
|
21615
22356
|
}
|
|
@@ -22097,7 +22838,7 @@ async function runRepl(opts) {
|
|
|
22097
22838
|
clientMailbox.registerClient({
|
|
22098
22839
|
clientId,
|
|
22099
22840
|
sessionId: replProjectRoot,
|
|
22100
|
-
name: `REPL [${
|
|
22841
|
+
name: `REPL [${path39.basename(replProjectRoot)}]`,
|
|
22101
22842
|
source: "repl",
|
|
22102
22843
|
pid: process.pid
|
|
22103
22844
|
}).then(() => {
|
|
@@ -22162,10 +22903,10 @@ async function runRepl(opts) {
|
|
|
22162
22903
|
}
|
|
22163
22904
|
} catch (err) {
|
|
22164
22905
|
opts.renderer.writeError(
|
|
22165
|
-
`[eternal] ${
|
|
22906
|
+
`[eternal] ${toErrorMessage(err)}`
|
|
22166
22907
|
);
|
|
22167
22908
|
}
|
|
22168
|
-
await new Promise((
|
|
22909
|
+
await new Promise((resolve11) => setTimeout(resolve11, 250));
|
|
22169
22910
|
continue;
|
|
22170
22911
|
}
|
|
22171
22912
|
} else if (opts.getAutonomy?.() === "eternal-parallel") {
|
|
@@ -22238,10 +22979,10 @@ async function runRepl(opts) {
|
|
|
22238
22979
|
}
|
|
22239
22980
|
} catch (err) {
|
|
22240
22981
|
opts.renderer.writeError(
|
|
22241
|
-
`[parallel] ${
|
|
22982
|
+
`[parallel] ${toErrorMessage(err)}`
|
|
22242
22983
|
);
|
|
22243
22984
|
}
|
|
22244
|
-
await new Promise((
|
|
22985
|
+
await new Promise((resolve11) => setTimeout(resolve11, 250));
|
|
22245
22986
|
continue;
|
|
22246
22987
|
}
|
|
22247
22988
|
}
|
|
@@ -22311,7 +23052,7 @@ ${lines.join("\n")}
|
|
|
22311
23052
|
if (res?.message) opts.renderer.write(`${res.message}
|
|
22312
23053
|
`);
|
|
22313
23054
|
} catch (err) {
|
|
22314
|
-
opts.renderer.writeError(
|
|
23055
|
+
opts.renderer.writeError(toErrorMessage(err));
|
|
22315
23056
|
}
|
|
22316
23057
|
continue;
|
|
22317
23058
|
}
|
|
@@ -22400,7 +23141,7 @@ ${color.dim(taskList2)}
|
|
|
22400
23141
|
}
|
|
22401
23142
|
}
|
|
22402
23143
|
} catch (err) {
|
|
22403
|
-
opts.renderer.writeError(
|
|
23144
|
+
opts.renderer.writeError(toErrorMessage(err));
|
|
22404
23145
|
}
|
|
22405
23146
|
continue;
|
|
22406
23147
|
}
|
|
@@ -22589,7 +23330,7 @@ ${color.dim(
|
|
|
22589
23330
|
}
|
|
22590
23331
|
} catch (err) {
|
|
22591
23332
|
opts.renderer.writeError(
|
|
22592
|
-
`[autonomy] ${
|
|
23333
|
+
`[autonomy] ${toErrorMessage(err)}`
|
|
22593
23334
|
);
|
|
22594
23335
|
} finally {
|
|
22595
23336
|
activeCtrl = void 0;
|
|
@@ -22660,7 +23401,7 @@ ${color.dim(lines)}
|
|
|
22660
23401
|
}
|
|
22661
23402
|
}
|
|
22662
23403
|
} catch (err) {
|
|
22663
|
-
opts.renderer.writeError(
|
|
23404
|
+
opts.renderer.writeError(toErrorMessage(err));
|
|
22664
23405
|
} finally {
|
|
22665
23406
|
activeCtrl = void 0;
|
|
22666
23407
|
}
|
|
@@ -22687,7 +23428,7 @@ async function pasteClipboardImage(builder, opts) {
|
|
|
22687
23428
|
`));
|
|
22688
23429
|
} catch (err) {
|
|
22689
23430
|
opts.renderer.writeError(
|
|
22690
|
-
`Clipboard image error: ${
|
|
23431
|
+
`Clipboard image error: ${toErrorMessage(err)}`
|
|
22691
23432
|
);
|
|
22692
23433
|
}
|
|
22693
23434
|
}
|
|
@@ -22725,7 +23466,7 @@ async function renderGoalBanner(opts) {
|
|
|
22725
23466
|
await opts.slashRegistry.dispatch("/autonomy eternal", opts.agent.ctx);
|
|
22726
23467
|
} catch (err) {
|
|
22727
23468
|
opts.renderer.writeError(
|
|
22728
|
-
`Auto-resume failed: ${
|
|
23469
|
+
`Auto-resume failed: ${toErrorMessage(err)}`
|
|
22729
23470
|
);
|
|
22730
23471
|
}
|
|
22731
23472
|
} else {
|
|
@@ -22808,12 +23549,12 @@ ${color.cyan("\u23F3 Auto")} ${color.dim("(Ctrl+C to cancel)")}
|
|
|
22808
23549
|
let interval;
|
|
22809
23550
|
let lastTickedSecond = sec + 1;
|
|
22810
23551
|
let onAbort;
|
|
22811
|
-
return new Promise((
|
|
22812
|
-
onAbort = () =>
|
|
23552
|
+
return new Promise((resolve11) => {
|
|
23553
|
+
onAbort = () => resolve11(false);
|
|
22813
23554
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
22814
23555
|
interval = setInterval(() => {
|
|
22815
23556
|
if (signal.aborted) {
|
|
22816
|
-
|
|
23557
|
+
resolve11(false);
|
|
22817
23558
|
return;
|
|
22818
23559
|
}
|
|
22819
23560
|
const elapsed = Date.now() - start;
|
|
@@ -22821,7 +23562,7 @@ ${color.cyan("\u23F3 Auto")} ${color.dim("(Ctrl+C to cancel)")}
|
|
|
22821
23562
|
if (remaining <= 0) {
|
|
22822
23563
|
opts.renderer.write(color.dim(` \u21B3 ${truncated}
|
|
22823
23564
|
`));
|
|
22824
|
-
|
|
23565
|
+
resolve11(true);
|
|
22825
23566
|
return;
|
|
22826
23567
|
}
|
|
22827
23568
|
if (opts.onCountdownTick && remaining !== lastTickedSecond) {
|
|
@@ -22832,7 +23573,7 @@ ${color.cyan("\u23F3 Auto")} ${color.dim("(Ctrl+C to cancel)")}
|
|
|
22832
23573
|
opts.renderer.write(
|
|
22833
23574
|
color.yellow(" \u21B3 Countdown cancelled \u2014 switching to manual mode\n")
|
|
22834
23575
|
);
|
|
22835
|
-
|
|
23576
|
+
resolve11(false);
|
|
22836
23577
|
return;
|
|
22837
23578
|
}
|
|
22838
23579
|
} catch {
|
|
@@ -22937,6 +23678,7 @@ async function execute(deps) {
|
|
|
22937
23678
|
effectiveMaxContext,
|
|
22938
23679
|
queueStore,
|
|
22939
23680
|
context,
|
|
23681
|
+
mailbox,
|
|
22940
23682
|
stats,
|
|
22941
23683
|
detachTodosCheckpoint,
|
|
22942
23684
|
savedProviderCfg,
|
|
@@ -22978,6 +23720,23 @@ async function execute(deps) {
|
|
|
22978
23720
|
restoredToolCalls,
|
|
22979
23721
|
needsSetup
|
|
22980
23722
|
} = deps;
|
|
23723
|
+
const rootTraceId = context.traceId;
|
|
23724
|
+
const storageLog = (event, payload) => {
|
|
23725
|
+
const traceId = payload.traceId ?? rootTraceId;
|
|
23726
|
+
console.warn(JSON.stringify({
|
|
23727
|
+
level: "info",
|
|
23728
|
+
event,
|
|
23729
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
23730
|
+
traceId,
|
|
23731
|
+
...payload
|
|
23732
|
+
}));
|
|
23733
|
+
};
|
|
23734
|
+
const onStorageRead = (...args) => storageLog("storage.read", args[0]);
|
|
23735
|
+
const onStorageWrite = (...args) => storageLog("storage.write", args[0]);
|
|
23736
|
+
const onStorageError = (...args) => storageLog("storage.error", args[0]);
|
|
23737
|
+
events.on("storage.read", onStorageRead);
|
|
23738
|
+
events.on("storage.write", onStorageWrite);
|
|
23739
|
+
events.on("storage.error", onStorageError);
|
|
22981
23740
|
let pendingChimeraWork;
|
|
22982
23741
|
events.onPattern("chimera.review_needed", (_event, payload) => {
|
|
22983
23742
|
const p = payload;
|
|
@@ -23009,8 +23768,8 @@ async function execute(deps) {
|
|
|
23009
23768
|
timeoutMs: 3e5
|
|
23010
23769
|
};
|
|
23011
23770
|
const subagentId = await dir.spawn(cfg);
|
|
23012
|
-
const { randomUUID:
|
|
23013
|
-
const taskId =
|
|
23771
|
+
const { randomUUID: randomUUID7 } = await import('crypto');
|
|
23772
|
+
const taskId = randomUUID7();
|
|
23014
23773
|
await dir.assign({
|
|
23015
23774
|
id: taskId,
|
|
23016
23775
|
description: taskDesc,
|
|
@@ -23223,6 +23982,76 @@ async function execute(deps) {
|
|
|
23223
23982
|
};
|
|
23224
23983
|
const PROJECT_SWITCH_EXIT_CODE = 42;
|
|
23225
23984
|
let pendingProjectSwitch = null;
|
|
23985
|
+
const coordinatorEvents = /* @__PURE__ */ new Set();
|
|
23986
|
+
let autonomousCoordinator = null;
|
|
23987
|
+
const onDirectorReady = (dir) => {
|
|
23988
|
+
if (autonomousCoordinator) return;
|
|
23989
|
+
const transcript = context.session.transcriptPath;
|
|
23990
|
+
const sessionDir = transcript ? path39.dirname(transcript) : wpaths.projectDir;
|
|
23991
|
+
const llmProvider = {
|
|
23992
|
+
decide: async (prompt) => {
|
|
23993
|
+
const sysPrompt = [
|
|
23994
|
+
{
|
|
23995
|
+
type: "text",
|
|
23996
|
+
text: 'You are the autonomous brain of a multi-agent coordination system. Pick the best option for the decision described and reply with JSON: {"optionId":"<id>","rationale":"<short why>"}.'
|
|
23997
|
+
}
|
|
23998
|
+
];
|
|
23999
|
+
const userPrompt = {
|
|
24000
|
+
type: "text",
|
|
24001
|
+
text: `Decision: ${prompt.question}
|
|
24002
|
+
|
|
24003
|
+
Context: ${prompt.context}
|
|
24004
|
+
|
|
24005
|
+
Options:
|
|
24006
|
+
${prompt.options.map((o, i) => ` ${i + 1}. [${o.id}] ${o.label}${o.consequence ? ` \u2014 ${o.consequence}` : ""}`).join("\n")}
|
|
24007
|
+
|
|
24008
|
+
Risk: ${prompt.risk}
|
|
24009
|
+
|
|
24010
|
+
Reply with ONLY the JSON object.`
|
|
24011
|
+
};
|
|
24012
|
+
const resp = await context.provider.complete(
|
|
24013
|
+
{
|
|
24014
|
+
model: context.model,
|
|
24015
|
+
system: sysPrompt,
|
|
24016
|
+
messages: [
|
|
24017
|
+
{
|
|
24018
|
+
role: "user",
|
|
24019
|
+
content: [userPrompt]
|
|
24020
|
+
}
|
|
24021
|
+
],
|
|
24022
|
+
maxTokens: 1024,
|
|
24023
|
+
temperature: 0
|
|
24024
|
+
},
|
|
24025
|
+
{ signal: context.signal }
|
|
24026
|
+
);
|
|
24027
|
+
const text = resp.content.filter((b) => b.type === "text").map((b) => b.text).join("\n").trim();
|
|
24028
|
+
const cleaned = text.replace(/^```(?:json)?\s*/i, "").replace(/```$/, "").trim();
|
|
24029
|
+
try {
|
|
24030
|
+
const parsed = JSON.parse(cleaned);
|
|
24031
|
+
const optId = parsed.optionId ?? prompt.options[0]?.id ?? "";
|
|
24032
|
+
return { optionId: optId, rationale: parsed.rationale ?? "" };
|
|
24033
|
+
} catch {
|
|
24034
|
+
return { optionId: prompt.options[0]?.id ?? "", rationale: text };
|
|
24035
|
+
}
|
|
24036
|
+
}
|
|
24037
|
+
};
|
|
24038
|
+
autonomousCoordinator = new AutonomousCoordinator({
|
|
24039
|
+
sessionDir,
|
|
24040
|
+
fleet: dir.fleet,
|
|
24041
|
+
mailbox,
|
|
24042
|
+
selfAgentId: `leader@${context.session.id ?? "unknown"}`,
|
|
24043
|
+
selfAgentName: "Leader",
|
|
24044
|
+
llmProvider
|
|
24045
|
+
});
|
|
24046
|
+
};
|
|
24047
|
+
if (director) onDirectorReady(director);
|
|
24048
|
+
const offDirectorSpawned = events.onPattern("subagent.spawned", () => {
|
|
24049
|
+
const dir = director;
|
|
24050
|
+
if (dir) {
|
|
24051
|
+
offDirectorSpawned();
|
|
24052
|
+
onDirectorReady(dir);
|
|
24053
|
+
}
|
|
24054
|
+
});
|
|
23226
24055
|
try {
|
|
23227
24056
|
code = await runTui({
|
|
23228
24057
|
agent,
|
|
@@ -23307,6 +24136,7 @@ async function execute(deps) {
|
|
|
23307
24136
|
featureSkills: cfg.features?.skills !== false,
|
|
23308
24137
|
featureModelsRegistry: cfg.features?.modelsRegistry !== false,
|
|
23309
24138
|
featureTokenSaving: cfg.features?.tokenSavingMode ?? false,
|
|
24139
|
+
allowOutsideProjectRoot: cfg.features?.allowOutsideProjectRoot ?? true,
|
|
23310
24140
|
contextAutoCompact: cfg.context?.autoCompact !== false,
|
|
23311
24141
|
contextStrategy: cfg.context?.strategy ?? "hybrid",
|
|
23312
24142
|
logLevel: cfg.log?.level ?? "info",
|
|
@@ -23345,9 +24175,11 @@ async function execute(deps) {
|
|
|
23345
24175
|
if (s.enhanceEnabled !== void 0) a["enhance"] = s.enhanceEnabled;
|
|
23346
24176
|
if (s.enhanceLanguage !== void 0) a["enhanceLanguage"] = s.enhanceLanguage;
|
|
23347
24177
|
if (s.autonomyNextPrompt !== void 0) a["autonomyNextPrompt"] = s.autonomyNextPrompt;
|
|
24178
|
+
if (s.autoProceedMaxIterations !== void 0)
|
|
24179
|
+
a["autoProceedMaxIterations"] = s.autoProceedMaxIterations;
|
|
23348
24180
|
}
|
|
23349
24181
|
);
|
|
23350
|
-
if (s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0 || s.contextAutoCompact !== void 0 || s.contextStrategy !== void 0 || s.logLevel !== void 0 || s.auditLevel !== void 0 || s.indexOnStart !== void 0 || s.maxIterations !== void 0 || s.nextPrediction !== void 0 || s.debugStream !== void 0 || s.configScope !== void 0 || s.enhanceDelayMs !== void 0) {
|
|
24182
|
+
if (s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0 || s.featureTokenSaving !== void 0 || s.allowOutsideProjectRoot !== void 0 || s.contextAutoCompact !== void 0 || s.contextStrategy !== void 0 || s.logLevel !== void 0 || s.auditLevel !== void 0 || s.indexOnStart !== void 0 || s.maxIterations !== void 0 || s.nextPrediction !== void 0 || s.debugStream !== void 0 || s.configScope !== void 0 || s.enhanceDelayMs !== void 0) {
|
|
23351
24183
|
const configScope = s.configScope ?? configStore.get().configScope ?? "global";
|
|
23352
24184
|
const targetPath = configScope === "project" && wpaths.inProjectConfig ? wpaths.inProjectConfig : wpaths.globalConfig;
|
|
23353
24185
|
let raw;
|
|
@@ -23364,7 +24196,7 @@ async function execute(deps) {
|
|
|
23364
24196
|
if (s.nextPrediction !== void 0) {
|
|
23365
24197
|
decrypted.nextPrediction = s.nextPrediction;
|
|
23366
24198
|
}
|
|
23367
|
-
if (s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0 || s.featureTokenSaving !== void 0) {
|
|
24199
|
+
if (s.featureMcp !== void 0 || s.featurePlugins !== void 0 || s.featureMemory !== void 0 || s.featureSkills !== void 0 || s.featureModelsRegistry !== void 0 || s.featureTokenSaving !== void 0 || s.allowOutsideProjectRoot !== void 0) {
|
|
23368
24200
|
const feats = decrypted.features ?? {};
|
|
23369
24201
|
if (s.featureMcp !== void 0) feats.mcp = s.featureMcp;
|
|
23370
24202
|
if (s.featurePlugins !== void 0) feats.plugins = s.featurePlugins;
|
|
@@ -23374,6 +24206,8 @@ async function execute(deps) {
|
|
|
23374
24206
|
feats.modelsRegistry = s.featureModelsRegistry;
|
|
23375
24207
|
if (s.featureTokenSaving !== void 0)
|
|
23376
24208
|
feats.tokenSavingMode = s.featureTokenSaving;
|
|
24209
|
+
if (s.allowOutsideProjectRoot !== void 0)
|
|
24210
|
+
feats.allowOutsideProjectRoot = s.allowOutsideProjectRoot;
|
|
23377
24211
|
decrypted.features = feats;
|
|
23378
24212
|
}
|
|
23379
24213
|
if (s.contextAutoCompact !== void 0 || s.contextStrategy !== void 0) {
|
|
@@ -23423,7 +24257,7 @@ async function execute(deps) {
|
|
|
23423
24257
|
const toWrite = targetPath === wpaths.globalConfig ? decrypted : filterSafeForProject(decrypted);
|
|
23424
24258
|
const encrypted = encryptConfigSecrets(toWrite, noOpVault);
|
|
23425
24259
|
if (targetPath !== wpaths.globalConfig) {
|
|
23426
|
-
await fsp5.mkdir(
|
|
24260
|
+
await fsp5.mkdir(path39.dirname(targetPath), { recursive: true });
|
|
23427
24261
|
}
|
|
23428
24262
|
await atomicWrite(targetPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
|
|
23429
24263
|
configStore.update({
|
|
@@ -23459,6 +24293,7 @@ async function execute(deps) {
|
|
|
23459
24293
|
if (s.streamFleet !== void 0) {
|
|
23460
24294
|
fleetStreamController?.setEnabled(s.streamFleet);
|
|
23461
24295
|
}
|
|
24296
|
+
deps.applyLiveSettings?.(s);
|
|
23462
24297
|
return null;
|
|
23463
24298
|
} catch (err) {
|
|
23464
24299
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -23483,6 +24318,17 @@ async function execute(deps) {
|
|
|
23483
24318
|
confirmExit: config.autonomy?.["confirmExit"] ?? true,
|
|
23484
24319
|
director,
|
|
23485
24320
|
fleetRoster,
|
|
24321
|
+
// ── AutonomousCoordinator: project-level multi-session coordination ─────────
|
|
24322
|
+
// The coordinator tracks goals, tasks, knowledge, and consensus across all
|
|
24323
|
+
// active sessions in the same project. It runs independently of the leader
|
|
24324
|
+
// agent and is accessible to any session in the project via the GlobalMailbox.
|
|
24325
|
+
getAutonomousCoordinator: () => autonomousCoordinator,
|
|
24326
|
+
subscribeCoordinatorEvents: (fn) => {
|
|
24327
|
+
coordinatorEvents.add(fn);
|
|
24328
|
+
return () => {
|
|
24329
|
+
coordinatorEvents.delete(fn);
|
|
24330
|
+
};
|
|
24331
|
+
},
|
|
23486
24332
|
// /clear: signal the TUI to wipe entries and reset fleet/leader stats
|
|
23487
24333
|
// AND bump the context chip version — so the display reflects a
|
|
23488
24334
|
// completely fresh session after the backend has been cleared.
|
|
@@ -23500,7 +24346,7 @@ async function execute(deps) {
|
|
|
23500
24346
|
agentsMonitorController,
|
|
23501
24347
|
getLiveSessions: async () => {
|
|
23502
24348
|
const { SessionRegistry } = await import('@wrongstack/core');
|
|
23503
|
-
const globalRoot =
|
|
24349
|
+
const globalRoot = path39.dirname(wpaths.globalConfig);
|
|
23504
24350
|
const registry = new SessionRegistry(globalRoot);
|
|
23505
24351
|
const sessions = await registry.list();
|
|
23506
24352
|
return sessions.filter((s) => s.status !== "stale").map((s) => ({
|
|
@@ -23629,7 +24475,7 @@ async function execute(deps) {
|
|
|
23629
24475
|
if (!sessionStore) return null;
|
|
23630
24476
|
try {
|
|
23631
24477
|
const { SessionRegistry } = await import('@wrongstack/core');
|
|
23632
|
-
const registry = new SessionRegistry(
|
|
24478
|
+
const registry = new SessionRegistry(path39.dirname(wpaths.globalConfig));
|
|
23633
24479
|
const live = (await registry.list()).find(
|
|
23634
24480
|
(s) => s.sessionId === sessionId && s.status !== "stale" && s.pid !== process.pid
|
|
23635
24481
|
);
|
|
@@ -23767,7 +24613,7 @@ async function execute(deps) {
|
|
|
23767
24613
|
if (slug === "new-session") {
|
|
23768
24614
|
pendingProjectSwitch = {
|
|
23769
24615
|
root: projectRoot,
|
|
23770
|
-
name:
|
|
24616
|
+
name: path39.basename(projectRoot) || projectRoot
|
|
23771
24617
|
};
|
|
23772
24618
|
}
|
|
23773
24619
|
return;
|
|
@@ -23828,13 +24674,13 @@ ${parts.join("\n")}
|
|
|
23828
24674
|
const { root, name, resumeSessionId } = pendingProjectSwitch;
|
|
23829
24675
|
process.stdout.write("\x1B[2J\x1B[H");
|
|
23830
24676
|
const { spawn: spawn6 } = await import('child_process');
|
|
23831
|
-
const { createRequire:
|
|
24677
|
+
const { createRequire: createRequire8 } = await import('module');
|
|
23832
24678
|
let cliPath;
|
|
23833
24679
|
try {
|
|
23834
|
-
const req2 =
|
|
24680
|
+
const req2 = createRequire8(import.meta.url);
|
|
23835
24681
|
const pkgPath = req2.resolve("@wrongstack/cli/package.json");
|
|
23836
|
-
const pkgDir =
|
|
23837
|
-
cliPath =
|
|
24682
|
+
const pkgDir = path39.dirname(pkgPath);
|
|
24683
|
+
cliPath = path39.join(pkgDir, "dist", "index.js");
|
|
23838
24684
|
await fsp5.access(cliPath);
|
|
23839
24685
|
} catch {
|
|
23840
24686
|
cliPath = process.argv[1] ?? "";
|
|
@@ -23866,6 +24712,7 @@ ${parts.join("\n")}
|
|
|
23866
24712
|
}
|
|
23867
24713
|
} finally {
|
|
23868
24714
|
renderer.setSilent(false);
|
|
24715
|
+
offDirectorSpawned();
|
|
23869
24716
|
}
|
|
23870
24717
|
} else if (flags.webui) {
|
|
23871
24718
|
agent.disableInteractiveConfirmation();
|
|
@@ -23908,12 +24755,12 @@ ${parts.join("\n")}
|
|
|
23908
24755
|
)
|
|
23909
24756
|
);
|
|
23910
24757
|
renderer.writeInfo(color.dim(" Press Ctrl+C in this terminal to stop the WebUI server.\n"));
|
|
23911
|
-
const webuiExit = new Promise((
|
|
24758
|
+
const webuiExit = new Promise((resolve11) => {
|
|
23912
24759
|
const onSigint = () => {
|
|
23913
24760
|
renderer.setSilent(false);
|
|
23914
24761
|
renderer.write("\n");
|
|
23915
24762
|
renderer.writeInfo(color.yellow(" Shutting down WebUI server\u2026"));
|
|
23916
|
-
|
|
24763
|
+
resolve11(0);
|
|
23917
24764
|
};
|
|
23918
24765
|
process.on("SIGINT", onSigint);
|
|
23919
24766
|
process.on("SIGTERM", onSigint);
|
|
@@ -23921,13 +24768,13 @@ ${parts.join("\n")}
|
|
|
23921
24768
|
renderer.setSilent(false);
|
|
23922
24769
|
process.off("SIGINT", onSigint);
|
|
23923
24770
|
process.off("SIGTERM", onSigint);
|
|
23924
|
-
|
|
24771
|
+
resolve11(0);
|
|
23925
24772
|
}).catch((err) => {
|
|
23926
24773
|
renderer.setSilent(false);
|
|
23927
24774
|
process.off("SIGINT", onSigint);
|
|
23928
24775
|
process.off("SIGTERM", onSigint);
|
|
23929
24776
|
console.debug(`[execution] webui error: ${err}`);
|
|
23930
|
-
|
|
24777
|
+
resolve11(1);
|
|
23931
24778
|
});
|
|
23932
24779
|
});
|
|
23933
24780
|
code = await webuiExit;
|
|
@@ -23942,7 +24789,7 @@ ${parts.join("\n")}
|
|
|
23942
24789
|
supportsVision,
|
|
23943
24790
|
attachments,
|
|
23944
24791
|
effectiveMaxContext,
|
|
23945
|
-
projectName:
|
|
24792
|
+
projectName: path39.basename(projectRoot) || void 0,
|
|
23946
24793
|
getAutonomy,
|
|
23947
24794
|
onAutonomy,
|
|
23948
24795
|
getNextPredict,
|
|
@@ -24155,7 +25002,8 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
24155
25002
|
if (this.opts.sessionsRoot && !this.sessionFactory) {
|
|
24156
25003
|
this.sessionFactory = makeDirectorSessionFactory({
|
|
24157
25004
|
sessionsRoot: this.opts.sessionsRoot,
|
|
24158
|
-
directorRunId: this.opts.directorRunId
|
|
25005
|
+
directorRunId: this.opts.directorRunId,
|
|
25006
|
+
traceId: this.opts.traceId
|
|
24159
25007
|
});
|
|
24160
25008
|
}
|
|
24161
25009
|
const coordinatorConfig = {
|
|
@@ -24163,7 +25011,7 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
24163
25011
|
doneCondition: { type: "all_tasks_done" },
|
|
24164
25012
|
maxConcurrent: this.opts.maxConcurrent ?? 4
|
|
24165
25013
|
};
|
|
24166
|
-
const defaultScratchpad = this.opts.sharedScratchpadPath || (this.opts.sessionsRoot && this.opts.directorRunId ?
|
|
25014
|
+
const defaultScratchpad = this.opts.sharedScratchpadPath || (this.opts.sessionsRoot && this.opts.directorRunId ? path39.join(this.opts.sessionsRoot, this.opts.directorRunId, "shared") : void 0);
|
|
24167
25015
|
this.director = new Director({
|
|
24168
25016
|
config: coordinatorConfig,
|
|
24169
25017
|
manifestPath: this.opts.manifestPath,
|
|
@@ -24720,16 +25568,16 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
24720
25568
|
if (this.director) return this.director;
|
|
24721
25569
|
this.opts.directorMode = true;
|
|
24722
25570
|
if (this.opts.fleetRoot && !this.opts.manifestPath) {
|
|
24723
|
-
this.opts.manifestPath =
|
|
25571
|
+
this.opts.manifestPath = path39.join(this.opts.fleetRoot, "fleet.json");
|
|
24724
25572
|
}
|
|
24725
25573
|
if (this.opts.fleetRoot && !this.opts.sharedScratchpadPath) {
|
|
24726
|
-
this.opts.sharedScratchpadPath =
|
|
25574
|
+
this.opts.sharedScratchpadPath = path39.join(this.opts.fleetRoot, "shared");
|
|
24727
25575
|
}
|
|
24728
25576
|
if (this.opts.fleetRoot && !this.opts.sessionsRoot) {
|
|
24729
|
-
this.opts.sessionsRoot =
|
|
25577
|
+
this.opts.sessionsRoot = path39.join(this.opts.fleetRoot, "subagents");
|
|
24730
25578
|
}
|
|
24731
25579
|
if (this.opts.fleetRoot && !this.opts.stateCheckpointPath) {
|
|
24732
|
-
this.opts.stateCheckpointPath =
|
|
25580
|
+
this.opts.stateCheckpointPath = path39.join(this.opts.fleetRoot, "director-state.json");
|
|
24733
25581
|
}
|
|
24734
25582
|
await this.ensureDirector();
|
|
24735
25583
|
return this.director ?? null;
|
|
@@ -24858,11 +25706,11 @@ var SessionStats = class {
|
|
|
24858
25706
|
if (tool.name === "bash") this.bashCommands++;
|
|
24859
25707
|
else if (tool.name === "fetch") this.fetches++;
|
|
24860
25708
|
if (!tool.ok) return;
|
|
24861
|
-
const
|
|
24862
|
-
if (tool.name === "read" &&
|
|
24863
|
-
else if (tool.name === "edit" &&
|
|
24864
|
-
else if (tool.name === "write" &&
|
|
24865
|
-
this.writtenPaths.add(
|
|
25709
|
+
const path40 = typeof input?.path === "string" ? input.path : void 0;
|
|
25710
|
+
if (tool.name === "read" && path40) this.readPaths.add(path40);
|
|
25711
|
+
else if (tool.name === "edit" && path40) this.editedPaths.add(path40);
|
|
25712
|
+
else if (tool.name === "write" && path40) {
|
|
25713
|
+
this.writtenPaths.add(path40);
|
|
24866
25714
|
const content = typeof input?.content === "string" ? input.content : "";
|
|
24867
25715
|
this.bytesWritten += Buffer.byteLength(content, "utf8");
|
|
24868
25716
|
}
|
|
@@ -25174,7 +26022,7 @@ async function setupCodebaseIndexing(deps) {
|
|
|
25174
26022
|
if (tool?.mutating && FILE_EDIT_TOOLS.has(tool.name)) {
|
|
25175
26023
|
const fp = payload.toolUse.input?.file_path;
|
|
25176
26024
|
if (typeof fp === "string" && fp.length > 0) {
|
|
25177
|
-
const abs =
|
|
26025
|
+
const abs = path39.resolve(payload.ctx.cwd, fp);
|
|
25178
26026
|
if (isIndexableFile(abs)) {
|
|
25179
26027
|
enqueueReindex({ projectRoot, files: [abs], debounceMs, onError });
|
|
25180
26028
|
}
|
|
@@ -25193,7 +26041,7 @@ async function setupCodebaseIndexing(deps) {
|
|
|
25193
26041
|
if (!filename) return;
|
|
25194
26042
|
const rel = filename.toString();
|
|
25195
26043
|
if (isIgnored(rel)) return;
|
|
25196
|
-
const abs =
|
|
26044
|
+
const abs = path39.resolve(projectRoot, rel);
|
|
25197
26045
|
if (!isIndexableFile(abs)) return;
|
|
25198
26046
|
enqueueReindex({ projectRoot, files: [abs], debounceMs, onError });
|
|
25199
26047
|
});
|
|
@@ -25247,7 +26095,7 @@ function setupMetrics(params) {
|
|
|
25247
26095
|
const dumpMetrics = () => {
|
|
25248
26096
|
if (!metricsSink) return;
|
|
25249
26097
|
try {
|
|
25250
|
-
const out =
|
|
26098
|
+
const out = path39.join(wpaths.projectSessions, "metrics.json");
|
|
25251
26099
|
const snap = metricsSink.snapshot();
|
|
25252
26100
|
writeFileSync(out, JSON.stringify(snap, null, 2));
|
|
25253
26101
|
} catch {
|
|
@@ -25271,7 +26119,7 @@ function setupMetrics(params) {
|
|
|
25271
26119
|
});
|
|
25272
26120
|
} catch (err) {
|
|
25273
26121
|
logger.warn(
|
|
25274
|
-
`metrics endpoint failed to start: ${
|
|
26122
|
+
`metrics endpoint failed to start: ${toErrorMessage(err)}`
|
|
25275
26123
|
);
|
|
25276
26124
|
}
|
|
25277
26125
|
}
|
|
@@ -25327,9 +26175,11 @@ async function setupCompaction(params) {
|
|
|
25327
26175
|
context.meta["contextWindowMode"] = initialPolicy.id;
|
|
25328
26176
|
context.meta["contextWindowPolicy"] = initialPolicy;
|
|
25329
26177
|
let autoCompactor;
|
|
25330
|
-
|
|
26178
|
+
let resolvedBridge;
|
|
26179
|
+
if (effectiveMaxContext > 0) {
|
|
25331
26180
|
const auditLevel = resolveAuditLevel(fullConfig ?? config);
|
|
25332
26181
|
const sessionBridge = providedBridge ?? createSessionEventBridge(sessionWriter, auditLevel);
|
|
26182
|
+
resolvedBridge = sessionBridge;
|
|
25333
26183
|
autoCompactor = new AutoCompactionMiddleware(
|
|
25334
26184
|
compactor,
|
|
25335
26185
|
effectiveMaxContext,
|
|
@@ -25353,9 +26203,10 @@ async function setupCompaction(params) {
|
|
|
25353
26203
|
sessionBridge
|
|
25354
26204
|
}
|
|
25355
26205
|
);
|
|
26206
|
+
autoCompactor.setEnabled(config.context.autoCompact !== false);
|
|
25356
26207
|
pipelines.contextWindow.use({ name: "AutoCompaction", handler: autoCompactor.handler() });
|
|
25357
26208
|
}
|
|
25358
|
-
return { effectiveMaxContext, autoCompactor };
|
|
26209
|
+
return { effectiveMaxContext, autoCompactor, sessionBridge: resolvedBridge };
|
|
25359
26210
|
}
|
|
25360
26211
|
function createAgent(params) {
|
|
25361
26212
|
const secretScrubber = params.container.resolve(TOKENS.SecretScrubber);
|
|
@@ -25425,7 +26276,9 @@ async function setupSession(params) {
|
|
|
25425
26276
|
tokenCounter,
|
|
25426
26277
|
renderer,
|
|
25427
26278
|
flags,
|
|
25428
|
-
onRecovery
|
|
26279
|
+
onRecovery,
|
|
26280
|
+
// Optional EventBus for storage observability
|
|
26281
|
+
events: eventsBus
|
|
25429
26282
|
} = params;
|
|
25430
26283
|
sessionStore.prune(DEFAULT_SESSION_PRUNE_DAYS).then((count) => {
|
|
25431
26284
|
if (count > 0) renderer.writeInfo(`Pruned ${count} old session${count === 1 ? "" : "s"}.`);
|
|
@@ -25459,7 +26312,7 @@ async function setupSession(params) {
|
|
|
25459
26312
|
`Resumed session ${resumed.data.metadata.id} \u2014 ${restoredMessages.length} messages, ${restoredToolCalls.length} tool executions, ${resumed.data.usage.input + resumed.data.usage.output} tokens used previously.`
|
|
25460
26313
|
);
|
|
25461
26314
|
} catch (err) {
|
|
25462
|
-
renderer.writeError(`Resume failed: ${
|
|
26315
|
+
renderer.writeError(`Resume failed: ${toErrorMessage(err)}`);
|
|
25463
26316
|
throw Object.assign(new Error("RESUME_FAILED"), { exitCode: 2 });
|
|
25464
26317
|
}
|
|
25465
26318
|
} else {
|
|
@@ -25482,10 +26335,10 @@ async function setupSession(params) {
|
|
|
25482
26335
|
);
|
|
25483
26336
|
});
|
|
25484
26337
|
const attachments = new DefaultAttachmentStore({
|
|
25485
|
-
spoolDir:
|
|
26338
|
+
spoolDir: path39.join(wpaths.projectSessions, session?.id, "attachments")
|
|
25486
26339
|
});
|
|
25487
|
-
const queueStore = new QueueStore({ dir: path38.join(wpaths.projectSessions, session?.id) });
|
|
25488
26340
|
const ctxSignal = new AbortController().signal;
|
|
26341
|
+
const traceId = randomBytes(16).toString("hex");
|
|
25489
26342
|
const context = new Context({
|
|
25490
26343
|
systemPrompt,
|
|
25491
26344
|
provider,
|
|
@@ -25496,17 +26349,28 @@ async function setupSession(params) {
|
|
|
25496
26349
|
projectRoot,
|
|
25497
26350
|
model: config.model,
|
|
25498
26351
|
agentId: "leader",
|
|
25499
|
-
agentName: "Leader Agent"
|
|
26352
|
+
agentName: "Leader Agent",
|
|
26353
|
+
traceId,
|
|
26354
|
+
allowOutsideProjectRoot: config.features?.allowOutsideProjectRoot ?? true
|
|
25500
26355
|
});
|
|
25501
26356
|
context.meta["packageTrackerOpts"] = {
|
|
25502
26357
|
storageDir: wpaths.projectDir,
|
|
25503
26358
|
projectRoot
|
|
25504
26359
|
};
|
|
25505
26360
|
if (restoredMessages.length > 0) context.state.replaceMessages(restoredMessages);
|
|
25506
|
-
const
|
|
26361
|
+
const queueStore = new QueueStore({
|
|
26362
|
+
dir: path39.join(wpaths.projectSessions, session?.id),
|
|
26363
|
+
...eventsBus ? { events: eventsBus } : {},
|
|
26364
|
+
...traceId ? { traceId } : {}
|
|
26365
|
+
});
|
|
26366
|
+
const todosCheckpointPath = path39.join(wpaths.projectSessions, `${session?.id}.todos.json`);
|
|
25507
26367
|
if (resumeId) {
|
|
25508
26368
|
try {
|
|
25509
|
-
const restoredTodos = await loadTodosCheckpoint(
|
|
26369
|
+
const restoredTodos = await loadTodosCheckpoint(
|
|
26370
|
+
todosCheckpointPath,
|
|
26371
|
+
eventsBus,
|
|
26372
|
+
traceId
|
|
26373
|
+
);
|
|
25510
26374
|
if (restoredTodos && restoredTodos.length > 0) {
|
|
25511
26375
|
context.state.replaceTodos(restoredTodos);
|
|
25512
26376
|
renderer.writeInfo(
|
|
@@ -25519,17 +26383,19 @@ async function setupSession(params) {
|
|
|
25519
26383
|
const detachTodosCheckpoint = attachTodosCheckpoint(
|
|
25520
26384
|
context.state,
|
|
25521
26385
|
todosCheckpointPath,
|
|
25522
|
-
session?.id
|
|
26386
|
+
session?.id,
|
|
26387
|
+
eventsBus,
|
|
26388
|
+
traceId
|
|
25523
26389
|
);
|
|
25524
|
-
const planPath =
|
|
26390
|
+
const planPath = path39.join(wpaths.projectSessions, `${session?.id}.plan.json`);
|
|
25525
26391
|
context.state.setMeta("plan.path", planPath);
|
|
25526
|
-
const taskPath =
|
|
26392
|
+
const taskPath = path39.join(wpaths.projectSessions, `${session?.id}.tasks.json`);
|
|
25527
26393
|
context.state.setMeta("task.path", taskPath);
|
|
25528
26394
|
let dirState;
|
|
25529
26395
|
if (resumeId) {
|
|
25530
26396
|
try {
|
|
25531
|
-
const fleetRoot =
|
|
25532
|
-
dirState = await loadDirectorState(
|
|
26397
|
+
const fleetRoot = path39.join(wpaths.projectSessions, session?.id);
|
|
26398
|
+
dirState = await loadDirectorState(path39.join(fleetRoot, "director-state.json"));
|
|
25533
26399
|
if (dirState) {
|
|
25534
26400
|
const tCounts = {};
|
|
25535
26401
|
for (const t of dirState.tasks) tCounts[t.status] = (tCounts[t.status] ?? 0) + 1;
|
|
@@ -25555,6 +26421,7 @@ async function setupSession(params) {
|
|
|
25555
26421
|
return {
|
|
25556
26422
|
session: expectDefined(session),
|
|
25557
26423
|
sessionRef,
|
|
26424
|
+
traceId,
|
|
25558
26425
|
context,
|
|
25559
26426
|
restoredMessages,
|
|
25560
26427
|
attachments,
|
|
@@ -25775,7 +26642,7 @@ async function main(argv) {
|
|
|
25775
26642
|
projectGoal: wpaths.projectGoal,
|
|
25776
26643
|
projectSessions: wpaths.projectSessions
|
|
25777
26644
|
},
|
|
25778
|
-
pathJoiner: { join: (a, b) =>
|
|
26645
|
+
pathJoiner: { join: (a, b) => path39.join(a, b) },
|
|
25779
26646
|
systemPromptBuilderToken: TOKENS.SystemPromptBuilder
|
|
25780
26647
|
});
|
|
25781
26648
|
const toolRegistry = new ToolRegistry();
|
|
@@ -25804,7 +26671,10 @@ async function main(argv) {
|
|
|
25804
26671
|
const evOn = (event, handler) => {
|
|
25805
26672
|
events.on(event, handler);
|
|
25806
26673
|
teardownHandlers.push(
|
|
25807
|
-
() =>
|
|
26674
|
+
() => (
|
|
26675
|
+
// biome-ignore lint/suspicious/noExplicitAny: dynamic event dispatcher signature
|
|
26676
|
+
events.off(event, handler)
|
|
26677
|
+
)
|
|
25808
26678
|
);
|
|
25809
26679
|
};
|
|
25810
26680
|
evOn("provider.response", (e) => {
|
|
@@ -25895,6 +26765,7 @@ async function main(argv) {
|
|
|
25895
26765
|
tokenCounter,
|
|
25896
26766
|
renderer,
|
|
25897
26767
|
flags,
|
|
26768
|
+
events,
|
|
25898
26769
|
onRecovery: (abandoned, autoRecover) => promptRecovery(reader, renderer, abandoned, autoRecover)
|
|
25899
26770
|
});
|
|
25900
26771
|
const session = sessResult.session;
|
|
@@ -25906,12 +26777,13 @@ async function main(argv) {
|
|
|
25906
26777
|
const planPath = sessResult.planPath;
|
|
25907
26778
|
const detachTodosCheckpoint = sessResult.detachTodosCheckpoint;
|
|
25908
26779
|
const priorFleetState = sessResult.priorFleetState;
|
|
26780
|
+
memoryStore.withTraceId(sessResult.traceId);
|
|
25909
26781
|
let tracker;
|
|
25910
26782
|
try {
|
|
25911
26783
|
const { getSessionRegistry, AgentStatusTracker } = await import('@wrongstack/core');
|
|
25912
26784
|
const registry = getSessionRegistry(wpaths.globalRoot);
|
|
25913
|
-
const projectSlug2 =
|
|
25914
|
-
const projectName =
|
|
26785
|
+
const projectSlug2 = path39.basename(wpaths.projectDir);
|
|
26786
|
+
const projectName = path39.basename(projectRoot);
|
|
25915
26787
|
let gitBranch;
|
|
25916
26788
|
try {
|
|
25917
26789
|
const { execSync } = await import('child_process');
|
|
@@ -26010,7 +26882,7 @@ async function main(argv) {
|
|
|
26010
26882
|
if (e.ok && (e.name === "write" || e.name === "edit" || e.name === "replace" || e.name === "patch")) {
|
|
26011
26883
|
const filePath = e.input?.path;
|
|
26012
26884
|
if (filePath) {
|
|
26013
|
-
const projectDir =
|
|
26885
|
+
const projectDir = path39.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
|
|
26014
26886
|
void recordFileAction(
|
|
26015
26887
|
{ storageDir: projectDir, projectRoot },
|
|
26016
26888
|
{
|
|
@@ -26176,7 +27048,7 @@ async function main(argv) {
|
|
|
26176
27048
|
let depWatcherDispose;
|
|
26177
27049
|
if (dwCfg?.["enabled"] === true) {
|
|
26178
27050
|
try {
|
|
26179
|
-
const projectDir =
|
|
27051
|
+
const projectDir = path39.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
|
|
26180
27052
|
const dwMailbox = new GlobalMailbox(projectDir, events);
|
|
26181
27053
|
depWatcherDispose = attachDepWatcherBridge({
|
|
26182
27054
|
events,
|
|
@@ -26275,12 +27147,12 @@ async function main(argv) {
|
|
|
26275
27147
|
}
|
|
26276
27148
|
}
|
|
26277
27149
|
};
|
|
26278
|
-
const fleetRoot = directorMode ?
|
|
26279
|
-
const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] :
|
|
26280
|
-
const sharedScratchpadPath = directorMode ?
|
|
26281
|
-
const subagentSessionsRoot = directorMode ?
|
|
26282
|
-
const stateCheckpointPath = directorMode ?
|
|
26283
|
-
const fleetRootForPromotion =
|
|
27150
|
+
const fleetRoot = directorMode ? path39.join(wpaths.projectSessions, session.id) : void 0;
|
|
27151
|
+
const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] : path39.join(expectDefined(fleetRoot), "fleet.json") : void 0;
|
|
27152
|
+
const sharedScratchpadPath = directorMode ? path39.join(expectDefined(fleetRoot), "shared") : void 0;
|
|
27153
|
+
const subagentSessionsRoot = directorMode ? path39.join(expectDefined(fleetRoot), "subagents") : void 0;
|
|
27154
|
+
const stateCheckpointPath = directorMode ? path39.join(expectDefined(fleetRoot), "director-state.json") : void 0;
|
|
27155
|
+
const fleetRootForPromotion = path39.join(wpaths.projectSessions, session.id);
|
|
26284
27156
|
const brainSettings = {
|
|
26285
27157
|
maxAutoRisk: "medium"
|
|
26286
27158
|
};
|
|
@@ -26385,7 +27257,8 @@ async function main(argv) {
|
|
|
26385
27257
|
sessionWriter: session,
|
|
26386
27258
|
maxConcurrent,
|
|
26387
27259
|
getLeaderMaxContext: () => effectiveMaxContext,
|
|
26388
|
-
brain
|
|
27260
|
+
brain,
|
|
27261
|
+
traceId: sessResult.traceId
|
|
26389
27262
|
}
|
|
26390
27263
|
);
|
|
26391
27264
|
toolRegistry.register(
|
|
@@ -26422,7 +27295,7 @@ async function main(argv) {
|
|
|
26422
27295
|
let techStackConsumerDispose;
|
|
26423
27296
|
if (dwCfg?.["enabled"] === true) {
|
|
26424
27297
|
try {
|
|
26425
|
-
const projectDir =
|
|
27298
|
+
const projectDir = path39.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
|
|
26426
27299
|
const tsMailbox = new GlobalMailbox(projectDir, events);
|
|
26427
27300
|
const fileAuthorOpts = {
|
|
26428
27301
|
storageDir: projectDir,
|
|
@@ -26455,7 +27328,7 @@ async function main(argv) {
|
|
|
26455
27328
|
let pkgOutdatedDispose;
|
|
26456
27329
|
if (dwCfg?.["enabled"] === true) {
|
|
26457
27330
|
try {
|
|
26458
|
-
const projectDir =
|
|
27331
|
+
const projectDir = path39.join(wpaths.globalRoot, "projects", wpaths.projectSlug);
|
|
26459
27332
|
const pkgMailbox = new GlobalMailbox(projectDir, events);
|
|
26460
27333
|
const pkgTrackerOpts = {
|
|
26461
27334
|
storageDir: projectDir,
|
|
@@ -26829,7 +27702,7 @@ ${color.dim("\u2500".repeat(40))}` : "";
|
|
|
26829
27702
|
return director.spawn(cfg);
|
|
26830
27703
|
},
|
|
26831
27704
|
onFleetLog: async (subagentId, mode) => {
|
|
26832
|
-
const subagentsRoot =
|
|
27705
|
+
const subagentsRoot = path39.join(fleetRootForPromotion, "subagents");
|
|
26833
27706
|
let runDirs;
|
|
26834
27707
|
try {
|
|
26835
27708
|
runDirs = await fsp5.readdir(subagentsRoot);
|
|
@@ -26838,7 +27711,7 @@ ${color.dim("\u2500".repeat(40))}` : "";
|
|
|
26838
27711
|
}
|
|
26839
27712
|
const found = [];
|
|
26840
27713
|
for (const runId of runDirs) {
|
|
26841
|
-
const runDir =
|
|
27714
|
+
const runDir = path39.join(subagentsRoot, runId);
|
|
26842
27715
|
let files;
|
|
26843
27716
|
try {
|
|
26844
27717
|
files = await fsp5.readdir(runDir);
|
|
@@ -26847,7 +27720,7 @@ ${color.dim("\u2500".repeat(40))}` : "";
|
|
|
26847
27720
|
}
|
|
26848
27721
|
for (const f of files) {
|
|
26849
27722
|
if (!f.endsWith(".jsonl")) continue;
|
|
26850
|
-
const full =
|
|
27723
|
+
const full = path39.join(runDir, f);
|
|
26851
27724
|
try {
|
|
26852
27725
|
const stat7 = await fsp5.stat(full);
|
|
26853
27726
|
found.push({
|
|
@@ -26948,7 +27821,7 @@ ${color.dim("\u2500".repeat(40))}` : "";
|
|
|
26948
27821
|
}
|
|
26949
27822
|
const dir = await multiAgentHost.ensureDirector();
|
|
26950
27823
|
if (!dir) return "Director is not available.";
|
|
26951
|
-
const dirStatePath =
|
|
27824
|
+
const dirStatePath = path39.join(fleetRootForPromotion, "director-state.json");
|
|
26952
27825
|
const prior = await loadDirectorState(dirStatePath);
|
|
26953
27826
|
if (!prior) {
|
|
26954
27827
|
return "No prior director-state.json found \u2014 nothing to retry.";
|
|
@@ -27017,9 +27890,9 @@ ${color.dim("\u2500".repeat(40))}` : "";
|
|
|
27017
27890
|
for (const tool of director2.tools(FLEET_ROSTER)) {
|
|
27018
27891
|
toolRegistry.register(tool);
|
|
27019
27892
|
}
|
|
27020
|
-
const mp =
|
|
27021
|
-
const sp =
|
|
27022
|
-
const ss =
|
|
27893
|
+
const mp = path39.join(fleetRootForPromotion, "fleet.json");
|
|
27894
|
+
const sp = path39.join(fleetRootForPromotion, "shared");
|
|
27895
|
+
const ss = path39.join(fleetRootForPromotion, "subagents");
|
|
27023
27896
|
const lines = [
|
|
27024
27897
|
`${color.green("\u2713")} Promoted to director mode.`,
|
|
27025
27898
|
` Roster: ${Object.keys(FLEET_ROSTER).join(", ")}`,
|
|
@@ -27123,7 +27996,8 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
27123
27996
|
// Real per-role factory: each dispatched slot runs as a fresh,
|
|
27124
27997
|
// isolated agent with the role's filtered tools + persona prompt
|
|
27125
27998
|
// (instead of sharing the leader agent's Context).
|
|
27126
|
-
subagentFactory: multiAgentHost.makeSubagentFactory(config)
|
|
27999
|
+
subagentFactory: multiAgentHost.makeSubagentFactory(config),
|
|
28000
|
+
events
|
|
27127
28001
|
};
|
|
27128
28002
|
parallelEngine = new ParallelEternalEngine(parallelOptions);
|
|
27129
28003
|
}
|
|
@@ -27137,7 +28011,8 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
27137
28011
|
maxContextTokens: effectiveMaxContext > 0 ? effectiveMaxContext : void 0,
|
|
27138
28012
|
onIteration: broadcastEternalIteration,
|
|
27139
28013
|
onStage: broadcastAutonomyStage,
|
|
27140
|
-
brain
|
|
28014
|
+
brain,
|
|
28015
|
+
events
|
|
27141
28016
|
});
|
|
27142
28017
|
}
|
|
27143
28018
|
void eternalEngine.prime();
|
|
@@ -27160,7 +28035,7 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
27160
28035
|
onBeforeExit: async () => {
|
|
27161
28036
|
const cwd2 = projectRoot;
|
|
27162
28037
|
const statusResult = await new Promise(
|
|
27163
|
-
(
|
|
28038
|
+
(resolve11, reject) => {
|
|
27164
28039
|
const child = spawn("git", ["status", "--porcelain"], {
|
|
27165
28040
|
cwd: cwd2,
|
|
27166
28041
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -27172,7 +28047,7 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
27172
28047
|
stdout += d;
|
|
27173
28048
|
});
|
|
27174
28049
|
child.on("error", reject);
|
|
27175
|
-
child.on("close", (code) =>
|
|
28050
|
+
child.on("close", (code) => resolve11({ stdout, code: code ?? 0 }));
|
|
27176
28051
|
}
|
|
27177
28052
|
);
|
|
27178
28053
|
if (statusResult.stdout.trim().length > 0) {
|
|
@@ -27318,6 +28193,10 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
27318
28193
|
tokenCounter,
|
|
27319
28194
|
config,
|
|
27320
28195
|
configStore,
|
|
28196
|
+
// Project-scoped mailbox — the AutonomousCoordinator in execution.ts
|
|
28197
|
+
// subscribes to it so goals/tasks/knowledge are shared with other
|
|
28198
|
+
// terminals working on the same project.
|
|
28199
|
+
mailbox: brainMailbox,
|
|
27321
28200
|
renderer,
|
|
27322
28201
|
reader,
|
|
27323
28202
|
session,
|
|
@@ -27357,6 +28236,34 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
27357
28236
|
return autonomyMode;
|
|
27358
28237
|
},
|
|
27359
28238
|
getNextPredict: () => nextPredictEnabled,
|
|
28239
|
+
applyLiveSettings: (s) => {
|
|
28240
|
+
try {
|
|
28241
|
+
if (s.yolo !== void 0) {
|
|
28242
|
+
container.resolve(TOKENS.PermissionPolicy).setYolo?.(s.yolo);
|
|
28243
|
+
config = patchConfig(config, { yolo: s.yolo });
|
|
28244
|
+
}
|
|
28245
|
+
if (s.nextPrediction !== void 0) {
|
|
28246
|
+
nextPredictEnabled = s.nextPrediction;
|
|
28247
|
+
config = patchConfig(config, { nextPrediction: s.nextPrediction });
|
|
28248
|
+
}
|
|
28249
|
+
if (s.enhanceEnabled !== void 0) {
|
|
28250
|
+
enhanceController?.setEnabled(s.enhanceEnabled);
|
|
28251
|
+
}
|
|
28252
|
+
if (s.maxIterations !== void 0) {
|
|
28253
|
+
agent.maxIterations = s.maxIterations;
|
|
28254
|
+
}
|
|
28255
|
+
if (s.logLevel !== void 0) {
|
|
28256
|
+
container.resolve(TOKENS.Logger).level = s.logLevel;
|
|
28257
|
+
}
|
|
28258
|
+
if (s.auditLevel !== void 0) {
|
|
28259
|
+
sessionBridge.setAuditLevel(s.auditLevel);
|
|
28260
|
+
}
|
|
28261
|
+
if (s.contextAutoCompact !== void 0) {
|
|
28262
|
+
autoCompactor?.setEnabled(s.contextAutoCompact);
|
|
28263
|
+
}
|
|
28264
|
+
} catch {
|
|
28265
|
+
}
|
|
28266
|
+
},
|
|
27360
28267
|
onSuggestionsParsed: (suggestions) => {
|
|
27361
28268
|
currentSuggestions = suggestions ?? [];
|
|
27362
28269
|
setSuggestions(suggestions ?? []);
|
|
@@ -27439,7 +28346,9 @@ Reply YES to auto-proceed, NO to wait for human input.`
|
|
|
27439
28346
|
getBrainLog: () => brainLog,
|
|
27440
28347
|
// Clean up SessionStats event listeners and all EventBus handlers when the REPL exits.
|
|
27441
28348
|
onDestroy: () => {
|
|
27442
|
-
teardownHandlers.forEach((fn) =>
|
|
28349
|
+
teardownHandlers.forEach((fn) => {
|
|
28350
|
+
fn();
|
|
28351
|
+
});
|
|
27443
28352
|
stats.destroy(events);
|
|
27444
28353
|
}
|
|
27445
28354
|
});
|