@wrongstack/cli 1.0.8 → 1.0.10

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
  }
@@ -17876,7 +17897,6 @@ function parseTaskEvidenceFlags(tokens) {
17876
17897
  }
17877
17898
  if (!inline && value.startsWith("-")) {
17878
17899
  warnings.push(`${key} expects a URL but none was provided`);
17879
- i++;
17880
17900
  continue;
17881
17901
  }
17882
17902
  if (!inline) i++;
@@ -17892,7 +17912,6 @@ function parseTaskEvidenceFlags(tokens) {
17892
17912
  }
17893
17913
  if (!inline && raw.startsWith("-")) {
17894
17914
  warnings.push(`${key} expects <checkId>=<status> but none was provided`);
17895
- i++;
17896
17915
  continue;
17897
17916
  }
17898
17917
  const sep2 = raw.lastIndexOf("=");
@@ -18127,10 +18146,16 @@ async function handleTaskSubcommand(opts, projectRoot, args, showHelp) {
18127
18146
  }
18128
18147
  if (board.lifecycle?.mode === "managed") {
18129
18148
  const stageToColumn = board.lifecycle?.columns ?? {};
18130
- const currentColumnId = board.tasks.find((t) => t.id === taskId)?.columnId;
18149
+ const currentTask = board.tasks.find((t) => t.id === taskId);
18150
+ if (!currentTask) {
18151
+ return { message: color36.red("Task not found") };
18152
+ }
18131
18153
  const currentStage = Object.entries(stageToColumn).find(
18132
- ([, columnId]) => columnId === currentColumnId
18154
+ ([, columnId]) => columnId === currentTask.columnId
18133
18155
  )?.[0];
18156
+ if (currentStage === "done") {
18157
+ return { message: color36.yellow("Task is already in Done \u2014 nothing to do.") };
18158
+ }
18134
18159
  const path35 = currentStage === "backlog" ? ["todo", "running", "review", "done"] : currentStage === "todo" ? ["running", "review", "done"] : currentStage === "running" ? ["review", "done"] : currentStage === "review" ? ["done"] : null;
18135
18160
  if (path35 === null) {
18136
18161
  return {
@@ -18139,10 +18164,6 @@ async function handleTaskSubcommand(opts, projectRoot, args, showHelp) {
18139
18164
  )
18140
18165
  };
18141
18166
  }
18142
- const currentTask = board.tasks.find((t) => t.id === taskId);
18143
- if (!currentTask) {
18144
- return { message: color36.red("Task not found") };
18145
- }
18146
18167
  const preflightIssues = [];
18147
18168
  if (!note?.trim()) {
18148
18169
  return {
@@ -19899,7 +19920,7 @@ async function runAudienceMemory(store, rest) {
19899
19920
  let imported = 0;
19900
19921
  let skipped = 0;
19901
19922
  for (const entry of entries) {
19902
- if (typeof entry.text !== "string" || !entry.text.trim()) {
19923
+ if (!entry || typeof entry !== "object" || typeof entry.text !== "string" || !entry.text.trim()) {
19903
19924
  skipped++;
19904
19925
  continue;
19905
19926
  }
@@ -20213,6 +20234,8 @@ var MEMORY_KINDS = [
20213
20234
  "summary"
20214
20235
  ];
20215
20236
  var MEMORY_SCOPES = ["project", "user", "session", "file", "symbol"];
20237
+ var DECIMAL_RE = /^(?:\d+(?:\.\d*)?|\.\d+)$/;
20238
+ var INTEGER_RE = /^\d+$/;
20216
20239
  var MEMORY_STATUSES = [
20217
20240
  "active",
20218
20241
  "stale",
@@ -20237,7 +20260,7 @@ function parseMemoryFlags(tokens) {
20237
20260
  errors.push(`${name} needs a value between 0 and 1.`);
20238
20261
  return void 0;
20239
20262
  }
20240
- const parsed = Number.parseFloat(value);
20263
+ const parsed = DECIMAL_RE.test(value) ? Number.parseFloat(value) : Number.NaN;
20241
20264
  if (!Number.isFinite(parsed) || parsed < 0 || parsed > 1) {
20242
20265
  errors.push(`${name} must be a number between 0 and 1 (got "${value}").`);
20243
20266
  return void 0;
@@ -20347,7 +20370,7 @@ function parseForFileFlags(tokens) {
20347
20370
  out.errors.push("--line needs a 1-indexed line number.");
20348
20371
  continue;
20349
20372
  }
20350
- const parsed = Number.parseInt(next, 10);
20373
+ const parsed = INTEGER_RE.test(next) ? Number.parseInt(next, 10) : Number.NaN;
20351
20374
  if (!Number.isFinite(parsed) || parsed < 1) {
20352
20375
  out.errors.push(`--line must be a positive integer (got "${next}").`);
20353
20376
  continue;
@@ -20359,7 +20382,7 @@ function parseForFileFlags(tokens) {
20359
20382
  out.errors.push("--limit needs a value between 1 and 200.");
20360
20383
  continue;
20361
20384
  }
20362
- const parsed = Number.parseInt(next, 10);
20385
+ const parsed = INTEGER_RE.test(next) ? Number.parseInt(next, 10) : Number.NaN;
20363
20386
  if (!Number.isFinite(parsed) || parsed < 1 || parsed > 200) {
20364
20387
  out.errors.push(`--limit must be between 1 and 200 (got "${next}").`);
20365
20388
  continue;
@@ -20859,7 +20882,8 @@ async function runStats(opts) {
20859
20882
  "anti_pattern",
20860
20883
  "untyped"
20861
20884
  ];
20862
- for (const t of typeOrder) {
20885
+ const extraTypes = [...byType.keys()].filter((t) => !typeOrder.includes(t)).sort();
20886
+ for (const t of [...typeOrder.slice(0, -1), ...extraTypes, "untyped"]) {
20863
20887
  const count = byType.get(t);
20864
20888
  if (count) {
20865
20889
  const bar2 = "\u2588".repeat(Math.min(count, 20));
@@ -26810,6 +26834,11 @@ import { leaderTierPolicy, listTierIds, resolveTier } from "@wrongstack/core/coo
26810
26834
  import { ConfigError as ConfigError6 } from "@wrongstack/core/types";
26811
26835
  import { atomicWrite as atomicWrite10, color as color54, toErrorMessage as toErrorMessage25 } from "@wrongstack/core/utils";
26812
26836
  var LEADER_MODES = ["off", "propose", "auto"];
26837
+ var DECIMAL_RE2 = /^(?:\d+(?:\.\d*)?|\.\d+)$/;
26838
+ var INTEGER_RE2 = /^\d+$/;
26839
+ function strictNumber(raw, pattern) {
26840
+ return pattern.test(raw) ? Number(raw) : Number.NaN;
26841
+ }
26813
26842
  async function patchGlobalConfig3(globalConfigPath, mutate) {
26814
26843
  let raw = "{}";
26815
26844
  let fileExists2 = true;
@@ -26998,15 +27027,15 @@ function buildTierCommand(opts) {
26998
27027
  message: `${color54.amber("Usage:")} /tier budget <tier> <maxCostUsd> [maxIterations] [maxToolCalls]`
26999
27028
  };
27000
27029
  }
27001
- const usd = Number.parseFloat(parts[2] ?? "");
27030
+ const usd = strictNumber(parts[2] ?? "", DECIMAL_RE2);
27002
27031
  if (!Number.isFinite(usd) || usd < 0) {
27003
27032
  return { message: `${color54.red("Invalid")} maxCostUsd: "${parts[2] ?? ""}"` };
27004
27033
  }
27005
- const iters = parts[3] !== void 0 ? Number.parseInt(parts[3], 10) : void 0;
27034
+ const iters = parts[3] !== void 0 ? strictNumber(parts[3], INTEGER_RE2) : void 0;
27006
27035
  if (iters !== void 0 && (!Number.isFinite(iters) || iters < 0)) {
27007
27036
  return { message: `${color54.red("Invalid")} maxIterations: "${parts[3]}"` };
27008
27037
  }
27009
- const tools = parts[4] !== void 0 ? Number.parseInt(parts[4], 10) : void 0;
27038
+ const tools = parts[4] !== void 0 ? strictNumber(parts[4], INTEGER_RE2) : void 0;
27010
27039
  if (tools !== void 0 && (!Number.isFinite(tools) || tools < 0)) {
27011
27040
  return { message: `${color54.red("Invalid")} maxToolCalls: "${parts[4]}"` };
27012
27041
  }
@@ -27036,6 +27065,12 @@ function buildTierCommand(opts) {
27036
27065
  for (const [key, value] of Object.entries(routing)) {
27037
27066
  if (value === tier) delete routing[key];
27038
27067
  }
27068
+ if (tiers["default"] === tier) delete tiers["default"];
27069
+ const leader = tiers["leader"];
27070
+ if (leader && typeof leader === "object") {
27071
+ const leaderRecord = leader;
27072
+ if (leaderRecord["maxTier"] === tier) delete leaderRecord["maxTier"];
27073
+ }
27039
27074
  });
27040
27075
  return { message: `${color54.green("\u2713")} tier ${color54.cyan(tier)} removed` };
27041
27076
  }
@@ -27096,7 +27131,7 @@ function buildTierCommand(opts) {
27096
27131
  };
27097
27132
  }
27098
27133
  if (action === "dwell") {
27099
- const turns = Number.parseInt(parts[2] ?? "", 10);
27134
+ const turns = strictNumber(parts[2] ?? "", INTEGER_RE2);
27100
27135
  if (!Number.isFinite(turns) || turns < 0) {
27101
27136
  return { message: `${color54.amber("Usage:")} /tier leader dwell <turns>` };
27102
27137
  }
@@ -27159,7 +27194,7 @@ function buildAgentsCommand(opts) {
27159
27194
  return {
27160
27195
  name: "agents",
27161
27196
  category: "Agent",
27162
- description: "Monitor subagent activity: /agents [chat off|compact|full | stream on|off|status | list | show <id>]",
27197
+ description: "Monitor subagent activity: /agents [chat off|full|status | stream on|off|status | list | show <id>]",
27163
27198
  async run(args) {
27164
27199
  if ((!args.trim() || args.trim() === "list") && opts.onPanelOpen?.current) {
27165
27200
  const opened = opts.onPanelOpen.current("toggleAgentsMonitor");
@@ -31302,8 +31337,8 @@ function buildTelegramSettingsCommand(opts) {
31302
31337
  message: `${color65.green("\u2713")} long-tool \u2192 ${color65.dim("off")}`
31303
31338
  };
31304
31339
  }
31305
- const ms = Number.parseInt(raw, 10);
31306
- if (Number.isNaN(ms) || ms < 0) {
31340
+ const ms = /^\d+$/.test(raw) ? Number(raw) : Number.NaN;
31341
+ if (!Number.isSafeInteger(ms)) {
31307
31342
  return {
31308
31343
  message: `${color65.red("Invalid number")}: "${raw}". Enter milliseconds, e.g. /telegram-settings long-tool 15000`
31309
31344
  };
@@ -31322,7 +31357,7 @@ function buildTelegramSettingsCommand(opts) {
31322
31357
  message: `${color65.amber("Usage:")} /telegram-settings poll <seconds> ${color65.dim("(1\u201360)")}`
31323
31358
  };
31324
31359
  }
31325
- const sec = Number.parseInt(raw, 10);
31360
+ const sec = /^\d+$/.test(raw) ? Number(raw) : Number.NaN;
31326
31361
  if (Number.isNaN(sec) || sec < 1 || sec > 60) {
31327
31362
  return {
31328
31363
  message: `${color65.red("Invalid value")}: "${raw}". Enter seconds between 1 and 60.`
@@ -37660,7 +37695,21 @@ function wireSessionEvents(deps) {
37660
37695
  ts: (/* @__PURE__ */ new Date()).toISOString(),
37661
37696
  target: e.target,
37662
37697
  task: e.task,
37663
- ...e.subagentId ? { subagentId: e.subagentId } : {}
37698
+ ...e.subagentId ? { subagentId: e.subagentId } : {},
37699
+ ...e.delegationId ? { delegationId: e.delegationId } : {},
37700
+ ...e.taskId ? { taskId: e.taskId } : {},
37701
+ ...e.mode ? { mode: e.mode } : {}
37702
+ });
37703
+ }
37704
+ );
37705
+ evOn(
37706
+ "delegation.delivered",
37707
+ (e) => {
37708
+ appendSessionEvent(e.sessionId, {
37709
+ type: "delegation_delivered",
37710
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
37711
+ delegationId: e.delegationId,
37712
+ ...e.via ? { via: e.via } : {}
37664
37713
  });
37665
37714
  }
37666
37715
  );
@@ -37668,6 +37717,11 @@ function wireSessionEvents(deps) {
37668
37717
  "delegate.completed",
37669
37718
  (e) => {
37670
37719
  appendSessionEvent(e.sessionId, {
37720
+ ...e.delegationId ? { delegationId: e.delegationId } : {},
37721
+ ...e.taskId ? { taskId: e.taskId } : {},
37722
+ ...e.stopReason ? { stopReason: e.stopReason } : {},
37723
+ ...e.resultExcerpt ? { resultExcerpt: e.resultExcerpt } : {},
37724
+ ...e.mode ? { mode: e.mode } : {},
37671
37725
  type: "delegate_completed",
37672
37726
  ts: (/* @__PURE__ */ new Date()).toISOString(),
37673
37727
  target: e.target,
@@ -38896,4 +38950,4 @@ export {
38896
38950
  CLI_VERSION,
38897
38951
  runInteractive
38898
38952
  };
38899
- //# sourceMappingURL=cli-main-AKJHDSHX.js.map
38953
+ //# sourceMappingURL=cli-main-BU5DEHDO.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-BU5DEHDO.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.10",
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",
52
- "@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",
44
+ "@wrongstack/acp": "1.0.10",
45
+ "@wrongstack/bench": "1.0.10",
46
+ "@wrongstack/core": "1.0.10",
47
+ "@wrongstack/kanban": "1.0.10",
48
+ "@wrongstack/mcp": "1.0.10",
49
+ "@wrongstack/persistence": "1.0.10",
50
+ "@wrongstack/plug-lsp": "1.0.10",
51
+ "@wrongstack/plugins": "1.0.10",
52
+ "@wrongstack/primitives": "1.0.10",
53
+ "@wrongstack/providers": "1.0.10",
54
+ "@wrongstack/requirement-intake": "1.0.10",
55
+ "@wrongstack/runtime": "1.0.10",
56
+ "@wrongstack/sage": "1.0.10",
57
+ "@wrongstack/sdd": "1.0.10",
58
+ "@wrongstack/security-scanner": "1.0.10",
59
+ "@wrongstack/simpleui": "1.0.10",
60
+ "@wrongstack/techstack": "1.0.10",
61
+ "@wrongstack/telegram": "1.0.10",
62
+ "@wrongstack/tools": "1.0.10",
63
+ "@wrongstack/tui": "1.0.10",
64
+ "@wrongstack/vector-memory": "1.0.10",
65
+ "@wrongstack/webui": "1.0.10",
66
+ "@wrongstack/webui-hq": "1.0.10",
67
+ "@wrongstack/webui-protocol": "1.0.10",
68
+ "@wrongstack/webui-server": "1.0.10",
69
+ "@wrongstack/wrongtrace": "1.0.10",
70
70
  "ws": "^8.21.3"
71
71
  },
72
72
  "optionalDependencies": {
73
- "@wrongstack/desktop": "1.0.8"
73
+ "@wrongstack/desktop": "1.0.10"
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"