@wrongstack/cli 0.280.0 → 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 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, DefaultPathResolver, EventBus, runProviderWithRetry, ReplayLogStore, ReplayProviderRunner, mergeCustomModelDefs, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, sessionScopedPath as sessionScopedPath$1, createContextManagerTool, makeMailboxTool, makeMailSendTool, makeMailInboxTool, 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, DefaultTokenCounter, AutoApprovePermissionPolicy, mergeModelRuntime, ToolValidationError, WIDE_SUBAGENT_CAPABILITIES, AgentError, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, assignNickname, makeLLMClassifier, writeOut, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1, CHIMERA_REVIEW_PROMPT, setQueuedMessagesSnapshot, resolveProjectDir, 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, isSecretField as isSecretField$1, renderInstructionTemplate, recentTextTurns, gatedEnhancerReasoning, enhanceUserPrompt, mutatePlan, setPlanItemStatus, getPlanTemplate, resolveProviderModelList, ParseError, startSessionTelemetryBridge, startFleetTelemetryBridge, startBrainTelemetryBridge, startWorktreeTelemetryBridge, startToolTelemetryBridge, startCostTelemetryBridge } from '@wrongstack/core';
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 active = keys.find((k) => k.label === cfg.activeKey) ?? expectDefined(keys[0]);
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 = active.label;
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 active = cfg.activeKey ? cfg.apiKeys.find((k) => k.label === cfg.activeKey) : void 0;
88
- return (active ?? cfg.apiKeys[0])?.apiKey;
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, color89) {
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 ? color89.dim(` [${errKind}]`) : "";
676
- const errSnip = errMsg || errKind ? `${errKindChip}${color89.dim(errTail)}` : "";
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: color89.green("\u2713"), stats, tail: "" };
679
+ return { mark: color90.green("\u2713"), stats, tail: "" };
680
680
  case "timeout":
