@wrongstack/cli 0.276.3 → 0.277.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/README.md CHANGED
@@ -17,8 +17,7 @@ The `wrongstack` umbrella package transitively installs `@wrongstack/cli` along
17
17
  ```bash
18
18
  wstack # interactive REPL — no flags = default
19
19
  wstack --tui # Ink-based TUI
20
- wstack --yolo # auto-approve normal project work
21
- wstack --yolo --yolo-destructive # also auto-approve destructive-gated calls
20
+ wstack --yolo # auto-approve normal project work; destructive calls still prompt
22
21
  wstack "refactor src/auth.ts" # one-shot query (no interactive loop)
23
22
 
24
23
  wstack --provider <id> --model <id> # skip the picker
@@ -97,7 +96,8 @@ API keys are encrypted at rest with AES-256-GCM and the key file at `~/.wrongsta
97
96
  |------|--------|
98
97
  | `--tui` / `--no-tui` | Force/disable Ink TUI |
99
98
  | `--yolo` | Auto-approve normal in-project tool calls |
100
- | `--yolo-destructive` | Also auto-approve clearly destructive YOLO-gated calls |
99
+ | `--confirm-destructive` | Deprecated; destructive confirmations are always on in YOLO |
100
+ | `--yolo-destructive` | Deprecated compatibility flag; destructive calls still prompt |
101
101
  | `--force-all-yolo` | Deprecated alias for `--yolo-destructive` |
102
102
  | `--provider <id>` | Override the configured provider |
103
103
  | `--model <id>` | Override the configured model |
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { color, CODEX_MODELS, 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, watchHqAuthFile, parseHqFrame, HQ_PROTOCOL_VERSION, parseHqEventPayload, scrubAndTruncateHqPreview, FetchError, DefaultSecretScrubber, resolveWstackPaths, watchProviderConfig, projectHash, wstackGlobalRoot, 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, DEFAULT_CONTEXT_WINDOW_MODE_ID, resolveProjectDir, 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, ToolValidationError, WIDE_SUBAGENT_CAPABILITIES, AgentError, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, assignNickname, makeLLMClassifier, writeOut, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1, CHIMERA_REVIEW_PROMPT, setQueuedMessagesSnapshot, projectSlug, recentTextTurns, gatedEnhancerReasoning, enhanceUserPrompt, repairToolUseAdjacency, mutateTasks, loadTasks, mutatePlan, setPlanItemStatus, getPlanTemplate, emptyPlan, addPlanItem, savePlan, resolveProviderModelList, bootConfig as bootConfig$1, setOutputLineGuard, setRawMode, DefaultSessionReader, ToolAuditLog, DefaultSessionRewinder, DefaultSessionStore as DefaultSessionStore$1, ProviderRegistry, makeAgentSubagentRunner, NULL_FLEET_BUS, ERROR_CODES, buildChildEnv, formatContextWindowModeList, getContextWindowMode, AGENT_CATALOG, dispatchAgent, normalizeToolDescriptionMode, getToolDescriptionMode, noOpVault, formatTodosList, formatTaskList, formatTaskProgress, formatPlan, 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, InputBuilder, DefaultSecretVault as DefaultSecretVault$1, decryptConfigSecrets as decryptConfigSecrets$1, encryptConfigSecrets as encryptConfigSecrets$1, HQ_AUTH_FILE_VERSION, truncate, estimateMessageTokens, AGENTS_BY_PHASE, validateAgainstSchema, normalizeToolResultRenderMode, getToolResultRenderMode, setToolResultRenderMode, resolveMailboxIdentity, smartDefaultFallbackChain, AutonomousCoordinator, mutateHqAuthFile, mintHqToken, readHqAuthFile, isSecretField as isSecretField$1, renderInstructionTemplate, ParseError, startSessionTelemetryBridge } from '@wrongstack/core';
2
+ import { color, CODEX_MODELS, 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, watchHqAuthFile, parseHqFrame, HQ_PROTOCOL_VERSION, parseHqEventPayload, scrubAndTruncateHqPreview, FetchError, DefaultSecretScrubber, resolveWstackPaths, watchProviderConfig, projectHash, wstackGlobalRoot, 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, DEFAULT_CONTEXT_WINDOW_MODE_ID, resolveProjectDir, 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, ToolValidationError, WIDE_SUBAGENT_CAPABILITIES, AgentError, PhaseStore, AutoPhasePlanner, PhaseGraphBuilder, WorktreeManager, PhaseOrchestrator, assignNickname, makeLLMClassifier, writeOut, ParallelEternalEngine, EternalAutonomyEngine, allServers as allServers$1, CHIMERA_REVIEW_PROMPT, setQueuedMessagesSnapshot, projectSlug, recentTextTurns, gatedEnhancerReasoning, enhanceUserPrompt, repairToolUseAdjacency, mutateTasks, loadTasks, mutatePlan, setPlanItemStatus, getPlanTemplate, emptyPlan, addPlanItem, savePlan, resolveProviderModelList, bootConfig as bootConfig$1, setOutputLineGuard, setRawMode, DefaultSessionReader, ToolAuditLog, DefaultSessionRewinder, DefaultSessionStore as DefaultSessionStore$1, ProviderRegistry, makeAgentSubagentRunner, NULL_FLEET_BUS, ERROR_CODES, buildChildEnv, formatContextWindowModeList, getContextWindowMode, AGENT_CATALOG, dispatchAgent, normalizeToolDescriptionMode, getToolDescriptionMode, noOpVault, formatTodosList, formatTaskList, formatTaskProgress, formatPlan, 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, HQ_AUTH_FILE_VERSION, truncate, estimateMessageTokens, AGENTS_BY_PHASE, validateAgainstSchema, normalizeToolResultRenderMode, getToolResultRenderMode, setToolResultRenderMode, resolveMailboxIdentity, smartDefaultFallbackChain, AutonomousCoordinator, mutateHqAuthFile, mintHqToken, readHqAuthFile, isSecretField as isSecretField$1, renderInstructionTemplate, ParseError, startSessionTelemetryBridge } from '@wrongstack/core';
3
3
  import * as fs5 from 'fs/promises';
