@wrongstack/cli 0.280.1 → 0.281.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +559 -112
- package/dist/index.js.map +1 -1
- package/package.json +15 -15
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { CODEX_MODELS, color, ToolCapabilities, readBundledInstructionText, writeErr, loadPlugins, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, expectDefined, DefaultTaskStore, TaskTracker, renderTaskGraph, withFileLock, ConfigError, resolveHqDataDir, ensureHqFirstRunAuthFile, DEFAULT_HQ_REDACTION_POLICY, createHqPersistence, HqCommandAuditLog, HqAlertEngine, toAlertMessage, validateHqCommand, watchHqAuthFile, parseHqFrame, HQ_PROTOCOL_VERSION, parseHqEventPayload, scrubAndTruncateHqPreview, FetchError, wstackGlobalRoot, DefaultSecretScrubber, resolveWstackPaths, watchProviderConfig, PromptUsageStore, TOKENS, ToolRegistry, GlobalMailbox, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, shellHooksEqual, countShellHooks, normalizeTokenSavingTier, SlashCommandRegistry, attachDepWatcherBridge, createFallbackModelExtension, SessionMemoryConsolidator, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, createTieredBrainArbiter, DefaultBrainArbiter, BrainMonitor, mailboxSessionTag, createDelegateTool, FLEET_ROSTER, createMcpControlTool, startTechStackConsumer, startPackageOutdatedWatcher, SddRunRegistry, recordFileAction, createAutonomyBrain, DefaultPluginAPI, SpecVersioning, buildTranscriptFromEvents, DefaultLogger, DefaultModelsRegistry, isStdinTTY, FsError, atomicWrite,
|
|
2
|
+
import { CODEX_MODELS, color, ToolCapabilities, readBundledInstructionText, writeErr, loadPlugins, renderProgress, SpecStore, TaskGraphStore, analyzeCriticalPath, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, expectDefined, DefaultTaskStore, TaskTracker, renderTaskGraph, withFileLock, ConfigError, resolveHqDataDir, ensureHqFirstRunAuthFile, DEFAULT_HQ_REDACTION_POLICY, createHqPersistence, HqCommandAuditLog, HqAlertEngine, toAlertMessage, validateHqCommand, watchHqAuthFile, parseHqFrame, HQ_PROTOCOL_VERSION, parseHqEventPayload, scrubAndTruncateHqPreview, FetchError, wstackGlobalRoot, DefaultSecretScrubber, resolveWstackPaths, watchProviderConfig, PromptUsageStore, TOKENS, ToolRegistry, GlobalMailbox, resolveSessionLoggingConfig, createSessionEventBridge, HookRegistry, HookRunner, shellHooksEqual, countShellHooks, normalizeTokenSavingTier, SlashCommandRegistry, attachDepWatcherBridge, createFallbackModelExtension, SessionMemoryConsolidator, BrainDecisionQueue, ObservableBrainArbiter, HumanEscalatingBrainArbiter, createTieredBrainArbiter, DefaultBrainArbiter, BrainMonitor, mailboxSessionTag, createDelegateTool, FLEET_ROSTER, createMcpControlTool, startTechStackConsumer, startPackageOutdatedWatcher, SddRunRegistry, recordFileAction, createAutonomyBrain, DefaultPluginAPI, SpecVersioning, buildTranscriptFromEvents, DefaultLogger, DefaultModelsRegistry, isStdinTTY, FsError, atomicWrite, EventBus, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, mergeCustomModelDefs, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, sessionScopedPath as sessionScopedPath$1, createContextManagerTool, makeMailboxTool, makeMailSendTool, makeMailInboxTool, makeFleetStatusTool, applyToolDescriptionModes, applyToolResultRenderModes, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, createHqPublisherFromEnv, DEFAULT_SESSION_PRUNE_DAYS, RecoveryLock, DefaultAttachmentStore, Context, QueueStore, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, applyModelRuntime, installDesignStudioMiddleware, resolveContextWindowPolicy, resolveAuditLevel, AutoCompactionMiddleware, estimateRequestTokensCalibrated, Agent, FleetManager, makeDirectorSessionFactory, Director, makeFleetEmitTool, AdaptiveConcurrencyController, resolveModelMatrix, resolveModelTargetFromEntry, DEFAULT_SUBAGENT_BASELINE, AutoApprovePermissionPolicy, mergeModelRuntime, ToolValidationError, WIDE_SUBAGENT_CAPABILITIES, resolveProjectDir, FleetSupervisor, AgentError, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, assignNickname, makeLLMClassifier, writeOut, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1, CHIMERA_REVIEW_PROMPT, setQueuedMessagesSnapshot, bootConfig as bootConfig$1, setOutputLineGuard, setRawMode, DefaultSessionReader, ToolAuditLog, DefaultSessionRewinder, DefaultSessionStore, ProviderRegistry, makeAgentSubagentRunner, NULL_FLEET_BUS, ERROR_CODES, buildChildEnv, formatContextWindowModeList, repairToolUseAdjacency, getContextWindowMode, AGENT_CATALOG, dispatchAgent, normalizeToolDescriptionMode, getToolDescriptionMode, noOpVault, formatTodosList, loadTasks, formatTaskList, formatTaskProgress, mutateTasks, emptyPlan, addPlanItem, savePlan, formatPlan, recordCompletedWorkEvidence, saveCompletedWorkCheckpoint, SessionRecovery, loadGoal, goalFilePath, summarizeUsage, saveGoal, formatGoal, emptyGoal, buildGoalPreamble, pendingBtwCount, setBtwNote, getDesignKitLoader, getDesignState, clearActiveKit, clearPersistedActiveKit, recordOverrides, setDesignOverrides, loadActiveKit, isDesignStack, applyTokenOverrides, materializeTokens, setActiveKit, readHqRuntimeFileSync, resolveHqConfig, MATRIX_PHASE_KEYS, matrixKeyKind, phaseForRole, fallbackProfileChain, parseModelRef, normalizeModelRef, onResize, hasOpenTodos, InputBuilder, DefaultSecretVault as DefaultSecretVault$1, decryptConfigSecrets as decryptConfigSecrets$1, encryptConfigSecrets as encryptConfigSecrets$1, DEFAULT_CONTEXT_WINDOW_MODE_ID, HQ_AUTH_FILE_VERSION, truncate, estimateMessageTokens, AGENTS_BY_PHASE, validateAgainstSchema, normalizeToolResultRenderMode, getToolResultRenderMode, setToolResultRenderMode, resolveMailboxIdentity, smartDefaultFallbackChain, AutonomousCoordinator, mutateHqAuthFile, mintHqToken, readHqAuthFile, renderInstructionTemplate, recentTextTurns, gatedEnhancerReasoning, enhanceUserPrompt, mutatePlan, setPlanItemStatus, getPlanTemplate, resolveProviderModelList, ParseError, startSessionTelemetryBridge, startFleetTelemetryBridge, startBrainTelemetryBridge, startWorktreeTelemetryBridge, startToolTelemetryBridge, startCostTelemetryBridge } from '@wrongstack/core';
|
|
3
3
|
import * as fs2 from 'fs/promises';
|
|
4
4
|
import { access, readFile } from 'fs/promises';
|
|
5
5
|
import { DefaultSecretVault, decryptConfigSecrets, encryptConfigSecrets, isSecretField } from '@wrongstack/core/security';
|
|
@@ -32,10 +32,10 @@ import * as readline from 'readline';
|
|
|
32
32
|
import { ACP_AGENT_COMMANDS, findAgentDescriptor, makeACPSubagentRunner, runEnsemble, renderEnsembleText, EnsembleRegistry, resolveAcpAgentCommand, runOneAcpTask, probeAcpAgents, runAcpBench, renderAcpBenchText, fetchAcpRegistry } from '@wrongstack/acp';
|
|
33
33
|
import { parseNextSteps } from '@wrongstack/tui';
|
|
34
34
|
import { WrongStackACPServer, makeACPServerAgentTurn, ACPSessionStore, WsBridgeTransport, ACPProtocolHandler } from '@wrongstack/acp/agent';
|
|
35
|
+
import { allServers, DefaultPathResolver, DefaultTokenCounter } from '@wrongstack/core/infrastructure';
|
|
35
36
|
import { ToolExecutor } from '@wrongstack/core/execution';
|
|
36
37
|
import { loadBenchConfig, reportHeaderLine, readSummary, renderMarkdownReport, createPolyglotSuite, createSwebenchSuite, runBenchmark, writeJsonArtifacts, collectCellPredictions, writePredictionsJsonl, gradePolyglot, gradeSwebench } from '@wrongstack/bench';
|
|
37
38
|
import { createAgentMonitorService, acquireOrJoin, release, finalize, readLiveLock } from '@wrongstack/core/coordination';
|
|
38
|
-
import { allServers } from '@wrongstack/core/infrastructure';
|
|
39
39
|
import * as net from 'net';
|
|
40
40
|
import { createToolVisionAdapters } from '@wrongstack/runtime/vision';
|
|
41
41
|
|
|
@@ -76,16 +76,16 @@ function writeKeysBack(cfg, keys) {
|
|
|
76
76
|
return;
|
|
77
77
|
}
|
|
78
78
|
cfg.apiKeys = keys;
|
|
79
|
-
const
|
|
79
|
+
const active2 = keys.find((k) => k.label === cfg.activeKey) ?? expectDefined(keys[0]);
|
|
80
80
|
delete cfg.apiKey;
|
|
81
81
|
if (!cfg.activeKey || !keys.some((k) => k.label === cfg.activeKey)) {
|
|
82
|
-
cfg.activeKey =
|
|
82
|
+
cfg.activeKey = active2.label;
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
function resolveActiveApiKey(cfg) {
|
|
86
86
|
if (Array.isArray(cfg.apiKeys) && cfg.apiKeys.length > 0) {
|
|
87
|
-
const
|
|
88
|
-
return (
|
|
87
|
+
const active2 = cfg.activeKey ? cfg.apiKeys.find((k) => k.label === cfg.activeKey) : void 0;
|
|
88
|
+
return (active2 ?? cfg.apiKeys[0])?.apiKey;
|
|
89
89
|
}
|
|
90
90
|
return cfg.apiKey && cfg.apiKey.length > 0 ? cfg.apiKey : void 0;
|
|
91
91
|
}
|
|
@@ -667,26 +667,26 @@ function fmtDuration(ms) {
|
|
|
667
667
|
const remMin = m - h * 60;
|
|
668
668
|
return `${h}h${remMin}m`;
|
|
669
669
|
}
|
|
670
|
-
function fmtTaskResultLine(r,
|
|
670
|
+
function fmtTaskResultLine(r, color90) {
|
|
671
671
|
const stats = `${r.iterations}it ${r.toolCalls}tc ${fmtDuration(r.durationMs)}`;
|
|
672
672
|
const errMsg = typeof r.error === "string" ? r.error : r.error?.message;
|
|
673
673
|
const errKind = typeof r.error === "object" ? r.error?.kind : void 0;
|
|
674
674
|
const errTail = errMsg ? ` \u2014 ${errMsg.replace(/\s+/g, " ").slice(0, 80)}${errMsg.length > 80 ? "\u2026" : ""}` : "";
|
|
675
|
-
const errKindChip = errKind ?
|
|
676
|
-
const errSnip = errMsg || errKind ? `${errKindChip}${
|
|
675
|
+
const errKindChip = errKind ? color90.dim(` [${errKind}]`) : "";
|
|
676
|
+
const errSnip = errMsg || errKind ? `${errKindChip}${color90.dim(errTail)}` : "";
|
|
677
677
|
switch (r.status) {
|
|
678
678
|
case "success":
|
|
679
|
-
return { mark:
|
|
679
|
+
return { mark: color90.green("\u2713"), stats, tail: "" };
|
|
680
680
|
case "timeout":
|
|
681
681
|
return {
|
|
682
|
-
mark:
|
|
683
|
-
stats: `${
|
|
682
|
+
mark: color90.yellow("\u23F1"),
|
|
683
|
+
stats: `${color90.yellow("timeout")} ${stats}`,
|
|
684
684
|
tail: errSnip
|
|
685
685
|
};
|
|
686
686
|
case "stopped":
|
|
687
|
-
return { mark:
|
|
687
|
+
return { mark: color90.dim("\u2298"), stats: `${color90.dim("stopped")} ${stats}`, tail: errSnip };
|
|
688
688
|
case "failed":
|
|
689
|
-
return { mark:
|
|
689
|
+
return { mark: color90.red("\u2717"), stats: `${color90.red("failed")} ${stats}`, tail: errSnip };
|
|
690
690
|
}
|
|
691
691
|
}
|
|
692
692
|
var init_utils = __esm({
|
|
@@ -3112,6 +3112,8 @@ var init_hq_dashboard_html = __esm({
|
|
|
3112
3112
|
#root { height: 100vh; display: flex; flex-direction: column; }
|
|
3113
3113
|
.hq-top { display: flex; align-items: center; gap: 14px; padding: 12px 20px; border-bottom: 1px solid var(--border); background: rgba(13,17,23,0.7); backdrop-filter: blur(8px); }
|
|
3114
3114
|
.hq-brand { font-size: 17px; font-weight: 800; letter-spacing: 0.2px; background: linear-gradient(90deg, var(--accent), var(--purple)); -webkit-background-clip: text; background-clip: text; color: transparent; white-space: nowrap; }
|
|
3115
|
+
.hq-ui-badge { color: var(--amber); border: 1px solid var(--border); background: var(--inset); border-radius: 7px; padding: 3px 8px; font-size: 10px; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; white-space: nowrap; }
|
|
3116
|
+
.hq-ui-badge::before { content: 'UI:'; color: var(--dim); margin-right: 4px; }
|
|
3115
3117
|
.hq-led { width: 9px; height: 9px; border-radius: 50%; display: inline-block; margin-right: 6px; }
|
|
3116
3118
|
.hq-led.live { background: var(--green); box-shadow: 0 0 8px var(--green); }
|
|
3117
3119
|
.hq-led.dead { background: var(--dim); }
|
|
@@ -3681,6 +3683,7 @@ async function boot(){
|
|
|
3681
3683
|
});
|
|
3682
3684
|
return h('div', { className: 'hq-top' },
|
|
3683
3685
|
h('div', { className: 'hq-brand' }, '\u{1F4CB} WrongStack HQ'),
|
|
3686
|
+
h('div', { className: 'hq-ui-badge', title: 'Served from packages/cli/src/hq-dashboard-html.ts' }, 'inline fallback'),
|
|
3684
3687
|
h('div', { className: 'hq-conn' }, h('span', { className: 'hq-led ' + (p.connected?'live':'dead') }), p.connected ? 'Live' : 'Reconnecting\u2026'),
|
|
3685
3688
|
h('button', { className: 'theme-btn', title: 'Toggle light / dark', onClick: toggleTheme }, Store.theme==='light' ? '\u{1F319}' : '\u2600\uFE0F'),
|
|
3686
3689
|
h('div', { className: 'statbar' },
|
|
@@ -4127,7 +4130,7 @@ function renderFallback(){
|
|
|
4127
4130
|
var tree = buildTree(snap);
|
|
4128
4131
|
var sel = Store.selected;
|
|
4129
4132
|
var html = '';
|
|
4130
|
-
html += '<div class="hq-top"><div class="hq-brand">\u{1F4CB} WrongStack HQ</div>';
|
|
4133
|
+
html += '<div class="hq-top"><div class="hq-brand">\u{1F4CB} WrongStack HQ</div><div class="hq-ui-badge" title="Served from packages/cli/src/hq-dashboard-html.ts">inline fallback</div>';
|
|
4131
4134
|
html += '<div class="hq-conn"><span class="hq-led '+(Store.connected?'live':'dead')+'"></span>'+(Store.connected?'Live':'Reconnecting\u2026')+'</div>';
|
|
4132
4135
|
html += '<button class="theme-btn" id="fb-theme">'+(Store.theme==='light'?'\u{1F319}':'\u2600\uFE0F')+'</button>';
|
|
4133
4136
|
html += '<div class="statbar">';
|
|
@@ -7123,7 +7126,7 @@ async function handleModesList(ctx, ws) {
|
|
|
7123
7126
|
}
|
|
7124
7127
|
try {
|
|
7125
7128
|
const modes = await ctx.modeStore.listModes();
|
|
7126
|
-
const
|
|
7129
|
+
const active2 = await ctx.modeStore.getActiveMode();
|
|
7127
7130
|
ctx.send(ws, {
|
|
7128
7131
|
type: "modes.list",
|
|
7129
7132
|
payload: {
|
|
@@ -7131,9 +7134,9 @@ async function handleModesList(ctx, ws) {
|
|
|
7131
7134
|
id: m.id,
|
|
7132
7135
|
name: m.name,
|
|
7133
7136
|
description: m.description,
|
|
7134
|
-
isActive: m.id === (
|
|
7137
|
+
isActive: m.id === (active2?.id ?? "default")
|
|
7135
7138
|
})),
|
|
7136
|
-
activeId:
|
|
7139
|
+
activeId: active2?.id ?? "default"
|
|
7137
7140
|
}
|
|
7138
7141
|
});
|
|
7139
7142
|
} catch (err) {
|
|
@@ -7567,17 +7570,17 @@ function handleContextRepair(ctx, ws) {
|
|
|
7567
7570
|
);
|
|
7568
7571
|
}
|
|
7569
7572
|
async function handleContextModesList(ctx, ws) {
|
|
7570
|
-
const
|
|
7573
|
+
const active2 = String(ctx.agent.ctx.meta["contextWindowMode"] ?? DEFAULT_CONTEXT_WINDOW_MODE_ID);
|
|
7571
7574
|
const modeStore = await ctx.getCustomModeStore();
|
|
7572
7575
|
ctx.send(ws, {
|
|
7573
7576
|
type: "context.modes.list",
|
|
7574
7577
|
payload: sessionPayload2(ctx, {
|
|
7575
|
-
activeId:
|
|
7578
|
+
activeId: active2,
|
|
7576
7579
|
modes: modeStore.list().map((m) => ({
|
|
7577
7580
|
id: m.id,
|
|
7578
7581
|
name: m.name,
|
|
7579
7582
|
description: m.description,
|
|
7580
|
-
isActive: m.id ===
|
|
7583
|
+
isActive: m.id === active2,
|
|
7581
7584
|
thresholds: m.thresholds,
|
|
7582
7585
|
preserveK: m.preserveK,
|
|
7583
7586
|
eliseThreshold: m.eliseThreshold,
|
|
@@ -8063,7 +8066,7 @@ async function handleProviderModels(ctx, ws, providerId) {
|
|
|
8063
8066
|
type: "provider.models",
|
|
8064
8067
|
payload: {
|
|
8065
8068
|
provider: providerId,
|
|
8066
|
-
models: resolveProviderModelList(cfg?.models, provider)
|
|
8069
|
+
models: resolveProviderModelList(cfg?.models, provider, cfg?.type ?? providerId)
|
|
8067
8070
|
}
|
|
8068
8071
|
});
|
|
8069
8072
|
} catch (err) {
|
|
@@ -8255,10 +8258,10 @@ async function handleProviderProbe(ctx, ws, providerId, timeoutMs) {
|
|
|
8255
8258
|
return;
|
|
8256
8259
|
}
|
|
8257
8260
|
const keys = normalizeKeys(cfg);
|
|
8258
|
-
const
|
|
8261
|
+
const active2 = keys.find((k) => k.label === cfg.activeKey) ?? keys[0];
|
|
8259
8262
|
const result = await probeLocalLlm({
|
|
8260
8263
|
baseUrl: cfg.baseUrl,
|
|
8261
|
-
apiKey:
|
|
8264
|
+
apiKey: active2?.apiKey,
|
|
8262
8265
|
noAuth: false,
|
|
8263
8266
|
scrubber: probeScrubber,
|
|
8264
8267
|
...timeoutMs !== void 0 ? { timeoutMs } : {}
|
|
@@ -8508,6 +8511,36 @@ async function handleSessionDelete(ctx, ws, id) {
|
|
|
8508
8511
|
sendResult8(ctx, ws, false, toErrorMessage$1(err));
|
|
8509
8512
|
}
|
|
8510
8513
|
}
|
|
8514
|
+
async function handleSessionRename(ctx, ws, id, name) {
|
|
8515
|
+
if (!id) {
|
|
8516
|
+
sendResult8(ctx, ws, false, "Session id is required");
|
|
8517
|
+
return;
|
|
8518
|
+
}
|
|
8519
|
+
try {
|
|
8520
|
+
const store = storeFor(ctx.opts);
|
|
8521
|
+
await store.rename(id, name);
|
|
8522
|
+
sendResult8(ctx, ws, true, name ? `Renamed session to "${name}"` : `Cleared session name`);
|
|
8523
|
+
const list = await store.list(50);
|
|
8524
|
+
const currentId = currentSessionId3(ctx);
|
|
8525
|
+
ctx.broadcast({
|
|
8526
|
+
type: "sessions.list",
|
|
8527
|
+
payload: {
|
|
8528
|
+
sessions: list.map((s) => ({
|
|
8529
|
+
id: s.id,
|
|
8530
|
+
name: s.name,
|
|
8531
|
+
title: s.title,
|
|
8532
|
+
startedAt: s.startedAt,
|
|
8533
|
+
model: s.model,
|
|
8534
|
+
provider: s.provider,
|
|
8535
|
+
tokenTotal: s.tokenTotal,
|
|
8536
|
+
isCurrent: s.id === currentId
|
|
8537
|
+
}))
|
|
8538
|
+
}
|
|
8539
|
+
});
|
|
8540
|
+
} catch (err) {
|
|
8541
|
+
sendResult8(ctx, ws, false, toErrorMessage$1(err));
|
|
8542
|
+
}
|
|
8543
|
+
}
|
|
8511
8544
|
function handleSessionSave(ctx, ws) {
|
|
8512
8545
|
sendResult8(ctx, ws, true, `Session ${ctx.opts.session.id} is auto-saved`);
|
|
8513
8546
|
}
|
|
@@ -9186,6 +9219,12 @@ function createMessageRouter(deps) {
|
|
|
9186
9219
|
msg.payload?.limit ?? 50
|
|
9187
9220
|
),
|
|
9188
9221
|
"session.new": (_msg, ws) => handleSessionNew(sessionsCtx),
|
|
9222
|
+
"session.rename": (msg, ws) => handleSessionRename(
|
|
9223
|
+
sessionsCtx,
|
|
9224
|
+
ws,
|
|
9225
|
+
msg.payload.id,
|
|
9226
|
+
msg.payload.name ?? ""
|
|
9227
|
+
),
|
|
9189
9228
|
"session.delete": (msg, ws) => handleSessionDelete(sessionsCtx, ws, msg.payload.id),
|
|
9190
9229
|
"session.save": (_msg, ws) => handleSessionSave(sessionsCtx, ws),
|
|
9191
9230
|
"session.resume": (msg, ws) => handleSessionResume(sessionsCtx, ws, msg.payload.id),
|
|
@@ -12110,7 +12149,7 @@ async function resolveModelSelection(answer, models, provider, _registry, render
|
|
|
12110
12149
|
init_provider_config_utils();
|
|
12111
12150
|
function renderProviderLine(renderer, id, cfg, idx) {
|
|
12112
12151
|
const keys = normalizeKeys(cfg);
|
|
12113
|
-
const
|
|
12152
|
+
const active2 = activeLabel(cfg, keys);
|
|
12114
12153
|
const firstKey = keys[0];
|
|
12115
12154
|
let summary;
|
|
12116
12155
|
if (keys.length === 0) {
|
|
@@ -12118,8 +12157,8 @@ function renderProviderLine(renderer, id, cfg, idx) {
|
|
|
12118
12157
|
} else if (keys.length === 1) {
|
|
12119
12158
|
summary = maskedKey(firstKey?.apiKey ?? "");
|
|
12120
12159
|
} else {
|
|
12121
|
-
const activeKeyObj =
|
|
12122
|
-
summary = `${color.dim(`${keys.length} keys`)} ${color.dim("active:")} ${color.bold(
|
|
12160
|
+
const activeKeyObj = active2 != null ? keys.find((k) => k.label === active2) : void 0;
|
|
12161
|
+
summary = `${color.dim(`${keys.length} keys`)} ${color.dim("active:")} ${color.bold(active2 ?? "?")} ` + maskedKey(activeKeyObj?.apiKey ?? firstKey?.apiKey ?? "");
|
|
12123
12162
|
}
|
|
12124
12163
|
const fam = cfg.family ? color.dim(`[${cfg.family}]`) : "";
|
|
12125
12164
|
const aliasHint = cfg.type && cfg.type !== id ? color.dim(`\u2192 ${cfg.type}`) : "";
|
|
@@ -12130,7 +12169,7 @@ function renderProviderLine(renderer, id, cfg, idx) {
|
|
|
12130
12169
|
}
|
|
12131
12170
|
function renderProviderHeader(renderer, providerId, cfg) {
|
|
12132
12171
|
const keys = normalizeKeys(cfg);
|
|
12133
|
-
const
|
|
12172
|
+
const active2 = activeLabel(cfg, keys);
|
|
12134
12173
|
renderer.write(
|
|
12135
12174
|
`
|
|
12136
12175
|
${color.bold(providerId)} ${cfg.family ? color.dim(`[${cfg.family}]`) : color.amber("[no family]")}
|
|
@@ -12153,12 +12192,12 @@ ${color.bold(providerId)} ${cfg.family ? color.dim(`[${cfg.family}]`) : color.am
|
|
|
12153
12192
|
} else {
|
|
12154
12193
|
for (let i = 0; i < keys.length; i++) {
|
|
12155
12194
|
const key = keys[i];
|
|
12156
|
-
if (key) renderKeyLine(renderer, key, i + 1,
|
|
12195
|
+
if (key) renderKeyLine(renderer, key, i + 1, active2);
|
|
12157
12196
|
}
|
|
12158
12197
|
}
|
|
12159
12198
|
}
|
|
12160
|
-
function renderKeyLine(renderer, key, idx,
|
|
12161
|
-
const marker = key.label ===
|
|
12199
|
+
function renderKeyLine(renderer, key, idx, active2) {
|
|
12200
|
+
const marker = key.label === active2 ? color.green("\u25CF") : color.dim("\u25CB");
|
|
12162
12201
|
renderer.write(
|
|
12163
12202
|
` ${color.dim(`${idx}.`.padStart(4))} ${marker} ${key.label.padEnd(20)} ${maskedKey(key.apiKey)} ${color.dim(key.createdAt)}
|
|
12164
12203
|
`
|
|
@@ -12877,12 +12916,12 @@ function createAuthPanelHost(deps) {
|
|
|
12877
12916
|
const cfg = providers[id];
|
|
12878
12917
|
if (!cfg) continue;
|
|
12879
12918
|
const keys = normalizeKeys(cfg);
|
|
12880
|
-
const
|
|
12919
|
+
const active2 = activeLabel(cfg, keys);
|
|
12881
12920
|
const keyRows = keys.map((k) => ({
|
|
12882
12921
|
label: k.label,
|
|
12883
12922
|
masked: plainMaskedKey(k.apiKey),
|
|
12884
12923
|
createdAt: k.createdAt,
|
|
12885
|
-
active: k.label ===
|
|
12924
|
+
active: k.label === active2,
|
|
12886
12925
|
authMethod: k.authMethod,
|
|
12887
12926
|
expiresAt: k.expiresAt
|
|
12888
12927
|
}));
|
|
@@ -13250,7 +13289,7 @@ async function readScripts(cwd) {
|
|
|
13250
13289
|
}
|
|
13251
13290
|
function createAutoPhaseHost(deps) {
|
|
13252
13291
|
const store = new PhaseStore({ baseDir: deps.storeDir });
|
|
13253
|
-
let
|
|
13292
|
+
let active2 = null;
|
|
13254
13293
|
const log = deps.log ?? (() => {
|
|
13255
13294
|
});
|
|
13256
13295
|
async function runOnce(prompt, label, signal, cwd) {
|
|
@@ -13349,7 +13388,7 @@ ${res.out}` };
|
|
|
13349
13388
|
}
|
|
13350
13389
|
return {
|
|
13351
13390
|
async onAutoPhaseStart({ goal, projectContext }) {
|
|
13352
|
-
if (
|
|
13391
|
+
if (active2?.orchestrator.isRunning()) {
|
|
13353
13392
|
return {
|
|
13354
13393
|
ok: false,
|
|
13355
13394
|
error: "An AutoPhase run is already in progress. Use /autophase stop first."
|
|
@@ -13415,7 +13454,7 @@ ${res.out}` };
|
|
|
13415
13454
|
const nick = assignNickname("executor", usedNicknames);
|
|
13416
13455
|
usedNicknames.add(nick.key);
|
|
13417
13456
|
agentName = nick.display.replace(/\s*\([^)]*\)\s*$/, "");
|
|
13418
|
-
|
|
13457
|
+
active2?.orchestrator.setTaskAssignee(task.id, void 0, agentName);
|
|
13419
13458
|
}
|
|
13420
13459
|
return runOnce(
|
|
13421
13460
|
buildTaskPrompt(task, phaseName, goal),
|
|
@@ -13474,9 +13513,9 @@ ${res.out}` };
|
|
|
13474
13513
|
const onUntyped = (event, handler) => busOn.on(event, handler);
|
|
13475
13514
|
const offUntyped = (event, handler) => busOn.off(event, handler);
|
|
13476
13515
|
const finalizeActiveRun = () => {
|
|
13477
|
-
if (
|
|
13478
|
-
|
|
13479
|
-
|
|
13516
|
+
if (active2?.graph.id !== graph.id) return;
|
|
13517
|
+
active2.unsubscribe();
|
|
13518
|
+
active2 = null;
|
|
13480
13519
|
};
|
|
13481
13520
|
const onDone = () => {
|
|
13482
13521
|
log(`\u{1F389} AutoPhase complete: ${graph.title}`);
|
|
@@ -13493,7 +13532,7 @@ ${res.out}` };
|
|
|
13493
13532
|
offUntyped("graph.completed", onDone);
|
|
13494
13533
|
offUntyped("graph.failed", onFailed);
|
|
13495
13534
|
};
|
|
13496
|
-
|
|
13535
|
+
active2 = { graph, orchestrator, abort, unsubscribe };
|
|
13497
13536
|
void orchestrator.start().catch((err) => {
|
|
13498
13537
|
log(`\u{1F4A5} AutoPhase aborted: ${err instanceof Error ? err.message : String(err)}`);
|
|
13499
13538
|
void persist(graph);
|
|
@@ -13502,22 +13541,22 @@ ${res.out}` };
|
|
|
13502
13541
|
return { ok: true, graph };
|
|
13503
13542
|
},
|
|
13504
13543
|
onAutoPhasePause() {
|
|
13505
|
-
|
|
13544
|
+
active2?.orchestrator.pause();
|
|
13506
13545
|
},
|
|
13507
13546
|
onAutoPhaseResume() {
|
|
13508
|
-
|
|
13547
|
+
active2?.orchestrator.resume();
|
|
13509
13548
|
},
|
|
13510
13549
|
onAutoPhaseStop() {
|
|
13511
|
-
if (!
|
|
13512
|
-
|
|
13513
|
-
|
|
13514
|
-
|
|
13515
|
-
void persist(
|
|
13516
|
-
|
|
13550
|
+
if (!active2) return;
|
|
13551
|
+
active2.abort.abort();
|
|
13552
|
+
active2.orchestrator.stop();
|
|
13553
|
+
active2.unsubscribe();
|
|
13554
|
+
void persist(active2.graph);
|
|
13555
|
+
active2 = null;
|
|
13517
13556
|
},
|
|
13518
13557
|
getAutoPhaseRunner() {
|
|
13519
|
-
if (!
|
|
13520
|
-
const a =
|
|
13558
|
+
if (!active2) return null;
|
|
13559
|
+
const a = active2;
|
|
13521
13560
|
return {
|
|
13522
13561
|
graph: a.graph,
|
|
13523
13562
|
getProgress: () => a.orchestrator.getProgress(),
|
|
@@ -13525,27 +13564,27 @@ ${res.out}` };
|
|
|
13525
13564
|
};
|
|
13526
13565
|
},
|
|
13527
13566
|
onAutoPhaseMoveTask(taskId, toPhaseId) {
|
|
13528
|
-
if (!
|
|
13529
|
-
const ok =
|
|
13530
|
-
if (ok) void persist(
|
|
13567
|
+
if (!active2) return false;
|
|
13568
|
+
const ok = active2.orchestrator.moveTask(taskId, toPhaseId);
|
|
13569
|
+
if (ok) void persist(active2.graph);
|
|
13531
13570
|
return ok;
|
|
13532
13571
|
},
|
|
13533
13572
|
onAutoPhaseAssignTask(taskId, agentId, agentName) {
|
|
13534
|
-
if (!
|
|
13535
|
-
const ok =
|
|
13536
|
-
if (ok) void persist(
|
|
13573
|
+
if (!active2) return false;
|
|
13574
|
+
const ok = active2.orchestrator.setTaskAssignee(taskId, agentId, agentName);
|
|
13575
|
+
if (ok) void persist(active2.graph);
|
|
13537
13576
|
return ok;
|
|
13538
13577
|
},
|
|
13539
13578
|
onAutoPhaseAddTask(phaseId, spec) {
|
|
13540
|
-
if (!
|
|
13541
|
-
const id =
|
|
13542
|
-
if (id) void persist(
|
|
13579
|
+
if (!active2) return null;
|
|
13580
|
+
const id = active2.orchestrator.addTask(phaseId, spec);
|
|
13581
|
+
if (id) void persist(active2.graph);
|
|
13543
13582
|
return id;
|
|
13544
13583
|
},
|
|
13545
13584
|
onAutoPhaseRetryTask(taskId) {
|
|
13546
|
-
if (!
|
|
13547
|
-
const ok =
|
|
13548
|
-
if (ok) void persist(
|
|
13585
|
+
if (!active2) return false;
|
|
13586
|
+
const ok = active2.orchestrator.requeueTask(taskId);
|
|
13587
|
+
if (ok) void persist(active2.graph);
|
|
13549
13588
|
return ok;
|
|
13550
13589
|
},
|
|
13551
13590
|
async onWorktree(action, target) {
|
|
@@ -13750,8 +13789,8 @@ function parseSpawnFlags(input) {
|
|
|
13750
13789
|
}
|
|
13751
13790
|
function resolveKey(cfg) {
|
|
13752
13791
|
if (Array.isArray(cfg.apiKeys) && cfg.apiKeys.length > 0) {
|
|
13753
|
-
const
|
|
13754
|
-
return (
|
|
13792
|
+
const active2 = cfg.activeKey ? cfg.apiKeys.find((k) => k.label === cfg.activeKey) : void 0;
|
|
13793
|
+
return (active2 ?? cfg.apiKeys[0])?.apiKey;
|
|
13755
13794
|
}
|
|
13756
13795
|
return cfg.apiKey && cfg.apiKey.length > 0 ? cfg.apiKey : void 0;
|
|
13757
13796
|
}
|
|
@@ -14334,7 +14373,7 @@ function buildAuthCommand(opts) {
|
|
|
14334
14373
|
};
|
|
14335
14374
|
}
|
|
14336
14375
|
const keys = cfg.apiKeys ?? [];
|
|
14337
|
-
const
|
|
14376
|
+
const active2 = keys.find((k) => cfg && cfg.activeKey === k.label) ?? keys[0];
|
|
14338
14377
|
const lines2 = [
|
|
14339
14378
|
`${color.bold(pid)} ${cfg.family ? color.dim(`[${cfg.family}]`) : color.amber("[no family]")}`,
|
|
14340
14379
|
"",
|
|
@@ -14354,8 +14393,8 @@ function buildAuthCommand(opts) {
|
|
|
14354
14393
|
} else {
|
|
14355
14394
|
lines2.push(` ${color.dim("Keys:")}`);
|
|
14356
14395
|
for (const k of keys) {
|
|
14357
|
-
const marker = k.label ===
|
|
14358
|
-
const masked = k.label ===
|
|
14396
|
+
const marker = k.label === active2?.label ? color.green("\u25CF") : color.dim("\u25CB");
|
|
14397
|
+
const masked = k.label === active2?.label ? color.dim("(active \u2014 masked)") : color.dim("(masked)");
|
|
14359
14398
|
lines2.push(
|
|
14360
14399
|
` ${marker} ${color.bold(k.label.padEnd(18))} ${masked} ${color.dim(k.createdAt)}`
|
|
14361
14400
|
);
|
|
@@ -15280,9 +15319,9 @@ function buildContextCommand(opts) {
|
|
|
15280
15319
|
}
|
|
15281
15320
|
const trimmed = args.trim();
|
|
15282
15321
|
if (trimmed === "mode" || trimmed === "modes") {
|
|
15283
|
-
const
|
|
15322
|
+
const active2 = readPolicy(ctx)?.id ?? "balanced";
|
|
15284
15323
|
const msg2 = `${color.bold("Context Window Modes")}
|
|
15285
|
-
${formatContextWindowModeList(
|
|
15324
|
+
${formatContextWindowModeList(active2)}`;
|
|
15286
15325
|
opts.renderer.write(`${msg2}
|
|
15287
15326
|
`);
|
|
15288
15327
|
return { message: msg2 };
|
|
@@ -16145,7 +16184,7 @@ function scanPlaintextSecrets(node, prefix, findings) {
|
|
|
16145
16184
|
for (const [key, value] of Object.entries(node)) {
|
|
16146
16185
|
const path62 = prefix ? `${prefix}.${key}` : key;
|
|
16147
16186
|
if (typeof value === "string") {
|
|
16148
|
-
if (value.length > 0 && isSecretField
|
|
16187
|
+
if (value.length > 0 && isSecretField(key) && !value.startsWith(ENC_PREFIX)) {
|
|
16149
16188
|
findings.push({
|
|
16150
16189
|
path: path62,
|
|
16151
16190
|
problem: "looks like a plaintext secret (not vault-encrypted) \u2014 it will be encrypted on next boot",
|
|
@@ -20587,20 +20626,20 @@ ${color.dim("Pin one with /design <kit-id> [stack].")}`
|
|
|
20587
20626
|
return { message: color.green(`Overrides set: ${list || "(none)"}`) };
|
|
20588
20627
|
}
|
|
20589
20628
|
if (sub === "materialize") {
|
|
20590
|
-
const
|
|
20591
|
-
if (!
|
|
20629
|
+
const active2 = await loadActiveKit(opts.projectRoot);
|
|
20630
|
+
if (!active2) {
|
|
20592
20631
|
return { message: "No active kit. Pin one first: /design <kit-id>." };
|
|
20593
20632
|
}
|
|
20594
20633
|
const stackArg2 = tokens[1]?.toLowerCase();
|
|
20595
|
-
const matStack = stackArg2 && isDesignStack(stackArg2) ? stackArg2 :
|
|
20634
|
+
const matStack = stackArg2 && isDesignStack(stackArg2) ? stackArg2 : active2.stack && isDesignStack(active2.stack) ? active2.stack : "web";
|
|
20596
20635
|
const outPath = stackArg2 && !isDesignStack(stackArg2) ? tokens[1] : tokens[2];
|
|
20597
|
-
const raw = await loader.readTokens(
|
|
20598
|
-
if (!raw) return { message: `Kit "${
|
|
20599
|
-
const merged = applyTokenOverrides(raw,
|
|
20636
|
+
const raw = await loader.readTokens(active2.kit);
|
|
20637
|
+
if (!raw) return { message: `Kit "${active2.kit}" has no tokens.json.` };
|
|
20638
|
+
const merged = applyTokenOverrides(raw, active2.overrides);
|
|
20600
20639
|
const result = materializeTokens({
|
|
20601
20640
|
tokens: merged,
|
|
20602
20641
|
stack: matStack,
|
|
20603
|
-
kitId:
|
|
20642
|
+
kitId: active2.kit,
|
|
20604
20643
|
outPath
|
|
20605
20644
|
});
|
|
20606
20645
|
const abs = path5.join(opts.projectRoot, result.path);
|
|
@@ -20635,8 +20674,8 @@ ${menu}` };
|
|
|
20635
20674
|
}
|
|
20636
20675
|
async function runModePicker(modeStore, reader) {
|
|
20637
20676
|
const modes = await modeStore.listModes();
|
|
20638
|
-
const
|
|
20639
|
-
const activeIdx = modes.findIndex((m) => m.id ===
|
|
20677
|
+
const active2 = await modeStore.getActiveMode();
|
|
20678
|
+
const activeIdx = modes.findIndex((m) => m.id === active2?.id);
|
|
20640
20679
|
let cursor = activeIdx >= 0 ? activeIdx : 0;
|
|
20641
20680
|
const render = (currentCursor) => {
|
|
20642
20681
|
const lines = [];
|
|
@@ -20646,7 +20685,7 @@ ${color.bold(color.amber("WrongStack") + color.dim(" \u2014 Mode Selection"))}
|
|
|
20646
20685
|
lines.push(color.dim(" \u2191\u2193 navigate Enter select q quit\n"));
|
|
20647
20686
|
lines.push("");
|
|
20648
20687
|
for (const [i, m] of modes.entries()) {
|
|
20649
|
-
const mark = m.id ===
|
|
20688
|
+
const mark = m.id === active2?.id ? color.green(" [active]") : "";
|
|
20650
20689
|
const prefix = i === currentCursor ? color.bold("\u276F ") : " ";
|
|
20651
20690
|
const name = i === currentCursor ? color.bold(m.name) : m.name;
|
|
20652
20691
|
lines.push(` ${prefix}${name} ${color.dim(m.description)}${mark}`);
|
|
@@ -20700,7 +20739,7 @@ function buildModeCommand(opts) {
|
|
|
20700
20739
|
return { message: "Mode store not available in this context." };
|
|
20701
20740
|
}
|
|
20702
20741
|
const modes = await modeStore.listModes();
|
|
20703
|
-
const
|
|
20742
|
+
const active2 = await modeStore.getActiveMode();
|
|
20704
20743
|
if (!args.trim()) {
|
|
20705
20744
|
if (opts.inputReader) {
|
|
20706
20745
|
const selected = await runModePicker(modeStore, opts.inputReader);
|
|
@@ -20714,9 +20753,9 @@ function buildModeCommand(opts) {
|
|
|
20714
20753
|
${selected.description}`
|
|
20715
20754
|
};
|
|
20716
20755
|
}
|
|
20717
|
-
const lines = [`Current mode: ${
|
|
20756
|
+
const lines = [`Current mode: ${active2?.name ?? "none"}`, "", "Available modes:"];
|
|
20718
20757
|
for (const m of modes) {
|
|
20719
|
-
const mark = m.id ===
|
|
20758
|
+
const mark = m.id === active2?.id ? " [active]" : "";
|
|
20720
20759
|
lines.push(` ${m.id} \u2014 ${m.description}${mark}`);
|
|
20721
20760
|
}
|
|
20722
20761
|
return { message: lines.join("\n") };
|
|
@@ -21508,6 +21547,51 @@ function buildLoadCommand(opts) {
|
|
|
21508
21547
|
}
|
|
21509
21548
|
return killSession(targetId, force ? void 0 : opts.confirm);
|
|
21510
21549
|
}
|
|
21550
|
+
if (first === "rename") {
|
|
21551
|
+
const targetId = parts[1];
|
|
21552
|
+
if (!targetId) {
|
|
21553
|
+
return { message: color.yellow("Usage: /sessions rename <sessionId> [name...] (empty name clears)") };
|
|
21554
|
+
}
|
|
21555
|
+
if (!opts.sessionStore) {
|
|
21556
|
+
return { message: color.yellow("No session store configured.") };
|
|
21557
|
+
}
|
|
21558
|
+
const nameParts = parts.slice(2);
|
|
21559
|
+
const name = nameParts.join(" ").trim();
|
|
21560
|
+
try {
|
|
21561
|
+
const summary = await opts.sessionStore.rename(targetId, name);
|
|
21562
|
+
return {
|
|
21563
|
+
message: name ? color.green(`Renamed ${targetId} \u2192 "${name}"`) : color.green(`Cleared name on ${targetId} (title: "${summary.title}")`)
|
|
21564
|
+
};
|
|
21565
|
+
} catch (err) {
|
|
21566
|
+
return { message: color.red(`Rename failed: ${toErrorMessage$1(err)}`) };
|
|
21567
|
+
}
|
|
21568
|
+
}
|
|
21569
|
+
if (first === "delete") {
|
|
21570
|
+
const targetId = parts[1];
|
|
21571
|
+
if (!targetId) {
|
|
21572
|
+
return { message: color.yellow("Usage: /sessions delete <sessionId> [--force]") };
|
|
21573
|
+
}
|
|
21574
|
+
if (!opts.sessionStore) {
|
|
21575
|
+
return { message: color.yellow("No session store configured.") };
|
|
21576
|
+
}
|
|
21577
|
+
const currentId2 = opts.context?.session?.id;
|
|
21578
|
+
if (targetId === currentId2) {
|
|
21579
|
+
return {
|
|
21580
|
+
message: color.yellow("Cannot delete the active session. Resume or start another first.")
|
|
21581
|
+
};
|
|
21582
|
+
}
|
|
21583
|
+
const force = parts.includes("--force") || parts.includes("-y");
|
|
21584
|
+
if (!force && opts.confirm) {
|
|
21585
|
+
const ok = await opts.confirm(`Delete session ${targetId}? This cannot be undone.`, false);
|
|
21586
|
+
if (ok !== true) return { message: color.dim("Delete cancelled.") };
|
|
21587
|
+
}
|
|
21588
|
+
try {
|
|
21589
|
+
await opts.sessionStore.delete(targetId);
|
|
21590
|
+
return { message: color.green(`Deleted session ${targetId}`) };
|
|
21591
|
+
} catch (err) {
|
|
21592
|
+
return { message: color.red(`Delete failed: ${toErrorMessage$1(err)}`) };
|
|
21593
|
+
}
|
|
21594
|
+
}
|
|
21511
21595
|
const showIncomplete = parts.includes("--incomplete") || parts.includes("-i");
|
|
21512
21596
|
const recoverIdx = parts.indexOf("--recover");
|
|
21513
21597
|
const recoverTarget = recoverIdx >= 0 ? parts[recoverIdx + 1] : void 0;
|
|
@@ -21604,7 +21688,8 @@ function buildLoadCommand(opts) {
|
|
|
21604
21688
|
const date = color.dim(s.startedAt.slice(0, 16).replace("T", " "));
|
|
21605
21689
|
const isCurrent = s.id === currentId;
|
|
21606
21690
|
const marker = isCurrent ? color.cyan(" (current)") : "";
|
|
21607
|
-
|
|
21691
|
+
const label = s.name ? `${color.bold(s.id)} ${color.cyan(`(${s.name})`)}` : color.bold(s.id);
|
|
21692
|
+
return ` ${label}${marker}
|
|
21608
21693
|
${date} ${stat8}
|
|
21609
21694
|
${color.dim(s.title)}`;
|
|
21610
21695
|
});
|
|
@@ -23445,10 +23530,13 @@ function scanCommand(opts) {
|
|
|
23445
23530
|
""
|
|
23446
23531
|
];
|
|
23447
23532
|
lines.push(color.dim(" The TUI is a synchronous surface and cannot host a subagent itself."));
|
|
23448
|
-
lines.push(color.dim("
|
|
23533
|
+
lines.push(color.dim(" Start or use a director-capable session, then dispatch with:"));
|
|
23449
23534
|
lines.push("");
|
|
23450
|
-
lines.push(color.cyan("
|
|
23451
|
-
lines.push(
|
|
23535
|
+
lines.push(color.cyan(" /director"));
|
|
23536
|
+
lines.push(
|
|
23537
|
+
color.cyan(' /delegate --role=bug-hunter "Run a security scan of the current project"')
|
|
23538
|
+
);
|
|
23539
|
+
lines.push(color.cyan(" /fleet dispatch Run a security scan of the current project"));
|
|
23452
23540
|
lines.push(color.cyan(" \u2192 HQ \u2192 Security \u2192 Run scan"));
|
|
23453
23541
|
lines.push("");
|
|
23454
23542
|
lines.push(color.dim(" The subagent role is `bug-hunter`. Findings stream on the"));
|
|
@@ -26487,6 +26575,97 @@ function parseFlags2(args) {
|
|
|
26487
26575
|
);
|
|
26488
26576
|
}
|
|
26489
26577
|
|
|
26578
|
+
// src/fleet/supervisor-registry.ts
|
|
26579
|
+
var active = null;
|
|
26580
|
+
function setActiveFleetSupervisor(supervisor) {
|
|
26581
|
+
active = supervisor;
|
|
26582
|
+
}
|
|
26583
|
+
function getActiveFleetSupervisor() {
|
|
26584
|
+
return active;
|
|
26585
|
+
}
|
|
26586
|
+
|
|
26587
|
+
// src/slash-commands/supervisor.ts
|
|
26588
|
+
function fmtAge3(at) {
|
|
26589
|
+
const s = Math.max(0, Math.round((Date.now() - at) / 1e3));
|
|
26590
|
+
if (s < 60) return `${s}s ago`;
|
|
26591
|
+
if (s < 3600) return `${Math.round(s / 60)}m ago`;
|
|
26592
|
+
return `${Math.round(s / 3600)}h ago`;
|
|
26593
|
+
}
|
|
26594
|
+
var NO_SUPERVISOR_MSG = "No fleet supervisor is active. It starts automatically with the Director fleet (--director / first delegate) when a Brain is available and config fleet.supervisor.enabled is not false.";
|
|
26595
|
+
function buildSupervisorCommand(opts) {
|
|
26596
|
+
return {
|
|
26597
|
+
name: "supervisor",
|
|
26598
|
+
category: "Agent",
|
|
26599
|
+
argsHint: "[status|on|off|log [n]]",
|
|
26600
|
+
description: "Inspect or toggle the fleet supervisor (brain-gated rebalance/steer watcher).",
|
|
26601
|
+
help: [
|
|
26602
|
+
"Usage:",
|
|
26603
|
+
" /supervisor Show supervisor status (running state, config, last activity)",
|
|
26604
|
+
" /supervisor status Same as /supervisor",
|
|
26605
|
+
" /supervisor on Arm the supervision loop for this session",
|
|
26606
|
+
" /supervisor off Disarm it (no further automatic interventions)",
|
|
26607
|
+
" /supervisor log [n] Show the last n supervision entries (default 10)",
|
|
26608
|
+
"",
|
|
26609
|
+
"The supervisor watches the Director fleet and, gated by the Brain",
|
|
26610
|
+
"(/brain risk), rebalances pending tasks off busy workers, spawns",
|
|
26611
|
+
"helpers on deep backlogs, and steers stuck or failing workers.",
|
|
26612
|
+
"Configure via ~/.wrongstack/config.json \u2192 fleet.supervisor."
|
|
26613
|
+
].join("\n"),
|
|
26614
|
+
async run(args) {
|
|
26615
|
+
const supervisor = getActiveFleetSupervisor();
|
|
26616
|
+
const sub = (args.trim().split(/\s+/)[0] ?? "").toLowerCase();
|
|
26617
|
+
if (!supervisor) {
|
|
26618
|
+
opts.renderer.writeWarning(NO_SUPERVISOR_MSG);
|
|
26619
|
+
return { message: NO_SUPERVISOR_MSG };
|
|
26620
|
+
}
|
|
26621
|
+
if (sub === "on") {
|
|
26622
|
+
supervisor.start();
|
|
26623
|
+
const msg2 = `Fleet supervisor ${color.green("armed")} \u2014 evaluating every ${Math.round(supervisor.configSnapshot().intervalMs / 1e3)}s.`;
|
|
26624
|
+
opts.renderer.write(msg2);
|
|
26625
|
+
return { message: msg2 };
|
|
26626
|
+
}
|
|
26627
|
+
if (sub === "off") {
|
|
26628
|
+
supervisor.stop();
|
|
26629
|
+
const msg2 = `Fleet supervisor ${color.yellow("disarmed")} \u2014 no further automatic interventions this session.`;
|
|
26630
|
+
opts.renderer.write(msg2);
|
|
26631
|
+
return { message: msg2 };
|
|
26632
|
+
}
|
|
26633
|
+
if (sub === "log") {
|
|
26634
|
+
const n = Math.max(1, Number.parseInt(args.trim().split(/\s+/)[1] ?? "10", 10) || 10);
|
|
26635
|
+
const entries = supervisor.history().slice(-n);
|
|
26636
|
+
if (entries.length === 0) {
|
|
26637
|
+
const msg3 = "No supervision activity yet.";
|
|
26638
|
+
opts.renderer.write(msg3);
|
|
26639
|
+
return { message: msg3 };
|
|
26640
|
+
}
|
|
26641
|
+
const lines2 = entries.map((e) => {
|
|
26642
|
+
const who = e.subagentId ? ` ${color.cyan(e.subagentId)}` : "";
|
|
26643
|
+
const task = e.taskId ? ` task=${e.taskId.slice(0, 8)}` : "";
|
|
26644
|
+
const outcome = e.outcome === "approved" ? color.green(e.outcome) : e.outcome === "denied" || e.outcome === "error" ? color.red(e.outcome) : color.yellow(e.outcome);
|
|
26645
|
+
return `${color.dim(fmtAge3(e.at))} ${e.kind}${who}${task} \u2192 ${e.proposedAction} [${outcome}] ${color.dim(e.detail)}`;
|
|
26646
|
+
});
|
|
26647
|
+
const msg2 = lines2.join("\n");
|
|
26648
|
+
opts.renderer.write(msg2);
|
|
26649
|
+
return { message: msg2 };
|
|
26650
|
+
}
|
|
26651
|
+
const cfg = supervisor.configSnapshot();
|
|
26652
|
+
const history = supervisor.history();
|
|
26653
|
+
const last = history[history.length - 1];
|
|
26654
|
+
const lines = [
|
|
26655
|
+
`Fleet supervisor: ${supervisor.isRunning() ? color.green("armed") : color.yellow("disarmed")}`,
|
|
26656
|
+
` interval ${Math.round(cfg.intervalMs / 1e3)}s \xB7 cooldown ${Math.round(cfg.cooldownMs / 1e3)}s \xB7 max ${cfg.maxInterventionsPerSubagent} interventions/agent`,
|
|
26657
|
+
` signals: starvation>${Math.round(cfg.pinnedWaitMs / 1e3)}s \xB7 overload\u2265${cfg.overloadPinnedThreshold} pinned \xB7 backlog>${cfg.backlogFactor}\xD7workers \xB7 stuck>${Math.round(cfg.stuckMs / 1e3)}s \xB7 failstreak\u2265${cfg.failureStreak}`,
|
|
26658
|
+
` actions: retarget \u2713 \xB7 spawn ${cfg.allowSpawn ? "\u2713" : "\u2717"} \xB7 steer \u2713 \xB7 terminate ${cfg.allowTerminate ? "\u2713" : "\u2717 (config fleet.supervisor.allowTerminate)"}`,
|
|
26659
|
+
` activity: ${history.length} engagement(s)${last ? ` \u2014 last: ${last.kind} \u2192 ${last.proposedAction} [${last.outcome}] ${fmtAge3(last.at)}` : ""}`,
|
|
26660
|
+
color.dim(" decisions are gated by the Brain \u2014 see /brain (risk ceiling applies)")
|
|
26661
|
+
];
|
|
26662
|
+
const msg = lines.join("\n");
|
|
26663
|
+
opts.renderer.write(msg);
|
|
26664
|
+
return { message: msg };
|
|
26665
|
+
}
|
|
26666
|
+
};
|
|
26667
|
+
}
|
|
26668
|
+
|
|
26490
26669
|
// src/slash-commands/audit.ts
|
|
26491
26670
|
function buildAuditCommand(opts) {
|
|
26492
26671
|
return {
|
|
@@ -26624,6 +26803,7 @@ function buildBuiltinSlashCommands(opts) {
|
|
|
26624
26803
|
onPanelOpen: opts.onPanelOpen
|
|
26625
26804
|
}),
|
|
26626
26805
|
buildShadowCommand(opts),
|
|
26806
|
+
buildSupervisorCommand(opts),
|
|
26627
26807
|
buildAuditCommand(opts)
|
|
26628
26808
|
];
|
|
26629
26809
|
}
|
|
@@ -28778,7 +28958,7 @@ ${color.bold("Saved providers")} ${color.dim(`(${ids.length})`)}
|
|
|
28778
28958
|
const cfg = providers[id];
|
|
28779
28959
|
if (!cfg) continue;
|
|
28780
28960
|
const keys = normalizeKeys(cfg);
|
|
28781
|
-
const
|
|
28961
|
+
const active2 = cfg.activeKey ?? keys[0]?.label;
|
|
28782
28962
|
const famTag = cfg.family ? `${cfg.family}` : color.amber("no-family");
|
|
28783
28963
|
const aliasHint = cfg.type && cfg.type !== id ? color.dim(` (\u2192 ${cfg.type})`) : "";
|
|
28784
28964
|
const modelHint = cfg.models && cfg.models.length > 0 ? color.dim(` [${cfg.models.length} models]`) : "";
|
|
@@ -28797,7 +28977,7 @@ ${color.bold("Saved providers")} ${color.dim(`(${ids.length})`)}
|
|
|
28797
28977
|
`
|
|
28798
28978
|
);
|
|
28799
28979
|
for (const k of keys) {
|
|
28800
|
-
const marker = k.label ===
|
|
28980
|
+
const marker = k.label === active2 ? color.green("\u25CF") : color.dim("\u25CB");
|
|
28801
28981
|
deps.renderer.write(
|
|
28802
28982
|
` ${marker} ${k.label.padEnd(18)} ${maskedKey(k.apiKey)} ${color.dim(k.createdAt)}
|
|
28803
28983
|
`
|
|
@@ -28828,7 +29008,7 @@ async function runAuthStatus(deps, providerId) {
|
|
|
28828
29008
|
return 1;
|
|
28829
29009
|
}
|
|
28830
29010
|
const keys = normalizeKeys(cfg);
|
|
28831
|
-
const
|
|
29011
|
+
const active2 = cfg.activeKey ?? keys[0]?.label;
|
|
28832
29012
|
const lines = [
|
|
28833
29013
|
`
|
|
28834
29014
|
${color.bold(providerId)} ${cfg.family ? color.dim(`[${cfg.family}]`) : color.amber("[no family]")}`,
|
|
@@ -28849,7 +29029,7 @@ ${color.bold(providerId)} ${cfg.family ? color.dim(`[${cfg.family}]`) : color.am
|
|
|
28849
29029
|
} else {
|
|
28850
29030
|
lines.push(` ${color.dim("Keys:")}`);
|
|
28851
29031
|
for (const k of keys) {
|
|
28852
|
-
const marker = k.label ===
|
|
29032
|
+
const marker = k.label === active2 ? color.green("\u25CF") : color.dim("\u25CB");
|
|
28853
29033
|
lines.push(
|
|
28854
29034
|
` ${marker} ${color.bold(k.label.padEnd(18))} ${maskedKey(k.apiKey)} ${color.dim(k.createdAt)}`
|
|
28855
29035
|
);
|
|
@@ -34003,6 +34183,9 @@ function registerBuiltinTools(deps) {
|
|
|
34003
34183
|
deps.toolRegistry.register(
|
|
34004
34184
|
makeMailInboxTool({ projectDir: deps.wpaths.projectDir, events: deps.events })
|
|
34005
34185
|
);
|
|
34186
|
+
deps.toolRegistry.register(
|
|
34187
|
+
makeFleetStatusTool({ projectDir: deps.wpaths.projectDir, events: deps.events })
|
|
34188
|
+
);
|
|
34006
34189
|
applyToolDescriptionModes(deps.toolRegistry, deps.config.tools?.descriptionMode);
|
|
34007
34190
|
applyToolResultRenderModes(deps.toolRegistry, deps.config.tools?.resultRenderMode);
|
|
34008
34191
|
if (deps.config.tools?.disabledTools) {
|
|
@@ -35478,6 +35661,10 @@ async function resumeSession(ctx, sessionId) {
|
|
|
35478
35661
|
return null;
|
|
35479
35662
|
}
|
|
35480
35663
|
}
|
|
35664
|
+
var ANIMATION_STYLES = ["rainbow", "wave", "pulse", "dots", "breathe", "cycle"];
|
|
35665
|
+
function normalizeAnimationStyle(raw) {
|
|
35666
|
+
return typeof raw === "string" && ANIMATION_STYLES.includes(raw) ? raw : "rainbow";
|
|
35667
|
+
}
|
|
35481
35668
|
function createSettingsAdapter(ctx) {
|
|
35482
35669
|
const { configStore, wpaths, fleetStreamController, applyLiveSettings } = ctx;
|
|
35483
35670
|
const deriveFsAccess = deriveFsAccessPair;
|
|
@@ -35524,6 +35711,7 @@ function createSettingsAdapter(ctx) {
|
|
|
35524
35711
|
debugStream: cfg.debugStream ?? false,
|
|
35525
35712
|
statuslineMode: autonomy?.statuslineMode === "minimum" ? "minimum" : "detailed",
|
|
35526
35713
|
thinkingWord: normalizeTuiThinkingWord(autonomy?.thinkingWord),
|
|
35714
|
+
animationStyle: normalizeAnimationStyle(autonomy?.animationStyle),
|
|
35527
35715
|
configScope: cfg.configScope ?? "global",
|
|
35528
35716
|
enhanceDelayMs: cfg.autonomy?.enhanceDelayMs ?? 6e4,
|
|
35529
35717
|
enhanceEnabled: cfg.autonomy?.enhance ?? true,
|
|
@@ -35541,7 +35729,7 @@ function createSettingsAdapter(ctx) {
|
|
|
35541
35729
|
}
|
|
35542
35730
|
async function saveSettings(s) {
|
|
35543
35731
|
try {
|
|
35544
|
-
if (s.mode !== void 0 || s.delayMs !== void 0 || s.titleAnimation !== void 0 || s.yolo !== void 0 || s.streamFleet !== void 0 || s.chime !== void 0 || s.confirmExit !== void 0 || s.mouseMode !== void 0 || 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.contextMode !== void 0 || s.maxConcurrent !== void 0 || s.logLevel !== void 0 || s.auditLevel !== void 0 || s.indexOnStart !== void 0 || s.maxIterations !== void 0 || s.restrictFsToRoot !== void 0 || s.nextPrediction !== void 0 || s.debugStream !== void 0 || s.configScope !== void 0 || s.enhanceDelayMs !== void 0 || s.enhanceEnabled !== void 0 || s.enhanceLanguage !== void 0 || s.midRunSendPicker !== void 0 || s.statuslineMode !== void 0 || s.thinkingWord !== void 0 || s.autonomyNextPrompt !== void 0 || s.autoProceedMaxIterations !== void 0 || s.reasoningMode !== void 0 || s.reasoningEffort !== void 0 || s.reasoningPreserve !== void 0 || s.cacheTtl !== void 0) {
|
|
35732
|
+
if (s.mode !== void 0 || s.delayMs !== void 0 || s.titleAnimation !== void 0 || s.yolo !== void 0 || s.streamFleet !== void 0 || s.chime !== void 0 || s.confirmExit !== void 0 || s.mouseMode !== void 0 || 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.contextMode !== void 0 || s.maxConcurrent !== void 0 || s.logLevel !== void 0 || s.auditLevel !== void 0 || s.indexOnStart !== void 0 || s.maxIterations !== void 0 || s.restrictFsToRoot !== void 0 || s.nextPrediction !== void 0 || s.debugStream !== void 0 || s.configScope !== void 0 || s.enhanceDelayMs !== void 0 || s.enhanceEnabled !== void 0 || s.enhanceLanguage !== void 0 || s.midRunSendPicker !== void 0 || s.statuslineMode !== void 0 || s.thinkingWord !== void 0 || s.animationStyle !== void 0 || s.autonomyNextPrompt !== void 0 || s.autoProceedMaxIterations !== void 0 || s.reasoningMode !== void 0 || s.reasoningEffort !== void 0 || s.reasoningPreserve !== void 0 || s.cacheTtl !== void 0) {
|
|
35545
35733
|
const configScope = s.configScope ?? configStore.get().configScope ?? "global";
|
|
35546
35734
|
const targetPath = configScope === "project" && wpaths.inProjectConfig ? wpaths.inProjectConfig : wpaths.globalConfig;
|
|
35547
35735
|
let raw;
|
|
@@ -35574,6 +35762,7 @@ function createSettingsAdapter(ctx) {
|
|
|
35574
35762
|
if (s.statuslineMode !== void 0) autonomy.statuslineMode = s.statuslineMode;
|
|
35575
35763
|
if (s.thinkingWord !== void 0)
|
|
35576
35764
|
autonomy.thinkingWord = normalizeTuiThinkingWord(s.thinkingWord);
|
|
35765
|
+
if (s.animationStyle !== void 0) autonomy.animationStyle = s.animationStyle;
|
|
35577
35766
|
if (s.autonomyNextPrompt !== void 0) autonomy.autonomyNextPrompt = s.autonomyNextPrompt;
|
|
35578
35767
|
if (s.autoProceedMaxIterations !== void 0)
|
|
35579
35768
|
autonomy.autoProceedMaxIterations = s.autoProceedMaxIterations;
|
|
@@ -37482,7 +37671,7 @@ async function execute(deps) {
|
|
|
37482
37671
|
onProjectSelect: (slug, kind) => onProjectSelect(pickerCtx, slug, kind),
|
|
37483
37672
|
// `wrongstack quick` sets flags.quick — open the F3 agents monitor by default.
|
|
37484
37673
|
initialAgentsMonitorOpen: !!flags.quick,
|
|
37485
|
-
tokenSavingMode: normalizeTokenSavingTier(config.features.tokenSavingMode)
|
|
37674
|
+
tokenSavingMode: normalizeTokenSavingTier(config.features.tokenSavingMode),
|
|
37486
37675
|
toolCount: agent.tools.list().length,
|
|
37487
37676
|
onPanelOpen
|
|
37488
37677
|
});
|
|
@@ -37785,6 +37974,137 @@ function buildRoutingRunner(config, host) {
|
|
|
37785
37974
|
};
|
|
37786
37975
|
}
|
|
37787
37976
|
|
|
37977
|
+
// src/fleet/status-broadcast.ts
|
|
37978
|
+
var DEFAULT_MIN_INTERVAL_MS = 15e3;
|
|
37979
|
+
var DEFAULT_GLOBAL_PER_MINUTE = 20;
|
|
37980
|
+
var BODY_MAX = 200;
|
|
37981
|
+
function createFleetStatusBroadcaster(opts) {
|
|
37982
|
+
const cfg = opts.config;
|
|
37983
|
+
const minIntervalMs = cfg?.minIntervalMsPerAgent ?? DEFAULT_MIN_INTERVAL_MS;
|
|
37984
|
+
const perMinuteCap = cfg?.globalPerMinuteCap ?? DEFAULT_GLOBAL_PER_MINUTE;
|
|
37985
|
+
const disabled = cfg?.enabled === false;
|
|
37986
|
+
let mailbox = null;
|
|
37987
|
+
const mb = () => {
|
|
37988
|
+
if (!mailbox) mailbox = opts.mailboxFactory();
|
|
37989
|
+
return mailbox;
|
|
37990
|
+
};
|
|
37991
|
+
const nameOf = (id) => opts.subagentName?.(id) ?? id;
|
|
37992
|
+
const from = () => `fleet@${opts.sessionTag()}`;
|
|
37993
|
+
const lastSentAt = /* @__PURE__ */ new Map();
|
|
37994
|
+
const coalesced = /* @__PURE__ */ new Map();
|
|
37995
|
+
const sentTimestamps = [];
|
|
37996
|
+
let droppedSinceLastSend = 0;
|
|
37997
|
+
const announcedSpawn = /* @__PURE__ */ new Set();
|
|
37998
|
+
const announcedBudget = /* @__PURE__ */ new Set();
|
|
37999
|
+
const disposers = [];
|
|
38000
|
+
let stopped = false;
|
|
38001
|
+
function underGlobalCap(now) {
|
|
38002
|
+
while (sentTimestamps.length > 0 && now - (sentTimestamps[0] ?? 0) > 6e4) {
|
|
38003
|
+
sentTimestamps.shift();
|
|
38004
|
+
}
|
|
38005
|
+
return sentTimestamps.length < perMinuteCap;
|
|
38006
|
+
}
|
|
38007
|
+
function sendNow(subagentId, p) {
|
|
38008
|
+
const now = Date.now();
|
|
38009
|
+
if (!underGlobalCap(now)) {
|
|
38010
|
+
droppedSinceLastSend++;
|
|
38011
|
+
return;
|
|
38012
|
+
}
|
|
38013
|
+
sentTimestamps.push(now);
|
|
38014
|
+
lastSentAt.set(subagentId, now);
|
|
38015
|
+
let body = p.body;
|
|
38016
|
+
if (droppedSinceLastSend > 0) {
|
|
38017
|
+
body = `${body} [${droppedSinceLastSend} earlier status broadcast(s) dropped by rate cap]`;
|
|
38018
|
+
droppedSinceLastSend = 0;
|
|
38019
|
+
}
|
|
38020
|
+
void mb().send({
|
|
38021
|
+
from: from(),
|
|
38022
|
+
to: "*",
|
|
38023
|
+
type: "status",
|
|
38024
|
+
subject: p.subject,
|
|
38025
|
+
body: body.length > BODY_MAX ? `${body.slice(0, BODY_MAX)}\u2026` : body,
|
|
38026
|
+
priority: p.priority
|
|
38027
|
+
}).catch(() => {
|
|
38028
|
+
});
|
|
38029
|
+
}
|
|
38030
|
+
function broadcast(subagentId, p) {
|
|
38031
|
+
if (stopped) return;
|
|
38032
|
+
const now = Date.now();
|
|
38033
|
+
const last = lastSentAt.get(subagentId) ?? 0;
|
|
38034
|
+
const waitMs = last + minIntervalMs - now;
|
|
38035
|
+
if (waitMs <= 0) {
|
|
38036
|
+
sendNow(subagentId, p);
|
|
38037
|
+
return;
|
|
38038
|
+
}
|
|
38039
|
+
const existing = coalesced.get(subagentId);
|
|
38040
|
+
if (existing) {
|
|
38041
|
+
existing.pending = p;
|
|
38042
|
+
return;
|
|
38043
|
+
}
|
|
38044
|
+
const timer = setTimeout(() => {
|
|
38045
|
+
const entry = coalesced.get(subagentId);
|
|
38046
|
+
coalesced.delete(subagentId);
|
|
38047
|
+
if (!stopped && entry) sendNow(subagentId, entry.pending);
|
|
38048
|
+
}, waitMs);
|
|
38049
|
+
timer.unref?.();
|
|
38050
|
+
coalesced.set(subagentId, { pending: p, timer });
|
|
38051
|
+
}
|
|
38052
|
+
function heartbeat(input) {
|
|
38053
|
+
void mb().heartbeat(input).catch(() => {
|
|
38054
|
+
});
|
|
38055
|
+
}
|
|
38056
|
+
return {
|
|
38057
|
+
start() {
|
|
38058
|
+
if (disabled || stopped) return;
|
|
38059
|
+
disposers.push(
|
|
38060
|
+
opts.events.on("subagent.spawned", (e) => {
|
|
38061
|
+
if (announcedSpawn.has(e.subagentId)) return;
|
|
38062
|
+
announcedSpawn.add(e.subagentId);
|
|
38063
|
+
broadcast(e.subagentId, {
|
|
38064
|
+
subject: `[${e.name ?? nameOf(e.subagentId)}] joined the fleet`,
|
|
38065
|
+
body: `${e.name ?? nameOf(e.subagentId)} spawned${e.model ? ` on ${e.model}` : ""}${e.description ? ` \u2014 ${e.description.slice(0, 80)}` : ""}`,
|
|
38066
|
+
priority: "low"
|
|
38067
|
+
});
|
|
38068
|
+
}),
|
|
38069
|
+
opts.events.on("subagent.task_started", (e) => {
|
|
38070
|
+
heartbeat({
|
|
38071
|
+
agentId: e.subagentId,
|
|
38072
|
+
status: "running",
|
|
38073
|
+
currentTask: e.description?.slice(0, 80)
|
|
38074
|
+
});
|
|
38075
|
+
}),
|
|
38076
|
+
opts.events.on("subagent.task_completed", (e) => {
|
|
38077
|
+
heartbeat({ agentId: e.subagentId, status: "idle" });
|
|
38078
|
+
const name = nameOf(e.subagentId);
|
|
38079
|
+
const ok = e.status === "success";
|
|
38080
|
+
const secs = Math.round(e.durationMs / 1e3);
|
|
38081
|
+
broadcast(e.subagentId, {
|
|
38082
|
+
subject: `[${name}] task ${e.status}`,
|
|
38083
|
+
body: `${name} finished a task: ${e.status} \u2014 ${e.iterations} iter, ${e.toolCalls} tools, ${secs}s${!ok && e.error ? ` (${e.error.kind})` : ""}`,
|
|
38084
|
+
priority: ok ? "low" : "normal"
|
|
38085
|
+
});
|
|
38086
|
+
}),
|
|
38087
|
+
opts.events.on("subagent.budget_warning", (e) => {
|
|
38088
|
+
const key = `${e.subagentId}|${e.kind}`;
|
|
38089
|
+
if (announcedBudget.has(key)) return;
|
|
38090
|
+
announcedBudget.add(key);
|
|
38091
|
+
broadcast(e.subagentId, {
|
|
38092
|
+
subject: `[${nameOf(e.subagentId)}] budget pressure`,
|
|
38093
|
+
body: `${nameOf(e.subagentId)} hit its ${e.kind} limit (${e.used}/${e.limit}) \u2014 coordinator negotiating`,
|
|
38094
|
+
priority: "low"
|
|
38095
|
+
});
|
|
38096
|
+
})
|
|
38097
|
+
);
|
|
38098
|
+
},
|
|
38099
|
+
stop() {
|
|
38100
|
+
stopped = true;
|
|
38101
|
+
for (const d of disposers.splice(0)) d();
|
|
38102
|
+
for (const { timer } of coalesced.values()) clearTimeout(timer);
|
|
38103
|
+
coalesced.clear();
|
|
38104
|
+
}
|
|
38105
|
+
};
|
|
38106
|
+
}
|
|
38107
|
+
|
|
37788
38108
|
// src/fleet/host.ts
|
|
37789
38109
|
function buildShadowAgentTaskDescription(reason) {
|
|
37790
38110
|
return `Shadow Agent one-shot fleet monitor. Reason: ${reason}.
|
|
@@ -37861,6 +38181,14 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
37861
38181
|
shadowPassInFlight = false;
|
|
37862
38182
|
shadowQueuedProblem = null;
|
|
37863
38183
|
shadowActivityOffHandles = [];
|
|
38184
|
+
/** Peer-awareness status broadcaster (mailbox `status` mails on subagent
|
|
38185
|
+
* transitions + rich registry heartbeats). Started in buildDirector(),
|
|
38186
|
+
* stopped in dispose(). */
|
|
38187
|
+
statusBroadcaster = null;
|
|
38188
|
+
/** Brain-gated FleetSupervisor over this director's fleet. Built in
|
|
38189
|
+
* buildDirector() (when a BrainArbiter is available), stopped in
|
|
38190
|
+
* dispose(). Also published to the supervisor registry for /supervisor. */
|
|
38191
|
+
fleetSupervisor = null;
|
|
37864
38192
|
/**
|
|
37865
38193
|
* Force the lazy build path to run *now* and return the live Director,
|
|
37866
38194
|
* or null when director mode is off. Used by the CLI to register the
|
|
@@ -37939,8 +38267,12 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
37939
38267
|
fleetManager,
|
|
37940
38268
|
// pass so director.fleetManager is never undefined
|
|
37941
38269
|
brain: this.opts.brain,
|
|
37942
|
-
roster: FLEET_ROSTER
|
|
38270
|
+
roster: FLEET_ROSTER,
|
|
37943
38271
|
// pass so spawn_subagent recognizes shadow-agent role
|
|
38272
|
+
// Fire-and-forget report-back: when an assign_task completes with no
|
|
38273
|
+
// pending await, post the result to this session's leader via the
|
|
38274
|
+
// project mailbox (injected inline before the leader's next step).
|
|
38275
|
+
taskResultNotifier: (n) => this.reportTaskResultToLeader(n)
|
|
37944
38276
|
});
|
|
37945
38277
|
this.director.on("task.completed", ({ task, result }) => {
|
|
37946
38278
|
this.fleetManager?.removePendingTask(task.id);
|
|
@@ -37966,6 +38298,15 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
37966
38298
|
agentMonitor.setFleetBus(this.director.fleet);
|
|
37967
38299
|
agentMonitor.start();
|
|
37968
38300
|
}
|
|
38301
|
+
this.statusBroadcaster = createFleetStatusBroadcaster({
|
|
38302
|
+
events: this.deps.events,
|
|
38303
|
+
mailboxFactory: () => new GlobalMailbox(this.mailboxProjectDir(), this.deps.events),
|
|
38304
|
+
sessionTag: () => mailboxSessionTag(this.deps.session.id),
|
|
38305
|
+
subagentName: (id) => this.director?.status().subagents.find((s) => s.id === id)?.name,
|
|
38306
|
+
config: config.fleet?.statusBroadcasts
|
|
38307
|
+
});
|
|
38308
|
+
this.statusBroadcaster.start();
|
|
38309
|
+
this.buildFleetSupervisor(config);
|
|
37969
38310
|
this.directorOffHandles.push(
|
|
37970
38311
|
this.director.fleet.filter("budget.threshold_reached", (e) => {
|
|
37971
38312
|
const payload = e.payload;
|
|
@@ -38251,7 +38592,7 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
38251
38592
|
const subCwd = subCfg.cwd ?? this.deps.cwd;
|
|
38252
38593
|
let onlineAgents = [];
|
|
38253
38594
|
try {
|
|
38254
|
-
const subagentMailbox = new GlobalMailbox(this.
|
|
38595
|
+
const subagentMailbox = new GlobalMailbox(this.mailboxProjectDir());
|
|
38255
38596
|
onlineAgents = await subagentMailbox.getAgentStatuses();
|
|
38256
38597
|
} catch {
|
|
38257
38598
|
}
|
|
@@ -38607,6 +38948,107 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
38607
38948
|
}
|
|
38608
38949
|
return [...caps];
|
|
38609
38950
|
}
|
|
38951
|
+
/** Resolved shared-mailbox project dir — same as the leader's checker. */
|
|
38952
|
+
mailboxProjectDir() {
|
|
38953
|
+
return this.opts.mailboxProjectDir ?? resolveProjectDir(this.deps.projectRoot, wstackGlobalRoot());
|
|
38954
|
+
}
|
|
38955
|
+
/**
|
|
38956
|
+
* Construct + start the brain-gated FleetSupervisor over this director's
|
|
38957
|
+
* fleet. Requires a BrainArbiter (production always wires one); without
|
|
38958
|
+
* it there is no safe decision path, so supervision stays off. All
|
|
38959
|
+
* actions flow through Director APIs — the supervisor never touches
|
|
38960
|
+
* coordinator internals or budget negotiation.
|
|
38961
|
+
*/
|
|
38962
|
+
buildFleetSupervisor(config) {
|
|
38963
|
+
const director = this.director;
|
|
38964
|
+
const brain = this.opts.brain;
|
|
38965
|
+
const supervisorCfg = config.fleet?.supervisor;
|
|
38966
|
+
if (!director || !brain || supervisorCfg?.enabled === false) return;
|
|
38967
|
+
const supTag = () => mailboxSessionTag(this.deps.session.id);
|
|
38968
|
+
const mailbox = () => new GlobalMailbox(this.mailboxProjectDir(), this.deps.events);
|
|
38969
|
+
this.fleetSupervisor = new FleetSupervisor({
|
|
38970
|
+
events: this.deps.events,
|
|
38971
|
+
fleet: director.fleet,
|
|
38972
|
+
brain,
|
|
38973
|
+
sessionId: () => this.deps.session.id,
|
|
38974
|
+
config: supervisorCfg,
|
|
38975
|
+
source: {
|
|
38976
|
+
subagents: () => director.status().subagents,
|
|
38977
|
+
listPendingTasks: () => director.listPendingTasks(),
|
|
38978
|
+
isWorkComplete: () => director.isWorkComplete()
|
|
38979
|
+
},
|
|
38980
|
+
actions: {
|
|
38981
|
+
retargetPendingTask: (taskId, subagentId) => director.retargetPendingTask(taskId, subagentId),
|
|
38982
|
+
spawnHelper: async ({ reason }) => {
|
|
38983
|
+
try {
|
|
38984
|
+
const subagentId = await director.spawn({
|
|
38985
|
+
id: `helper-${randomUUID().slice(0, 8)}`,
|
|
38986
|
+
name: "fleet helper",
|
|
38987
|
+
systemPromptOverride: `You are a helper worker spawned by the fleet supervisor to drain a task backlog (${reason}). Complete assigned tasks efficiently and report results.`
|
|
38988
|
+
});
|
|
38989
|
+
return { subagentId };
|
|
38990
|
+
} catch (err) {
|
|
38991
|
+
return { error: toErrorMessage(err) };
|
|
38992
|
+
}
|
|
38993
|
+
},
|
|
38994
|
+
steerAgent: async (subagentId, subject, body) => {
|
|
38995
|
+
await mailbox().send({
|
|
38996
|
+
from: `supervisor@${supTag()}`,
|
|
38997
|
+
to: `${subagentId}@${supTag()}`,
|
|
38998
|
+
type: "steer",
|
|
38999
|
+
subject,
|
|
39000
|
+
body,
|
|
39001
|
+
priority: "high"
|
|
39002
|
+
});
|
|
39003
|
+
},
|
|
39004
|
+
notifyLeader: async (subject, body) => {
|
|
39005
|
+
const leaderId2 = this.opts.getLeaderMailboxId?.() ?? `leader@${supTag()}`;
|
|
39006
|
+
await mailbox().send({
|
|
39007
|
+
from: `supervisor@${supTag()}`,
|
|
39008
|
+
to: leaderId2,
|
|
39009
|
+
type: "status",
|
|
39010
|
+
subject,
|
|
39011
|
+
body,
|
|
39012
|
+
priority: "normal"
|
|
39013
|
+
});
|
|
39014
|
+
},
|
|
39015
|
+
terminate: (subagentId) => director.terminate(subagentId)
|
|
39016
|
+
}
|
|
39017
|
+
});
|
|
39018
|
+
this.fleetSupervisor.start();
|
|
39019
|
+
setActiveFleetSupervisor(this.fleetSupervisor);
|
|
39020
|
+
}
|
|
39021
|
+
/**
|
|
39022
|
+
* Fire-and-forget report-back (wired as the Director's
|
|
39023
|
+
* `taskResultNotifier`): posts a non-awaited task's outcome to THIS
|
|
39024
|
+
* session's leader via the project mailbox. The leader's mailbox checker
|
|
39025
|
+
* injects it inline before the next step, so `assign_task`-without-await
|
|
39026
|
+
* results reach the conversation without polling. The body carries a
|
|
39027
|
+
* short excerpt only — the full result stays in the director's completed
|
|
39028
|
+
* cache, retrievable via `await_tasks`/`roll_up`.
|
|
39029
|
+
*/
|
|
39030
|
+
async reportTaskResultToLeader(n) {
|
|
39031
|
+
try {
|
|
39032
|
+
const leaderId2 = this.opts.getLeaderMailboxId?.() ?? `leader@${mailboxSessionTag(this.deps.session.id)}`;
|
|
39033
|
+
const mailbox = new GlobalMailbox(this.mailboxProjectDir(), this.deps.events);
|
|
39034
|
+
const ok = n.status === "success";
|
|
39035
|
+
const MAX_BODY = 700;
|
|
39036
|
+
const raw = (ok ? n.resultText : n.errorText ?? n.status) ?? "(no textual result)";
|
|
39037
|
+
const excerpt = raw.length > MAX_BODY ? `${raw.slice(0, MAX_BODY)}\u2026` : raw;
|
|
39038
|
+
const title = n.title.length > 80 ? `${n.title.slice(0, 80)}\u2026` : n.title;
|
|
39039
|
+
await mailbox.send({
|
|
39040
|
+
from: n.subagentName ?? n.subagentId,
|
|
39041
|
+
to: leaderId2,
|
|
39042
|
+
type: "result",
|
|
39043
|
+
subject: `[task ${n.status}] ${title}`,
|
|
39044
|
+
body: `${excerpt}
|
|
39045
|
+
|
|
39046
|
+
(taskId: ${n.taskId} \u2014 ${n.iterations} iterations, ${n.toolCalls} tool calls, ${Math.round(n.durationMs / 1e3)}s. Full result: await_tasks(["${n.taskId}"]) or roll_up.)`,
|
|
39047
|
+
priority: ok ? "normal" : "high"
|
|
39048
|
+
});
|
|
39049
|
+
} catch {
|
|
39050
|
+
}
|
|
39051
|
+
}
|
|
38610
39052
|
subagentToolRegistry(allow) {
|
|
38611
39053
|
if (!allow || allow.length === 0) return this.deps.toolRegistry;
|
|
38612
39054
|
const sub = new ToolRegistry();
|
|
@@ -38945,6 +39387,11 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
38945
39387
|
this.directorOffHandles.length = 0;
|
|
38946
39388
|
this.coordinatorOffHandle?.();
|
|
38947
39389
|
this.coordinatorOffHandle = null;
|
|
39390
|
+
this.statusBroadcaster?.stop();
|
|
39391
|
+
this.statusBroadcaster = null;
|
|
39392
|
+
this.fleetSupervisor?.stop();
|
|
39393
|
+
if (this.fleetSupervisor) setActiveFleetSupervisor(null);
|
|
39394
|
+
this.fleetSupervisor = null;
|
|
38948
39395
|
this.adaptiveConcurrencyController?.dispose();
|
|
38949
39396
|
this.adaptiveConcurrencyController = void 0;
|
|
38950
39397
|
if (this.director) {
|
|
@@ -40150,12 +40597,12 @@ async function main(argv) {
|
|
|
40150
40597
|
const p = all[providerId];
|
|
40151
40598
|
if (!p) return;
|
|
40152
40599
|
const keys = normalizeKeys(p);
|
|
40153
|
-
const
|
|
40154
|
-
if (!
|
|
40155
|
-
|
|
40156
|
-
|
|
40157
|
-
|
|
40158
|
-
if (creds.accountId)
|
|
40600
|
+
const active2 = p.activeKey ? keys.find((k) => k.label === p.activeKey) : keys[0];
|
|
40601
|
+
if (!active2) return;
|
|
40602
|
+
active2.apiKey = creds.accessToken;
|
|
40603
|
+
active2.refreshToken = creds.refreshToken;
|
|
40604
|
+
active2.expiresAt = new Date(creds.expiresAt).toISOString();
|
|
40605
|
+
if (creds.accountId) active2.accountId = creds.accountId;
|
|
40159
40606
|
writeKeysBack(p, keys);
|
|
40160
40607
|
}).catch(() => {
|
|
40161
40608
|
});
|
|
@@ -42104,24 +42551,24 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
42104
42551
|
},
|
|
42105
42552
|
onSddRetryAllFailed: () => sddRunRegistry.getActive()?.retryAllFailed() ?? 0,
|
|
42106
42553
|
onSddSplitTask: (taskId, subtasks) => {
|
|
42107
|
-
const
|
|
42108
|
-
if (!
|
|
42109
|
-
const snap =
|
|
42554
|
+
const active2 = sddRunRegistry.getActive();
|
|
42555
|
+
if (!active2) return null;
|
|
42556
|
+
const snap = active2.snapshot();
|
|
42110
42557
|
const match = snap.tasks.find((t) => t.id === taskId || t.shortId === taskId);
|
|
42111
|
-
const ids =
|
|
42558
|
+
const ids = active2.splitTask(match?.id ?? taskId, subtasks);
|
|
42112
42559
|
return ids.length ? ids : null;
|
|
42113
42560
|
},
|
|
42114
42561
|
// ── SDD lifecycle: clean worktrees · rollback commits · destroy project ──
|
|
42115
42562
|
onSddCleanWorktrees: async () => {
|
|
42116
|
-
const
|
|
42117
|
-
if (
|
|
42563
|
+
const active2 = sddRunRegistry.getActive();
|
|
42564
|
+
if (active2) return active2.cleanupWorktrees();
|
|
42118
42565
|
const core = await import('@wrongstack/core');
|
|
42119
42566
|
const { removed } = await core.cleanupSddWorktrees(projectRoot);
|
|
42120
42567
|
return removed;
|
|
42121
42568
|
},
|
|
42122
42569
|
onSddRollback: async () => {
|
|
42123
|
-
const
|
|
42124
|
-
if (
|
|
42570
|
+
const active2 = sddRunRegistry.getActive();
|
|
42571
|
+
if (active2) return active2.rollback();
|
|
42125
42572
|
const core = await import('@wrongstack/core');
|
|
42126
42573
|
return core.rollbackSddRunFromDisk({ projectRoot, boardsDir: wpaths.projectSddBoards });
|
|
42127
42574
|
},
|
|
@@ -42408,9 +42855,9 @@ Reply YES to auto-proceed, NO to wait for human input.`
|
|
|
42408
42855
|
// Stops a live run for destroy; refuses clean/rollback while one is still
|
|
42409
42856
|
// running. Reuses the same disk-backed core helper as the WebUI + /sdd.
|
|
42410
42857
|
onSddLifecycle: async (op, lcOpts) => {
|
|
42411
|
-
const
|
|
42412
|
-
if (op === "destroy")
|
|
42413
|
-
else if (
|
|
42858
|
+
const active2 = sddRunRegistry.getActive();
|
|
42859
|
+
if (op === "destroy") active2?.stop();
|
|
42860
|
+
else if (active2?.isRunning()) {
|
|
42414
42861
|
return { op, ok: false, reason: "Stop the run first (Ctrl+C), then retry." };
|
|
42415
42862
|
}
|
|
42416
42863
|
const core = await import('@wrongstack/core');
|