@wrongstack/cli 0.280.1 → 0.281.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 +553 -109
- 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
|
});
|
|
@@ -26487,6 +26572,97 @@ function parseFlags2(args) {
|
|
|
26487
26572
|
);
|
|
26488
26573
|
}
|
|
26489
26574
|
|
|
26575
|
+
// src/fleet/supervisor-registry.ts
|
|
26576
|
+
var active = null;
|
|
26577
|
+
function setActiveFleetSupervisor(supervisor) {
|
|
26578
|
+
active = supervisor;
|
|
26579
|
+
}
|
|
26580
|
+
function getActiveFleetSupervisor() {
|
|
26581
|
+
return active;
|
|
26582
|
+
}
|
|
26583
|
+
|
|
26584
|
+
// src/slash-commands/supervisor.ts
|
|
26585
|
+
function fmtAge3(at) {
|
|
26586
|
+
const s = Math.max(0, Math.round((Date.now() - at) / 1e3));
|
|
26587
|
+
if (s < 60) return `${s}s ago`;
|
|
26588
|
+
if (s < 3600) return `${Math.round(s / 60)}m ago`;
|
|
26589
|
+
return `${Math.round(s / 3600)}h ago`;
|
|
26590
|
+
}
|
|
26591
|
+
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.";
|
|
26592
|
+
function buildSupervisorCommand(opts) {
|
|
26593
|
+
return {
|
|
26594
|
+
name: "supervisor",
|
|
26595
|
+
category: "Agent",
|
|
26596
|
+
argsHint: "[status|on|off|log [n]]",
|
|
26597
|
+
description: "Inspect or toggle the fleet supervisor (brain-gated rebalance/steer watcher).",
|
|
26598
|
+
help: [
|
|
26599
|
+
"Usage:",
|
|
26600
|
+
" /supervisor Show supervisor status (running state, config, last activity)",
|
|
26601
|
+
" /supervisor status Same as /supervisor",
|
|
26602
|
+
" /supervisor on Arm the supervision loop for this session",
|
|
26603
|
+
" /supervisor off Disarm it (no further automatic interventions)",
|
|
26604
|
+
" /supervisor log [n] Show the last n supervision entries (default 10)",
|
|
26605
|
+
"",
|
|
26606
|
+
"The supervisor watches the Director fleet and, gated by the Brain",
|
|
26607
|
+
"(/brain risk), rebalances pending tasks off busy workers, spawns",
|
|
26608
|
+
"helpers on deep backlogs, and steers stuck or failing workers.",
|
|
26609
|
+
"Configure via ~/.wrongstack/config.json \u2192 fleet.supervisor."
|
|
26610
|
+
].join("\n"),
|
|
26611
|
+
async run(args) {
|
|
26612
|
+
const supervisor = getActiveFleetSupervisor();
|
|
26613
|
+
const sub = (args.trim().split(/\s+/)[0] ?? "").toLowerCase();
|
|
26614
|
+
if (!supervisor) {
|
|
26615
|
+
opts.renderer.writeWarning(NO_SUPERVISOR_MSG);
|
|
26616
|
+
return { message: NO_SUPERVISOR_MSG };
|
|
26617
|
+
}
|
|
26618
|
+
if (sub === "on") {
|
|
26619
|
+
supervisor.start();
|
|
26620
|
+
const msg2 = `Fleet supervisor ${color.green("armed")} \u2014 evaluating every ${Math.round(supervisor.configSnapshot().intervalMs / 1e3)}s.`;
|
|
26621
|
+
opts.renderer.write(msg2);
|
|
26622
|
+
return { message: msg2 };
|
|
26623
|
+
}
|
|
26624
|
+
if (sub === "off") {
|
|
26625
|
+
supervisor.stop();
|
|
26626
|
+
const msg2 = `Fleet supervisor ${color.yellow("disarmed")} \u2014 no further automatic interventions this session.`;
|
|
26627
|
+
opts.renderer.write(msg2);
|
|
26628
|
+
return { message: msg2 };
|
|
26629
|
+
}
|
|
26630
|
+
if (sub === "log") {
|
|
26631
|
+
const n = Math.max(1, Number.parseInt(args.trim().split(/\s+/)[1] ?? "10", 10) || 10);
|
|
26632
|
+
const entries = supervisor.history().slice(-n);
|
|
26633
|
+
if (entries.length === 0) {
|
|
26634
|
+
const msg3 = "No supervision activity yet.";
|
|
26635
|
+
opts.renderer.write(msg3);
|
|
26636
|
+
return { message: msg3 };
|
|
26637
|
+
}
|
|
26638
|
+
const lines2 = entries.map((e) => {
|
|
26639
|
+
const who = e.subagentId ? ` ${color.cyan(e.subagentId)}` : "";
|
|
26640
|
+
const task = e.taskId ? ` task=${e.taskId.slice(0, 8)}` : "";
|
|
26641
|
+
const outcome = e.outcome === "approved" ? color.green(e.outcome) : e.outcome === "denied" || e.outcome === "error" ? color.red(e.outcome) : color.yellow(e.outcome);
|
|
26642
|
+
return `${color.dim(fmtAge3(e.at))} ${e.kind}${who}${task} \u2192 ${e.proposedAction} [${outcome}] ${color.dim(e.detail)}`;
|
|
26643
|
+
});
|
|
26644
|
+
const msg2 = lines2.join("\n");
|
|
26645
|
+
opts.renderer.write(msg2);
|
|
26646
|
+
return { message: msg2 };
|
|
26647
|
+
}
|
|
26648
|
+
const cfg = supervisor.configSnapshot();
|
|
26649
|
+
const history = supervisor.history();
|
|
26650
|
+
const last = history[history.length - 1];
|
|
26651
|
+
const lines = [
|
|
26652
|
+
`Fleet supervisor: ${supervisor.isRunning() ? color.green("armed") : color.yellow("disarmed")}`,
|
|
26653
|
+
` interval ${Math.round(cfg.intervalMs / 1e3)}s \xB7 cooldown ${Math.round(cfg.cooldownMs / 1e3)}s \xB7 max ${cfg.maxInterventionsPerSubagent} interventions/agent`,
|
|
26654
|
+
` 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}`,
|
|
26655
|
+
` actions: retarget \u2713 \xB7 spawn ${cfg.allowSpawn ? "\u2713" : "\u2717"} \xB7 steer \u2713 \xB7 terminate ${cfg.allowTerminate ? "\u2713" : "\u2717 (config fleet.supervisor.allowTerminate)"}`,
|
|
26656
|
+
` activity: ${history.length} engagement(s)${last ? ` \u2014 last: ${last.kind} \u2192 ${last.proposedAction} [${last.outcome}] ${fmtAge3(last.at)}` : ""}`,
|
|
26657
|
+
color.dim(" decisions are gated by the Brain \u2014 see /brain (risk ceiling applies)")
|
|
26658
|
+
];
|
|
26659
|
+
const msg = lines.join("\n");
|
|
26660
|
+
opts.renderer.write(msg);
|
|
26661
|
+
return { message: msg };
|
|
26662
|
+
}
|
|
26663
|
+
};
|
|
26664
|
+
}
|
|
26665
|
+
|
|
26490
26666
|
// src/slash-commands/audit.ts
|
|
26491
26667
|
function buildAuditCommand(opts) {
|
|
26492
26668
|
return {
|
|
@@ -26624,6 +26800,7 @@ function buildBuiltinSlashCommands(opts) {
|
|
|
26624
26800
|
onPanelOpen: opts.onPanelOpen
|
|
26625
26801
|
}),
|
|
26626
26802
|
buildShadowCommand(opts),
|
|
26803
|
+
buildSupervisorCommand(opts),
|
|
26627
26804
|
buildAuditCommand(opts)
|
|
26628
26805
|
];
|
|
26629
26806
|
}
|
|
@@ -28778,7 +28955,7 @@ ${color.bold("Saved providers")} ${color.dim(`(${ids.length})`)}
|
|
|
28778
28955
|
const cfg = providers[id];
|
|
28779
28956
|
if (!cfg) continue;
|
|
28780
28957
|
const keys = normalizeKeys(cfg);
|
|
28781
|
-
const
|
|
28958
|
+
const active2 = cfg.activeKey ?? keys[0]?.label;
|
|
28782
28959
|
const famTag = cfg.family ? `${cfg.family}` : color.amber("no-family");
|
|
28783
28960
|
const aliasHint = cfg.type && cfg.type !== id ? color.dim(` (\u2192 ${cfg.type})`) : "";
|
|
28784
28961
|
const modelHint = cfg.models && cfg.models.length > 0 ? color.dim(` [${cfg.models.length} models]`) : "";
|
|
@@ -28797,7 +28974,7 @@ ${color.bold("Saved providers")} ${color.dim(`(${ids.length})`)}
|
|
|
28797
28974
|
`
|
|
28798
28975
|
);
|
|
28799
28976
|
for (const k of keys) {
|
|
28800
|
-
const marker = k.label ===
|
|
28977
|
+
const marker = k.label === active2 ? color.green("\u25CF") : color.dim("\u25CB");
|
|
28801
28978
|
deps.renderer.write(
|
|
28802
28979
|
` ${marker} ${k.label.padEnd(18)} ${maskedKey(k.apiKey)} ${color.dim(k.createdAt)}
|
|
28803
28980
|
`
|
|
@@ -28828,7 +29005,7 @@ async function runAuthStatus(deps, providerId) {
|
|
|
28828
29005
|
return 1;
|
|
28829
29006
|
}
|
|
28830
29007
|
const keys = normalizeKeys(cfg);
|
|
28831
|
-
const
|
|
29008
|
+
const active2 = cfg.activeKey ?? keys[0]?.label;
|
|
28832
29009
|
const lines = [
|
|
28833
29010
|
`
|
|
28834
29011
|
${color.bold(providerId)} ${cfg.family ? color.dim(`[${cfg.family}]`) : color.amber("[no family]")}`,
|
|
@@ -28849,7 +29026,7 @@ ${color.bold(providerId)} ${cfg.family ? color.dim(`[${cfg.family}]`) : color.am
|
|
|
28849
29026
|
} else {
|
|
28850
29027
|
lines.push(` ${color.dim("Keys:")}`);
|
|
28851
29028
|
for (const k of keys) {
|
|
28852
|
-
const marker = k.label ===
|
|
29029
|
+
const marker = k.label === active2 ? color.green("\u25CF") : color.dim("\u25CB");
|
|
28853
29030
|
lines.push(
|
|
28854
29031
|
` ${marker} ${color.bold(k.label.padEnd(18))} ${maskedKey(k.apiKey)} ${color.dim(k.createdAt)}`
|
|
28855
29032
|
);
|
|
@@ -34003,6 +34180,9 @@ function registerBuiltinTools(deps) {
|
|
|
34003
34180
|
deps.toolRegistry.register(
|
|
34004
34181
|
makeMailInboxTool({ projectDir: deps.wpaths.projectDir, events: deps.events })
|
|
34005
34182
|
);
|
|
34183
|
+
deps.toolRegistry.register(
|
|
34184
|
+
makeFleetStatusTool({ projectDir: deps.wpaths.projectDir, events: deps.events })
|
|
34185
|
+
);
|
|
34006
34186
|
applyToolDescriptionModes(deps.toolRegistry, deps.config.tools?.descriptionMode);
|
|
34007
34187
|
applyToolResultRenderModes(deps.toolRegistry, deps.config.tools?.resultRenderMode);
|
|
34008
34188
|
if (deps.config.tools?.disabledTools) {
|
|
@@ -35478,6 +35658,10 @@ async function resumeSession(ctx, sessionId) {
|
|
|
35478
35658
|
return null;
|
|
35479
35659
|
}
|
|
35480
35660
|
}
|
|
35661
|
+
var ANIMATION_STYLES = ["rainbow", "wave", "pulse", "dots", "breathe", "cycle"];
|
|
35662
|
+
function normalizeAnimationStyle(raw) {
|
|
35663
|
+
return typeof raw === "string" && ANIMATION_STYLES.includes(raw) ? raw : "rainbow";
|
|
35664
|
+
}
|
|
35481
35665
|
function createSettingsAdapter(ctx) {
|
|
35482
35666
|
const { configStore, wpaths, fleetStreamController, applyLiveSettings } = ctx;
|
|
35483
35667
|
const deriveFsAccess = deriveFsAccessPair;
|
|
@@ -35524,6 +35708,7 @@ function createSettingsAdapter(ctx) {
|
|
|
35524
35708
|
debugStream: cfg.debugStream ?? false,
|
|
35525
35709
|
statuslineMode: autonomy?.statuslineMode === "minimum" ? "minimum" : "detailed",
|
|
35526
35710
|
thinkingWord: normalizeTuiThinkingWord(autonomy?.thinkingWord),
|
|
35711
|
+
animationStyle: normalizeAnimationStyle(autonomy?.animationStyle),
|
|
35527
35712
|
configScope: cfg.configScope ?? "global",
|
|
35528
35713
|
enhanceDelayMs: cfg.autonomy?.enhanceDelayMs ?? 6e4,
|
|
35529
35714
|
enhanceEnabled: cfg.autonomy?.enhance ?? true,
|
|
@@ -35541,7 +35726,7 @@ function createSettingsAdapter(ctx) {
|
|
|
35541
35726
|
}
|
|
35542
35727
|
async function saveSettings(s) {
|
|
35543
35728
|
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) {
|
|
35729
|
+
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
35730
|
const configScope = s.configScope ?? configStore.get().configScope ?? "global";
|
|
35546
35731
|
const targetPath = configScope === "project" && wpaths.inProjectConfig ? wpaths.inProjectConfig : wpaths.globalConfig;
|
|
35547
35732
|
let raw;
|
|
@@ -35574,6 +35759,7 @@ function createSettingsAdapter(ctx) {
|
|
|
35574
35759
|
if (s.statuslineMode !== void 0) autonomy.statuslineMode = s.statuslineMode;
|
|
35575
35760
|
if (s.thinkingWord !== void 0)
|
|
35576
35761
|
autonomy.thinkingWord = normalizeTuiThinkingWord(s.thinkingWord);
|
|
35762
|
+
if (s.animationStyle !== void 0) autonomy.animationStyle = s.animationStyle;
|
|
35577
35763
|
if (s.autonomyNextPrompt !== void 0) autonomy.autonomyNextPrompt = s.autonomyNextPrompt;
|
|
35578
35764
|
if (s.autoProceedMaxIterations !== void 0)
|
|
35579
35765
|
autonomy.autoProceedMaxIterations = s.autoProceedMaxIterations;
|
|
@@ -37482,7 +37668,7 @@ async function execute(deps) {
|
|
|
37482
37668
|
onProjectSelect: (slug, kind) => onProjectSelect(pickerCtx, slug, kind),
|
|
37483
37669
|
// `wrongstack quick` sets flags.quick — open the F3 agents monitor by default.
|
|
37484
37670
|
initialAgentsMonitorOpen: !!flags.quick,
|
|
37485
|
-
tokenSavingMode: normalizeTokenSavingTier(config.features.tokenSavingMode)
|
|
37671
|
+
tokenSavingMode: normalizeTokenSavingTier(config.features.tokenSavingMode),
|
|
37486
37672
|
toolCount: agent.tools.list().length,
|
|
37487
37673
|
onPanelOpen
|
|
37488
37674
|
});
|
|
@@ -37785,6 +37971,137 @@ function buildRoutingRunner(config, host) {
|
|
|
37785
37971
|
};
|
|
37786
37972
|
}
|
|
37787
37973
|
|
|
37974
|
+
// src/fleet/status-broadcast.ts
|
|
37975
|
+
var DEFAULT_MIN_INTERVAL_MS = 15e3;
|
|
37976
|
+
var DEFAULT_GLOBAL_PER_MINUTE = 20;
|
|
37977
|
+
var BODY_MAX = 200;
|
|
37978
|
+
function createFleetStatusBroadcaster(opts) {
|
|
37979
|
+
const cfg = opts.config;
|
|
37980
|
+
const minIntervalMs = cfg?.minIntervalMsPerAgent ?? DEFAULT_MIN_INTERVAL_MS;
|
|
37981
|
+
const perMinuteCap = cfg?.globalPerMinuteCap ?? DEFAULT_GLOBAL_PER_MINUTE;
|
|
37982
|
+
const disabled = cfg?.enabled === false;
|
|
37983
|
+
let mailbox = null;
|
|
37984
|
+
const mb = () => {
|
|
37985
|
+
if (!mailbox) mailbox = opts.mailboxFactory();
|
|
37986
|
+
return mailbox;
|
|
37987
|
+
};
|
|
37988
|
+
const nameOf = (id) => opts.subagentName?.(id) ?? id;
|
|
37989
|
+
const from = () => `fleet@${opts.sessionTag()}`;
|
|
37990
|
+
const lastSentAt = /* @__PURE__ */ new Map();
|
|
37991
|
+
const coalesced = /* @__PURE__ */ new Map();
|
|
37992
|
+
const sentTimestamps = [];
|
|
37993
|
+
let droppedSinceLastSend = 0;
|
|
37994
|
+
const announcedSpawn = /* @__PURE__ */ new Set();
|
|
37995
|
+
const announcedBudget = /* @__PURE__ */ new Set();
|
|
37996
|
+
const disposers = [];
|
|
37997
|
+
let stopped = false;
|
|
37998
|
+
function underGlobalCap(now) {
|
|
37999
|
+
while (sentTimestamps.length > 0 && now - (sentTimestamps[0] ?? 0) > 6e4) {
|
|
38000
|
+
sentTimestamps.shift();
|
|
38001
|
+
}
|
|
38002
|
+
return sentTimestamps.length < perMinuteCap;
|
|
38003
|
+
}
|
|
38004
|
+
function sendNow(subagentId, p) {
|
|
38005
|
+
const now = Date.now();
|
|
38006
|
+
if (!underGlobalCap(now)) {
|
|
38007
|
+
droppedSinceLastSend++;
|
|
38008
|
+
return;
|
|
38009
|
+
}
|
|
38010
|
+
sentTimestamps.push(now);
|
|
38011
|
+
lastSentAt.set(subagentId, now);
|
|
38012
|
+
let body = p.body;
|
|
38013
|
+
if (droppedSinceLastSend > 0) {
|
|
38014
|
+
body = `${body} [${droppedSinceLastSend} earlier status broadcast(s) dropped by rate cap]`;
|
|
38015
|
+
droppedSinceLastSend = 0;
|
|
38016
|
+
}
|
|
38017
|
+
void mb().send({
|
|
38018
|
+
from: from(),
|
|
38019
|
+
to: "*",
|
|
38020
|
+
type: "status",
|
|
38021
|
+
subject: p.subject,
|
|
38022
|
+
body: body.length > BODY_MAX ? `${body.slice(0, BODY_MAX)}\u2026` : body,
|
|
38023
|
+
priority: p.priority
|
|
38024
|
+
}).catch(() => {
|
|
38025
|
+
});
|
|
38026
|
+
}
|
|
38027
|
+
function broadcast(subagentId, p) {
|
|
38028
|
+
if (stopped) return;
|
|
38029
|
+
const now = Date.now();
|
|
38030
|
+
const last = lastSentAt.get(subagentId) ?? 0;
|
|
38031
|
+
const waitMs = last + minIntervalMs - now;
|
|
38032
|
+
if (waitMs <= 0) {
|
|
38033
|
+
sendNow(subagentId, p);
|
|
38034
|
+
return;
|
|
38035
|
+
}
|
|
38036
|
+
const existing = coalesced.get(subagentId);
|
|
38037
|
+
if (existing) {
|
|
38038
|
+
existing.pending = p;
|
|
38039
|
+
return;
|
|
38040
|
+
}
|
|
38041
|
+
const timer = setTimeout(() => {
|
|
38042
|
+
const entry = coalesced.get(subagentId);
|
|
38043
|
+
coalesced.delete(subagentId);
|
|
38044
|
+
if (!stopped && entry) sendNow(subagentId, entry.pending);
|
|
38045
|
+
}, waitMs);
|
|
38046
|
+
timer.unref?.();
|
|
38047
|
+
coalesced.set(subagentId, { pending: p, timer });
|
|
38048
|
+
}
|
|
38049
|
+
function heartbeat(input) {
|
|
38050
|
+
void mb().heartbeat(input).catch(() => {
|
|
38051
|
+
});
|
|
38052
|
+
}
|
|
38053
|
+
return {
|
|
38054
|
+
start() {
|
|
38055
|
+
if (disabled || stopped) return;
|
|
38056
|
+
disposers.push(
|
|
38057
|
+
opts.events.on("subagent.spawned", (e) => {
|
|
38058
|
+
if (announcedSpawn.has(e.subagentId)) return;
|
|
38059
|
+
announcedSpawn.add(e.subagentId);
|
|
38060
|
+
broadcast(e.subagentId, {
|
|
38061
|
+
subject: `[${e.name ?? nameOf(e.subagentId)}] joined the fleet`,
|
|
38062
|
+
body: `${e.name ?? nameOf(e.subagentId)} spawned${e.model ? ` on ${e.model}` : ""}${e.description ? ` \u2014 ${e.description.slice(0, 80)}` : ""}`,
|
|
38063
|
+
priority: "low"
|
|
38064
|
+
});
|
|
38065
|
+
}),
|
|
38066
|
+
opts.events.on("subagent.task_started", (e) => {
|
|
38067
|
+
heartbeat({
|
|
38068
|
+
agentId: e.subagentId,
|
|
38069
|
+
status: "running",
|
|
38070
|
+
currentTask: e.description?.slice(0, 80)
|
|
38071
|
+
});
|
|
38072
|
+
}),
|
|
38073
|
+
opts.events.on("subagent.task_completed", (e) => {
|
|
38074
|
+
heartbeat({ agentId: e.subagentId, status: "idle" });
|
|
38075
|
+
const name = nameOf(e.subagentId);
|
|
38076
|
+
const ok = e.status === "success";
|
|
38077
|
+
const secs = Math.round(e.durationMs / 1e3);
|
|
38078
|
+
broadcast(e.subagentId, {
|
|
38079
|
+
subject: `[${name}] task ${e.status}`,
|
|
38080
|
+
body: `${name} finished a task: ${e.status} \u2014 ${e.iterations} iter, ${e.toolCalls} tools, ${secs}s${!ok && e.error ? ` (${e.error.kind})` : ""}`,
|
|
38081
|
+
priority: ok ? "low" : "normal"
|
|
38082
|
+
});
|
|
38083
|
+
}),
|
|
38084
|
+
opts.events.on("subagent.budget_warning", (e) => {
|
|
38085
|
+
const key = `${e.subagentId}|${e.kind}`;
|
|
38086
|
+
if (announcedBudget.has(key)) return;
|
|
38087
|
+
announcedBudget.add(key);
|
|
38088
|
+
broadcast(e.subagentId, {
|
|
38089
|
+
subject: `[${nameOf(e.subagentId)}] budget pressure`,
|
|
38090
|
+
body: `${nameOf(e.subagentId)} hit its ${e.kind} limit (${e.used}/${e.limit}) \u2014 coordinator negotiating`,
|
|
38091
|
+
priority: "low"
|
|
38092
|
+
});
|
|
38093
|
+
})
|
|
38094
|
+
);
|
|
38095
|
+
},
|
|
38096
|
+
stop() {
|
|
38097
|
+
stopped = true;
|
|
38098
|
+
for (const d of disposers.splice(0)) d();
|
|
38099
|
+
for (const { timer } of coalesced.values()) clearTimeout(timer);
|
|
38100
|
+
coalesced.clear();
|
|
38101
|
+
}
|
|
38102
|
+
};
|
|
38103
|
+
}
|
|
38104
|
+
|
|
37788
38105
|
// src/fleet/host.ts
|
|
37789
38106
|
function buildShadowAgentTaskDescription(reason) {
|
|
37790
38107
|
return `Shadow Agent one-shot fleet monitor. Reason: ${reason}.
|
|
@@ -37861,6 +38178,14 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
37861
38178
|
shadowPassInFlight = false;
|
|
37862
38179
|
shadowQueuedProblem = null;
|
|
37863
38180
|
shadowActivityOffHandles = [];
|
|
38181
|
+
/** Peer-awareness status broadcaster (mailbox `status` mails on subagent
|
|
38182
|
+
* transitions + rich registry heartbeats). Started in buildDirector(),
|
|
38183
|
+
* stopped in dispose(). */
|
|
38184
|
+
statusBroadcaster = null;
|
|
38185
|
+
/** Brain-gated FleetSupervisor over this director's fleet. Built in
|
|
38186
|
+
* buildDirector() (when a BrainArbiter is available), stopped in
|
|
38187
|
+
* dispose(). Also published to the supervisor registry for /supervisor. */
|
|
38188
|
+
fleetSupervisor = null;
|
|
37864
38189
|
/**
|
|
37865
38190
|
* Force the lazy build path to run *now* and return the live Director,
|
|
37866
38191
|
* or null when director mode is off. Used by the CLI to register the
|
|
@@ -37939,8 +38264,12 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
37939
38264
|
fleetManager,
|
|
37940
38265
|
// pass so director.fleetManager is never undefined
|
|
37941
38266
|
brain: this.opts.brain,
|
|
37942
|
-
roster: FLEET_ROSTER
|
|
38267
|
+
roster: FLEET_ROSTER,
|
|
37943
38268
|
// pass so spawn_subagent recognizes shadow-agent role
|
|
38269
|
+
// Fire-and-forget report-back: when an assign_task completes with no
|
|
38270
|
+
// pending await, post the result to this session's leader via the
|
|
38271
|
+
// project mailbox (injected inline before the leader's next step).
|
|
38272
|
+
taskResultNotifier: (n) => this.reportTaskResultToLeader(n)
|
|
37944
38273
|
});
|
|
37945
38274
|
this.director.on("task.completed", ({ task, result }) => {
|
|
37946
38275
|
this.fleetManager?.removePendingTask(task.id);
|
|
@@ -37966,6 +38295,15 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
37966
38295
|
agentMonitor.setFleetBus(this.director.fleet);
|
|
37967
38296
|
agentMonitor.start();
|
|
37968
38297
|
}
|
|
38298
|
+
this.statusBroadcaster = createFleetStatusBroadcaster({
|
|
38299
|
+
events: this.deps.events,
|
|
38300
|
+
mailboxFactory: () => new GlobalMailbox(this.mailboxProjectDir(), this.deps.events),
|
|
38301
|
+
sessionTag: () => mailboxSessionTag(this.deps.session.id),
|
|
38302
|
+
subagentName: (id) => this.director?.status().subagents.find((s) => s.id === id)?.name,
|
|
38303
|
+
config: config.fleet?.statusBroadcasts
|
|
38304
|
+
});
|
|
38305
|
+
this.statusBroadcaster.start();
|
|
38306
|
+
this.buildFleetSupervisor(config);
|
|
37969
38307
|
this.directorOffHandles.push(
|
|
37970
38308
|
this.director.fleet.filter("budget.threshold_reached", (e) => {
|
|
37971
38309
|
const payload = e.payload;
|
|
@@ -38251,7 +38589,7 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
38251
38589
|
const subCwd = subCfg.cwd ?? this.deps.cwd;
|
|
38252
38590
|
let onlineAgents = [];
|
|
38253
38591
|
try {
|
|
38254
|
-
const subagentMailbox = new GlobalMailbox(this.
|
|
38592
|
+
const subagentMailbox = new GlobalMailbox(this.mailboxProjectDir());
|
|
38255
38593
|
onlineAgents = await subagentMailbox.getAgentStatuses();
|
|
38256
38594
|
} catch {
|
|
38257
38595
|
}
|
|
@@ -38607,6 +38945,107 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
38607
38945
|
}
|
|
38608
38946
|
return [...caps];
|
|
38609
38947
|
}
|
|
38948
|
+
/** Resolved shared-mailbox project dir — same as the leader's checker. */
|
|
38949
|
+
mailboxProjectDir() {
|
|
38950
|
+
return this.opts.mailboxProjectDir ?? resolveProjectDir(this.deps.projectRoot, wstackGlobalRoot());
|
|
38951
|
+
}
|
|
38952
|
+
/**
|
|
38953
|
+
* Construct + start the brain-gated FleetSupervisor over this director's
|
|
38954
|
+
* fleet. Requires a BrainArbiter (production always wires one); without
|
|
38955
|
+
* it there is no safe decision path, so supervision stays off. All
|
|
38956
|
+
* actions flow through Director APIs — the supervisor never touches
|
|
38957
|
+
* coordinator internals or budget negotiation.
|
|
38958
|
+
*/
|
|
38959
|
+
buildFleetSupervisor(config) {
|
|
38960
|
+
const director = this.director;
|
|
38961
|
+
const brain = this.opts.brain;
|
|
38962
|
+
const supervisorCfg = config.fleet?.supervisor;
|
|
38963
|
+
if (!director || !brain || supervisorCfg?.enabled === false) return;
|
|
38964
|
+
const supTag = () => mailboxSessionTag(this.deps.session.id);
|
|
38965
|
+
const mailbox = () => new GlobalMailbox(this.mailboxProjectDir(), this.deps.events);
|
|
38966
|
+
this.fleetSupervisor = new FleetSupervisor({
|
|
38967
|
+
events: this.deps.events,
|
|
38968
|
+
fleet: director.fleet,
|
|
38969
|
+
brain,
|
|
38970
|
+
sessionId: () => this.deps.session.id,
|
|
38971
|
+
config: supervisorCfg,
|
|
38972
|
+
source: {
|
|
38973
|
+
subagents: () => director.status().subagents,
|
|
38974
|
+
listPendingTasks: () => director.listPendingTasks(),
|
|
38975
|
+
isWorkComplete: () => director.isWorkComplete()
|
|
38976
|
+
},
|
|
38977
|
+
actions: {
|
|
38978
|
+
retargetPendingTask: (taskId, subagentId) => director.retargetPendingTask(taskId, subagentId),
|
|
38979
|
+
spawnHelper: async ({ reason }) => {
|
|
38980
|
+
try {
|
|
38981
|
+
const subagentId = await director.spawn({
|
|
38982
|
+
id: `helper-${randomUUID().slice(0, 8)}`,
|
|
38983
|
+
name: "fleet helper",
|
|
38984
|
+
systemPromptOverride: `You are a helper worker spawned by the fleet supervisor to drain a task backlog (${reason}). Complete assigned tasks efficiently and report results.`
|
|
38985
|
+
});
|
|
38986
|
+
return { subagentId };
|
|
38987
|
+
} catch (err) {
|
|
38988
|
+
return { error: toErrorMessage(err) };
|
|
38989
|
+
}
|
|
38990
|
+
},
|
|
38991
|
+
steerAgent: async (subagentId, subject, body) => {
|
|
38992
|
+
await mailbox().send({
|
|
38993
|
+
from: `supervisor@${supTag()}`,
|
|
38994
|
+
to: `${subagentId}@${supTag()}`,
|
|
38995
|
+
type: "steer",
|
|
38996
|
+
subject,
|
|
38997
|
+
body,
|
|
38998
|
+
priority: "high"
|
|
38999
|
+
});
|
|
39000
|
+
},
|
|
39001
|
+
notifyLeader: async (subject, body) => {
|
|
39002
|
+
const leaderId2 = this.opts.getLeaderMailboxId?.() ?? `leader@${supTag()}`;
|
|
39003
|
+
await mailbox().send({
|
|
39004
|
+
from: `supervisor@${supTag()}`,
|
|
39005
|
+
to: leaderId2,
|
|
39006
|
+
type: "status",
|
|
39007
|
+
subject,
|
|
39008
|
+
body,
|
|
39009
|
+
priority: "normal"
|
|
39010
|
+
});
|
|
39011
|
+
},
|
|
39012
|
+
terminate: (subagentId) => director.terminate(subagentId)
|
|
39013
|
+
}
|
|
39014
|
+
});
|
|
39015
|
+
this.fleetSupervisor.start();
|
|
39016
|
+
setActiveFleetSupervisor(this.fleetSupervisor);
|
|
39017
|
+
}
|
|
39018
|
+
/**
|
|
39019
|
+
* Fire-and-forget report-back (wired as the Director's
|
|
39020
|
+
* `taskResultNotifier`): posts a non-awaited task's outcome to THIS
|
|
39021
|
+
* session's leader via the project mailbox. The leader's mailbox checker
|
|
39022
|
+
* injects it inline before the next step, so `assign_task`-without-await
|
|
39023
|
+
* results reach the conversation without polling. The body carries a
|
|
39024
|
+
* short excerpt only — the full result stays in the director's completed
|
|
39025
|
+
* cache, retrievable via `await_tasks`/`roll_up`.
|
|
39026
|
+
*/
|
|
39027
|
+
async reportTaskResultToLeader(n) {
|
|
39028
|
+
try {
|
|
39029
|
+
const leaderId2 = this.opts.getLeaderMailboxId?.() ?? `leader@${mailboxSessionTag(this.deps.session.id)}`;
|
|
39030
|
+
const mailbox = new GlobalMailbox(this.mailboxProjectDir(), this.deps.events);
|
|
39031
|
+
const ok = n.status === "success";
|
|
39032
|
+
const MAX_BODY = 700;
|
|
39033
|
+
const raw = (ok ? n.resultText : n.errorText ?? n.status) ?? "(no textual result)";
|
|
39034
|
+
const excerpt = raw.length > MAX_BODY ? `${raw.slice(0, MAX_BODY)}\u2026` : raw;
|
|
39035
|
+
const title = n.title.length > 80 ? `${n.title.slice(0, 80)}\u2026` : n.title;
|
|
39036
|
+
await mailbox.send({
|
|
39037
|
+
from: n.subagentName ?? n.subagentId,
|
|
39038
|
+
to: leaderId2,
|
|
39039
|
+
type: "result",
|
|
39040
|
+
subject: `[task ${n.status}] ${title}`,
|
|
39041
|
+
body: `${excerpt}
|
|
39042
|
+
|
|
39043
|
+
(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.)`,
|
|
39044
|
+
priority: ok ? "normal" : "high"
|
|
39045
|
+
});
|
|
39046
|
+
} catch {
|
|
39047
|
+
}
|
|
39048
|
+
}
|
|
38610
39049
|
subagentToolRegistry(allow) {
|
|
38611
39050
|
if (!allow || allow.length === 0) return this.deps.toolRegistry;
|
|
38612
39051
|
const sub = new ToolRegistry();
|
|
@@ -38945,6 +39384,11 @@ var MultiAgentHost = class _MultiAgentHost {
|
|
|
38945
39384
|
this.directorOffHandles.length = 0;
|
|
38946
39385
|
this.coordinatorOffHandle?.();
|
|
38947
39386
|
this.coordinatorOffHandle = null;
|
|
39387
|
+
this.statusBroadcaster?.stop();
|
|
39388
|
+
this.statusBroadcaster = null;
|
|
39389
|
+
this.fleetSupervisor?.stop();
|
|
39390
|
+
if (this.fleetSupervisor) setActiveFleetSupervisor(null);
|
|
39391
|
+
this.fleetSupervisor = null;
|
|
38948
39392
|
this.adaptiveConcurrencyController?.dispose();
|
|
38949
39393
|
this.adaptiveConcurrencyController = void 0;
|
|
38950
39394
|
if (this.director) {
|
|
@@ -40150,12 +40594,12 @@ async function main(argv) {
|
|
|
40150
40594
|
const p = all[providerId];
|
|
40151
40595
|
if (!p) return;
|
|
40152
40596
|
const keys = normalizeKeys(p);
|
|
40153
|
-
const
|
|
40154
|
-
if (!
|
|
40155
|
-
|
|
40156
|
-
|
|
40157
|
-
|
|
40158
|
-
if (creds.accountId)
|
|
40597
|
+
const active2 = p.activeKey ? keys.find((k) => k.label === p.activeKey) : keys[0];
|
|
40598
|
+
if (!active2) return;
|
|
40599
|
+
active2.apiKey = creds.accessToken;
|
|
40600
|
+
active2.refreshToken = creds.refreshToken;
|
|
40601
|
+
active2.expiresAt = new Date(creds.expiresAt).toISOString();
|
|
40602
|
+
if (creds.accountId) active2.accountId = creds.accountId;
|
|
40159
40603
|
writeKeysBack(p, keys);
|
|
40160
40604
|
}).catch(() => {
|
|
40161
40605
|
});
|
|
@@ -42104,24 +42548,24 @@ Restart WrongStack to load or unload plugin code in this session.`;
|
|
|
42104
42548
|
},
|
|
42105
42549
|
onSddRetryAllFailed: () => sddRunRegistry.getActive()?.retryAllFailed() ?? 0,
|
|
42106
42550
|
onSddSplitTask: (taskId, subtasks) => {
|
|
42107
|
-
const
|
|
42108
|
-
if (!
|
|
42109
|
-
const snap =
|
|
42551
|
+
const active2 = sddRunRegistry.getActive();
|
|
42552
|
+
if (!active2) return null;
|
|
42553
|
+
const snap = active2.snapshot();
|
|
42110
42554
|
const match = snap.tasks.find((t) => t.id === taskId || t.shortId === taskId);
|
|
42111
|
-
const ids =
|
|
42555
|
+
const ids = active2.splitTask(match?.id ?? taskId, subtasks);
|
|
42112
42556
|
return ids.length ? ids : null;
|
|
42113
42557
|
},
|
|
42114
42558
|
// ── SDD lifecycle: clean worktrees · rollback commits · destroy project ──
|
|
42115
42559
|
onSddCleanWorktrees: async () => {
|
|
42116
|
-
const
|
|
42117
|
-
if (
|
|
42560
|
+
const active2 = sddRunRegistry.getActive();
|
|
42561
|
+
if (active2) return active2.cleanupWorktrees();
|
|
42118
42562
|
const core = await import('@wrongstack/core');
|
|
42119
42563
|
const { removed } = await core.cleanupSddWorktrees(projectRoot);
|
|
42120
42564
|
return removed;
|
|
42121
42565
|
},
|
|
42122
42566
|
onSddRollback: async () => {
|
|
42123
|
-
const
|
|
42124
|
-
if (
|
|
42567
|
+
const active2 = sddRunRegistry.getActive();
|
|
42568
|
+
if (active2) return active2.rollback();
|
|
42125
42569
|
const core = await import('@wrongstack/core');
|
|
42126
42570
|
return core.rollbackSddRunFromDisk({ projectRoot, boardsDir: wpaths.projectSddBoards });
|
|
42127
42571
|
},
|
|
@@ -42408,9 +42852,9 @@ Reply YES to auto-proceed, NO to wait for human input.`
|
|
|
42408
42852
|
// Stops a live run for destroy; refuses clean/rollback while one is still
|
|
42409
42853
|
// running. Reuses the same disk-backed core helper as the WebUI + /sdd.
|
|
42410
42854
|
onSddLifecycle: async (op, lcOpts) => {
|
|
42411
|
-
const
|
|
42412
|
-
if (op === "destroy")
|
|
42413
|
-
else if (
|
|
42855
|
+
const active2 = sddRunRegistry.getActive();
|
|
42856
|
+
if (op === "destroy") active2?.stop();
|
|
42857
|
+
else if (active2?.isRunning()) {
|
|
42414
42858
|
return { op, ok: false, reason: "Stop the run first (Ctrl+C), then retry." };
|
|
42415
42859
|
}
|
|
42416
42860
|
const core = await import('@wrongstack/core');
|