4
4
  import { access, readFile } from 'fs/promises';
5
5
  import { DefaultSecretVault, decryptConfigSecrets, encryptConfigSecrets, isSecretField } from '@wrongstack/core/security';
@@ -16995,6 +16995,32 @@ function buildMailboxDemoCommand(opts) {
16995
16995
  }
16996
16996
  };
16997
16997
  }
16998
+
16999
+ // src/utils/win32-cmd.ts
17000
+ var WIN32_CMD_META = /[&|<>"\r\n\0]/;
17001
+ function buildWin32CmdShimInvocation(command2, args = []) {
17002
+ assertSafeWin32CmdArgs([command2, ...args]);
17003
+ const line = ["call", quoteWin32CmdArg(command2), ...args.map(quoteWin32CmdArg)].join(" ");
17004
+ return {
17005
+ command: process.env["COMSPEC"] ?? "cmd.exe",
17006
+ args: ["/d", "/c", line],
17007
+ windowsVerbatimArguments: true
17008
+ };
17009
+ }
17010
+ function assertSafeWin32CmdArgs(args) {
17011
+ for (const arg of args) {
17012
+ if (typeof arg === "string" && WIN32_CMD_META.test(arg)) {
17013
+ throw new Error(
17014
+ 'win32 cmd shim spawn: argument contains a shell metacharacter (one of & | < > ", or a newline) that could enable command injection through the .cmd/.bat wrapper - refusing to run. Offending argument: ' + JSON.stringify(arg)
17015
+ );
17016
+ }
17017
+ }
17018
+ }
17019
+ function quoteWin32CmdArg(arg) {
17020
+ return `"${arg}"`;
17021
+ }
17022
+
17023
+ // src/slash-commands/mailbox-serve.ts
16998
17024
  function buildMailboxServeCommand(opts) {
16999
17025
  return {
17000
17026
  name: "mailbox-serve",
@@ -17040,16 +17066,16 @@ function buildMailboxServeCommand(opts) {
17040
17066
  const isWin = process.platform === "win32";
17041
17067
  let wstackCmd;
17042
17068
  let spawnArgs;
17043
- let useShell = false;
17069
+ let shim = null;
17044
17070
  if (cliEntry && /wstack|wrongstack|index\.(js|ts|mjs|cjs)$/.test(cliEntry)) {
17045
17071
  wstackCmd = process.execPath;
17046
17072
  spawnArgs = [cliEntry, "mailbox", "serve", ...flags];
17047
17073
  } else {
17048
17074
  wstackCmd = "wstack";
17049
17075
  spawnArgs = ["mailbox", "serve", ...flags];
17050
- useShell = isWin;
17076
+ if (isWin) shim = buildWin32CmdShimInvocation(wstackCmd, spawnArgs);
17051
17077
  }
17052
- const child = spawn(wstackCmd, spawnArgs, {
17078
+ const child = spawn(shim?.command ?? wstackCmd, shim?.args ?? spawnArgs, {
17053
17079
  cwd,
17054
17080
  // POSIX-only: own process group so the bridge outlives the REPL.
17055
17081
  // On win32 `detached` opens a visible console window (project
@@ -17057,7 +17083,7 @@ function buildMailboxServeCommand(opts) {
17057
17083
  detached: !isWin,
17058
17084
  stdio: ["ignore", "pipe", "pipe"],
17059
17085
  windowsHide: true,
17060
- shell: useShell,
17086
+ ...shim ? { windowsVerbatimArguments: shim.windowsVerbatimArguments } : {},
17061
17087
  env: process.env
17062
17088
  });
17063
17089
  child.unref();
@@ -23274,12 +23300,12 @@ function buildYoloCommand(opts) {
23274
23300
  help: [
23275
23301
  "Usage:",
23276
23302
  " /yolo Show current YOLO status",
23277
- " /yolo on Enable YOLO mode (auto-approve all tool calls)",
23303
+ " /yolo on Enable YOLO mode (auto-approve normal project work)",
23278
23304
  " /yolo off Disable YOLO mode (restore permission prompts)",
23279
- " /yolo destructive Toggle destructive confirmation gate (YOLO mode only)",
23305
+ " /yolo destructive Show destructive confirmation gate status",
23280
23306
  "",
23281
- "YOLO mode auto-approves everything, including destructive calls.",
23282
- "Use /yolo destructive to re-enable confirmation for risky operations."
23307
+ "YOLO mode auto-approves normal project work, including shell/build/test/write flows.",
23308
+ "Clearly destructive operations still require explicit confirmation."
23283
23309
  ].join("\n"),
23284
23310
  async run(args) {
23285
23311
  const arg = args.trim().toLowerCase();
@@ -23305,11 +23331,11 @@ function buildYoloCommand(opts) {
23305
23331
  } else if (arg === "destructive") {
23306
23332
  const currentMode = opts.onYolo();
23307
23333
  if (!currentMode) {
23308
- const msg3 = `${color.amber("YOLO is OFF.")} Enable YOLO first with /yolo on, then /yolo destructive will control the confirmation gate.`;
23334
+ const msg3 = `${color.amber("YOLO is OFF.")} Destructive operations still require confirmation when YOLO is enabled.`;
23309
23335
  opts.renderer.writeWarning(msg3);
23310
23336
  return { message: msg3 };
23311
23337
  }
23312
- const msg2 = `${color.amber("Destructive gate:")} ${color.dim("YOLO is ON \u2014 destructive operations require confirmation. Use /yolo destructive to toggle this gate.")}`;
23338
+ const msg2 = `${color.amber("Destructive gate:")} ${color.dim("ON \u2014 YOLO is enabled, but clearly destructive operations still require confirmation.")}`;
23313
23339
  opts.renderer.writeWarning(msg2);
23314
23340
  return { message: msg2 };
23315
23341
  } else {
@@ -31653,9 +31679,9 @@ var helpCmd = async (_args, deps) => {
31653
31679
  " wstack version Print version",
31654
31680
  "",
31655
31681
  color.bold("Common flags"),
31656
- " --yolo Auto-approve all tool calls (including destructive)",
31657
- " --confirm-destructive In YOLO mode, still prompt for destructive operations",
31658
- " --yolo-destructive Deprecated \u2014 YOLO now auto-approves everything by default",
31682
+ " --yolo Auto-approve normal project work; destructive calls still prompt",
31683
+ " --confirm-destructive Deprecated \u2014 destructive confirmations are always on in YOLO mode",
31684
+ " --yolo-destructive Deprecated compatibility flag; destructive calls still prompt",
31659
31685
  " --tui / --no-tui Force or disable TUI mode",
31660
31686
  " --mouse Full mouse mode in the TUI (in-app scroll + clickable UI)",
31661
31687
  " --webui [--host <h>] [--webui-port <n>] [--ws-port <n>] [--webui-token <t>] [--open]",
@@ -32443,6 +32469,67 @@ function bindSystemPromptBuilder(deps) {
32443
32469
  );
32444
32470
  }
32445
32471
 
32472
+ // src/boot/brain-decision-log.ts
32473
+ var MAX_BRAIN_LOG_ENTRIES = 20;
32474
+ function subscribeBrainDecisionLog(events) {
32475
+ const listeners = [];
32476
+ const brainLog = [];
32477
+ const pushBrainLog = (entry) => {
32478
+ brainLog.push(entry);
32479
+ if (brainLog.length > MAX_BRAIN_LOG_ENTRIES) brainLog.shift();
32480
+ };
32481
+ const subscribe = (name, handler) => {
32482
+ events.on(name, handler);
32483
+ listeners.push([name, handler]);
32484
+ };
32485
+ subscribe("brain.decision_answered", (raw) => {
32486
+ const e = raw;
32487
+ pushBrainLog({
32488
+ at: e.at,
32489
+ kind: "answered",
32490
+ question: e.request.question,
32491
+ outcome: e.decision.type === "answer" ? e.decision.optionId ?? e.decision.text ?? "" : ""
32492
+ });
32493
+ });
32494
+ subscribe("brain.decision_ask_human", (raw) => {
32495
+ const e = raw;
32496
+ pushBrainLog({
32497
+ at: e.at,
32498
+ kind: "ask_human",
32499
+ question: e.request.question,
32500
+ outcome: "escalated to human"
32501
+ });
32502
+ });
32503
+ subscribe("brain.decision_denied", (raw) => {
32504
+ const e = raw;
32505
+ pushBrainLog({
32506
+ at: e.at,
32507
+ kind: "denied",
32508
+ question: e.request.question,
32509
+ outcome: e.decision.type === "deny" ? e.decision.reason ?? "" : ""
32510
+ });
32511
+ });
32512
+ subscribe("brain.intervention", (raw) => {
32513
+ const e = raw;
32514
+ pushBrainLog({
32515
+ at: e.at,
32516
+ kind: "intervention",
32517
+ question: e.request.question,
32518
+ outcome: e.intervened ? "steered the agent" : "observed (no action)"
32519
+ });
32520
+ });
32521
+ const dispose = () => {
32522
+ for (const [name, handler] of listeners) {
32523
+ events.off.call(
32524
+ events,
32525
+ name,
32526
+ handler
32527
+ );
32528
+ }
32529
+ };
32530
+ return { brainLog, pushBrainLog, dispose };
32531
+ }
32532
+
32446
32533
  // src/boot/short-circuit-flags.ts
32447
32534
  async function handleHelpVersionShortCircuit(argv) {
32448
32535
  const earlyFlags = parseArgs(argv).flags;
@@ -34109,7 +34196,10 @@ init_hq_publisher();
34109
34196
  init_sdd();
34110
34197
  init_utils();
34111
34198
  var DEFAULT_MAX_CONSECUTIVE_AUTO_PROCEED = 50;
34112
- function parseSuggestionsFromOutput(finalText) {
34199
+ function parseSuggestionsFromOutput(finalText, todos) {
34200
+ if (hasOpenTodos(todos)) {
34201
+ return null;
34202
+ }
34113
34203
  const { texts, autoTexts } = parseNextSteps(finalText, false);
34114
34204
  if (autoTexts.length > 0) ;
34115
34205
  return texts.length > 0 ? texts : null;
@@ -34467,7 +34557,10 @@ ${color.dim(taskList2)}
34467
34557
  }
34468
34558
  }
34469
34559
  if (opts.onSuggestionsParsed) {
34470
- const parsed = parseSuggestionsFromOutput(runResult.finalText);
34560
+ const parsed = parseSuggestionsFromOutput(
34561
+ runResult.finalText,
34562
+ opts.agent.ctx.todos
34563
+ );
34471
34564
  opts.onSuggestionsParsed(parsed);
34472
34565
  }
34473
34566
  }
@@ -34626,7 +34719,7 @@ ${color.dim(taskList2)}
34626
34719
  }
34627
34720
  }
34628
34721
  if (result.status === "done" && result.finalText && opts.onSuggestionsParsed) {
34629
- const parsed = parseSuggestionsFromOutput(result.finalText);
34722
+ const parsed = parseSuggestionsFromOutput(result.finalText, opts.agent.ctx.todos);
34630
34723
  opts.onSuggestionsParsed(parsed);
34631
34724
  }
34632
34725
  if (opts.tokenCounter && before) {
@@ -34671,7 +34764,7 @@ ${color.dim(
34671
34764
  } finally {
34672
34765
  activeCtrl = void 0;
34673
34766
  }
34674
- } else if (autonomy === "suggest") {
34767
+ } else if (autonomy === "suggest" && !hasOpenTodos(opts.agent.ctx.todos)) {
34675
34768
  const suggestPrompt = 'Based on what you just did, suggest 3 concrete next steps. Format: numbered list, one line each, no explanation. If there is nothing meaningful left, say "No further steps needed."';
34676
34769
  const suggestBlocks = [{ type: "text", text: suggestPrompt }];
34677
34770
  const suggestCtrl = new AbortController();
@@ -34695,7 +34788,10 @@ ${suggestResult.finalText}
34695
34788
  `
34696
34789
  );
34697
34790
  if (opts.onSuggestionsParsed) {
34698
- const parsed = parseSuggestionsFromOutput(suggestResult.finalText);
34791
+ const parsed = parseSuggestionsFromOutput(
34792
+ suggestResult.finalText,
34793
+ opts.agent.ctx.todos
34794
+ );
34699
34795
  opts.onSuggestionsParsed(parsed);
34700
34796
  }
34701
34797
  }
@@ -34705,7 +34801,7 @@ ${suggestResult.finalText}
34705
34801
  }
34706
34802
  }
34707
34803
  }
34708
- if (result.status === "done" && opts.getNextPredict?.()) {
34804
+ if (result.status === "done" && opts.getNextPredict?.() && !hasOpenTodos(opts.agent.ctx.todos)) {
34709
34805
  const autonomy = opts.getAutonomy?.() ?? "off";
34710
34806
  if (autonomy === "off") {
34711
34807
  const predictCtrl = new AbortController();
@@ -34866,7 +34962,7 @@ async function runAutoProceed(opts, suggestion, delayMs, ctrl) {
34866
34962
  ).total
34867
34963
  );
34868
34964
  if (runResult.status === "done" && runResult.finalText && opts.onSuggestionsParsed) {
34869
- const parsed = parseSuggestionsFromOutput(runResult.finalText);
34965
+ const parsed = parseSuggestionsFromOutput(runResult.finalText, opts.agent.ctx.todos);
34870
34966
  opts.onSuggestionsParsed(parsed);
34871
34967
  }
34872
34968
  } finally {
@@ -35327,9 +35423,13 @@ async function execute(deps) {
35327
35423
  },
35328
35424
  // Parse 💡 Next steps from assistant output and store them in the
35329
35425
  // shared suggestion store so `/next 1`, `/next 1 2 3` work without
35330
- // requiring `/suggest` first. Called unconditionally on every done turn.
35426
+ // requiring `/suggest` first. Called unconditionally on every done
35427
+ // turn. The live todo list is passed through so we suppress
35428
+ // <next_steps> while the in-flight todo loop is still in progress
35429
+ // — finishing the open todos comes before offering new prompt
35430
+ // options.
35331
35431
  onSuggestionsParsed: (finalText) => {
35332
- const parsed = parseSuggestionsFromOutput(finalText);
35432
+ const parsed = parseSuggestionsFromOutput(finalText, context.todos);
35333
35433
  setSuggestions(parsed ?? []);
35334
35434
  },
35335
35435
  // Retrieve current suggestions for next-steps auto-submit countdown.
@@ -36503,11 +36603,13 @@ var MultiAgentHost = class _MultiAgentHost {
36503
36603
  * and `shell.*` are not in the read-only default), silently crippling
36504
36604
  * every code-writing / build role in the catalog.
36505
36605
  * 3. No tool restriction (full registry) → the WIDE working set
36506
- * (`WIDE_SUBAGENT_CAPABILITIES`: read, write, net, shell, install). The
36507
- * user authorized full developer work when they invoked the leader, so a
36508
- * delegated agent runs the same toolchain end-to-end. The genuinely
36509
- * blast-radius-escaping capabilities (fs.write.outside-project, mcp.proxy,
36510
- * subagent.spawn, config.mutate) stay off and need an explicit (1) grant.
36606
+ * (`WIDE_SUBAGENT_CAPABILITIES`: read, write, net, shell, install,
36607
+ * session todo, tool metadata, read-only memory). The user authorized full
36608
+ * developer work when they invoked the leader, so a delegated agent runs
36609
+ * the same toolchain end-to-end. The genuinely blast-radius-escaping
36610
+ * capabilities (fs.write.outside-project, tool.mutate.any, memory writes,
36611
+ * mcp.proxy, subagent.spawn, config.mutate) stay off and need an explicit
36612
+ * (1) grant.
36511
36613
  */
36512
36614
  resolveSubagentCapabilities(subCfg) {
36513
36615
  if (subCfg.allowedCapabilities) return subCfg.allowedCapabilities;
@@ -36801,6 +36903,7 @@ var MultiAgentHost = class _MultiAgentHost {
36801
36903
  if (this.director) {
36802
36904
  await this.getCoordinator().stopAll();
36803
36905
  }
36906
+ await this.fleetManager?.flushManifest();
36804
36907
  }
36805
36908
  /**
36806
36909
  * Current effective concurrent-subagent ceiling. Reads the live
@@ -36861,6 +36964,9 @@ var MultiAgentHost = class _MultiAgentHost {
36861
36964
  if (this.director) {
36862
36965
  await this.director.shutdown();
36863
36966
  }
36967
+ await this.fleetManager?.flushManifest();
36968
+ this.fleetManager?.dispose();
36969
+ this.fleetManager = void 0;
36864
36970
  const monitor = this.opts.agentMonitor;
36865
36971
  if (monitor) {
36866
36972
  monitor.stop();
@@ -37310,16 +37416,16 @@ function defaultSpawn(args, cwd) {
37310
37416
  const isWin = process.platform === "win32";
37311
37417
  let cmd;
37312
37418
  let spawnArgs;
37313
- let useShell = false;
37419
+ let shim = null;
37314
37420
  if (cliEntry && /wstack|wrongstack|index\.(js|ts|mjs|cjs)$/.test(cliEntry)) {
37315
37421
  cmd = process.execPath;
37316
37422
  spawnArgs = [cliEntry, ...args];
37317
37423
  } else {
37318
37424
  cmd = "wstack";
37319
37425
  spawnArgs = args;
37320
- useShell = isWin;
37426
+ if (isWin) shim = buildWin32CmdShimInvocation(cmd, spawnArgs);
37321
37427
  }
37322
- const child = spawn(cmd, spawnArgs, {
37428
+ const child = spawn(shim?.command ?? cmd, shim?.args ?? spawnArgs, {
37323
37429
  cwd,
37324
37430
  // `detached` gives the bridge its own process group on POSIX so it
37325
37431
  // outlives the REPL. On win32 it forces a visible console window
@@ -37328,7 +37434,7 @@ function defaultSpawn(args, cwd) {
37328
37434
  detached: !isWin,
37329
37435
  stdio: ["ignore", "pipe", "pipe"],
37330
37436
  windowsHide: true,
37331
- shell: useShell,
37437
+ ...shim ? { windowsVerbatimArguments: shim.windowsVerbatimArguments } : {},
37332
37438
  env: process.env
37333
37439
  });
37334
37440
  return {
@@ -38075,7 +38181,7 @@ async function main(argv) {
38075
38181
  renderer,
38076
38182
  modelsRegistry,
38077
38183
  yoloDestructive: flags["yolo-destructive"] === true || flags["force-all-yolo"] === true,
38078
- confirmDestructive: flags["confirm-destructive"] === true
38184
+ confirmDestructive: true
38079
38185
  });
38080
38186
  const replayFlag = flags["replay"];
38081
38187
  const recordFlag = flags["record"];
@@ -38732,43 +38838,8 @@ async function main(argv) {
38732
38838
  events
38733
38839
  );
38734
38840
  container.bind(TOKENS.BrainArbiter, () => brain);
38735
- const brainLog = [];
38736
- const pushBrainLog = (entry) => {
38737
- brainLog.push(entry);
38738
- if (brainLog.length > 20) brainLog.shift();
38739
- };
38740
- evOn("brain.decision_answered", (e) => {
38741
- pushBrainLog({
38742
- at: e.at,
38743
- kind: "answered",
38744
- question: e.request.question,
38745
- outcome: e.decision.type === "answer" ? e.decision.optionId ?? e.decision.text : ""
38746
- });
38747
- });
38748
- evOn("brain.decision_ask_human", (e) => {
38749
- pushBrainLog({
38750
- at: e.at,
38751
- kind: "ask_human",
38752
- question: e.request.question,
38753
- outcome: "escalated to human"
38754
- });
38755
- });
38756
- evOn("brain.decision_denied", (e) => {
38757
- pushBrainLog({
38758
- at: e.at,
38759
- kind: "denied",
38760
- question: e.request.question,
38761
- outcome: e.decision.type === "deny" ? e.decision.reason : ""
38762
- });
38763
- });
38764
- evOn("brain.intervention", (e) => {
38765
- pushBrainLog({
38766
- at: e.at,
38767
- kind: "intervention",
38768
- question: e.request.question,
38769
- outcome: e.intervened ? "steered the agent" : "observed (no action)"
38770
- });
38771
- });
38841
+ const { brainLog, dispose: disposeBrainLog } = subscribeBrainDecisionLog(events);
38842
+ teardownHandlers.push(disposeBrainLog);
38772
38843
  let hqPublisher;
38773
38844
  let stopHqSessionBridge;
38774
38845
  const hqConnection = startCliHqConnection({