@zhushanwen/subagent-core 0.7.0 → 0.8.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.
Files changed (28) hide show
  1. package/dist/{chunk-VURUAGMM.js → chunk-OL5BK4VN.js} +237 -40
  2. package/dist/{engine-discovery-scan-ocpM8FmI.d.cts → engine-discovery-scan-BXLA8y-z.d.cts} +17 -1
  3. package/dist/{engine-discovery-scan-ocpM8FmI.d.ts → engine-discovery-scan-BXLA8y-z.d.ts} +17 -1
  4. package/dist/execution/engine/engine-discovery-scan.cjs +150 -40
  5. package/dist/execution/engine/engine-discovery-scan.d.cts +1 -1
  6. package/dist/execution/engine/engine-discovery-scan.d.ts +1 -1
  7. package/dist/execution/engine/engine-discovery-scan.js +1 -1
  8. package/dist/index.cjs +278 -225
  9. package/dist/index.d.cts +24 -4
  10. package/dist/index.d.ts +24 -4
  11. package/dist/index.js +188 -304
  12. package/dist.bundle/index.cjs +279 -226
  13. package/package.json +5 -5
  14. package/src/__tests__/record-store-last-line.test.ts +12 -0
  15. package/src/core/logger.ts +1 -1
  16. package/src/execution/__tests__/inflight-production-wiring.test.ts +223 -0
  17. package/src/execution/__tests__/nested-visibility.test.ts +13 -1
  18. package/src/execution/engine/__tests__/conformance/registry-fork-filter.test.ts +55 -53
  19. package/src/execution/engine/__tests__/inflight-snapshot.test.ts +180 -0
  20. package/src/execution/engine/client/engine-client.ts +30 -1
  21. package/src/execution/engine/host/host-bridge.ts +7 -0
  22. package/src/execution/engine/host/spawned-children.ts +1 -1
  23. package/src/execution/engine/inflight-snapshot.ts +92 -0
  24. package/src/execution/engine/port.ts +18 -0
  25. package/src/execution/path-encoding.ts +6 -0
  26. package/src/execution/subagent-service.ts +25 -0
  27. package/src/execution/worktree-registry.ts +11 -7
  28. package/src/index.ts +11 -1
