@wrongstack/cli 1.0.8 → 1.0.9

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.
@@ -35,6 +35,7 @@ import {
35
35
  WrongStackACPServer,
36
36
  WsBridgeTransport
37
37
  } from "@wrongstack/acp/agent";
38
+ import { leaderDeliveryHub } from "@wrongstack/core/coordination";
38
39
  import { WebSocketServer } from "ws";
39
40
 
40
41
  // src/acp-hq-telemetry.ts
@@ -455,7 +456,7 @@ function buildAcpServerAgentFactory(deps, options = {}) {
455
456
  providerId: config.provider,
456
457
  events
457
458
  });
458
- const session = { append: async () => {
459
+ const session = { id: sessionId, append: async () => {
459
460
  } };
460
461
  const context = new Context({
461
462
  systemPrompt: [],
@@ -468,7 +469,10 @@ function buildAcpServerAgentFactory(deps, options = {}) {
468
469
  allowOutsideProjectRoot: config.features?.allowOutsideProjectRoot ?? false,
469
470
  model: config.model,
470
471
  tools: [...tools.listForProvider()],
471
- agentId: "acp-server",
472
+ // The per-session agent IS that ACP session's leader. Leader-only loop
473
+ // behaviour keys on this id — notably draining background delegation
474
+ // results (`isLeaderAgentId`), which never matched 'acp-server'.
475
+ agentId: "leader",
472
476
  agentName: "wrongstack-acp"
473
477
  });
474
478
  const toolExecutor = new ToolExecutor(tools, {
@@ -571,6 +575,9 @@ function createAcpConnectionGate(opts) {
571
575
  }
572
576
 
573
577
  // src/subcommands/handlers/acp.ts
578
+ function pendingLeaderDeliveries(sessionId) {
579
+ return leaderDeliveryHub.pending(sessionId);
580
+ }
574
581
  function acpOverrides(deps) {
575
582
  return deps.config.acp?.agents;
576
583
  }
@@ -698,7 +705,7 @@ async function runACPWebSocketServer(deps, port) {
698
705
  });
699
706
  const tracked = hqTelemetry.wrapAgentFactory(agentFor);
700
707
  wsAgentFactory = agentFor;
701
- turnFactory = () => makeACPServerAgentTurn({ agentFor: tracked });
708
+ turnFactory = () => makeACPServerAgentTurn({ agentFor: tracked, pendingDeliveries: pendingLeaderDeliveries });
702
709
  store = deps.paths?.projectDir ? new ACPSessionStore({ dir: path.join(deps.paths.projectDir, "acp-sessions") }) : void 0;
703
710
  }
704
711
  const acpToken = process.env["WRONGSTACK_ACP_TOKEN"]?.trim() || randomBytes(32).toString("hex");
@@ -814,7 +821,8 @@ async function runACPServer(deps) {
814
821
  });
815
822
  const stdioAgentFactory = agentFor;
816
823
  const turn = makeACPServerAgentTurn({
817
- agentFor: stdioHqTelemetry.wrapAgentFactory(agentFor)
824
+ agentFor: stdioHqTelemetry.wrapAgentFactory(agentFor),
825
+ pendingDeliveries: pendingLeaderDeliveries
818
826
  });
819
827
  const store = deps.paths?.projectDir ? new ACPSessionStore({ dir: path.join(deps.paths.projectDir, "acp-sessions") }) : void 0;
820
828
  return {
@@ -1157,4 +1165,4 @@ ${renderAcpBenchText(result)}
1157
1165
  export {
1158
1166
  acpCmd
1159
1167
  };
1160
- //# sourceMappingURL=acp-A5EQEXQO.js.map
1168
+ //# sourceMappingURL=acp-CXLANMEZ.js.map
@@ -100,6 +100,8 @@ export interface WebUIDispatchContext {
100
100
  getFleetBudget?: CliWebUIOptions['getFleetBudget'];
101
101
  stopSessionFleet?: CliWebUIOptions['stopSessionFleet'];
102
102
  onSessionRetired?: CliWebUIOptions['onSessionRetired'];
103
+ /** The process's single background-delegation auto-wake controller. */
104
+ leaderAutoWake?: CliWebUIOptions['leaderAutoWake'];
103
105
  /** Internal one-session child launch metadata, when --webui-session-child is active. */
104
106
  webuiSessionChild?: WebuiSessionChildOptions | undefined;
105
107
  }
@@ -1,5 +1,5 @@
1
1
  import type { Agent } from '@wrongstack/core/agent';
2
- import type { EventBus } from '@wrongstack/core/kernel';
2
+ import { type EventBus } from '@wrongstack/core/kernel';
3
3
  import type { ContextSnapshot, SessionLoadProgress, TokenCounter } from '@wrongstack/core/types';
4
4
  import type { TuiRuntimeState } from './tui-runtime-state.js';
5
5
  interface SessionResumeContext {
@@ -22,7 +22,7 @@ async function handleHelpVersionShortCircuit(argv) {
22
22
  }
23
23
  if (earlyFlags["help"] === true) {
24
24
  if (positional.length > 0) {
25
- const { subcommandNames } = await import("./subcommands-VFVUTYAV.js");
25
+ const { subcommandNames } = await import("./subcommands-UXWY3OPC.js");
26
26
  if (subcommandNames.includes(positional[0])) {
27
27
  const first = positional[0];
28
28
  const deep = positional[1];
@@ -43,4 +43,4 @@ async function handleHelpVersionShortCircuit(argv) {
43
43
  export {
44
44
  handleHelpVersionShortCircuit
45
45
  };
46
- //# sourceMappingURL=chunk-4OLAZIIN.js.map
46
+ //# sourceMappingURL=chunk-5VPDKQDH.js.map
@@ -460,6 +460,7 @@ var LoginAttemptStore = class _LoginAttemptStore {
460
460
  * debounced rewrite scrubs them from the file.
461
461
  */
462
462
  async load() {
463
+ this.store.clear();
463
464
  let legacyCredEntries = 0;
464
465
  try {
465
466
  const raw = await fs.readFile(this.filePath, "utf8");
@@ -4931,4 +4932,4 @@ export {
4931
4932
  startHqServer,
4932
4933
  HqInsecureExposureError2 as HqInsecureExposureError
4933
4934
  };
4934
- //# sourceMappingURL=chunk-XIZZ2EXG.js.map
4935
+ //# sourceMappingURL=chunk-7FPN3QTH.js.map
@@ -1,7 +1,7 @@
1
1
 
2
2
  // src/subcommands/index.ts
3
3
  var loaders = {
4
- acp: async () => (await import("./acp-A5EQEXQO.js")).acpCmd,
4
+ acp: async () => (await import("./acp-CXLANMEZ.js")).acpCmd,
5
5
  init: async () => (await import("./init-DRMMFG2G.js")).initCmd,
6
6
  auth: async () => (await import("./auth-3K6EN5JP.js")).authCmd,
7
7
  update: async () => (await import("./update-5DPB4YD4.js")).updateCmd,
@@ -31,7 +31,7 @@ var loaders = {
31
31
  quick: async () => (await import("./quick-CNB4Z3HS.js")).quickCmd,
32
32
  bench: async () => (await import("./bench-YBU6BVQP.js")).benchCmd,
33
33
  chronicle: async () => (await import("./chronicle-UTFETHP6.js")).chronicleCmd,
34
- hq: async () => (await import("./hq-G5PCP325.js")).hqCmd,
34
+ hq: async () => (await import("./hq-DQTUE7RC.js")).hqCmd,
35
35
  mailbox: async () => (await import("./mailbox-serve-X3CR6VFW.js")).mailboxServeCmd,
36
36
  permissions: async () => (await import("./permissions-CFDLXETM.js")).permissionsCmd,
37
37
  project: async () => (await import("./project-AOQI5XTK.js")).projectCmd,
@@ -51,4 +51,4 @@ export {
51
51
  subcommands,
52
52
  subcommandNames
53
53
  };
54
- //# sourceMappingURL=chunk-RLCKKLJA.js.map
54
+ //# sourceMappingURL=chunk-7K3IJGWC.js.map
@@ -19,17 +19,17 @@ import {
19
19
  } from "./chunk-GPPRNAKW.js";
20
20
  import {
21
21
  subcommands
22
- } from "./chunk-RLCKKLJA.js";
22
+ } from "./chunk-7K3IJGWC.js";
23
23
  import {
24
24
  renderDeepHelp,
25
25
  renderFocusedHelp
26
26
  } from "./chunk-JX6KL7OF.js";
27
27
  import {
28
28
  handleHelpVersionShortCircuit
29
- } from "./chunk-4OLAZIIN.js";
29
+ } from "./chunk-5VPDKQDH.js";
30
30
  import {
31
31
  DEFAULT_PORT
32
- } from "./chunk-XIZZ2EXG.js";
32
+ } from "./chunk-7FPN3QTH.js";
33
33
  import "./chunk-HXJYHAR4.js";
34
34
  import {
35
35
  resolveHqPasswordInput
@@ -866,7 +866,7 @@ async function isPortInUse(host, port) {
866
866
  }
867
867
  async function handleHqShortCircuit(flags) {
868
868
  if (flags["hq"] !== true) return null;
869
- const { startHqServer } = await import("./hq-server-G3VVVMIG.js");
869
+ const { startHqServer } = await import("./hq-server-JJFGFWI5.js");
870
870
  const tunnelRequested = flags["tunnel"] === true;
871
871
  const rawPublicUrl = typeof flags["hq-public-url"] === "string" ? flags["hq-public-url"] : process.env.WRONGSTACK_HQ_PUBLIC_URL;
872
872
  let publicOrigin;
@@ -3303,4 +3303,4 @@ async function initializeCli(argv) {
3303
3303
  export {
3304
3304
  initializeCli
3305
3305
  };
3306
- //# sourceMappingURL=cli-context-TIAP6YQK.js.map
3306
+ //# sourceMappingURL=cli-context-6GMXUOXD.js.map
@@ -1132,6 +1132,7 @@ import {
1132
1132
  BrainMonitor,
1133
1133
  BrainTraceRecorder,
1134
1134
  createDelegateTool,
1135
+ DelegationTracker,
1135
1136
  EscalationRoutingBrainArbiter,
1136
1137
  ObservableBrainArbiter,
1137
1138
  terminalPolicyDecision
@@ -4938,13 +4939,33 @@ function setupBrainAndOrchestration(deps) {
4938
4939
  ...deps.statusTracker ? { statusTracker: deps.statusTracker } : {}
4939
4940
  }
4940
4941
  );
4942
+ const delegationTracker = new DelegationTracker({ events });
4943
+ container.bind(TOKENS6.DelegationTracker, () => delegationTracker);
4944
+ teardownHandlers.push(() => delegationTracker.dispose());
4945
+ try {
4946
+ const restoredEvents = sessResult.restoredEvents;
4947
+ if (Array.isArray(restoredEvents) && restoredEvents.length > 0) {
4948
+ delegationTracker.rehydrate(session.id, restoredEvents);
4949
+ }
4950
+ } catch {
4951
+ }
4941
4952
  toolRegistry.register(
4942
4953
  createDelegateTool({
4943
4954
  host: multiAgentHost,
4944
4955
  roster: multiAgentHost.getRoster(),
4945
4956
  sessionsRoot: subagentSessionsRoot,
4946
4957
  directorRunId: session.id,
4947
- events
4958
+ events,
4959
+ tracker: delegationTracker,
4960
+ // Temporary rollback switch (user config only — `fleet` is denied in
4961
+ // project config). Read live so a settings change applies next call.
4962
+ defaultWait: () => {
4963
+ try {
4964
+ return configStore.get()?.fleet?.delegate?.defaultWait;
4965
+ } catch {
4966
+ return void 0;
4967
+ }
4968
+ }
4948
4969
  })
4949
4970
  );
4950
4971
  toolRegistry.exposeToProvider("delegate");
@@ -6210,7 +6231,7 @@ async function runCliExecution(params) {
6210
6231
  governanceHandle,
6211
6232
  setConfig
6212
6233
  } = params;
6213
- const { execute } = await import("./execution-FJE27VQ6.js");
6234
+ const { execute } = await import("./execution-4RXQLDAF.js");
6214
6235
  return execute(
6215
6236
  toExecuteDeps({
6216
6237
  core: {
@@ -7328,7 +7349,7 @@ function registerBuiltinTools(deps) {
7328
7349
  makeMailSendTool({ projectDir: deps.wpaths.projectDir, events: deps.events }),
7329
7350
  makeMailInboxTool({ projectDir: deps.wpaths.projectDir, events: deps.events }),
7330
7351
  makeFleetStatusTool({ projectDir: deps.wpaths.projectDir, events: deps.events }),
7331
- makeSessionNoteTool2()
7352
+ makeSessionNoteTool2({ projectDir: deps.wpaths.projectDir, events: deps.events })
7332
7353
  ],
7333
7354
  descriptionMode: deps.config.tools?.descriptionMode,
7334
7355
  resultRenderMode: deps.config.tools?.resultRenderMode,
@@ -7929,7 +7950,7 @@ async function killFleet(director) {
7929
7950
  for (const subagent of director.status().subagents) {
7930
7951
  if (subagent.status === "running" || subagent.status === "idle") {
7931
7952
  try {
7932
- await director.remove(subagent.id);
7953
+ await director.terminate(subagent.id);
7933
7954
  killed++;
7934
7955
  } catch {
7935
7956
  }
@@ -37660,7 +37681,21 @@ function wireSessionEvents(deps) {
37660
37681
  ts: (/* @__PURE__ */ new Date()).toISOString(),
37661
37682
  target: e.target,
37662
37683
  task: e.task,
37663
- ...e.subagentId ? { subagentId: e.subagentId } : {}
37684
+ ...e.subagentId ? { subagentId: e.subagentId } : {},
37685
+ ...e.delegationId ? { delegationId: e.delegationId } : {},
37686
+ ...e.taskId ? { taskId: e.taskId } : {},
37687
+ ...e.mode ? { mode: e.mode } : {}
37688
+ });
37689
+ }
37690
+ );
37691
+ evOn(
37692
+ "delegation.delivered",
37693
+ (e) => {
37694
+ appendSessionEvent(e.sessionId, {
37695
+ type: "delegation_delivered",
37696
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
37697
+ delegationId: e.delegationId,
37698
+ ...e.via ? { via: e.via } : {}
37664
37699
  });
37665
37700
  }
37666
37701
  );
@@ -37668,6 +37703,11 @@ function wireSessionEvents(deps) {
37668
37703
  "delegate.completed",
37669
37704
  (e) => {
37670
37705
  appendSessionEvent(e.sessionId, {
37706
+ ...e.delegationId ? { delegationId: e.delegationId } : {},
37707
+ ...e.taskId ? { taskId: e.taskId } : {},
37708
+ ...e.stopReason ? { stopReason: e.stopReason } : {},
37709
+ ...e.resultExcerpt ? { resultExcerpt: e.resultExcerpt } : {},
37710
+ ...e.mode ? { mode: e.mode } : {},
37671
37711
  type: "delegate_completed",
37672
37712
  ts: (/* @__PURE__ */ new Date()).toISOString(),
37673
37713
  target: e.target,
@@ -38896,4 +38936,4 @@ export {
38896
38936
  CLI_VERSION,
38897
38937
  runInteractive
38898
38938
  };
38899
- //# sourceMappingURL=cli-main-AKJHDSHX.js.map
38939
+ //# sourceMappingURL=cli-main-4E4YMCNS.js.map
@@ -56,6 +56,7 @@ import {
56
56
  // src/execution.ts
57
57
  import * as path14 from "node:path";
58
58
  import { effectiveFallbackChain as effectiveFallbackChain2, setQueuedMessagesSnapshot as setQueuedMessagesSnapshot2 } from "@wrongstack/core/agent";
59
+ import { LeaderAutoWakeController } from "@wrongstack/core/coordination";
59
60
  import { updateReviewReportEvidence } from "@wrongstack/core/plugin";
60
61
  import { attachTodosCheckpoint as attachTodosCheckpoint2 } from "@wrongstack/core/storage";
61
62
  import { normalizeTokenSavingTier as normalizeTokenSavingTier2 } from "@wrongstack/core/types";
@@ -206,13 +207,14 @@ async function runWebUIDispatch(ctx) {
206
207
  getFleetBudget,
207
208
  stopSessionFleet,
208
209
  onSessionRetired,
210
+ leaderAutoWake,
209
211
  webuiSessionChild
210
212
  } = ctx;
211
213
  const isSessionChild = Boolean(webuiSessionChild);
212
214
  const isSimpleUi = !isSessionChild && flags["simpleui"] === true;
213
215
  agent.disableInteractiveConfirmation();
214
216
  renderer.setSilent(true);
215
- const { runWebUI } = await import("./webui-server-3NYXBZHX.js");
217
+ const { runWebUI } = await import("./webui-server-FRVJWLZA.js");
216
218
  const flagValue = (names) => {
217
219
  for (const name of names) {
218
220
  if (!Object.hasOwn(flags, name)) continue;
@@ -315,6 +317,7 @@ async function runWebUIDispatch(ctx) {
315
317
  ...getFleetBudget ? { getFleetBudget } : {},
316
318
  ...stopSessionFleet ? { stopSessionFleet } : {},
317
319
  ...onSessionRetired ? { onSessionRetired } : {},
320
+ ...leaderAutoWake ? { leaderAutoWake } : {},
318
321
  sessionsDir: projectSessionsDir,
319
322
  claimSession: activateSessionIdentity ? async (sessionId) => {
320
323
  const previousSessionId = agent.ctx.session?.id ?? session.id;
@@ -1259,6 +1262,7 @@ import {
1259
1262
  restoreSessionSubagentModelPlan,
1260
1263
  restoreSessionSubagentPolicy
1261
1264
  } from "@wrongstack/core/coordination";
1265
+ import { TOKENS as TOKENS2 } from "@wrongstack/core/kernel";
1262
1266
  import { attachTodosCheckpoint, loadTodosCheckpoint } from "@wrongstack/core/storage";
1263
1267
  import { projectLastRequestTokens } from "@wrongstack/core/types/session-timeline";
1264
1268
  import { sessionScopedPath as sessionScopedPath2, toErrorMessage } from "@wrongstack/core/utils";
@@ -1412,6 +1416,11 @@ async function resumeSession(ctx, sessionId) {
1412
1416
  /* startId */
1413
1417
  1
1414
1418
  );
1419
+ try {
1420
+ agent.container?.safeResolve?.(TOKENS2.DelegationTracker)?.rehydrate(canonicalSessionId, resumed.data.events ?? []);
1421
+ } catch (err) {
1422
+ warn("delegation rehydrate", err);
1423
+ }
1415
1424
  setStage("read_sidecars");
1416
1425
  const sessionsDir = state.wpaths.projectSessions;
1417
1426
  const resumedTodosPath = sessionScopedPath2(sessionsDir, resumed.writer.id, ".todos.json");
@@ -6511,9 +6520,20 @@ async function execute(deps) {
6511
6520
  switchCtx,
6512
6521
  switchProjectInPlace: switchProjectInPlace2
6513
6522
  };
6523
+ const leaderAutoWake = new LeaderAutoWakeController({
6524
+ events,
6525
+ config: () => {
6526
+ try {
6527
+ return configStore.get()?.fleet?.delegate;
6528
+ } catch {
6529
+ return void 0;
6530
+ }
6531
+ }
6532
+ });
6514
6533
  renderer.setTuiActive(true);
6515
6534
  try {
6516
6535
  code = await runTuiDispatch({
6536
+ leaderAutoWake,
6517
6537
  agent,
6518
6538
  events,
6519
6539
  slashRegistry,
@@ -6756,82 +6776,98 @@ async function execute(deps) {
6756
6776
  if (spawnResult !== null) return spawnResult;
6757
6777
  } finally {
6758
6778
  renderer.setTuiActive(false);
6779
+ leaderAutoWake.dispose();
6759
6780
  offDirectorSpawned();
6760
6781
  }
6761
6782
  } else if (executionMode === "webui") {
6762
- code = await runWebUIDispatch({
6763
- agent,
6783
+ const webuiLeaderAutoWake = new LeaderAutoWakeController({
6764
6784
  events,
6765
- session,
6766
- config,
6767
- flags,
6768
- projectRoot,
6769
- globalConfigPath: wpaths.globalConfig,
6770
- profileConfigPath: wpaths.profileConfig(profileName),
6771
- projectSessionsDir: wpaths.projectSessions,
6772
- modelsRegistry,
6773
- providerAuthRegistry,
6774
- mcpRegistry,
6775
- brain,
6776
- brainSettings,
6777
- brainRuntime,
6778
- getBrainLog,
6779
- subscribeEternalIteration,
6780
- sessionStore: activeSessionStore,
6781
- memoryStore,
6782
- getVectorMemoryStore: () => vectorMemoryStoreFromExecute,
6783
- vectorMemoryModelCacheDir: vectorMemoryModelCacheDirFromExecute,
6784
- skillLoader,
6785
- promptLoader,
6786
- modeStore,
6787
- modeId,
6788
- needsSetup,
6789
- renderer,
6790
- onAutonomy,
6791
- applyLiveSettings,
6792
- activateSessionIdentity,
6793
- rebindTodosCheckpoint,
6794
- agentTranscripts,
6795
- onModelContextResolved,
6796
- sddSubagentFactory,
6797
- statusTracker,
6798
- updateInfo: bootUpdateInfo,
6799
- webuiSessionChild,
6800
- // Stopping a tab's run stops the work that tab started — its
6801
- // subagents included. Aborting the leader's controller only unwinds
6802
- // workers it is BLOCKED on; anything started with `spawn_subagent` +
6803
- // `assign_task` keeps going unless asked to stop. Scoped to the
6804
- // session so one tab's Stop never reaches another tab's fleet.
6805
- stopSessionFleet: async (sessionId) => {
6806
- await getDirector?.()?.terminateSession(sessionId);
6807
- },
6808
- // Closing a tab is not stopping it. The run keeps going and keeps its
6809
- // fleet; what goes is the background help pinned to that conversation
6810
- // — the explore companion's poll timer and the shadow reviewer's
6811
- // bookkeeping — which nobody is watching any more.
6812
- ...releaseSessionHelpers ? { onSessionRetired: releaseSessionHelpers } : {},
6813
- getFleetBudget: () => {
6814
- const d = getDirector?.() ?? null;
6815
- if (!d) return null;
6816
- const snap = d.fleetManager?.budgetSnapshot?.();
6817
- const maxSpawns = snap?.maxSpawns ?? d.maxSpawns;
6818
- const usedSpawns = snap?.usedSpawns ?? d.spawnCount;
6819
- const remainingSpawns = snap?.remainingSpawns ?? Math.max(
6820
- 0,
6821
- (Number.isFinite(maxSpawns) ? maxSpawns : Number.POSITIVE_INFINITY) - usedSpawns
6822
- );
6823
- const activeAgents = d.status().subagents.filter((s) => s.status === "running" || s.status === "idle").length;
6824
- return {
6825
- maxSpawns,
6826
- usedSpawns,
6827
- remainingSpawns,
6828
- activeAgents,
6829
- ...snap?.checkpointMaxSpawns !== void 0 ? { checkpointMaxSpawns: snap.checkpointMaxSpawns } : {},
6830
- ...snap?.ceilingMismatch ? { ceilingMismatch: true } : {}
6831
- };
6832
- },
6833
- ...createKanbanDispatchHandler({ config, events, skillLoader, sddSubagentFactory })
6785
+ config: () => {
6786
+ try {
6787
+ return configStore.get()?.fleet?.delegate;
6788
+ } catch {
6789
+ return void 0;
6790
+ }
6791
+ }
6834
6792
  });
6793
+ try {
6794
+ code = await runWebUIDispatch({
6795
+ leaderAutoWake: webuiLeaderAutoWake,
6796
+ agent,
6797
+ events,
6798
+ session,
6799
+ config,
6800
+ flags,
6801
+ projectRoot,
6802
+ globalConfigPath: wpaths.globalConfig,
6803
+ profileConfigPath: wpaths.profileConfig(profileName),
6804
+ projectSessionsDir: wpaths.projectSessions,
6805
+ modelsRegistry,
6806
+ providerAuthRegistry,
6807
+ mcpRegistry,
6808
+ brain,
6809
+ brainSettings,
6810
+ brainRuntime,
6811
+ getBrainLog,
6812
+ subscribeEternalIteration,
6813
+ sessionStore: activeSessionStore,
6814
+ memoryStore,
6815
+ getVectorMemoryStore: () => vectorMemoryStoreFromExecute,
6816
+ vectorMemoryModelCacheDir: vectorMemoryModelCacheDirFromExecute,
6817
+ skillLoader,
6818
+ promptLoader,
6819
+ modeStore,
6820
+ modeId,
6821
+ needsSetup,
6822
+ renderer,
6823
+ onAutonomy,
6824
+ applyLiveSettings,
6825
+ activateSessionIdentity,
6826
+ rebindTodosCheckpoint,
6827
+ agentTranscripts,
6828
+ onModelContextResolved,
6829
+ sddSubagentFactory,
6830
+ statusTracker,
6831
+ updateInfo: bootUpdateInfo,
6832
+ webuiSessionChild,
6833
+ // Stopping a tab's run stops the work that tab started — its
6834
+ // subagents included. Aborting the leader's controller only unwinds
6835
+ // workers it is BLOCKED on; anything started with `spawn_subagent` +
6836
+ // `assign_task` keeps going unless asked to stop. Scoped to the
6837
+ // session so one tab's Stop never reaches another tab's fleet.
6838
+ stopSessionFleet: async (sessionId) => {
6839
+ await getDirector?.()?.terminateSession(sessionId);
6840
+ },
6841
+ // Closing a tab is not stopping it. The run keeps going and keeps its
6842
+ // fleet; what goes is the background help pinned to that conversation
6843
+ // — the explore companion's poll timer and the shadow reviewer's
6844
+ // bookkeeping — which nobody is watching any more.
6845
+ ...releaseSessionHelpers ? { onSessionRetired: releaseSessionHelpers } : {},
6846
+ getFleetBudget: () => {
6847
+ const d = getDirector?.() ?? null;
6848
+ if (!d) return null;
6849
+ const snap = d.fleetManager?.budgetSnapshot?.();
6850
+ const maxSpawns = snap?.maxSpawns ?? d.maxSpawns;
6851
+ const usedSpawns = snap?.usedSpawns ?? d.spawnCount;
6852
+ const remainingSpawns = snap?.remainingSpawns ?? Math.max(
6853
+ 0,
6854
+ (Number.isFinite(maxSpawns) ? maxSpawns : Number.POSITIVE_INFINITY) - usedSpawns
6855
+ );
6856
+ const activeAgents = d.status().subagents.filter((s) => s.status === "running" || s.status === "idle").length;
6857
+ return {
6858
+ maxSpawns,
6859
+ usedSpawns,
6860
+ remainingSpawns,
6861
+ activeAgents,
6862
+ ...snap?.checkpointMaxSpawns !== void 0 ? { checkpointMaxSpawns: snap.checkpointMaxSpawns } : {},
6863
+ ...snap?.ceilingMismatch ? { ceilingMismatch: true } : {}
6864
+ };
6865
+ },
6866
+ ...createKanbanDispatchHandler({ config, events, skillLoader, sddSubagentFactory })
6867
+ });
6868
+ } finally {
6869
+ webuiLeaderAutoWake.dispose();
6870
+ }
6835
6871
  } else {
6836
6872
  const headlessKanbanMirror = projectRoot ? (await import("./kanban-run-mirror-22HQ5FNG.js")).createKanbanRunMirror({
6837
6873
  projectRoot,
@@ -6917,4 +6953,4 @@ export {
6917
6953
  execute,
6918
6954
  resolveReviewerFallbackModels
6919
6955
  };
6920
- //# sourceMappingURL=execution-FJE27VQ6.js.map
6956
+ //# sourceMappingURL=execution-4RXQLDAF.js.map
@@ -62,7 +62,7 @@ var hqCmd = async (args, deps) => {
62
62
  return 1;
63
63
  };
64
64
  async function startServer(deps) {
65
- const { startHqServer } = await import("./hq-server-G3VVVMIG.js");
65
+ const { startHqServer } = await import("./hq-server-JJFGFWI5.js");
66
66
  const dataDir = resolveDataDir(deps);
67
67
  const flags = deps.flags ?? {};
68
68
  const rawPublicUrl = typeof flags["hq-public-url"] === "string" ? flags["hq-public-url"] : process.env.WRONGSTACK_HQ_PUBLIC_URL;
@@ -829,4 +829,4 @@ export {
829
829
  hqCmd,
830
830
  resolveAuditActor
831
831
  };
832
- //# sourceMappingURL=hq-G5PCP325.js.map
832
+ //# sourceMappingURL=hq-DQTUE7RC.js.map
@@ -10,7 +10,7 @@ import {
10
10
  readLocalSubagentTranscript,
11
11
  sanitizeApiError,
12
12
  startHqServer
13
- } from "./chunk-XIZZ2EXG.js";
13
+ } from "./chunk-7FPN3QTH.js";
14
14
  import "./chunk-HXJYHAR4.js";
15
15
  import "./chunk-5EA6OTLJ.js";
16
16
  import "./chunk-Q7E3BPDU.js";
@@ -27,4 +27,4 @@ export {
27
27
  sanitizeApiError,
28
28
  startHqServer
29
29
  };
30
- //# sourceMappingURL=hq-server-G3VVVMIG.js.map
30
+ //# sourceMappingURL=hq-server-JJFGFWI5.js.map
package/dist/index.js CHANGED
@@ -16,14 +16,14 @@ async function main(argv) {
16
16
  applySessionShellDefault();
17
17
  const earlyFlags = parseArgs(argv).flags;
18
18
  if (earlyFlags["help"] === true || earlyFlags["version"] === true) {
19
- const { handleHelpVersionShortCircuit } = await import("./short-circuit-flags-RAT6UZVI.js");
19
+ const { handleHelpVersionShortCircuit } = await import("./short-circuit-flags-LHUNLCZY.js");
20
20
  const earlyExit = await handleHelpVersionShortCircuit(argv);
21
21
  if (earlyExit !== null) return earlyExit;
22
22
  }
23
- const { initializeCli } = await import("./cli-context-TIAP6YQK.js");
23
+ const { initializeCli } = await import("./cli-context-6GMXUOXD.js");
24
24
  const cliCtx = await initializeCli(argv);
25
25
  if (typeof cliCtx === "number") return cliCtx;
26
- const { runInteractive } = await import("./cli-main-AKJHDSHX.js");
26
+ const { runInteractive } = await import("./cli-main-4E4YMCNS.js");
27
27
  return runInteractive(cliCtx);
28
28
  }
29
29
 
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  handleHelpVersionShortCircuit
3
- } from "./chunk-4OLAZIIN.js";
3
+ } from "./chunk-5VPDKQDH.js";
4
4
  import "./chunk-6WRKAACA.js";
5
5
  import "./chunk-XJXDOF63.js";
6
6
  import "./chunk-EK2P53GL.js";
7
7
  export {
8
8
  handleHelpVersionShortCircuit
9
9
  };
10
- //# sourceMappingURL=short-circuit-flags-RAT6UZVI.js.map
10
+ //# sourceMappingURL=short-circuit-flags-LHUNLCZY.js.map
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  subcommandNames,
3
3
  subcommands
4
- } from "./chunk-RLCKKLJA.js";
4
+ } from "./chunk-7K3IJGWC.js";
5
5
  export {
6
6
  subcommandNames,
7
7
  subcommands
8
8
  };
9
- //# sourceMappingURL=subcommands-VFVUTYAV.js.map
9
+ //# sourceMappingURL=subcommands-UXWY3OPC.js.map
@@ -1,5 +1,7 @@
1
1
  interface NodePtyLoaderLogger {
2
2
  debug?: (message: string) => void;
3
+ info?: (message: string) => void;
4
+ warn?: (message: string) => void;
3
5
  }
4
6
  export declare function createNodePtyLoader(importMetaUrl: string, logger: NodePtyLoaderLogger): () => unknown;
5
7
  export {};
@@ -53,6 +53,12 @@ export interface RouteContextsParams {
53
53
  }>;
54
54
  /** Retire the per-tab agents of sessions nobody is displaying any more. */
55
55
  onSessionsUndisplayed: (sessionIds: string[]) => void;
56
+ /**
57
+ * Background-delegation auto-wake host, when the CLI passed a controller.
58
+ * Feeds the conversation path (submits, finished runs, runtime turns) and
59
+ * the session routes (a tab started displaying a session).
60
+ */
61
+ autoWake?: import('@wrongstack/webui-server').WebuiLeaderAutoWakeHost | undefined;
56
62
  /** Stop the subagents one session spawned, when that session is aborted. */
57
63
  stopSessionFleet?: ((sessionId: string) => void | Promise<void>) | undefined;
58
64
  send: (ws: WebSocket, msg: WSServerMessage) => void;
@@ -64,7 +70,7 @@ export interface RouteContextsParams {
64
70
  */
65
71
  broadcastEveryone?: ((msg: WSServerMessage) => void) | undefined;
66
72
  }
67
- export declare function createWebuiRouteContexts({ opts, profileConfigPath, profileDir, globalRoot, sessionStartedAt, currentSessionId, getCustomModeStore, buildSessionStartPayload, prefSnapshot, persistPrefs, pendingConfirms, abortControllers, getSessionAgent, peekSessionAgent, isSessionLive, getForegroundSession, setForegroundSession, clients, onSessionsUndisplayed, stopSessionFleet, send, broadcast, broadcastEveryone, }: RouteContextsParams): {
73
+ export declare function createWebuiRouteContexts({ opts, profileConfigPath, profileDir, globalRoot, sessionStartedAt, currentSessionId, getCustomModeStore, buildSessionStartPayload, prefSnapshot, persistPrefs, pendingConfirms, abortControllers, getSessionAgent, peekSessionAgent, isSessionLive, getForegroundSession, setForegroundSession, clients, onSessionsUndisplayed, autoWake, stopSessionFleet, send, broadcast, broadcastEveryone, }: RouteContextsParams): {
68
74
  brainCtx: BrainHandlerContext;
69
75
  introspectionCtx: IntrospectionRouteContext;
70
76
  skillsCtx: SkillsContext;
@@ -34,6 +34,7 @@ import {
34
34
  createEmbeddedMessageRouter,
35
35
  createEmbeddedProviderOperations,
36
36
  createSessionAgentRegistry,
37
+ createWebuiLeaderAutoWakeHost,
37
38
  envFlag,
38
39
  findFreePort,
39
40
  findInstalledPackageJson,
@@ -406,27 +407,29 @@ import { existsSync } from "node:fs";
406
407
  import { createRequire } from "node:module";
407
408
  import * as path2 from "node:path";
408
409
  import { fileURLToPath } from "node:url";
410
+ import { repairNodePtySpawnHelper } from "@wrongstack/webui-server";
409
411
  function createNodePtyLoader(importMetaUrl, logger) {
410
412
  const requireFromCli = createRequire(importMetaUrl);
411
413
  let cachedNodePty;
414
+ const loaded = (req, via) => {
415
+ logger.debug?.(`[terminal] node-pty loaded via ${via}`);
416
+ repairNodePtySpawnHelper(req, logger);
417
+ return cachedNodePty;
418
+ };
412
419
  return () => {
413
420
  if (cachedNodePty !== void 0) return cachedNodePty;
414
421
  try {
415
422
  const webuiEntry = requireFromCli.resolve("@wrongstack/webui");
416
423
  const webuiDir = webuiEntry.replace(/[\\/]dist.*$/, "");
417
- const webuiPkgJson = path2.join(webuiDir, "package.json");
418
- cachedNodePty = createRequire(webuiPkgJson)("node-pty");
419
- if (cachedNodePty) {
420
- logger.debug?.(`[terminal] node-pty loaded via webui package (${webuiDir})`);
421
- return cachedNodePty;
422
- }
424
+ const webuiRequire = createRequire(path2.join(webuiDir, "package.json"));
425
+ cachedNodePty = webuiRequire("node-pty");
426
+ if (cachedNodePty) return loaded(webuiRequire, `webui package (${webuiDir})`);
423
427
  } catch (err) {
424
428
  logger.debug?.(`[terminal] webui-route failed: ${err.message}`);
425
429
  }
426
430
  try {
427
431
  cachedNodePty = requireFromCli("node-pty");
428
- logger.debug?.("[terminal] node-pty loaded via direct requireFromCli");
429
- return cachedNodePty;
432
+ return loaded(requireFromCli, "direct requireFromCli");
430
433
  } catch (err) {
431
434
  logger.debug?.(`[terminal] direct require failed: ${err.message}`);
432
435
  }
@@ -440,11 +443,9 @@ function createNodePtyLoader(importMetaUrl, logger) {
440
443
  for (let i = 0; i < 6; i++) {
441
444
  const candidate = path2.join(dir, "node_modules", "node-pty");
442
445
  if (existsSync(candidate)) {
443
- cachedNodePty = createRequire(candidate)("node-pty");
444
- if (cachedNodePty) {
445
- logger.debug?.(`[terminal] node-pty loaded via workspace root (${candidate})`);
446
- return cachedNodePty;
447
- }
446
+ const candidateRequire = createRequire(candidate);
447
+ cachedNodePty = candidateRequire("node-pty");
448
+ if (cachedNodePty) return loaded(candidateRequire, `workspace root (${candidate})`);
448
449
  }
449
450
  const parent = path2.dirname(dir);
450
451
  if (parent === dir) break;
@@ -900,6 +901,7 @@ function createWebuiRouteContexts({
900
901
  setForegroundSession,
901
902
  clients,
902
903
  onSessionsUndisplayed,
904
+ autoWake,
903
905
  stopSessionFleet,
904
906
  send,
905
907
  broadcast,
@@ -1115,6 +1117,9 @@ function createWebuiRouteContexts({
1115
1117
  setForegroundSession,
1116
1118
  clients,
1117
1119
  onSessionsUndisplayed,
1120
+ // A tab started showing a session: release background-delegation results
1121
+ // held while nobody displayed it (auto-wake hold).
1122
+ ...autoWake ? { onSessionsDisplayed: (ids) => autoWake.onSessionsDisplayed(ids) } : {},
1118
1123
  send,
1119
1124
  broadcast,
1120
1125
  log: (m) => console.log(m)
@@ -1128,6 +1133,7 @@ function createWebuiRouteContexts({
1128
1133
  ...peekSessionAgent ? { peekAgent: peekSessionAgent } : {},
1129
1134
  abortControllers,
1130
1135
  pendingConfirms,
1136
+ ...autoWake ? { autoWake } : {},
1131
1137
  ...stopSessionFleet ? { stopSessionFleet } : {},
1132
1138
  send,
1133
1139
  broadcast,
@@ -1698,6 +1704,23 @@ async function runWebUI(opts) {
1698
1704
  }
1699
1705
  });
1700
1706
  sessionAgentsRef = sessionAgents;
1707
+ const isSessionDisplayed = (sessionId) => {
1708
+ for (const client of clients.values()) {
1709
+ if (client.sessionId === sessionId) return true;
1710
+ if (client.sessionIds?.has(sessionId) === true) return true;
1711
+ }
1712
+ return false;
1713
+ };
1714
+ const autoWakeHost = opts.leaderAutoWake ? createWebuiLeaderAutoWakeHost({
1715
+ controller: opts.leaderAutoWake,
1716
+ events: opts.events,
1717
+ abortControllers,
1718
+ pendingConfirms,
1719
+ isOpen: (sessionId) => sessionId === opts.session.id || sessionAgents.isLive(sessionId),
1720
+ isDisplayed: isSessionDisplayed,
1721
+ broadcast,
1722
+ logger: { warn: (message) => consoleLogger.warn(message) }
1723
+ }) : void 0;
1701
1724
  const stopLiveStatusLogger = startWebUILiveStatusLogger({
1702
1725
  events: opts.events,
1703
1726
  dashboard: terminalLogView,
@@ -1783,6 +1806,7 @@ async function runWebUI(opts) {
1783
1806
  getSessionAgent: (sessionId) => sessionAgents.get(sessionId),
1784
1807
  peekSessionAgent: (sessionId) => sessionAgents.peek(sessionId),
1785
1808
  onSessionsUndisplayed: retireUndisplayedSessions,
1809
+ ...autoWakeHost ? { autoWake: autoWakeHost } : {},
1786
1810
  isSessionLive: (sessionId) => sessionAgents.isLive(sessionId),
1787
1811
  getForegroundSession: () => foregroundSession,
1788
1812
  setForegroundSession: (next) => {
@@ -1934,6 +1958,7 @@ async function runWebUI(opts) {
1934
1958
  },
1935
1959
  disposeResources: async () => {
1936
1960
  releaseSessionSalvage();
1961
+ autoWakeHost?.dispose();
1937
1962
  await stopEmptySessionCleanup?.dispose();
1938
1963
  await sessionAgents.closeAll().catch(() => void 0);
1939
1964
  credentialWatcherClose?.();
@@ -1998,4 +2023,4 @@ async function runWebUI(opts) {
1998
2023
  export {
1999
2024
  runWebUI
2000
2025
  };
2001
- //# sourceMappingURL=webui-server-3NYXBZHX.js.map
2026
+ //# sourceMappingURL=webui-server-FRVJWLZA.js.map
@@ -78,6 +78,13 @@ export interface CliWebUIOptions {
78
78
  * before calling this. Absent for hosts with a single conversation.
79
79
  */
80
80
  onSessionRetired?: ((sessionId: string) => void) | undefined;
81
+ /**
82
+ * The process's ONE background-delegation auto-wake controller. The CLI
83
+ * creates it (it owns the delegate tool and the delivery hub) and the WebUI
84
+ * binds its port; the WebUI never creates a second one, which would wake
85
+ * every session twice. Absent: this WebUI host never auto-wakes.
86
+ */
87
+ leaderAutoWake?: import('@wrongstack/webui-server').WebuiLeaderAutoWakeController | undefined;
81
88
  /** Browser-facing HTTP URL, used when WebUI is exposed behind a tunnel/proxy. */
82
89
  publicUrl?: string | undefined;
83
90
  /** Browser-facing WebSocket URL injected into the frontend. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/cli",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "license": "MIT",
5
5
  "description": "WrongStack CLI — terminal AI coding agent with provider catalog from models.dev. Provides `wrongstack` and `wstack` binaries.",
6
6
  "keywords": [
@@ -41,39 +41,39 @@
41
41
  "data"
42
42
  ],
43
43
  "dependencies": {
44
- "@wrongstack/acp": "1.0.8",
45
- "@wrongstack/bench": "1.0.8",
46
- "@wrongstack/core": "1.0.8",
47
- "@wrongstack/primitives": "1.0.8",
48
- "@wrongstack/kanban": "1.0.8",
49
- "@wrongstack/mcp": "1.0.8",
50
- "@wrongstack/persistence": "1.0.8",
51
- "@wrongstack/plug-lsp": "1.0.8",
44
+ "@wrongstack/acp": "1.0.9",
45
+ "@wrongstack/bench": "1.0.9",
46
+ "@wrongstack/core": "1.0.9",
47
+ "@wrongstack/kanban": "1.0.9",
48
+ "@wrongstack/mcp": "1.0.9",
49
+ "@wrongstack/persistence": "1.0.9",
50
+ "@wrongstack/plug-lsp": "1.0.9",
52
51
  "@wrongstack/plugins": "1.0.8",
53
- "@wrongstack/providers": "1.0.8",
54
- "@wrongstack/requirement-intake": "1.0.8",
55
- "@wrongstack/runtime": "1.0.8",
56
- "@wrongstack/sage": "1.0.8",
57
- "@wrongstack/sdd": "1.0.8",
58
- "@wrongstack/security-scanner": "1.0.8",
59
- "@wrongstack/simpleui": "1.0.8",
60
- "@wrongstack/techstack": "1.0.8",
61
- "@wrongstack/telegram": "1.0.8",
62
- "@wrongstack/tools": "1.0.8",
63
- "@wrongstack/tui": "1.0.8",
64
- "@wrongstack/vector-memory": "1.0.8",
65
- "@wrongstack/webui": "1.0.8",
66
- "@wrongstack/webui-hq": "1.0.8",
67
- "@wrongstack/webui-protocol": "1.0.8",
68
- "@wrongstack/wrongtrace": "1.0.8",
69
- "@wrongstack/webui-server": "1.0.8",
52
+ "@wrongstack/primitives": "1.0.9",
53
+ "@wrongstack/providers": "1.0.9",
54
+ "@wrongstack/requirement-intake": "1.0.9",
55
+ "@wrongstack/runtime": "1.0.9",
56
+ "@wrongstack/sage": "1.0.9",
57
+ "@wrongstack/sdd": "1.0.9",
58
+ "@wrongstack/security-scanner": "1.0.9",
59
+ "@wrongstack/simpleui": "1.0.9",
60
+ "@wrongstack/techstack": "1.0.9",
61
+ "@wrongstack/telegram": "1.0.9",
62
+ "@wrongstack/tools": "1.0.9",
63
+ "@wrongstack/tui": "1.0.9",
64
+ "@wrongstack/vector-memory": "1.0.9",
65
+ "@wrongstack/webui": "1.0.9",
66
+ "@wrongstack/webui-hq": "1.0.9",
67
+ "@wrongstack/webui-protocol": "1.0.9",
68
+ "@wrongstack/webui-server": "1.0.9",
69
+ "@wrongstack/wrongtrace": "1.0.9",
70
70
  "ws": "^8.21.3"
71
71
  },
72
72
  "optionalDependencies": {
73
- "@wrongstack/desktop": "1.0.8"
73
+ "@wrongstack/desktop": "1.0.9"
74
74
  },
75
75
  "devDependencies": {
76
- "@types/node": "^26.2.0",
76
+ "@types/node": "^26.5.1",
77
77
  "@types/ws": "^8.18.1",
78
78
  "jsdom": "^30.0.1",
79
79
  "typescript": "^7.0.2"