681
681
  return {
682
- mark: color89.yellow("\u23F1"),
683
- stats: `${color89.yellow("timeout")} ${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: color89.dim("\u2298"), stats: `${color89.dim("stopped")} ${stats}`, tail: errSnip };
687
+ return { mark: color90.dim("\u2298"), stats: `${color90.dim("stopped")} ${stats}`, tail: errSnip };
688
688
  case "failed":
689
- return { mark: color89.red("\u2717"), stats: `${color89.red("failed")} ${stats}`, tail: errSnip };
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 active = await ctx.modeStore.getActiveMode();
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 === (active?.id ?? "default")
7137
+ isActive: m.id === (active2?.id ?? "default")
7135
7138
  })),
7136
- activeId: active?.id ?? "default"
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 active = String(ctx.agent.ctx.meta["contextWindowMode"] ?? DEFAULT_CONTEXT_WINDOW_MODE_ID);
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: active,
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 === active,
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 active = keys.find((k) => k.label === cfg.activeKey) ?? keys[0];
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: active?.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 active = activeLabel(cfg, keys);
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 = active != null ? keys.find((k) => k.label === active) : void 0;
12122
- summary = `${color.dim(`${keys.length} keys`)} ${color.dim("active:")} ${color.bold(active ?? "?")} ` + maskedKey(activeKeyObj?.apiKey ?? firstKey?.apiKey ?? "");
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 active = activeLabel(cfg, keys);
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, active);
12195
+ if (key) renderKeyLine(renderer, key, i + 1, active2);
12157
12196
  }
12158
12197
  }
12159
12198
  }
12160
- function renderKeyLine(renderer, key, idx, active) {
12161
- const marker = key.label === active ? color.green("\u25CF") : color.dim("\u25CB");
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 active = activeLabel(cfg, keys);
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 === active,
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 active = null;
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 (active?.orchestrator.isRunning()) {
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
- active?.orchestrator.setTaskAssignee(task.id, void 0, agentName);
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 (active?.graph.id !== graph.id) return;
13478
- active.unsubscribe();
13479
- active = null;
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
- active = { graph, orchestrator, abort, unsubscribe };
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
- active?.orchestrator.pause();
13544
+ active2?.orchestrator.pause();
13506
13545
  },
13507
13546
  onAutoPhaseResume() {
13508
- active?.orchestrator.resume();
13547
+ active2?.orchestrator.resume();
13509
13548
  },
13510
13549
  onAutoPhaseStop() {
13511
- if (!active) return;
13512
- active.abort.abort();
13513
- active.orchestrator.stop();
13514
- active.unsubscribe();
13515
- void persist(active.graph);
13516
- active = null;
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 (!active) return null;
13520
- const a = active;
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 (!active) return false;
13529
- const ok = active.orchestrator.moveTask(taskId, toPhaseId);
13530
- if (ok) void persist(active.graph);
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 (!active) return false;
13535
- const ok = active.orchestrator.setTaskAssignee(taskId, agentId, agentName);
13536
- if (ok) void persist(active.graph);
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 (!active) return null;
13541
- const id = active.orchestrator.addTask(phaseId, spec);
13542
- if (id) void persist(active.graph);
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 (!active) return false;
13547
- const ok = active.orchestrator.requeueTask(taskId);
13548
- if (ok) void persist(active.graph);
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 active = cfg.activeKey ? cfg.apiKeys.find((k) => k.label === cfg.activeKey) : void 0;
13754
- return (active ?? cfg.apiKeys[0])?.apiKey;
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 active = keys.find((k) => cfg && cfg.activeKey === k.label) ?? keys[0];
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 === active?.label ? color.green("\u25CF") : color.dim("\u25CB");
14358
- const masked = k.label === active?.label ? color.dim("(active \u2014 masked)") : color.dim("(masked)");
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 active = readPolicy(ctx)?.id ?? "balanced";
15322
+ const active2 = readPolicy(ctx)?.id ?? "balanced";
15284
15323
  const msg2 = `${color.bold("Context Window Modes")}
15285
- ${formatContextWindowModeList(active)}`;
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$1(key) && !value.startsWith(ENC_PREFIX)) {
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 active = await loadActiveKit(opts.projectRoot);
20591
- if (!active) {
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 : active.stack && isDesignStack(active.stack) ? active.stack : "web";
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(active.kit);
20598
- if (!raw) return { message: `Kit "${active.kit}" has no tokens.json.` };
20599
- const merged = applyTokenOverrides(raw, active.overrides);
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: active.kit,
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 active = await modeStore.getActiveMode();
20639
- const activeIdx = modes.findIndex((m) => m.id === active?.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 === active?.id ? color.green(" [active]") : "";
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 active = await modeStore.getActiveMode();
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: ${active?.name ?? "none"}`, "", "Available modes:"];
20756
+ const lines = [`Current mode: ${active2?.name ?? "none"}`, "", "Available modes:"];
20718
20757
  for (const m of modes) {
20719
- const mark = m.id === active?.id ? " [active]" : "";
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
- return ` ${color.bold(s.id)}${marker}
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 active = cfg.activeKey ?? keys[0]?.label;
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 === active ? color.green("\u25CF") : color.dim("\u25CB");
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 active = cfg.activeKey ?? keys[0]?.label;
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 === active ? color.green("\u25CF") : color.dim("\u25CB");
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) !== "off",
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.deps.projectRoot);
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 active = p.activeKey ? keys.find((k) => k.label === p.activeKey) : keys[0];
40154
- if (!active) return;
40155
- active.apiKey = creds.accessToken;
40156
- active.refreshToken = creds.refreshToken;
40157
- active.expiresAt = new Date(creds.expiresAt).toISOString();
40158
- if (creds.accountId) active.accountId = 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 active = sddRunRegistry.getActive();
42108
- if (!active) return null;
42109
- const snap = active.snapshot();
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 = active.splitTask(match?.id ?? taskId, subtasks);
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 active = sddRunRegistry.getActive();
42117
- if (active) return active.cleanupWorktrees();
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 active = sddRunRegistry.getActive();
42124
- if (active) return active.rollback();
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 active = sddRunRegistry.getActive();
42412
- if (op === "destroy") active?.stop();
42413
- else if (active?.isRunning()) {
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');