@@ -7693,7 +7693,7 @@ var require_signal_exit = __commonJS({
7693
7693
  };
7694
7694
  sigListeners = {};
7695
7695
  signals.forEach(function(sig) {
7696
- sigListeners[sig] = function listener() {
7696
+ sigListeners[sig] = function listener2() {
7697
7697
  if (!processOk(global.process)) {
7698
7698
  return;
7699
7699
  }
@@ -15579,6 +15579,7 @@ __export(src_exports, {
15579
15579
  getCachedFileContent: () => getCachedFileContent,
15580
15580
  getCachedParsed: () => getCachedParsed,
15581
15581
  getHostServices: () => getHostServices,
15582
+ getInFlightSnapshot: () => getInFlightSnapshot,
15582
15583
  getLogger: () => getLogger,
15583
15584
  getModelConfigService: () => getModelConfigService,
15584
15585
  getOrCreateChannelRegistry: () => getOrCreateChannelRegistry,
@@ -15636,6 +15637,7 @@ __export(src_exports, {
15636
15637
  saveWorkflow: () => saveWorkflow,
15637
15638
  scheduleTimeBudget: () => scheduleTimeBudget,
15638
15639
  setEngineDiscoveryRescanOptions: () => setEngineDiscoveryRescanOptions,
15640
+ setInFlightListener: () => setInFlightListener,
15639
15641
  setModelConfigService: () => setModelConfigService,
15640
15642
  setSubagentService: () => setSubagentService,
15641
15643
  sortByCodepoint: () => sortByCodepoint,
@@ -16733,16 +16735,16 @@ var SpawnedChildrenMirror = class {
16733
16735
  return this.entries.size;
16734
16736
  }
16735
16737
  /** 订阅状态广播(W6 notify/谓词接线点)。返回退订函数。 */
16736
- onChange(listener) {
16737
- this.listeners.add(listener);
16738
+ onChange(listener2) {
16739
+ this.listeners.add(listener2);
16738
16740
  return () => {
16739
- this.listeners.delete(listener);
16741
+ this.listeners.delete(listener2);
16740
16742
  };
16741
16743
  }
16742
16744
  emit(event) {
16743
- for (const listener of this.listeners) {
16745
+ for (const listener2 of this.listeners) {
16744
16746
  try {
16745
- listener(event);
16747
+ listener2(event);
16746
16748
  } catch (err) {
16747
16749
  logger9.debug(
16748
16750
  `[spawned-children-mirror] listener threw for ${event.reason}: ${err instanceof Error ? err.message : String(err)}`
@@ -17187,8 +17189,189 @@ function waitForChildExit(child, timeoutMs) {
17187
17189
  });
17188
17190
  }
17189
17191
 
17192
+ // src/execution/engine/host/spawned-children.ts
17193
+ var CoreSpawnedChildrenMirror = class {
17194
+ entries = /* @__PURE__ */ new Map();
17195
+ /** 落项 / 覆盖(同 recordId 重 spawn = 覆盖旧句柄,与引擎侧 Map 同语义)。 */
17196
+ register(recordId, child) {
17197
+ this.entries.set(recordId, {
17198
+ pid: child.pid,
17199
+ killed: child.killed,
17200
+ updatedAt: Date.now()
17201
+ });
17202
+ }
17203
+ /** 单项置死(杀链入口:killRecordChildWithEscalation 途经)。 */
17204
+ markKilled(recordId) {
17205
+ const entry = this.entries.get(recordId);
17206
+ if (entry !== void 0) {
17207
+ entry.killed = true;
17208
+ entry.updatedAt = Date.now();
17209
+ }
17210
+ }
17211
+ /** 整体置死 + 清空(失效语义 2+3:引擎 exit / 重建 / dispose / killAll)。 */
17212
+ killAll() {
17213
+ this.entries.clear();
17214
+ }
17215
+ getChildByRecord(recordId) {
17216
+ return this.entries.get(recordId);
17217
+ }
17218
+ /** 句柄存活谓词(镜像面;判据与引擎侧 `!child.killed` 同构)。 */
17219
+ hasLiveProcessHandle(recordId) {
17220
+ const entry = this.entries.get(recordId);
17221
+ return entry !== void 0 && !entry.killed;
17222
+ }
17223
+ /** 快照(诊断/测试)。 */
17224
+ snapshot() {
17225
+ return [...this.entries.entries()].map(([recordId, entry]) => ({ recordId, ...entry }));
17226
+ }
17227
+ /** 测试隔离专用(生产禁用——进程级全局状态)。 */
17228
+ clear() {
17229
+ this.entries.clear();
17230
+ }
17231
+ };
17232
+ var MIRROR_SLOT_KEY = /* @__PURE__ */ Symbol.for(
17233
+ "@zhushanwen/pi-subagent-workflow.coreSpawnedChildrenMirror"
17234
+ );
17235
+ function coreMirrorSlot() {
17236
+ let slot = Reflect.get(globalThis, MIRROR_SLOT_KEY);
17237
+ if (!slot) {
17238
+ slot = new CoreSpawnedChildrenMirror();
17239
+ Reflect.set(globalThis, MIRROR_SLOT_KEY, slot);
17240
+ }
17241
+ return slot;
17242
+ }
17243
+ function coreSpawnedChildrenMirror() {
17244
+ return coreMirrorSlot();
17245
+ }
17246
+ function registerSpawnedChildForRecord(recordId, child) {
17247
+ coreMirrorSlot().register(recordId, { pid: child.pid, killed: child.killed });
17248
+ }
17249
+ function killRecordChildWithEscalation(recordId, _source) {
17250
+ coreMirrorSlot().markKilled(recordId);
17251
+ }
17252
+ function killAllSpawnedChildren(_signal = "SIGTERM") {
17253
+ const before = coreMirrorSlot().snapshot().length;
17254
+ coreMirrorSlot().killAll();
17255
+ return before;
17256
+ }
17257
+ function hasLiveProcessHandleCore(recordId) {
17258
+ return coreMirrorSlot().hasLiveProcessHandle(recordId);
17259
+ }
17260
+
17261
+ // src/shared/timer-delay.ts
17262
+ var MAX_TIMER_DELAY_MS = 2147483647;
17263
+ function assertSafeTimerDelay(ms, source) {
17264
+ if (!Number.isFinite(ms)) {
17265
+ throw new Error(
17266
+ `[subagent-workflow] ${source} = ${ms} is not a finite number (NaN/\xB1Infinity). Non-finite delays collapse to 1ms in Node setTimeout and fire immediately. Recovery: fix the upstream computation that produced this value (e.g. guard division/parse results before passing them in) and retry.`
17267
+ );
17268
+ }
17269
+ if (ms > MAX_TIMER_DELAY_MS) {
17270
+ throw new Error(
17271
+ `[subagent-workflow] ${source} = ${ms} exceeds the Node setTimeout limit (${MAX_TIMER_DELAY_MS} ms = 2^31-1); larger delays silently collapse to 1ms and fire immediately. Recovery: clamp the value to <= ${MAX_TIMER_DELAY_MS} (e.g. omit the option for "unlimited" semantics, or clamp explicitly) and retry.`
17272
+ );
17273
+ }
17274
+ }
17275
+
17276
+ // src/execution/lifecycle-manager.ts
17277
+ var logger14 = getLogger("subagents");
17278
+ var MS_PER_SECOND2 = 1e3;
17279
+ var SECONDS_PER_MINUTE2 = 60;
17280
+ var IDLE_TIMEOUT_MINUTES = 5;
17281
+ var DEFAULT_IDLE_TIMEOUT_MS = IDLE_TIMEOUT_MINUTES * SECONDS_PER_MINUTE2 * MS_PER_SECOND2;
17282
+ function getEnvIdleTimeoutMs() {
17283
+ const raw = process.env.XYZ_SUBAGENT_IDLE_TIMEOUT_MS;
17284
+ if (!raw) return void 0;
17285
+ const parsed = Number(raw);
17286
+ if (!Number.isFinite(parsed) || parsed <= 0) {
17287
+ logger14.warn(
17288
+ `[lifecycle-manager] XYZ_SUBAGENT_IDLE_TIMEOUT_MS="${raw}" is invalid (expected a positive millisecond number) \u2014 falling back to DEFAULT_IDLE_TIMEOUT_MS (${DEFAULT_IDLE_TIMEOUT_MS}ms); set a plain ms value (e.g. 1800000) to override`
17289
+ );
17290
+ return void 0;
17291
+ }
17292
+ return parsed;
17293
+ }
17294
+ var idleTimers = /* @__PURE__ */ new Map();
17295
+ function armIdleTimer(recordId, onTimeout, timeoutMs) {
17296
+ if (timeoutMs !== void 0 && timeoutMs <= 0) {
17297
+ disarmIdleTimer(recordId);
17298
+ return;
17299
+ }
17300
+ const resolved = timeoutMs ?? getEnvIdleTimeoutMs() ?? DEFAULT_IDLE_TIMEOUT_MS;
17301
+ assertSafeTimerDelay(resolved, "idleTimeoutMs");
17302
+ disarmIdleTimer(recordId);
17303
+ const timer = setTimeout(() => {
17304
+ if (idleTimers.get(recordId)?.timer === timer) {
17305
+ idleTimers.delete(recordId);
17306
+ }
17307
+ onTimeout();
17308
+ }, resolved);
17309
+ timer.unref?.();
17310
+ idleTimers.set(recordId, { timer, timeoutMs: resolved });
17311
+ }
17312
+ function disarmIdleTimer(recordId) {
17313
+ const entry = idleTimers.get(recordId);
17314
+ if (!entry) {
17315
+ return;
17316
+ }
17317
+ clearTimeout(entry.timer);
17318
+ idleTimers.delete(recordId);
17319
+ }
17320
+ function hasIdleTimer(recordId) {
17321
+ return idleTimers.has(recordId);
17322
+ }
17323
+ var ACTIVATE_LOCK_TIMEOUT_SECONDS = 30;
17324
+ var ACTIVATE_LOCK_TIMEOUT_MS = ACTIVATE_LOCK_TIMEOUT_SECONDS * MS_PER_SECOND2;
17325
+
17326
+ // src/execution/lifecycle-predicates.ts
17327
+ function hasLiveProcessHandle(recordId) {
17328
+ return hasLiveProcessHandleCore(recordId);
17329
+ }
17330
+ function isIdle(record) {
17331
+ return hasIdleTimer(record.id);
17332
+ }
17333
+ function isResumable(record) {
17334
+ return record.status === "running" && !hasLiveProcessHandle(record.id);
17335
+ }
17336
+
17337
+ // src/execution/engine/inflight-snapshot.ts
17338
+ var listener = null;
17339
+ function setInFlightListener(next) {
17340
+ listener = next;
17341
+ }
17342
+ function getInFlightSnapshot() {
17343
+ return { inFlight: countInFlight() };
17344
+ }
17345
+ function notifyInFlightChanged() {
17346
+ if (listener === null) return;
17347
+ try {
17348
+ listener(getInFlightSnapshot());
17349
+ } catch {
17350
+ }
17351
+ }
17352
+ function countInFlight() {
17353
+ let count = 0;
17354
+ for (const entry of coreSpawnedChildrenMirror().snapshot()) {
17355
+ const recordId = entry.recordId;
17356
+ if (hasLiveProcessHandle(recordId) && !hasIdleTimer(recordId)) count++;
17357
+ }
17358
+ return count;
17359
+ }
17360
+
17190
17361
  // src/execution/engine/client/engine-client.ts
17191
- var logger14 = getLogger2("subagents");
17362
+ var logger15 = getLogger2("subagents");
17363
+ function bridgeMirrorEventToCoreMirror(event, mirror) {
17364
+ if (event.recordId === void 0 || event.pid === void 0) return;
17365
+ const entry = mirror.getEntry(event.pid);
17366
+ if (entry === void 0) return;
17367
+ const core = coreSpawnedChildrenMirror();
17368
+ if (entry.killed) {
17369
+ core.markKilled(event.recordId);
17370
+ } else {
17371
+ core.register(event.recordId, { pid: entry.pid, killed: false });
17372
+ }
17373
+ notifyInFlightChanged();
17374
+ }
17192
17375
  var DISPOSE_GRACE_MS = 3e3;
17193
17376
  var SIGKILL_REAP_TIMEOUT_MS = 1e4;
17194
17377
  var FRAME_ECHO_MAX_CHARS = 200;
@@ -17251,6 +17434,7 @@ var EngineClient = class {
17251
17434
  };
17252
17435
  this.mirror.onChange((event) => {
17253
17436
  opts.onMirrorChanged?.(event);
17437
+ bridgeMirrorEventToCoreMirror(event, this.mirror);
17254
17438
  });
17255
17439
  }
17256
17440
  get currentState() {
@@ -17313,19 +17497,19 @@ var EngineClient = class {
17313
17497
  matchesEngineCmdline: this.opts.engineCmdlineMatcher ?? defaultEngineCmdlineMatcher(this.opts.command)
17314
17498
  });
17315
17499
  if (sweep.killed.length > 0) {
17316
- logger14.warn(
17500
+ logger15.warn(
17317
17501
  `[engine-client:${this.engineId}] startup sweep killed stale orphan engine pids: ${sweep.killed.join(",")}`
17318
17502
  );
17319
17503
  }
17320
17504
  for (const removed of sweep.removed) {
17321
- logger14.debug(`[engine-client:${this.engineId}] swept stale pidfile ${removed.file}: ${removed.reason}`);
17505
+ logger15.debug(`[engine-client:${this.engineId}] swept stale pidfile ${removed.file}: ${removed.reason}`);
17322
17506
  }
17323
17507
  }
17324
17508
  let attempt = 0;
17325
17509
  while (attempt <= CRASH_REBUILD_MAX_ATTEMPTS) {
17326
17510
  if (attempt > 0) {
17327
17511
  const backoff = CRASH_REBUILD_BACKOFF_MS[attempt - 1];
17328
- logger14.warn(
17512
+ logger15.warn(
17329
17513
  `[engine-client:${this.engineId}] rebuild attempt ${attempt}/${CRASH_REBUILD_MAX_ATTEMPTS} after ${backoff}ms backoff`
17330
17514
  );
17331
17515
  await delay(backoff);
@@ -17358,7 +17542,7 @@ var EngineClient = class {
17358
17542
  markUnavailable(reason) {
17359
17543
  this.state = "unavailable";
17360
17544
  this.unavailableReason = reason;
17361
- logger14.error(`[engine-client:${this.engineId}] marked unavailable: ${reason.message}`);
17545
+ logger15.error(`[engine-client:${this.engineId}] marked unavailable: ${reason.message}`);
17362
17546
  }
17363
17547
  /** spawn 引擎 CLI + initialize 握手 + 版本协商 + 诊断留痕。 */
17364
17548
  async spawnAndInitialize() {
@@ -17388,13 +17572,13 @@ var EngineClient = class {
17388
17572
  });
17389
17573
  const child = this.child;
17390
17574
  child.on("error", (err) => {
17391
- logger14.warn(`[engine-client:${this.engineId}] spawn error: ${err.message}`);
17575
+ logger15.warn(`[engine-client:${this.engineId}] spawn error: ${err.message}`);
17392
17576
  this.appendStderrTail(`spawn error: ${err.message}
17393
17577
  `);
17394
17578
  });
17395
17579
  for (const pipe of ["stdin", "stdout", "stderr"]) {
17396
17580
  child[pipe]?.on("error", (err) => {
17397
- logger14.debug(`[engine-client:${this.engineId}] ${pipe} pipe error (engine died concurrently = expected): ${err.message}`);
17581
+ logger15.debug(`[engine-client:${this.engineId}] ${pipe} pipe error (engine died concurrently = expected): ${err.message}`);
17398
17582
  });
17399
17583
  }
17400
17584
  child.stdout?.setEncoding("utf-8");
@@ -17463,7 +17647,7 @@ var EngineClient = class {
17463
17647
  try {
17464
17648
  frame = JSON.parse(line);
17465
17649
  } catch {
17466
- logger14.warn(
17650
+ logger15.warn(
17467
17651
  `[engine-client:${this.engineId}] dropped non-NDJSON stdout line (protocol contract: stdout is NDJSON-only): ${truncate(line, FRAME_ECHO_MAX_CHARS)}`
17468
17652
  );
17469
17653
  return;
@@ -17480,7 +17664,7 @@ var EngineClient = class {
17480
17664
  this.onReverseRequest(frame);
17481
17665
  return;
17482
17666
  }
17483
- logger14.warn(
17667
+ logger15.warn(
17484
17668
  `[engine-client:${this.engineId}] dropped unrecognized frame: ${truncate(line, FRAME_ECHO_MAX_CHARS)}`
17485
17669
  );
17486
17670
  }
@@ -17604,7 +17788,7 @@ var EngineClient = class {
17604
17788
  try {
17605
17789
  await this.request("dispose", {}, { timeoutMs: DISPOSE_GRACE_MS });
17606
17790
  } catch (err) {
17607
- logger14.debug(
17791
+ logger15.debug(
17608
17792
  `[engine-client:${this.engineId}] dispose frame failed, falling back to kill chain: ${err instanceof Error ? err.message : String(err)}`
17609
17793
  );
17610
17794
  }
@@ -17616,9 +17800,9 @@ var EngineClient = class {
17616
17800
  onEngineExit(code, signal) {
17617
17801
  const detail = signal !== null ? `signal ${signal}` : `exit code ${code}`;
17618
17802
  if (this.intentionalKill) {
17619
- logger14.debug(`[engine-client:${this.engineId}] engine exited after intentional kill (${detail}) \u2014 expected`);
17803
+ logger15.debug(`[engine-client:${this.engineId}] engine exited after intentional kill (${detail}) \u2014 expected`);
17620
17804
  } else {
17621
- logger14.warn(`[engine-client:${this.engineId}] engine process exited unexpectedly (${detail})`);
17805
+ logger15.warn(`[engine-client:${this.engineId}] engine process exited unexpectedly (${detail})`);
17622
17806
  }
17623
17807
  this.teardownProcess(detail);
17624
17808
  this.state = "exited";
@@ -17667,7 +17851,7 @@ var EngineClient = class {
17667
17851
  `);
17668
17852
  return true;
17669
17853
  } catch (err) {
17670
- logger14.debug(
17854
+ logger15.debug(
17671
17855
  `[engine-client:${this.engineId}] stdin write failed: ${err instanceof Error ? err.message : String(err)}`
17672
17856
  );
17673
17857
  return false;
@@ -18025,7 +18209,7 @@ function getHostUiRequestEndpoint() {
18025
18209
  }
18026
18210
 
18027
18211
  // src/execution/engine/engine-inspect-package.ts
18028
- var logger15 = getLogger("subagents");
18212
+ var logger16 = getLogger("subagents");
18029
18213
  function readEnginePackageJson(pkgDir) {
18030
18214
  let raw;
18031
18215
  try {
@@ -18104,12 +18288,12 @@ function parseOptionalDisplayFields(m, id) {
18104
18288
  if (typeof rawDisplayName === "string" && rawDisplayName.trim() !== "") {
18105
18289
  displayName = rawDisplayName;
18106
18290
  } else {
18107
- logger15.warn(`[engine-discovery] engine '${id}': displayName must be a non-empty string \u2014 ignoring`);
18291
+ logger16.warn(`[engine-discovery] engine '${id}': displayName must be a non-empty string \u2014 ignoring`);
18108
18292
  }
18109
18293
  }
18110
18294
  const rawDescription = m["description"];
18111
18295
  if (rawDescription !== void 0 && typeof rawDescription !== "string") {
18112
- logger15.warn(`[engine-discovery] engine '${id}': description must be a string \u2014 ignoring`);
18296
+ logger16.warn(`[engine-discovery] engine '${id}': description must be a string \u2014 ignoring`);
18113
18297
  }
18114
18298
  return displayName;
18115
18299
  }
@@ -18198,7 +18382,7 @@ function errorMessage(err) {
18198
18382
  // src/execution/engine/engine-discovery-roots.ts
18199
18383
  var fs3 = __toESM(require("fs"), 1);
18200
18384
  var path3 = __toESM(require("path"), 1);
18201
- var logger16 = getLogger("subagents");
18385
+ var logger17 = getLogger("subagents");
18202
18386
  var ENGINE_ROOTS_ENV = "XYZ_AGENT_ENGINE_ROOTS";
18203
18387
  function parseEngineRootsEnv(env) {
18204
18388
  const raw = env[ENGINE_ROOTS_ENV];
@@ -18209,7 +18393,7 @@ function parseEngineRootsEnv(env) {
18209
18393
  const dir = part.trim();
18210
18394
  if (dir === "") continue;
18211
18395
  if (!path3.isAbsolute(dir)) {
18212
- logger16.warn(
18396
+ logger17.warn(
18213
18397
  `[engine-discovery] ${ENGINE_ROOTS_ENV} entry '${dir}' is not an absolute path \u2014 dropping entry`
18214
18398
  );
18215
18399
  continue;
@@ -18278,7 +18462,7 @@ function isFile(p) {
18278
18462
  // src/execution/engine/config.ts
18279
18463
  var fs4 = __toESM(require("fs"), 1);
18280
18464
  var path4 = __toESM(require("path"), 1);
18281
- var logger17 = getLogger("subagents");
18465
+ var logger18 = getLogger("subagents");
18282
18466
  function readExplicitEngines(agentDir) {
18283
18467
  const configPath = path4.join(agentDir, "subagents", "config.json");
18284
18468
  let parsed;
@@ -18291,7 +18475,7 @@ function readExplicitEngines(agentDir) {
18291
18475
  const engines = parsed["engines"];
18292
18476
  if (typeof engines !== "object" || engines === null || Array.isArray(engines)) {
18293
18477
  if (engines !== void 0) {
18294
- logger17.warn(
18478
+ logger18.warn(
18295
18479
  `[engine-discovery] config.json engines section must be an object keyed by engine id, got ${jsonKindOf(engines)} \u2014 ignoring L3 explicit engines`
18296
18480
  );
18297
18481
  }
@@ -18319,7 +18503,7 @@ function sanitizeExplicitEntry(id, raw) {
18319
18503
  }
18320
18504
  function requireEntryObject(id, raw) {
18321
18505
  if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
18322
- logger17.warn(
18506
+ logger18.warn(
18323
18507
  `[engine-discovery] config.json engines.${id} must be an object, got ${jsonKindOf(raw)} \u2014 skipping entry`
18324
18508
  );
18325
18509
  return void 0;
@@ -18328,7 +18512,7 @@ function requireEntryObject(id, raw) {
18328
18512
  }
18329
18513
  function requireCommand(id, command) {
18330
18514
  if (typeof command !== "string" || command.trim() === "") {
18331
- logger17.warn(
18515
+ logger18.warn(
18332
18516
  `[engine-discovery] config.json engines.${id}.command is required (non-empty string) \u2014 skipping entry`
18333
18517
  );
18334
18518
  return void 0;
@@ -18337,7 +18521,7 @@ function requireCommand(id, command) {
18337
18521
  }
18338
18522
  function normalizeArgs(id, args) {
18339
18523
  if (args !== void 0 && !isStringArray(args)) {
18340
- logger17.warn(
18524
+ logger18.warn(
18341
18525
  `[engine-discovery] config.json engines.${id}.args must be a string array \u2014 ignoring args`
18342
18526
  );
18343
18527
  }
@@ -18345,7 +18529,7 @@ function normalizeArgs(id, args) {
18345
18529
  }
18346
18530
  function normalizeConfig(id, config) {
18347
18531
  if (config !== void 0 && !isStringRecord(config)) {
18348
- logger17.warn(
18532
+ logger18.warn(
18349
18533
  `[engine-discovery] config.json engines.${id}.config must be a Record<string, string> \u2014 ignoring config`
18350
18534
  );
18351
18535
  }
@@ -18353,7 +18537,7 @@ function normalizeConfig(id, config) {
18353
18537
  }
18354
18538
  function normalizeCwd(id, cwd) {
18355
18539
  if (cwd !== void 0 && typeof cwd !== "string") {
18356
- logger17.warn(
18540
+ logger18.warn(
18357
18541
  `[engine-discovery] config.json engines.${id}.cwd must be a string \u2014 ignoring cwd`
18358
18542
  );
18359
18543
  }
@@ -18373,14 +18557,14 @@ function jsonKindOf(value) {
18373
18557
  }
18374
18558
 
18375
18559
  // src/execution/engine/engine-discovery-scan.ts
18376
- var logger18 = getLogger("subagents");
18560
+ var logger19 = getLogger("subagents");
18377
18561
  function scanEngines(opts) {
18378
18562
  const env = opts.env ?? process.env;
18379
18563
  const result = { discovered: [], skipped: [], unusable: [] };
18380
18564
  const present = (entry) => {
18381
18565
  const idx = result.discovered.findIndex((d) => d.id === entry.id);
18382
18566
  if (idx >= 0) {
18383
- logger18.debug(
18567
+ logger19.debug(
18384
18568
  `[engine-discovery] engine id '${entry.id}' from '${entry.source}' overrides earlier discovery from '${result.discovered[idx].source}' \u2014 same-id override`
18385
18569
  );
18386
18570
  result.discovered[idx] = entry;
@@ -18392,13 +18576,13 @@ function scanEngines(opts) {
18392
18576
  const inspection = inspectEnginePackage(pkgDir, source, opts.hostKind, env);
18393
18577
  if (inspection.status === "skip") {
18394
18578
  if (!inspection.reason.includes("not an engine package")) {
18395
- logger18.warn(`[engine-discovery] skipping ${pkgDir} (${source}): ${inspection.reason}`);
18579
+ logger19.warn(`[engine-discovery] skipping ${pkgDir} (${source}): ${inspection.reason}`);
18396
18580
  result.skipped.push({ pkgDir, reason: inspection.reason });
18397
18581
  }
18398
18582
  return;
18399
18583
  }
18400
18584
  if (inspection.status === "unusable") {
18401
- logger18.warn(`[engine-discovery] engine unavailable: ${inspection.reason}`);
18585
+ logger19.warn(`[engine-discovery] engine unavailable: ${inspection.reason}`);
18402
18586
  result.unusable.push({ pkgDir, id: inspection.id, reason: inspection.reason });
18403
18587
  return;
18404
18588
  }
@@ -18423,7 +18607,7 @@ function scanEngines(opts) {
18423
18607
  const command = resolveExplicitCommand(entry.command, env);
18424
18608
  if (command === void 0) {
18425
18609
  const reason = `engine '${id}' (config.json engines.${id}): command '${entry.command}' not found or not executable`;
18426
- logger18.warn(`[engine-discovery] ${reason} \u2014 not registering`);
18610
+ logger19.warn(`[engine-discovery] ${reason} \u2014 not registering`);
18427
18611
  result.unusable.push({ pkgDir: `config.json engines.${id}`, id, reason });
18428
18612
  continue;
18429
18613
  }
@@ -18440,7 +18624,7 @@ function discoverAndRegisterEngines(opts) {
18440
18624
  registerEngineDescriptor(entry.id, entry.descriptor);
18441
18625
  loadedDiscoveryIdSet.add(entry.id);
18442
18626
  if (existed) {
18443
- logger18.debug(
18627
+ logger19.debug(
18444
18628
  `[engine-discovery] engine '${entry.id}' descriptor overwritten in registry (source '${entry.source}') \u2014 same-id override`
18445
18629
  );
18446
18630
  }
@@ -18470,7 +18654,7 @@ function resolveExplicitCommand(command, env) {
18470
18654
  }
18471
18655
  function buildExplicitDescriptor(id, command, entry, opts) {
18472
18656
  const env = opts.env ?? process.env;
18473
- logger18.debug(
18657
+ logger19.debug(
18474
18658
  `[engine-discovery] engine '${id}' registered from config.json engines section with conservative capabilities (no manifest)`
18475
18659
  );
18476
18660
  const caps = { ...CONSERVATIVE_CAPABILITIES };
@@ -18513,7 +18697,7 @@ function buildExplicitDescriptor(id, command, entry, opts) {
18513
18697
  }
18514
18698
 
18515
18699
  // src/execution/engine/routing.ts
18516
- var logger19 = getLogger("subagents");
18700
+ var logger20 = getLogger("subagents");
18517
18701
  var RESCAN_OPTS_SLOT_KEY = /* @__PURE__ */ Symbol.for("@zhushanwen/pi-subagent-workflow.engineDiscoveryRescanOpts");
18518
18702
  function setEngineDiscoveryRescanOptions(opts) {
18519
18703
  Reflect.set(globalThis, RESCAN_OPTS_SLOT_KEY, { current: opts });
@@ -18529,7 +18713,7 @@ function hasEngineWithRescan(id) {
18529
18713
  try {
18530
18714
  return ensureEngineDiscovered(id, opts);
18531
18715
  } catch (err) {
18532
- logger19.debug(
18716
+ logger20.debug(
18533
18717
  `[engine-routing] rescan for engine '${id}' failed (treated as not discovered): ${err instanceof Error ? err.message : String(err)}`
18534
18718
  );
18535
18719
  return false;
@@ -18553,7 +18737,7 @@ function resolveEngineRouting(input) {
18553
18737
  function resolveDefaultEngineFallback(requestedId, available) {
18554
18738
  if (available.length === 0) return void 0;
18555
18739
  const target = available[0];
18556
- logger19.warn(
18740
+ logger20.warn(
18557
18741
  `[engine-routing] default engine '${requestedId}' is not in the discovered engines [${available.join(", ")}]; falling back to first available engine '${target}' (D4, recorded via engineFallback)`
18558
18742
  );
18559
18743
  return { engineId: target, fallback: { from: requestedId, reason: "engine_not_found" } };
@@ -18700,7 +18884,7 @@ var SUBAGENTS_ENGINES_FILENAME = "engines.json";
18700
18884
  var import_node_fs3 = require("fs");
18701
18885
  var fsPromises = __toESM(require("fs/promises"), 1);
18702
18886
  var path6 = __toESM(require("path"), 1);
18703
- var logger20 = getLogger("subagents");
18887
+ var logger21 = getLogger("subagents");
18704
18888
  var TMP_MARKER = ".tmp.";
18705
18889
  var TMP_NAME_PATTERN = /^(.+)\.tmp\.(\d+)\.[0-9A-Za-z-]+$/;
18706
18890
  var tmpSeq = 0;
@@ -18722,7 +18906,7 @@ function removeTmpBestEffortSync(tmpPath) {
18722
18906
  try {
18723
18907
  (0, import_node_fs3.unlinkSync)(tmpPath);
18724
18908
  } catch (cleanupErr) {
18725
- logger20.debug("[subagent-core] atomic-write cleanup tmp failed", {
18909
+ logger21.debug("[subagent-core] atomic-write cleanup tmp failed", {
18726
18910
  detail: toErrorMessage(cleanupErr),
18727
18911
  tmpPath
18728
18912
  });
@@ -18770,7 +18954,7 @@ async function writeAtomicFile(filePath, content, options = {}) {
18770
18954
  await dirFh.close();
18771
18955
  }
18772
18956
  } catch (dirSyncErr) {
18773
- logger20.debug("[subagent-core] atomic-write fsync dir failed", {
18957
+ logger21.debug("[subagent-core] atomic-write fsync dir failed", {
18774
18958
  detail: toErrorMessage(dirSyncErr),
18775
18959
  dirPath
18776
18960
  });
@@ -18781,7 +18965,7 @@ async function writeAtomicFile(filePath, content, options = {}) {
18781
18965
  try {
18782
18966
  await fsPromises.unlink(tmpPath);
18783
18967
  } catch (cleanupErr) {
18784
- logger20.debug("[subagent-core] atomic-write cleanup tmp failed", {
18968
+ logger21.debug("[subagent-core] atomic-write cleanup tmp failed", {
18785
18969
  detail: toErrorMessage(cleanupErr),
18786
18970
  tmpPath
18787
18971
  });
@@ -18825,7 +19009,7 @@ function cleanupStaleTmpFiles(dir, options = {}) {
18825
19009
  result.removed.push(ref.tmpPath);
18826
19010
  continue;
18827
19011
  }
18828
- logger20.debug("[subagent-core] cleanupStaleTmpFiles stat failed", {
19012
+ logger21.debug("[subagent-core] cleanupStaleTmpFiles stat failed", {
18829
19013
  detail: toErrorMessage(statErr),
18830
19014
  tmpPath: ref.tmpPath
18831
19015
  });
@@ -18840,7 +19024,7 @@ function cleanupStaleTmpFiles(dir, options = {}) {
18840
19024
  if (typeof unlinkErr.code === "string" && unlinkErr.code === "ENOENT") {
18841
19025
  result.removed.push(ref.tmpPath);
18842
19026
  } else {
18843
- logger20.debug("[subagent-core] cleanupStaleTmpFiles unlink failed", {
19027
+ logger21.debug("[subagent-core] cleanupStaleTmpFiles unlink failed", {
18844
19028
  detail: toErrorMessage(unlinkErr),
18845
19029
  tmpPath: ref.tmpPath
18846
19030
  });
@@ -18880,78 +19064,12 @@ function syncEnginesFile(agentDir) {
18880
19064
  }
18881
19065
  }
18882
19066
 
18883
- // src/execution/engine/host/spawned-children.ts
18884
- var CoreSpawnedChildrenMirror = class {
18885
- entries = /* @__PURE__ */ new Map();
18886
- /** 落项 / 覆盖(同 recordId 重 spawn = 覆盖旧句柄,与引擎侧 Map 同语义)。 */
18887
- register(recordId, child) {
18888
- this.entries.set(recordId, {
18889
- pid: child.pid,
18890
- killed: child.killed,
18891
- updatedAt: Date.now()
18892
- });
18893
- }
18894
- /** 单项置死(杀链入口:killRecordChildWithEscalation 途经)。 */
18895
- markKilled(recordId) {
18896
- const entry = this.entries.get(recordId);
18897
- if (entry !== void 0) {
18898
- entry.killed = true;
18899
- entry.updatedAt = Date.now();
18900
- }
18901
- }
18902
- /** 整体置死 + 清空(失效语义 2+3:引擎 exit / 重建 / dispose / killAll)。 */
18903
- killAll() {
18904
- this.entries.clear();
18905
- }
18906
- getChildByRecord(recordId) {
18907
- return this.entries.get(recordId);
18908
- }
18909
- /** 句柄存活谓词(镜像面;判据与引擎侧 `!child.killed` 同构)。 */
18910
- hasLiveProcessHandle(recordId) {
18911
- const entry = this.entries.get(recordId);
18912
- return entry !== void 0 && !entry.killed;
18913
- }
18914
- /** 快照(诊断/测试)。 */
18915
- snapshot() {
18916
- return [...this.entries.entries()].map(([recordId, entry]) => ({ recordId, ...entry }));
18917
- }
18918
- /** 测试隔离专用(生产禁用——进程级全局状态)。 */
18919
- clear() {
18920
- this.entries.clear();
18921
- }
18922
- };
18923
- var MIRROR_SLOT_KEY = /* @__PURE__ */ Symbol.for(
18924
- "@zhushanwen/pi-subagent-workflow.coreSpawnedChildrenMirror"
18925
- );
18926
- function coreMirrorSlot() {
18927
- let slot = Reflect.get(globalThis, MIRROR_SLOT_KEY);
18928
- if (!slot) {
18929
- slot = new CoreSpawnedChildrenMirror();
18930
- Reflect.set(globalThis, MIRROR_SLOT_KEY, slot);
18931
- }
18932
- return slot;
18933
- }
18934
- function registerSpawnedChildForRecord(recordId, child) {
18935
- coreMirrorSlot().register(recordId, { pid: child.pid, killed: child.killed });
18936
- }
18937
- function killRecordChildWithEscalation(recordId, _source) {
18938
- coreMirrorSlot().markKilled(recordId);
18939
- }
18940
- function killAllSpawnedChildren(_signal = "SIGTERM") {
18941
- const before = coreMirrorSlot().snapshot().length;
18942
- coreMirrorSlot().killAll();
18943
- return before;
18944
- }
18945
- function hasLiveProcessHandleCore(recordId) {
18946
- return coreMirrorSlot().hasLiveProcessHandle(recordId);
18947
- }
18948
-
18949
19067
  // src/execution/engine/d8-compat.ts
18950
19068
  var fs6 = __toESM(require("fs"), 1);
18951
19069
  var path8 = __toESM(require("path"), 1);
18952
19070
  var import_node_url = require("url");
18953
19071
  var import_meta = {};
18954
- var logger21 = getLogger("subagents");
19072
+ var logger22 = getLogger("subagents");
18955
19073
  var D8_ZCODE_ENGINE_ID = "zcode";
18956
19074
  var D8_ZSW_HOST_KIND = "zsw";
18957
19075
  var D8_PI_HOST_KIND = "pi";
@@ -19029,7 +19147,7 @@ function registerZcodeEngine(engineDataDir = getEngineDataDir) {
19029
19147
  if (hasEngine(id)) return;
19030
19148
  const located = locateVendoredEnginePkg(id);
19031
19149
  if (located === void 0) {
19032
- logger21.warn(
19150
+ logger22.warn(
19033
19151
  `[d8-compat] engine '${id}' vendored package not located (enginePkg ${enginePkgName(id)}); left unregistered \u2014 dispatch will fail with engine_not_found + recovery guidance`
19034
19152
  );
19035
19153
  return;
@@ -19037,7 +19155,7 @@ function registerZcodeEngine(engineDataDir = getEngineDataDir) {
19037
19155
  const syntheticEnv = { ...process.env, XYZ_AGENT_DATA_DIR: engineDataDir() };
19038
19156
  const inspection = inspectEnginePackage(located.pkgDir, "d8-compat", D8_PI_HOST_KIND, syntheticEnv);
19039
19157
  if (inspection.status !== "ok") {
19040
- logger21.warn(
19158
+ logger22.warn(
19041
19159
  `[d8-compat] engine '${id}' vendored package inspection failed (${inspection.reason}); left unregistered \u2014 dispatch will fail with engine_not_found + recovery guidance`
19042
19160
  );
19043
19161
  return;
@@ -19047,7 +19165,7 @@ function registerZcodeEngine(engineDataDir = getEngineDataDir) {
19047
19165
  function warnSourcesIgnoredOnce(deps) {
19048
19166
  if (deps.sources === void 0 || warnedSourcesIgnored) return;
19049
19167
  warnedSourcesIgnored = true;
19050
- logger21.warn(
19168
+ logger22.warn(
19051
19169
  "[d8-compat] createZcodeEngine deps.sources is ignored (model/credential sources do not cross the process boundary \u2014 the engine resolves its own credentials, protocol invariant 5)"
19052
19170
  );
19053
19171
  }
@@ -19107,12 +19225,12 @@ function createZcodeEngine(deps) {
19107
19225
  var PI_POOL_KEY = "shared";
19108
19226
  var WATCHDOG_FLOOR_MINUTES = 30;
19109
19227
  var WATCHDOG_MINUTES_PER_TURN = 5;
19110
- var MS_PER_SECOND2 = 1e3;
19111
- var SECONDS_PER_MINUTE2 = 60;
19228
+ var MS_PER_SECOND3 = 1e3;
19229
+ var SECONDS_PER_MINUTE3 = 60;
19112
19230
  function maxTurnsToWatchdogMs(maxTurns) {
19113
19231
  return Math.max(
19114
- WATCHDOG_FLOOR_MINUTES * SECONDS_PER_MINUTE2 * MS_PER_SECOND2,
19115
- maxTurns * WATCHDOG_MINUTES_PER_TURN * SECONDS_PER_MINUTE2 * MS_PER_SECOND2
19232
+ WATCHDOG_FLOOR_MINUTES * SECONDS_PER_MINUTE3 * MS_PER_SECOND3,
19233
+ maxTurns * WATCHDOG_MINUTES_PER_TURN * SECONDS_PER_MINUTE3 * MS_PER_SECOND3
19116
19234
  );
19117
19235
  }
19118
19236
  function resolveHostPiEnginePort(_getService) {
@@ -19250,7 +19368,7 @@ var import_node_path7 = require("path");
19250
19368
  var ACTIVITY_LABEL_MAX = 60;
19251
19369
  var TURN_SUMMARY_MAX = 80;
19252
19370
  var TOOL_LABEL_MAX = 100;
19253
- var MS_PER_SECOND3 = 1e3;
19371
+ var MS_PER_SECOND4 = 1e3;
19254
19372
  function extractLabelFromArgs(toolName, args) {
19255
19373
  if (typeof args !== "object" || args === null) return toolName;
19256
19374
  const a = args;
@@ -19581,7 +19699,7 @@ function projectOutcome(record) {
19581
19699
  }
19582
19700
  function computeElapsedSeconds(record) {
19583
19701
  const end = record.endedAt ?? Date.now();
19584
- return Math.floor((end - record.startedAt) / MS_PER_SECOND3);
19702
+ return Math.floor((end - record.startedAt) / MS_PER_SECOND4);
19585
19703
  }
19586
19704
  function project(record) {
19587
19705
  return {
@@ -19640,7 +19758,7 @@ function snapshot(record) {
19640
19758
  // src/execution/engine/common/event-journal.ts
19641
19759
  var import_promises = require("fs/promises");
19642
19760
  var import_node_path6 = require("path");
19643
- var logger22 = getLogger("subagents");
19761
+ var logger23 = getLogger("subagents");
19644
19762
  var FLUSH_THRESHOLD_LINES = 64;
19645
19763
  var FLUSH_THRESHOLD_BYTES = 32 * 1024;
19646
19764
  var defaultFs = {
@@ -19749,11 +19867,11 @@ var JournalWriter = class {
19749
19867
  }
19750
19868
  };
19751
19869
  function defaultWarn2(msg) {
19752
- logger22.warn(msg);
19870
+ logger23.warn(msg);
19753
19871
  }
19754
19872
 
19755
19873
  // src/execution/engine/common/session-view-service.ts
19756
- var logger23 = getLogger("subagents");
19874
+ var logger24 = getLogger("subagents");
19757
19875
  var DEFAULT_ENGINE_ID2 = "pi";
19758
19876
  var OUTCOME_PLACEHOLDER_TEXT = "(no outcome recorded)";
19759
19877
  function extractEngineId(record) {
@@ -19780,18 +19898,18 @@ function lookupNativeSessionReader(engineId) {
19780
19898
  function readJournalTier(record, handle, dataDir) {
19781
19899
  const journalPath = handle.journalPath;
19782
19900
  if (journalPath === void 0) {
19783
- logger23.debug("[session-view-service] tier2 skipped: no journalPath in handle");
19901
+ logger24.debug("[session-view-service] tier2 skipped: no journalPath in handle");
19784
19902
  return void 0;
19785
19903
  }
19786
19904
  if (!isStrictlyUnder(resolveEnginesRoot(dataDir), journalPath)) {
19787
- logger23.warn(
19905
+ logger24.warn(
19788
19906
  `[session-view-service] journalPath escapes engines root, reject tier2: ${journalPath}`
19789
19907
  );
19790
19908
  return void 0;
19791
19909
  }
19792
19910
  const messages = replayEventsToHistory(replayJournal(journalPath), record);
19793
19911
  if (messages === void 0) {
19794
- logger23.debug(
19912
+ logger24.debug(
19795
19913
  `[session-view-service] tier2 journal replay produced no content, degrade to outcome-only (path=${journalPath})`
19796
19914
  );
19797
19915
  }
@@ -19961,7 +20079,7 @@ async function readSubagentHistoryMessages(record, dataDir) {
19961
20079
  if (engineId === DEFAULT_ENGINE_ID2) return [];
19962
20080
  const handle = parseEngineHandle(record.engineHandle);
19963
20081
  if (handle === void 0) {
19964
- logger23.debug(
20082
+ logger24.debug(
19965
20083
  `[session-view-service] engine '${engineId}' record has no engineHandle, degrade to outcome-only (subagentId=${record.subagentId})`
19966
20084
  );
19967
20085
  return outcomeOnlyMessages(record);
@@ -19971,12 +20089,12 @@ async function readSubagentHistoryMessages(record, dataDir) {
19971
20089
  const native = await reader(handle, dataDir);
19972
20090
  if (native !== void 0) {
19973
20091
  if (native.turns.some(hasTurnContent)) return sessionViewToMessages(native, record);
19974
- logger23.debug(
20092
+ logger24.debug(
19975
20093
  `[session-view-service] tier1 native view has no substantive content (turns=${native.turns.length}), degrade to journal tier (subagentId=${record.subagentId})`
19976
20094
  );
19977
20095
  }
19978
20096
  } else {
19979
- logger23.debug(
20097
+ logger24.debug(
19980
20098
  `[session-view-service] engine '${engineId}' has no native reader tier, fall through to journal tier (subagentId=${record.subagentId})`
19981
20099
  );
19982
20100
  }
@@ -20021,21 +20139,6 @@ var DirtyWorktreeError = class extends Error {
20021
20139
  // src/execution/subagent-service.ts
20022
20140
  var import_node_async_hooks2 = require("async_hooks");
20023
20141
 
20024
- // src/shared/timer-delay.ts
20025
- var MAX_TIMER_DELAY_MS = 2147483647;
20026
- function assertSafeTimerDelay(ms, source) {
20027
- if (!Number.isFinite(ms)) {
20028
- throw new Error(
20029
- `[subagent-workflow] ${source} = ${ms} is not a finite number (NaN/\xB1Infinity). Non-finite delays collapse to 1ms in Node setTimeout and fire immediately. Recovery: fix the upstream computation that produced this value (e.g. guard division/parse results before passing them in) and retry.`
20030
- );
20031
- }
20032
- if (ms > MAX_TIMER_DELAY_MS) {
20033
- throw new Error(
20034
- `[subagent-workflow] ${source} = ${ms} exceeds the Node setTimeout limit (${MAX_TIMER_DELAY_MS} ms = 2^31-1); larger delays silently collapse to 1ms and fire immediately. Recovery: clamp the value to <= ${MAX_TIMER_DELAY_MS} (e.g. omit the option for "unlimited" semantics, or clamp explicitly) and retry.`
20035
- );
20036
- }
20037
- }
20038
-
20039
20142
  // src/execution/agent-result-mapper.ts
20040
20143
  var TOOL_ARGS_JSON_MAX_CHARS = 500;
20041
20144
  function mapToWorkflowAgentResult(r) {
@@ -20133,14 +20236,14 @@ function isErrnoException(err) {
20133
20236
  }
20134
20237
 
20135
20238
  // src/execution/best-effort.ts
20136
- var logger24 = getLogger("subagents");
20239
+ var logger25 = getLogger("subagents");
20137
20240
  function bestEffort(err, context, level = "debug") {
20138
20241
  const detail = err instanceof Error ? err.message : err;
20139
20242
  const msg = `[subagents] best-effort ${context} failed`;
20140
20243
  if (level === "error") {
20141
- logger24.error(msg, { detail });
20244
+ logger25.error(msg, { detail });
20142
20245
  } else {
20143
- logger24.debug(msg, { detail });
20246
+ logger25.debug(msg, { detail });
20144
20247
  }
20145
20248
  }
20146
20249
 
@@ -20231,7 +20334,7 @@ var CollectCoordinator = class {
20231
20334
  // src/execution/config.ts
20232
20335
  var fs8 = __toESM(require("fs"), 1);
20233
20336
  var path9 = __toESM(require("path"), 1);
20234
- var logger25 = getLogger("subagents");
20337
+ var logger26 = getLogger("subagents");
20235
20338
  var DEFAULT_CONFIG = {
20236
20339
  version: 1,
20237
20340
  maxConcurrent: 6
@@ -20274,7 +20377,7 @@ function readGlobalConfig(agentDir) {
20274
20377
  }
20275
20378
  function readFailure(configPath, err) {
20276
20379
  const reason = err instanceof Error ? err.message : String(err);
20277
- logger25.warn(`[subagents] global config read failed (read-failure) at ${configPath}: ${reason}`);
20380
+ logger26.warn(`[subagents] global config read failed (read-failure) at ${configPath}: ${reason}`);
20278
20381
  return { status: "failed", reason };
20279
20382
  }
20280
20383
  function errnoCodeOf(err) {
@@ -20313,7 +20416,7 @@ function sanitizeCollectSync(value) {
20313
20416
  bad.push(`totalChars=${fmt(v.totalChars)}`);
20314
20417
  }
20315
20418
  if (bad.length > 0) {
20316
- logger25.warn(
20419
+ logger26.warn(
20317
20420
  `[subagents] config collectSync invalid field(s) reverted to defaults: ${bad.join(", ")}`
20318
20421
  );
20319
20422
  }
@@ -20338,56 +20441,6 @@ function sanitizeEngineRouting(value) {
20338
20441
  return typeof strict === "boolean" ? { strict } : void 0;
20339
20442
  }
20340
20443
 
20341
- // src/execution/lifecycle-manager.ts
20342
- var logger26 = getLogger("subagents");
20343
- var MS_PER_SECOND4 = 1e3;
20344
- var SECONDS_PER_MINUTE3 = 60;
20345
- var IDLE_TIMEOUT_MINUTES = 5;
20346
- var DEFAULT_IDLE_TIMEOUT_MS = IDLE_TIMEOUT_MINUTES * SECONDS_PER_MINUTE3 * MS_PER_SECOND4;
20347
- function getEnvIdleTimeoutMs() {
20348
- const raw = process.env.XYZ_SUBAGENT_IDLE_TIMEOUT_MS;
20349
- if (!raw) return void 0;
20350
- const parsed = Number(raw);
20351
- if (!Number.isFinite(parsed) || parsed <= 0) {
20352
- logger26.warn(
20353
- `[lifecycle-manager] XYZ_SUBAGENT_IDLE_TIMEOUT_MS="${raw}" is invalid (expected a positive millisecond number) \u2014 falling back to DEFAULT_IDLE_TIMEOUT_MS (${DEFAULT_IDLE_TIMEOUT_MS}ms); set a plain ms value (e.g. 1800000) to override`
20354
- );
20355
- return void 0;
20356
- }
20357
- return parsed;
20358
- }
20359
- var idleTimers = /* @__PURE__ */ new Map();
20360
- function armIdleTimer(recordId, onTimeout, timeoutMs) {
20361
- if (timeoutMs !== void 0 && timeoutMs <= 0) {
20362
- disarmIdleTimer(recordId);
20363
- return;
20364
- }
20365
- const resolved = timeoutMs ?? getEnvIdleTimeoutMs() ?? DEFAULT_IDLE_TIMEOUT_MS;
20366
- assertSafeTimerDelay(resolved, "idleTimeoutMs");
20367
- disarmIdleTimer(recordId);
20368
- const timer = setTimeout(() => {
20369
- if (idleTimers.get(recordId)?.timer === timer) {
20370
- idleTimers.delete(recordId);
20371
- }
20372
- onTimeout();
20373
- }, resolved);
20374
- timer.unref?.();
20375
- idleTimers.set(recordId, { timer, timeoutMs: resolved });
20376
- }
20377
- function disarmIdleTimer(recordId) {
20378
- const entry = idleTimers.get(recordId);
20379
- if (!entry) {
20380
- return;
20381
- }
20382
- clearTimeout(entry.timer);
20383
- idleTimers.delete(recordId);
20384
- }
20385
- function hasIdleTimer(recordId) {
20386
- return idleTimers.has(recordId);
20387
- }
20388
- var ACTIVATE_LOCK_TIMEOUT_SECONDS = 30;
20389
- var ACTIVATE_LOCK_TIMEOUT_MS = ACTIVATE_LOCK_TIMEOUT_SECONDS * MS_PER_SECOND4;
20390
-
20391
20444
  // src/execution/concurrency-pool.ts
20392
20445
  var DefaultConcurrencyPool = class {
20393
20446
  _active = 0;
@@ -21602,17 +21655,6 @@ function displayAgentName(ref) {
21602
21655
  return base.endsWith(AGENT_REF_EXT) ? base.slice(0, -AGENT_REF_EXT.length) : base;
21603
21656
  }
21604
21657
 
21605
- // src/execution/lifecycle-predicates.ts
21606
- function hasLiveProcessHandle(recordId) {
21607
- return hasLiveProcessHandleCore(recordId);
21608
- }
21609
- function isIdle(record) {
21610
- return hasIdleTimer(record.id);
21611
- }
21612
- function isResumable(record) {
21613
- return record.status === "running" && !hasLiveProcessHandle(record.id);
21614
- }
21615
-
21616
21658
  // src/execution/notifier.ts
21617
21659
  var import_node_crypto2 = require("crypto");
21618
21660
 
@@ -23008,10 +23050,10 @@ var RecordStore = class _RecordStore {
23008
23050
  return out;
23009
23051
  }
23010
23052
  /** 订阅变更。返回取消订阅函数。 */
23011
- onChange(listener) {
23012
- this.listeners.add(listener);
23053
+ onChange(listener2) {
23054
+ this.listeners.add(listener2);
23013
23055
  return () => {
23014
- this.listeners.delete(listener);
23056
+ this.listeners.delete(listener2);
23015
23057
  };
23016
23058
  }
23017
23059
  /** 触发所有监听器(TUI widget/list requestRender)。dispose 后短路。
@@ -23019,8 +23061,8 @@ var RecordStore = class _RecordStore {
23019
23061
  * 内存事件(register/archive)不改变磁盘文件——旧实现整体失效是全量重扫的根因。 */
23020
23062
  notifyChange() {
23021
23063
  if (this._disposed) return;
23022
- for (const listener of this.listeners) {
23023
- listener();
23064
+ for (const listener2 of this.listeners) {
23065
+ listener2();
23024
23066
  }
23025
23067
  }
23026
23068
  /** session 结束清理。 */
@@ -25448,9 +25490,11 @@ var WorktreeRegistry = class {
25448
25490
  }
25449
25491
  /**
25450
25492
  * proper-lockfile 直用的跨进程锁(取代已删除的共享 file-lock 包装,抽包去依赖)。
25451
- * 锁协议逐项对齐 extensions/shared/file-lock/src/file-lock.ts 的 withFileLock:
25452
- * - lockfile 路径 = <目标文件>.lock(proper-lockfile 默认,与包装/runtime 侧
25453
- * 同一路径才互斥)
25493
+ * 锁协议对齐现存三方同协议实现——runtime 侧 packages/runtime/src/utils/file-lock.ts
25494
+ * withFileLockAsync(范本 pi FileAuthStorageBackend,参数对齐 proper-lockfile
25495
+ * 内部 retry 库)与 extension 侧 @zhushanwen/pi-file-lock:
25496
+ * - lockfile 路径 = <目标文件>.lock(proper-lockfile 默认,与 runtime 侧/
25497
+ * extension 侧同一路径才互斥)
25454
25498
  * - realpath:false —— 目标文件不存在也可锁(realpath 默认 true 时 ENOENT)
25455
25499
  * - stale 30s:持锁进程崩溃后锁可被夺取
25456
25500
  * - async retries 指数退避:10 次 / factor 2 / 100ms~10s / randomize,耗尽抛
@@ -26237,7 +26281,7 @@ var SubagentService = class {
26237
26281
  lookupRecordAnyState: (id) => this.lookupRecordAnyState(id),
26238
26282
  collectRecords: (limit, statusFilter) => this.collectRecords(limit, statusFilter),
26239
26283
  getFullRecord: (id) => this.getFullRecord(id),
26240
- onChange: (listener) => this.onChange(listener)
26284
+ onChange: (listener2) => this.onChange(listener2)
26241
26285
  };
26242
26286
  /** [D4 对话 action 面聚合] chat 域 message/close 消费面(壳 subagent-actions 经此访问;
26243
26287
  * 纯委托同上。PiEngineService 适配器不经此——引擎边界走 piEngineServiceAdapter)。 */
@@ -26490,6 +26534,7 @@ var SubagentService = class {
26490
26534
  this.notifyHost.emitPendingUnregister(record.id, "closed");
26491
26535
  count++;
26492
26536
  }
26537
+ notifyInFlightChanged();
26493
26538
  return count;
26494
26539
  }
26495
26540
  /** SP-4: /fork 新 session 时清理旧 record。
@@ -26917,6 +26962,7 @@ var SubagentService = class {
26917
26962
  );
26918
26963
  }
26919
26964
  disarmIdleTimer(record.id);
26965
+ notifyInFlightChanged();
26920
26966
  const engine = this.resolveChatEnginePort();
26921
26967
  if (!this.chatRoundRoutes.has(record.id)) {
26922
26968
  this.chatRoundRoutes.set(
@@ -27050,6 +27096,7 @@ var SubagentService = class {
27050
27096
  `[subagents] settled watchdog (${fire.phase}) fired for ${record.id}: ${windowDesc}, terminating (LC-1 wedge recovery)`
27051
27097
  );
27052
27098
  killRecordChildWithEscalation(record.id, "settled watchdog (hot path)");
27099
+ notifyInFlightChanged();
27053
27100
  this.terminateChatSession(record, "cancel", "settled watchdog (hot path)");
27054
27101
  const failedResult = {
27055
27102
  text: "",
@@ -27162,6 +27209,7 @@ var SubagentService = class {
27162
27209
  */
27163
27210
  async closeChatIdle(record) {
27164
27211
  disarmIdleTimer(record.id);
27212
+ notifyInFlightChanged();
27165
27213
  disarmSettledWatchdog(record.id);
27166
27214
  disarmRoundFromProtocol(record.id);
27167
27215
  killRecordChildWithEscalation(record.id, "closeChatIdle");
@@ -27210,6 +27258,7 @@ var SubagentService = class {
27210
27258
  */
27211
27259
  async closeAfterRoundSettled(record) {
27212
27260
  disarmIdleTimer(record.id);
27261
+ notifyInFlightChanged();
27213
27262
  disarmSettledWatchdog(record.id);
27214
27263
  disarmRoundFromProtocol(record.id);
27215
27264
  killRecordChildWithEscalation(record.id, "closeAfterRoundSettled");
@@ -27286,8 +27335,8 @@ var SubagentService = class {
27286
27335
  }
27287
27336
  // ── 状态查询(TUI 调)──────────────────────────────────
27288
27337
  /** 订阅 store 变更(widget/list requestRender)。返回取消订阅。 */
27289
- onChange(listener) {
27290
- return this.store.onChange(listener);
27338
+ onChange(listener2) {
27339
+ return this.store.onChange(listener2);
27291
27340
  }
27292
27341
  // [D4] listRunning 已删除:零生产调用方(TUI 计数经 collectRecords / notify-host 的
27293
27342
  // piAdapter 直调 store.listRunning 覆盖),唯一消费是初始空态单测——保留 store 层方法。
@@ -27899,6 +27948,7 @@ var SubagentService = class {
27899
27948
  bestEffort(fallbackErr, "armIdleTimer fallback (chat idle phase)", "error");
27900
27949
  }
27901
27950
  }
27951
+ notifyInFlightChanged();
27902
27952
  }
27903
27953
  /** [W3] idle 帧锚点回填(冷续锚点 = 引擎侧会话滚动/compaction 后的最新定位)。 */
27904
27954
  backfillChatAnchor(record, anchor) {
@@ -28045,6 +28095,7 @@ var SubagentService = class {
28045
28095
  record.controller?.abort();
28046
28096
  killRecordChildWithEscalation(record.id, "cancelBackground");
28047
28097
  disarmIdleTimer(record.id);
28098
+ notifyInFlightChanged();
28048
28099
  disarmSettledWatchdog(record.id);
28049
28100
  disarmRoundFromProtocol(record.id);
28050
28101
  this.unregisterChatRoundRoute(record.id);
@@ -33314,7 +33365,7 @@ function boundedPrettySerialize(value, budget) {
33314
33365
  }
33315
33366
 
33316
33367
  // src/index.ts
33317
- var CORE_PACKAGE_VERSION = "0.7.0";
33368
+ var CORE_PACKAGE_VERSION = "0.8.0";
33318
33369
  // Annotate the CommonJS export names for ESM import in node:
33319
33370
  0 && (module.exports = {
33320
33371
  AGENT_REF_EXT,
@@ -33412,6 +33463,7 @@ var CORE_PACKAGE_VERSION = "0.7.0";
33412
33463
  getCachedFileContent,
33413
33464
  getCachedParsed,
33414
33465
  getHostServices,
33466
+ getInFlightSnapshot,
33415
33467
  getLogger,
33416
33468
  getModelConfigService,
33417
33469
  getOrCreateChannelRegistry,
@@ -33469,6 +33521,7 @@ var CORE_PACKAGE_VERSION = "0.7.0";
33469
33521
  saveWorkflow,
33470
33522
  scheduleTimeBudget,
33471
33523
  setEngineDiscoveryRescanOptions,
33524
+ setInFlightListener,
33472
33525
  setModelConfigService,
33473
33526
  setSubagentService,
33474
33527
  sortByCodepoint,