@zhushanwen/subagent-core 0.7.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-VURUAGMM.js → chunk-LZJOEDGQ.js} +603 -154
- package/dist/engine-discovery-scan-Da4Dkzsi.d.cts +818 -0
- package/dist/engine-discovery-scan-Da4Dkzsi.d.ts +818 -0
- package/dist/execution/engine/engine-discovery-scan.cjs +325 -154
- package/dist/execution/engine/engine-discovery-scan.d.cts +1 -2
- package/dist/execution/engine/engine-discovery-scan.d.ts +1 -2
- package/dist/execution/engine/engine-discovery-scan.js +1 -1
- package/dist/execution/relay-env.js +5 -4
- package/dist/index.cjs +9799 -7954
- package/dist/index.d.cts +1896 -1360
- package/dist/index.d.ts +1896 -1360
- package/dist/index.js +8952 -7513
- package/dist.bundle/index.cjs +7897 -6054
- package/package.json +5 -5
- package/src/__tests__/manifest-store.test.ts +16 -15
- package/src/__tests__/record-store-last-line.test.ts +12 -0
- package/src/__tests__/robustness-medium-batch1.test.ts +10 -14
- package/src/__tests__/robustness-medium-batch2.test.ts +7 -2
- package/src/core/logger.ts +1 -1
- package/src/execution/__tests__/alive-store.test.ts +117 -1
- package/src/execution/__tests__/batch-finalized.test.ts +206 -0
- package/src/execution/__tests__/chat-engine-routing.test.ts +117 -1
- package/src/execution/__tests__/{cold-resurrect.test.ts → cold-lookup.test.ts} +133 -33
- package/src/execution/__tests__/collect-coordinator-service.test.ts +7 -5
- package/src/execution/__tests__/conversation-continuation.test.ts +1452 -0
- package/src/execution/__tests__/delivery-methods.test.ts +90 -176
- package/src/execution/__tests__/dialog-queue.test.ts +70 -3
- package/src/execution/__tests__/dispose-manifest-recovery.test.ts +475 -0
- package/src/execution/__tests__/execute-and-await-worktree.test.ts +3 -3
- package/src/execution/__tests__/execute-nesting.test.ts +8 -5
- package/src/execution/__tests__/execution-record.test.ts +8 -148
- package/src/execution/__tests__/finalize-record.test.ts +295 -129
- package/src/execution/__tests__/get-record-for-action-restart.test.ts +21 -29
- package/src/execution/__tests__/helpers/fake-engine-port.ts +13 -68
- package/src/execution/__tests__/helpers/legacy-sidecar.ts +33 -0
- package/src/execution/__tests__/helpers/subagent-service-mocks.ts +9 -5
- package/src/execution/__tests__/idle-gc.test.ts +60 -0
- package/src/execution/__tests__/inflight-production-wiring.test.ts +223 -0
- package/src/execution/__tests__/lifecycle-manager.test.ts +3 -254
- package/src/execution/__tests__/manifest-store-tmp-recovery.test.ts +43 -22
- package/src/execution/__tests__/model-config-service.test.ts +111 -0
- package/src/execution/__tests__/nested-visibility-env-propagation.test.ts +10 -6
- package/src/execution/__tests__/nested-visibility.test.ts +13 -1
- package/src/execution/__tests__/notify-ledger.test.ts +42 -1
- package/src/execution/__tests__/rebuild-indexes.test.ts +329 -0
- package/src/execution/__tests__/record-binding.test.ts +613 -0
- package/src/execution/__tests__/record-origin.test.ts +318 -0
- package/src/execution/__tests__/record-store-intent-api.test.ts +600 -0
- package/src/execution/__tests__/record-store-orphan-revive.test.ts +71 -3
- package/src/execution/__tests__/record-store.test.ts +72 -54
- package/src/execution/__tests__/recursive-visibility-baseline.test.ts +8 -5
- package/src/execution/__tests__/round-supervisor-workflow-origin.test.ts +205 -0
- package/src/execution/__tests__/run-orchestration-write-lease.test.ts +293 -0
- package/src/execution/__tests__/run-orchestration.test.ts +199 -0
- package/src/execution/__tests__/session-file-gc.test.ts +35 -4
- package/src/execution/__tests__/session-reconstructor.test.ts +78 -0
- package/src/execution/__tests__/state-marker.test.ts +288 -0
- package/src/execution/__tests__/stream-sink-retirement.test.ts +8 -5
- package/src/execution/__tests__/subagent-actions-core.test.ts +67 -16
- package/src/execution/__tests__/subagent-service-multiproc-guard.test.ts +0 -1
- package/src/execution/__tests__/subagent-service-notify-gate.test.ts +27 -11
- package/src/execution/__tests__/subagent-service-parent-guard.test.ts +17 -11
- package/src/execution/__tests__/subagent-service-recovery-bounds.test.ts +130 -53
- package/src/execution/__tests__/subagent-service.test.ts +17 -215
- package/src/execution/__tests__/subprocess-agent-runner-no-progress-killall.test.ts +138 -0
- package/src/execution/__tests__/subprocess-agent-runner.test.ts +105 -660
- package/src/execution/__tests__/sync-collect-recovery.test.ts +23 -16
- package/src/execution/__tests__/terminal-write-latency.test.ts +79 -0
- package/src/execution/__tests__/workflow-agent-dispatch.test.ts +782 -0
- package/src/execution/alive-store.ts +37 -28
- package/src/execution/{cold-resurrect.ts → cold-lookup.ts} +48 -45
- package/src/execution/config.ts +1 -1
- package/src/execution/conversation-continuation.ts +541 -0
- package/src/execution/dialog-queue.ts +60 -12
- package/src/execution/engine/__tests__/common/capability-gate.test.ts +1 -1
- package/src/execution/engine/__tests__/common/journal-wiring.test.ts +16 -1
- package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/fake-engine-protocol.mjs +17 -51
- package/src/execution/engine/__tests__/conformance/__fixtures__/engine-protocol/smoke-run.fixture.json +0 -5
- package/src/execution/engine/__tests__/conformance/engine-conformance.live.test.ts +1 -1
- package/src/execution/engine/__tests__/conformance/protocol-blackbox.test.ts +5 -9
- package/src/execution/engine/__tests__/conformance/registry-fork-filter.test.ts +55 -53
- package/src/execution/engine/__tests__/engine-discovery-scan.test.ts +0 -3
- package/src/execution/engine/__tests__/engine-discovery.test.ts +0 -3
- package/src/execution/engine/__tests__/inflight-snapshot.test.ts +180 -0
- package/src/execution/engine/__tests__/model-prompt.test.ts +0 -3
- package/src/execution/engine/__tests__/registry.test.ts +0 -1
- package/src/execution/engine/__tests__/routing.test.ts +0 -1
- package/src/execution/engine/__tests__/run-failure-worktree-cleanup.test.ts +0 -3
- package/src/execution/engine/client/__tests__/__fixtures__/fake-engine.mjs +0 -3
- package/src/execution/engine/client/__tests__/engine-client-reap.test.ts +212 -0
- package/src/execution/engine/client/__tests__/mirror.test.ts +73 -1
- package/src/execution/engine/client/__tests__/protocol-closure.test.ts +2 -9
- package/src/execution/engine/client/__tests__/remote-engine.test.ts +46 -12
- package/src/execution/engine/client/engine-client.ts +111 -26
- package/src/execution/engine/client/mirror.ts +9 -0
- package/src/execution/engine/client/remote-engine.ts +52 -34
- package/src/execution/engine/client/reverse-router.ts +6 -27
- package/src/execution/engine/common/__tests__/run-signals.test.ts +79 -0
- package/src/execution/engine/common/capability-gate.ts +27 -0
- package/src/execution/engine/common/data-dir.ts +3 -2
- package/src/execution/engine/common/errors.ts +1 -1
- package/src/execution/engine/common/journal-wiring.ts +18 -9
- package/src/execution/engine/common/run-signals.ts +83 -0
- package/src/execution/engine/d8-compat.ts +1 -1
- package/src/execution/engine/host/__tests__/host-bridge.test.ts +3 -18
- package/src/execution/engine/host/host-bridge.ts +26 -41
- package/src/execution/engine/host/pi-host-binding.ts +5 -5
- package/src/execution/engine/host/spawned-children.ts +1 -1
- package/src/execution/engine/inflight-snapshot.ts +92 -0
- package/src/execution/engine/port.ts +52 -54
- package/src/execution/engine/types.ts +11 -14
- package/src/execution/execution-record.ts +15 -47
- package/src/execution/finalize-record.ts +94 -197
- package/src/execution/idle-gc.ts +9 -3
- package/src/execution/lifecycle-manager.ts +25 -361
- package/src/execution/lifecycle-predicates.ts +22 -19
- package/src/execution/manifest-store.ts +40 -42
- package/src/execution/model-config-service.ts +1 -1
- package/src/execution/notifier.ts +39 -1
- package/src/execution/notify-host.ts +13 -4
- package/src/execution/path-encoding.ts +6 -0
- package/src/execution/record-entry.ts +20 -2
- package/src/execution/record-store.ts +925 -160
- package/src/execution/relay-env.ts +8 -3
- package/src/execution/round-supervisor/domain.ts +17 -5
- package/src/execution/round-supervisor/service-binding.test.ts +89 -53
- package/src/execution/round-supervisor/service-binding.ts +71 -21
- package/src/execution/round-supervisor/supervisor.ts +23 -9
- package/src/execution/service/record-access.ts +547 -0
- package/src/execution/service/record-lifecycle.ts +542 -0
- package/src/execution/service/run-orchestration.ts +1647 -0
- package/src/execution/service/service-bootstrap.ts +111 -0
- package/src/execution/service/service-constants.ts +27 -0
- package/src/execution/service/session-baselines.ts +401 -0
- package/src/execution/service/sync-collect-domain.ts +442 -0
- package/src/execution/service/workflow-dispatch.ts +567 -0
- package/src/execution/session-file-gc.ts +11 -8
- package/src/execution/session-reconstructor.ts +44 -1
- package/src/execution/sessions-index.ts +20 -3
- package/src/execution/settled-watchdog.ts +86 -43
- package/src/execution/state-marker.ts +516 -0
- package/src/execution/stream-sink.ts +1 -1
- package/src/execution/subagent-actions-core.ts +53 -31
- package/src/execution/subagent-service.ts +643 -2955
- package/src/execution/subprocess-agent-runner.ts +46 -323
- package/src/execution/types.ts +60 -43
- package/src/execution/ui-request-handler-factory.ts +5 -0
- package/src/execution/worktree-manager.ts +1 -1
- package/src/execution/worktree-registry.ts +12 -8
- package/src/index.ts +16 -5
- package/src/orchestration/__tests__/agent-call-catch-fallback.test.ts +2 -3
- package/src/orchestration/__tests__/agent-call-stream.test.ts +24 -49
- package/src/orchestration/__tests__/error-recovery-terminal-hardening.test.ts +4 -4
- package/src/orchestration/__tests__/file-run-store.test.ts +5 -7
- package/src/orchestration/__tests__/lifecycle-abort-broadcast-signal.test.ts +2 -3
- package/src/orchestration/__tests__/run-snapshot.test.ts +27 -43
- package/src/orchestration/__tests__/worker-message-pump-workflow-dispatch.test.ts +274 -0
- package/src/orchestration/execute-agent-call.ts +1 -1
- package/src/orchestration/file-run-store.ts +1 -1
- package/src/orchestration/models/ports.ts +20 -13
- package/src/orchestration/models/types.ts +11 -13
- package/src/orchestration/run-snapshot.ts +18 -22
- package/src/orchestration/worker-message-pump.ts +33 -57
- package/src/shared/atomic-write.ts +10 -8
- package/dist/chunk-T2SYBW3I.js +0 -24
- package/dist/engine-discovery-scan-ocpM8FmI.d.cts +0 -1608
- package/dist/engine-discovery-scan-ocpM8FmI.d.ts +0 -1608
- package/src/execution/__tests__/chat-round-first-round-watchdog.test.ts +0 -255
- package/src/execution/__tests__/finalized-marker.test.ts +0 -104
- package/src/execution/__tests__/lifecycle-manager-lock.test.ts +0 -211
- package/src/execution/__tests__/subprocess-agent-runner-routing.test.ts +0 -488
- package/src/execution/__tests__/subprocess-agent-runner-timeout.test.ts +0 -75
- package/src/execution/__tests__/tombstone-store.test.ts +0 -73
- package/src/execution/engine/__tests__/conformance/chat-round-protocol.test.ts +0 -230
- package/src/execution/finalized-marker.ts +0 -70
- package/src/execution/tombstone-store.ts +0 -72
|
@@ -40,7 +40,7 @@ __export(engine_discovery_scan_exports, {
|
|
|
40
40
|
scanEngines: () => scanEngines
|
|
41
41
|
});
|
|
42
42
|
module.exports = __toCommonJS(engine_discovery_scan_exports);
|
|
43
|
-
var
|
|
43
|
+
var path6 = __toESM(require("path"), 1);
|
|
44
44
|
|
|
45
45
|
// src/execution/engine/engine-manifest.ts
|
|
46
46
|
var path = __toESM(require("path"), 1);
|
|
@@ -285,7 +285,7 @@ function describeValue(value) {
|
|
|
285
285
|
|
|
286
286
|
// src/execution/engine/engine-inspect-package.ts
|
|
287
287
|
var fs = __toESM(require("fs"), 1);
|
|
288
|
-
var
|
|
288
|
+
var path3 = __toESM(require("path"), 1);
|
|
289
289
|
var import_subagent_engine_sdk9 = require("@zhushanwen/subagent-engine-sdk");
|
|
290
290
|
|
|
291
291
|
// src/execution/engine/common/data-dir.ts
|
|
@@ -314,7 +314,20 @@ var import_subagent_engine_sdk6 = require("@zhushanwen/subagent-engine-sdk");
|
|
|
314
314
|
|
|
315
315
|
// src/execution/engine/client/mirror.ts
|
|
316
316
|
var import_subagent_engine_sdk = require("@zhushanwen/subagent-engine-sdk");
|
|
317
|
-
|
|
317
|
+
|
|
318
|
+
// src/execution/dialog-queue.ts
|
|
319
|
+
var logger3 = getLogger("subagents");
|
|
320
|
+
var SECONDS_PER_MINUTE = 60;
|
|
321
|
+
var MS_PER_SECOND = 1e3;
|
|
322
|
+
var DEFAULT_DIALOG_TIMEOUT_MINUTES = 30;
|
|
323
|
+
var DEFAULT_DIALOG_TIMEOUT_MS = DEFAULT_DIALOG_TIMEOUT_MINUTES * SECONDS_PER_MINUTE * MS_PER_SECOND;
|
|
324
|
+
var activeDialogQueue;
|
|
325
|
+
function notifyChildProcessExited(pid) {
|
|
326
|
+
activeDialogQueue?.rejectChildDialogs({ pid });
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// src/execution/engine/client/mirror.ts
|
|
330
|
+
var logger4 = (0, import_subagent_engine_sdk.getLogger)("subagents");
|
|
318
331
|
var SpawnedChildrenMirror = class {
|
|
319
332
|
entries = /* @__PURE__ */ new Map();
|
|
320
333
|
listeners = /* @__PURE__ */ new Set();
|
|
@@ -343,6 +356,7 @@ var SpawnedChildrenMirror = class {
|
|
|
343
356
|
};
|
|
344
357
|
this.entries.set(patch.pid, entry);
|
|
345
358
|
this.emit({ reason: "childStateChanged", pid: patch.pid, recordId: patch.recordId });
|
|
359
|
+
if (patch.state === "exited") notifyChildProcessExited(patch.pid);
|
|
346
360
|
}
|
|
347
361
|
/**
|
|
348
362
|
* 失效语义 2 + 3:全部镜像项整体置死(killed=true + state=exited)→ 逐项广播 →
|
|
@@ -358,6 +372,7 @@ var SpawnedChildrenMirror = class {
|
|
|
358
372
|
entry.state = "exited";
|
|
359
373
|
entry.updatedAt = Date.now();
|
|
360
374
|
this.emit({ reason: "killedAll", pid: entry.pid, recordId: entry.recordId });
|
|
375
|
+
notifyChildProcessExited(entry.pid);
|
|
361
376
|
}
|
|
362
377
|
this.entries.clear();
|
|
363
378
|
}
|
|
@@ -388,18 +403,18 @@ var SpawnedChildrenMirror = class {
|
|
|
388
403
|
return this.entries.size;
|
|
389
404
|
}
|
|
390
405
|
/** 订阅状态广播(W6 notify/谓词接线点)。返回退订函数。 */
|
|
391
|
-
onChange(
|
|
392
|
-
this.listeners.add(
|
|
406
|
+
onChange(listener2) {
|
|
407
|
+
this.listeners.add(listener2);
|
|
393
408
|
return () => {
|
|
394
|
-
this.listeners.delete(
|
|
409
|
+
this.listeners.delete(listener2);
|
|
395
410
|
};
|
|
396
411
|
}
|
|
397
412
|
emit(event) {
|
|
398
|
-
for (const
|
|
413
|
+
for (const listener2 of this.listeners) {
|
|
399
414
|
try {
|
|
400
|
-
|
|
415
|
+
listener2(event);
|
|
401
416
|
} catch (err) {
|
|
402
|
-
|
|
417
|
+
logger4.debug(
|
|
403
418
|
`[spawned-children-mirror] listener threw for ${event.reason}: ${err instanceof Error ? err.message : String(err)}`
|
|
404
419
|
);
|
|
405
420
|
}
|
|
@@ -412,22 +427,22 @@ var import_node_child_process = require("child_process");
|
|
|
412
427
|
var import_node_fs = require("fs");
|
|
413
428
|
var import_node_path2 = require("path");
|
|
414
429
|
var import_subagent_engine_sdk2 = require("@zhushanwen/subagent-engine-sdk");
|
|
415
|
-
var
|
|
430
|
+
var logger5 = (0, import_subagent_engine_sdk2.getLogger)("subagents");
|
|
416
431
|
var CMDLINE_PROBE_TIMEOUT_MS = 3e3;
|
|
417
432
|
var PIDFILE_JSON_INDENT = 2;
|
|
418
433
|
function pidfilePath(engineDataDir, engineId, hostKind, hostPid) {
|
|
419
434
|
return (0, import_node_path2.join)((0, import_subagent_engine_sdk2.resolveEngineDir)(engineDataDir, engineId), `engine.${hostKind}.${hostPid}.pid`);
|
|
420
435
|
}
|
|
421
|
-
function writePidfileAtomic(
|
|
422
|
-
(0, import_node_fs.mkdirSync)((0, import_node_path2.join)(
|
|
423
|
-
const tmp = `${
|
|
436
|
+
function writePidfileAtomic(path7, content) {
|
|
437
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path2.join)(path7, ".."), { recursive: true });
|
|
438
|
+
const tmp = `${path7}.${process.pid}.${Date.now()}.tmp`;
|
|
424
439
|
(0, import_node_fs.writeFileSync)(tmp, `${JSON.stringify(content, null, PIDFILE_JSON_INDENT)}
|
|
425
440
|
`, "utf-8");
|
|
426
|
-
(0, import_node_fs.renameSync)(tmp,
|
|
441
|
+
(0, import_node_fs.renameSync)(tmp, path7);
|
|
427
442
|
}
|
|
428
|
-
function readPidfile(
|
|
443
|
+
function readPidfile(path7) {
|
|
429
444
|
try {
|
|
430
|
-
const raw = (0, import_node_fs.readFileSync)(
|
|
445
|
+
const raw = (0, import_node_fs.readFileSync)(path7, "utf-8");
|
|
431
446
|
const parsed = JSON.parse(raw);
|
|
432
447
|
if (typeof parsed.enginePid !== "number" || typeof parsed.hostPid !== "number" || typeof parsed.engineId !== "string" || typeof parsed.hostKind !== "string") {
|
|
433
448
|
return void 0;
|
|
@@ -441,21 +456,21 @@ function readPidfile(path6) {
|
|
|
441
456
|
writtenAt: typeof parsed.writtenAt === "string" ? parsed.writtenAt : ""
|
|
442
457
|
};
|
|
443
458
|
} catch (err) {
|
|
444
|
-
|
|
459
|
+
logger5.debug(`[pid-file] readPidfile(${path7}) failed, treating as stale: ${err instanceof Error ? err.message : String(err)}`);
|
|
445
460
|
return void 0;
|
|
446
461
|
}
|
|
447
462
|
}
|
|
448
|
-
function removePidfile(
|
|
463
|
+
function removePidfile(path7) {
|
|
449
464
|
try {
|
|
450
|
-
(0, import_node_fs.rmSync)(
|
|
465
|
+
(0, import_node_fs.rmSync)(path7, { force: true });
|
|
451
466
|
} catch (err) {
|
|
452
|
-
|
|
467
|
+
logger5.debug(`[pid-file] removePidfile(${path7}) failed (concurrent sweep = expected): ${err instanceof Error ? err.message : String(err)}`);
|
|
453
468
|
}
|
|
454
469
|
}
|
|
455
470
|
function registerEnginePidfile(reg) {
|
|
456
|
-
const
|
|
471
|
+
const path7 = pidfilePath(reg.engineDataDir, reg.engineId, reg.hostKind, reg.hostPid);
|
|
457
472
|
try {
|
|
458
|
-
writePidfileAtomic(
|
|
473
|
+
writePidfileAtomic(path7, {
|
|
459
474
|
enginePid: reg.enginePid,
|
|
460
475
|
hostPid: reg.hostPid,
|
|
461
476
|
engineStartTime: readProcessStartTime(reg.enginePid) ?? null,
|
|
@@ -463,9 +478,9 @@ function registerEnginePidfile(reg) {
|
|
|
463
478
|
hostKind: reg.hostKind,
|
|
464
479
|
writtenAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
465
480
|
});
|
|
466
|
-
return
|
|
481
|
+
return path7;
|
|
467
482
|
} catch (err) {
|
|
468
|
-
|
|
483
|
+
logger5.warn(
|
|
469
484
|
`[pid-file] pidfile write failed for ${reg.engineId} (sweep coverage degraded): ${err instanceof Error ? err.message : String(err)}`
|
|
470
485
|
);
|
|
471
486
|
return void 0;
|
|
@@ -475,7 +490,7 @@ function sweepEnginePidfiles(args) {
|
|
|
475
490
|
try {
|
|
476
491
|
return sweepStalePidfiles(args);
|
|
477
492
|
} catch (err) {
|
|
478
|
-
|
|
493
|
+
logger5.warn(
|
|
479
494
|
`[pid-file] pidfile sweep failed for ${args.engineId} (conservative skip): ${err instanceof Error ? err.message : String(err)}`
|
|
480
495
|
);
|
|
481
496
|
return { killed: [], removed: [], skipped: [] };
|
|
@@ -533,10 +548,10 @@ function sweepStalePidfiles(opts) {
|
|
|
533
548
|
}
|
|
534
549
|
return result;
|
|
535
550
|
}
|
|
536
|
-
function sweepOnePidfile(
|
|
537
|
-
const content = readPidfile(
|
|
551
|
+
function sweepOnePidfile(path7, file, opts, platform, result) {
|
|
552
|
+
const content = readPidfile(path7);
|
|
538
553
|
if (content === void 0) {
|
|
539
|
-
removePidfile(
|
|
554
|
+
removePidfile(path7);
|
|
540
555
|
result.removed.push({ file, reason: "unreadable-or-corrupt" });
|
|
541
556
|
return;
|
|
542
557
|
}
|
|
@@ -553,11 +568,11 @@ function sweepOnePidfile(path6, file, opts, platform, result) {
|
|
|
553
568
|
result.skipped.push({ file, reason: "host-pid-alive" });
|
|
554
569
|
return;
|
|
555
570
|
}
|
|
556
|
-
reapDeadHostEngine(
|
|
571
|
+
reapDeadHostEngine(path7, file, content, opts, platform, result);
|
|
557
572
|
}
|
|
558
|
-
function reapDeadHostEngine(
|
|
573
|
+
function reapDeadHostEngine(path7, file, content, opts, platform, result) {
|
|
559
574
|
if (platform === "win32") {
|
|
560
|
-
removePidfile(
|
|
575
|
+
removePidfile(path7);
|
|
561
576
|
result.removed.push({ file, reason: "stale (host dead); win32 never kills" });
|
|
562
577
|
return;
|
|
563
578
|
}
|
|
@@ -567,7 +582,7 @@ function reapDeadHostEngine(path6, file, content, opts, platform, result) {
|
|
|
567
582
|
return;
|
|
568
583
|
}
|
|
569
584
|
if (!engineAlive) {
|
|
570
|
-
removePidfile(
|
|
585
|
+
removePidfile(path7);
|
|
571
586
|
result.removed.push({ file, reason: "engine-pid-dead" });
|
|
572
587
|
return;
|
|
573
588
|
}
|
|
@@ -577,7 +592,7 @@ function reapDeadHostEngine(path6, file, content, opts, platform, result) {
|
|
|
577
592
|
return;
|
|
578
593
|
}
|
|
579
594
|
if (!opts.matchesEngineCmdline(cmdline)) {
|
|
580
|
-
removePidfile(
|
|
595
|
+
removePidfile(path7);
|
|
581
596
|
result.removed.push({ file, reason: "cmdline-mismatch (pid reused by unrelated process)" });
|
|
582
597
|
return;
|
|
583
598
|
}
|
|
@@ -587,13 +602,13 @@ function reapDeadHostEngine(path6, file, content, opts, platform, result) {
|
|
|
587
602
|
return;
|
|
588
603
|
}
|
|
589
604
|
if (content.engineStartTime === null || content.engineStartTime !== startTime) {
|
|
590
|
-
removePidfile(
|
|
605
|
+
removePidfile(path7);
|
|
591
606
|
result.removed.push({ file, reason: "engine-start-time-mismatch (pid reused)" });
|
|
592
607
|
return;
|
|
593
608
|
}
|
|
594
609
|
const killEngine = opts.killEngine ?? defaultKillEngineProcessGroup;
|
|
595
610
|
killEngine(content.enginePid);
|
|
596
|
-
removePidfile(
|
|
611
|
+
removePidfile(path7);
|
|
597
612
|
result.killed.push(content.enginePid);
|
|
598
613
|
result.removed.push({ file, reason: "swept after kill (stale orphan)" });
|
|
599
614
|
}
|
|
@@ -602,18 +617,18 @@ function defaultKillEngineProcessGroup(enginePid) {
|
|
|
602
617
|
process.kill(-enginePid, "SIGKILL");
|
|
603
618
|
return;
|
|
604
619
|
} catch (groupErr) {
|
|
605
|
-
|
|
620
|
+
logger5.debug(`[pid-file] process-group kill(-${enginePid}) failed, falling back to single kill: ${groupErr instanceof Error ? groupErr.message : String(groupErr)}`);
|
|
606
621
|
}
|
|
607
622
|
try {
|
|
608
623
|
process.kill(enginePid, "SIGKILL");
|
|
609
624
|
} catch (err) {
|
|
610
|
-
|
|
625
|
+
logger5.debug(`[pid-file] single kill(${enginePid}) failed (already dead = expected): ${err instanceof Error ? err.message : String(err)}`);
|
|
611
626
|
}
|
|
612
627
|
}
|
|
613
628
|
|
|
614
629
|
// src/execution/engine/client/reverse-router.ts
|
|
615
630
|
var import_subagent_engine_sdk3 = require("@zhushanwen/subagent-engine-sdk");
|
|
616
|
-
var
|
|
631
|
+
var logger6 = (0, import_subagent_engine_sdk3.getLogger)("subagents");
|
|
617
632
|
function routeReverseRequest(deps, frame) {
|
|
618
633
|
if (frame.method === "host/askUser") {
|
|
619
634
|
handleInteractionRequest(deps, frame, deps.uiRequestHandler, (params) => params.request);
|
|
@@ -648,7 +663,7 @@ function handleInteractionRequest(deps, frame, handler, extract) {
|
|
|
648
663
|
const result = await handler(input);
|
|
649
664
|
if (!deps.isDisposed()) deps.sendResponse(frame.id, result);
|
|
650
665
|
} catch (err) {
|
|
651
|
-
|
|
666
|
+
logger6.warn(
|
|
652
667
|
`[engine-client:${deps.engineId}] ${frame.method} handler failed, answering cancelled: ${err instanceof Error ? err.message : String(err)}`
|
|
653
668
|
);
|
|
654
669
|
if (!deps.isDisposed()) deps.sendResponse(frame.id, { cancelled: true });
|
|
@@ -659,7 +674,7 @@ function handleDataPlaneRequest(deps, frame) {
|
|
|
659
674
|
let settled = false;
|
|
660
675
|
const guardTimer = setTimeout(() => {
|
|
661
676
|
if (settled) return;
|
|
662
|
-
|
|
677
|
+
logger6.error(
|
|
663
678
|
`[engine-client:${deps.engineId}] data-plane reverse request ${frame.method} unanswered for ${import_subagent_engine_sdk3.REVERSE_REQUEST_TIMEOUT_MS}ms \u2014 treating as engine fault`
|
|
664
679
|
);
|
|
665
680
|
void deps.failEngine(
|
|
@@ -675,7 +690,7 @@ function handleDataPlaneRequest(deps, frame) {
|
|
|
675
690
|
(err) => {
|
|
676
691
|
settled = true;
|
|
677
692
|
clearTimeout(guardTimer);
|
|
678
|
-
|
|
693
|
+
logger6.warn(
|
|
679
694
|
`[engine-client:${deps.engineId}] ${frame.method} dispatch failed (answering ok, host-side bug): ${err instanceof Error ? err.message : String(err)}`
|
|
680
695
|
);
|
|
681
696
|
deps.sendResponse(frame.id, { ok: true });
|
|
@@ -685,7 +700,6 @@ function handleDataPlaneRequest(deps, frame) {
|
|
|
685
700
|
var DATA_PLANE_HANDLERS = {
|
|
686
701
|
"host/log": (deps, params) => dispatchLog(deps, params),
|
|
687
702
|
"host/streamDelta": (deps, params) => dispatchStreamDelta(deps, params),
|
|
688
|
-
"host/roundLifecycle": (deps, params) => dispatchRoundLifecycle(deps, params),
|
|
689
703
|
"host/poolResolved": (deps, params) => dispatchPoolResolved(deps, params),
|
|
690
704
|
"host/handleReady": (deps, params) => dispatchHandleReady(deps, params),
|
|
691
705
|
"host/childSpawned": (deps, params) => dispatchChildSpawned(deps, params),
|
|
@@ -696,22 +710,12 @@ async function dispatchDataPlane(deps, method, params) {
|
|
|
696
710
|
}
|
|
697
711
|
function dispatchLog(deps, p) {
|
|
698
712
|
if (deps.log !== void 0) deps.log(p);
|
|
699
|
-
else
|
|
713
|
+
else logger6[p.level](`[engine:${p.component}] ${p.message}`, p.data);
|
|
700
714
|
}
|
|
701
715
|
async function dispatchStreamDelta(deps, p) {
|
|
702
|
-
if (p.
|
|
703
|
-
await deps.recordRoutes.get(p.recordId)?.onStreamDelta?.(p.delta);
|
|
704
|
-
return;
|
|
705
|
-
}
|
|
716
|
+
if (p.runId === void 0) return;
|
|
706
717
|
await deps.runRoutes.get(p.runId)?.onStreamDelta?.(p.delta);
|
|
707
718
|
}
|
|
708
|
-
async function dispatchRoundLifecycle(deps, p) {
|
|
709
|
-
if (p.recordId !== void 0) {
|
|
710
|
-
await deps.recordRoutes.get(p.recordId)?.onRoundLifecycle?.(p);
|
|
711
|
-
return;
|
|
712
|
-
}
|
|
713
|
-
await deps.runRoutes.get(p.runId)?.onRoundLifecycle?.(p);
|
|
714
|
-
}
|
|
715
719
|
async function dispatchPoolResolved(deps, p) {
|
|
716
720
|
await deps.runRoutes.get(p.runId)?.onPoolResolved?.(p.poolKey);
|
|
717
721
|
}
|
|
@@ -727,11 +731,11 @@ function dispatchChildSpawned(deps, p) {
|
|
|
727
731
|
if (process.platform !== "win32" && typeof p.pid === "number") {
|
|
728
732
|
try {
|
|
729
733
|
process.kill(-p.pid, 0);
|
|
730
|
-
|
|
734
|
+
logger6.warn(
|
|
731
735
|
`[engine-client:${deps.engineId}] childSpawned pid ${p.pid} leads its own process group (not in the engine harvest group \u2014 one-generation children + in-group descendants only; engine-detached descendants are an accepted cost per design \xA73.9)`
|
|
732
736
|
);
|
|
733
737
|
} catch (err) {
|
|
734
|
-
|
|
738
|
+
logger6.debug(
|
|
735
739
|
`[engine-client:${deps.engineId}] childSpawned pid ${p.pid} group probe settled (${err instanceof Error ? err.message : String(err)})`
|
|
736
740
|
);
|
|
737
741
|
}
|
|
@@ -751,7 +755,7 @@ function dispatchChildStateChanged(deps, p) {
|
|
|
751
755
|
// src/execution/engine/client/client-options.ts
|
|
752
756
|
var import_node_path3 = require("path");
|
|
753
757
|
var import_subagent_engine_sdk4 = require("@zhushanwen/subagent-engine-sdk");
|
|
754
|
-
var
|
|
758
|
+
var logger7 = (0, import_subagent_engine_sdk4.getLogger)("subagents");
|
|
755
759
|
function defaultEngineCmdlineMatcher(command) {
|
|
756
760
|
const base = (0, import_node_path3.basename)(command);
|
|
757
761
|
return (cmdline) => {
|
|
@@ -764,7 +768,7 @@ function warnOnManifestDiagnostics(engineId, diag, result) {
|
|
|
764
768
|
const answered = JSON.stringify(result.capabilities ?? null);
|
|
765
769
|
const declared = JSON.stringify(diag.capabilities);
|
|
766
770
|
if (answered !== declared) {
|
|
767
|
-
|
|
771
|
+
logger7.warn(
|
|
768
772
|
`[engine-client:${engineId}] initialize capabilities differ from manifest (diagnostic only): manifest=${declared} answered=${answered}`
|
|
769
773
|
);
|
|
770
774
|
}
|
|
@@ -773,7 +777,7 @@ function warnOnManifestDiagnostics(engineId, diag, result) {
|
|
|
773
777
|
const answered = JSON.stringify(result.models ?? null);
|
|
774
778
|
const declared = JSON.stringify(diag.models ?? null);
|
|
775
779
|
if (answered !== declared) {
|
|
776
|
-
|
|
780
|
+
logger7.warn(
|
|
777
781
|
`[engine-client:${engineId}] initialize models differ from manifest (diagnostic only): manifest=${declared} answered=${answered}`
|
|
778
782
|
);
|
|
779
783
|
}
|
|
@@ -783,13 +787,13 @@ function warnOnManifestDiagnostics(engineId, diag, result) {
|
|
|
783
787
|
// src/execution/engine/client/reaper.ts
|
|
784
788
|
var import_node_child_process2 = require("child_process");
|
|
785
789
|
var import_subagent_engine_sdk5 = require("@zhushanwen/subagent-engine-sdk");
|
|
786
|
-
var
|
|
790
|
+
var logger8 = (0, import_subagent_engine_sdk5.getLogger)("subagents");
|
|
787
791
|
var KILL_GRACE_MS = 5e3;
|
|
788
792
|
function killProcessTree(pid, reason, opts) {
|
|
789
793
|
if (process.platform === "win32") {
|
|
790
794
|
const r = (0, import_node_child_process2.spawnSync)("taskkill", ["/PID", String(pid), "/T", "/F"], { timeout: 1e4 });
|
|
791
795
|
if (r.error || r.status !== 0) {
|
|
792
|
-
|
|
796
|
+
logger8.warn(
|
|
793
797
|
`[engine-client:${opts.engineId}] taskkill failed for pid ${pid} (${reason}): ${r.error?.message ?? `exit ${r.status}`}`
|
|
794
798
|
);
|
|
795
799
|
}
|
|
@@ -800,7 +804,7 @@ function killProcessTree(pid, reason, opts) {
|
|
|
800
804
|
process.kill(-pid, signal);
|
|
801
805
|
return true;
|
|
802
806
|
} catch (err) {
|
|
803
|
-
|
|
807
|
+
logger8.debug(
|
|
804
808
|
`[engine-client:${opts.engineId}] process.kill(-${pid}, ${signal}) failed: ${err instanceof Error ? err.message : String(err)}`
|
|
805
809
|
);
|
|
806
810
|
return false;
|
|
@@ -810,7 +814,7 @@ function killProcessTree(pid, reason, opts) {
|
|
|
810
814
|
try {
|
|
811
815
|
process.kill(pid, "SIGTERM");
|
|
812
816
|
} catch (err) {
|
|
813
|
-
|
|
817
|
+
logger8.debug(
|
|
814
818
|
`[engine-client:${opts.engineId}] single SIGTERM to ${pid} failed (already dead = expected): ${err instanceof Error ? err.message : String(err)}`
|
|
815
819
|
);
|
|
816
820
|
return;
|
|
@@ -822,7 +826,7 @@ function killProcessTree(pid, reason, opts) {
|
|
|
822
826
|
try {
|
|
823
827
|
process.kill(pid, "SIGKILL");
|
|
824
828
|
} catch (err) {
|
|
825
|
-
|
|
829
|
+
logger8.debug(
|
|
826
830
|
`[engine-client:${opts.engineId}] SIGKILL to ${pid} failed (already dead = expected): ${err instanceof Error ? err.message : String(err)}`
|
|
827
831
|
);
|
|
828
832
|
}
|
|
@@ -846,8 +850,115 @@ function waitForChildExit(child, timeoutMs) {
|
|
|
846
850
|
});
|
|
847
851
|
}
|
|
848
852
|
|
|
853
|
+
// src/execution/engine/host/spawned-children.ts
|
|
854
|
+
var CoreSpawnedChildrenMirror = class {
|
|
855
|
+
entries = /* @__PURE__ */ new Map();
|
|
856
|
+
/** 落项 / 覆盖(同 recordId 重 spawn = 覆盖旧句柄,与引擎侧 Map 同语义)。 */
|
|
857
|
+
register(recordId, child) {
|
|
858
|
+
this.entries.set(recordId, {
|
|
859
|
+
pid: child.pid,
|
|
860
|
+
killed: child.killed,
|
|
861
|
+
updatedAt: Date.now()
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
/** 单项置死(杀链入口:killRecordChildWithEscalation 途经)。 */
|
|
865
|
+
markKilled(recordId) {
|
|
866
|
+
const entry = this.entries.get(recordId);
|
|
867
|
+
if (entry !== void 0) {
|
|
868
|
+
entry.killed = true;
|
|
869
|
+
entry.updatedAt = Date.now();
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
/** 整体置死 + 清空(失效语义 2+3:引擎 exit / 重建 / dispose / killAll)。 */
|
|
873
|
+
killAll() {
|
|
874
|
+
this.entries.clear();
|
|
875
|
+
}
|
|
876
|
+
getChildByRecord(recordId) {
|
|
877
|
+
return this.entries.get(recordId);
|
|
878
|
+
}
|
|
879
|
+
/** 句柄存活谓词(镜像面;判据与引擎侧 `!child.killed` 同构)。 */
|
|
880
|
+
hasLiveProcessHandle(recordId) {
|
|
881
|
+
const entry = this.entries.get(recordId);
|
|
882
|
+
return entry !== void 0 && !entry.killed;
|
|
883
|
+
}
|
|
884
|
+
/** 快照(诊断/测试)。 */
|
|
885
|
+
snapshot() {
|
|
886
|
+
return [...this.entries.entries()].map(([recordId, entry]) => ({ recordId, ...entry }));
|
|
887
|
+
}
|
|
888
|
+
/** 测试隔离专用(生产禁用——进程级全局状态)。 */
|
|
889
|
+
clear() {
|
|
890
|
+
this.entries.clear();
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
var MIRROR_SLOT_KEY = /* @__PURE__ */ Symbol.for(
|
|
894
|
+
"@zhushanwen/pi-subagent-workflow.coreSpawnedChildrenMirror"
|
|
895
|
+
);
|
|
896
|
+
function coreMirrorSlot() {
|
|
897
|
+
let slot = Reflect.get(globalThis, MIRROR_SLOT_KEY);
|
|
898
|
+
if (!slot) {
|
|
899
|
+
slot = new CoreSpawnedChildrenMirror();
|
|
900
|
+
Reflect.set(globalThis, MIRROR_SLOT_KEY, slot);
|
|
901
|
+
}
|
|
902
|
+
return slot;
|
|
903
|
+
}
|
|
904
|
+
function coreSpawnedChildrenMirror() {
|
|
905
|
+
return coreMirrorSlot();
|
|
906
|
+
}
|
|
907
|
+
function hasLiveProcessHandleCore(recordId) {
|
|
908
|
+
return coreMirrorSlot().hasLiveProcessHandle(recordId);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// src/execution/lifecycle-manager.ts
|
|
912
|
+
var logger9 = getLogger("subagents");
|
|
913
|
+
var MS_PER_SECOND2 = 1e3;
|
|
914
|
+
var SECONDS_PER_MINUTE2 = 60;
|
|
915
|
+
var IDLE_TIMEOUT_MINUTES = 5;
|
|
916
|
+
var DEFAULT_IDLE_TIMEOUT_MS = IDLE_TIMEOUT_MINUTES * SECONDS_PER_MINUTE2 * MS_PER_SECOND2;
|
|
917
|
+
var idleTimers = /* @__PURE__ */ new Map();
|
|
918
|
+
function hasIdleTimer(recordId) {
|
|
919
|
+
return idleTimers.has(recordId);
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// src/execution/lifecycle-predicates.ts
|
|
923
|
+
function hasLiveProcessHandle(recordId) {
|
|
924
|
+
return hasLiveProcessHandleCore(recordId);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
// src/execution/engine/inflight-snapshot.ts
|
|
928
|
+
var listener = null;
|
|
929
|
+
function getInFlightSnapshot() {
|
|
930
|
+
return { inFlight: countInFlight() };
|
|
931
|
+
}
|
|
932
|
+
function notifyInFlightChanged() {
|
|
933
|
+
if (listener === null) return;
|
|
934
|
+
try {
|
|
935
|
+
listener(getInFlightSnapshot());
|
|
936
|
+
} catch {
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
function countInFlight() {
|
|
940
|
+
let count = 0;
|
|
941
|
+
for (const entry of coreSpawnedChildrenMirror().snapshot()) {
|
|
942
|
+
const recordId = entry.recordId;
|
|
943
|
+
if (hasLiveProcessHandle(recordId) && !hasIdleTimer(recordId)) count++;
|
|
944
|
+
}
|
|
945
|
+
return count;
|
|
946
|
+
}
|
|
947
|
+
|
|
849
948
|
// src/execution/engine/client/engine-client.ts
|
|
850
|
-
var
|
|
949
|
+
var logger10 = (0, import_subagent_engine_sdk6.getLogger)("subagents");
|
|
950
|
+
function bridgeMirrorEventToCoreMirror(event, mirror) {
|
|
951
|
+
if (event.recordId === void 0 || event.pid === void 0) return;
|
|
952
|
+
const entry = mirror.getEntry(event.pid);
|
|
953
|
+
if (entry === void 0) return;
|
|
954
|
+
const core = coreSpawnedChildrenMirror();
|
|
955
|
+
if (entry.killed) {
|
|
956
|
+
core.markKilled(event.recordId);
|
|
957
|
+
} else {
|
|
958
|
+
core.register(event.recordId, { pid: entry.pid, killed: false });
|
|
959
|
+
}
|
|
960
|
+
notifyInFlightChanged();
|
|
961
|
+
}
|
|
851
962
|
var DISPOSE_GRACE_MS = 3e3;
|
|
852
963
|
var SIGKILL_REAP_TIMEOUT_MS = 1e4;
|
|
853
964
|
var FRAME_ECHO_MAX_CHARS = 200;
|
|
@@ -877,9 +988,6 @@ var EngineClient = class {
|
|
|
877
988
|
nextRequestId = 1;
|
|
878
989
|
pending = /* @__PURE__ */ new Map();
|
|
879
990
|
runRoutes = /* @__PURE__ */ new Map();
|
|
880
|
-
/** [W3 v1.x] chat 轮次路由(recordId 键,D1-A——续聊轮无 runId);注册经
|
|
881
|
-
* RemoteEngine.registerChatRoundRoute,record 终态注销。 */
|
|
882
|
-
recordRoutes = /* @__PURE__ */ new Map();
|
|
883
991
|
stderrTail = "";
|
|
884
992
|
unavailableReason;
|
|
885
993
|
connectInFlight;
|
|
@@ -899,7 +1007,6 @@ var EngineClient = class {
|
|
|
899
1007
|
permissionHandler: opts.permissionHandler,
|
|
900
1008
|
log: opts.log,
|
|
901
1009
|
runRoutes: this.runRoutes,
|
|
902
|
-
recordRoutes: this.recordRoutes,
|
|
903
1010
|
mirror: this.mirror,
|
|
904
1011
|
setPartialHandle: (partial) => {
|
|
905
1012
|
this.lastPartialHandle = partial;
|
|
@@ -910,6 +1017,7 @@ var EngineClient = class {
|
|
|
910
1017
|
};
|
|
911
1018
|
this.mirror.onChange((event) => {
|
|
912
1019
|
opts.onMirrorChanged?.(event);
|
|
1020
|
+
bridgeMirrorEventToCoreMirror(event, this.mirror);
|
|
913
1021
|
});
|
|
914
1022
|
}
|
|
915
1023
|
get currentState() {
|
|
@@ -972,19 +1080,19 @@ var EngineClient = class {
|
|
|
972
1080
|
matchesEngineCmdline: this.opts.engineCmdlineMatcher ?? defaultEngineCmdlineMatcher(this.opts.command)
|
|
973
1081
|
});
|
|
974
1082
|
if (sweep.killed.length > 0) {
|
|
975
|
-
|
|
1083
|
+
logger10.warn(
|
|
976
1084
|
`[engine-client:${this.engineId}] startup sweep killed stale orphan engine pids: ${sweep.killed.join(",")}`
|
|
977
1085
|
);
|
|
978
1086
|
}
|
|
979
1087
|
for (const removed of sweep.removed) {
|
|
980
|
-
|
|
1088
|
+
logger10.debug(`[engine-client:${this.engineId}] swept stale pidfile ${removed.file}: ${removed.reason}`);
|
|
981
1089
|
}
|
|
982
1090
|
}
|
|
983
1091
|
let attempt = 0;
|
|
984
1092
|
while (attempt <= import_subagent_engine_sdk6.CRASH_REBUILD_MAX_ATTEMPTS) {
|
|
985
1093
|
if (attempt > 0) {
|
|
986
1094
|
const backoff = import_subagent_engine_sdk6.CRASH_REBUILD_BACKOFF_MS[attempt - 1];
|
|
987
|
-
|
|
1095
|
+
logger10.warn(
|
|
988
1096
|
`[engine-client:${this.engineId}] rebuild attempt ${attempt}/${import_subagent_engine_sdk6.CRASH_REBUILD_MAX_ATTEMPTS} after ${backoff}ms backoff`
|
|
989
1097
|
);
|
|
990
1098
|
await delay(backoff);
|
|
@@ -1017,7 +1125,7 @@ var EngineClient = class {
|
|
|
1017
1125
|
markUnavailable(reason) {
|
|
1018
1126
|
this.state = "unavailable";
|
|
1019
1127
|
this.unavailableReason = reason;
|
|
1020
|
-
|
|
1128
|
+
logger10.error(`[engine-client:${this.engineId}] marked unavailable: ${reason.message}`);
|
|
1021
1129
|
}
|
|
1022
1130
|
/** spawn 引擎 CLI + initialize 握手 + 版本协商 + 诊断留痕。 */
|
|
1023
1131
|
async spawnAndInitialize() {
|
|
@@ -1047,13 +1155,13 @@ var EngineClient = class {
|
|
|
1047
1155
|
});
|
|
1048
1156
|
const child = this.child;
|
|
1049
1157
|
child.on("error", (err) => {
|
|
1050
|
-
|
|
1158
|
+
logger10.warn(`[engine-client:${this.engineId}] spawn error: ${err.message}`);
|
|
1051
1159
|
this.appendStderrTail(`spawn error: ${err.message}
|
|
1052
1160
|
`);
|
|
1053
1161
|
});
|
|
1054
1162
|
for (const pipe of ["stdin", "stdout", "stderr"]) {
|
|
1055
1163
|
child[pipe]?.on("error", (err) => {
|
|
1056
|
-
|
|
1164
|
+
logger10.debug(`[engine-client:${this.engineId}] ${pipe} pipe error (engine died concurrently = expected): ${err.message}`);
|
|
1057
1165
|
});
|
|
1058
1166
|
}
|
|
1059
1167
|
child.stdout?.setEncoding("utf-8");
|
|
@@ -1122,7 +1230,7 @@ var EngineClient = class {
|
|
|
1122
1230
|
try {
|
|
1123
1231
|
frame = JSON.parse(line);
|
|
1124
1232
|
} catch {
|
|
1125
|
-
|
|
1233
|
+
logger10.warn(
|
|
1126
1234
|
`[engine-client:${this.engineId}] dropped non-NDJSON stdout line (protocol contract: stdout is NDJSON-only): ${truncate(line, FRAME_ECHO_MAX_CHARS)}`
|
|
1127
1235
|
);
|
|
1128
1236
|
return;
|
|
@@ -1139,7 +1247,7 @@ var EngineClient = class {
|
|
|
1139
1247
|
this.onReverseRequest(frame);
|
|
1140
1248
|
return;
|
|
1141
1249
|
}
|
|
1142
|
-
|
|
1250
|
+
logger10.warn(
|
|
1143
1251
|
`[engine-client:${this.engineId}] dropped unrecognized frame: ${truncate(line, FRAME_ECHO_MAX_CHARS)}`
|
|
1144
1252
|
);
|
|
1145
1253
|
}
|
|
@@ -1220,14 +1328,6 @@ var EngineClient = class {
|
|
|
1220
1328
|
if (this.runRoutes.get(runId) === route) this.runRoutes.delete(runId);
|
|
1221
1329
|
};
|
|
1222
1330
|
}
|
|
1223
|
-
/** [W3 v1.x] 注册 chat 轮次路由(recordId 键);分发见 reverse-router.ts
|
|
1224
|
-
* (与 runRoutes 同构,键空间互斥:runId=run 帧分配 / recordId=宿主铸造)。 */
|
|
1225
|
-
registerRecordRoute(recordId, route) {
|
|
1226
|
-
this.recordRoutes.set(recordId, route);
|
|
1227
|
-
return () => {
|
|
1228
|
-
if (this.recordRoutes.get(recordId) === route) this.recordRoutes.delete(recordId);
|
|
1229
|
-
};
|
|
1230
|
-
}
|
|
1231
1331
|
/** 健康检查(ADR-0047:静默 ≠ 卡死,不据此杀任务)。 */
|
|
1232
1332
|
async ping() {
|
|
1233
1333
|
await this.ensureConnected();
|
|
@@ -1263,7 +1363,7 @@ var EngineClient = class {
|
|
|
1263
1363
|
try {
|
|
1264
1364
|
await this.request("dispose", {}, { timeoutMs: DISPOSE_GRACE_MS });
|
|
1265
1365
|
} catch (err) {
|
|
1266
|
-
|
|
1366
|
+
logger10.debug(
|
|
1267
1367
|
`[engine-client:${this.engineId}] dispose frame failed, falling back to kill chain: ${err instanceof Error ? err.message : String(err)}`
|
|
1268
1368
|
);
|
|
1269
1369
|
}
|
|
@@ -1275,18 +1375,20 @@ var EngineClient = class {
|
|
|
1275
1375
|
onEngineExit(code, signal) {
|
|
1276
1376
|
const detail = signal !== null ? `signal ${signal}` : `exit code ${code}`;
|
|
1277
1377
|
if (this.intentionalKill) {
|
|
1278
|
-
|
|
1378
|
+
logger10.debug(`[engine-client:${this.engineId}] engine exited after intentional kill (${detail}) \u2014 expected`);
|
|
1279
1379
|
} else {
|
|
1280
|
-
|
|
1380
|
+
logger10.warn(`[engine-client:${this.engineId}] engine process exited unexpectedly (${detail})`);
|
|
1281
1381
|
}
|
|
1282
1382
|
this.teardownProcess(detail);
|
|
1283
1383
|
this.state = "exited";
|
|
1284
1384
|
}
|
|
1285
1385
|
/**
|
|
1286
|
-
* 进程死亡 /
|
|
1287
|
-
*
|
|
1386
|
+
* 进程死亡 / 失败后的统一清理(收割/组杀分支各成子函数,此处只做顺序编排):
|
|
1387
|
+
* 孤儿收割(镜像置死前)→ 镜像置死(失效语义 2+3)→ pidfile 删 → 在途请求
|
|
1388
|
+
* engine_crashed(附 stderr 尾)→ run 路由清空 → 活引擎组杀兜底。
|
|
1288
1389
|
*/
|
|
1289
1390
|
teardownProcess(detail) {
|
|
1391
|
+
this.reapOrphansAfterUnexpectedDeath(detail);
|
|
1290
1392
|
this.mirror.killAll();
|
|
1291
1393
|
if (this.pidfileWritten !== void 0) {
|
|
1292
1394
|
removePidfile(this.pidfileWritten);
|
|
@@ -1299,8 +1401,46 @@ var EngineClient = class {
|
|
|
1299
1401
|
}
|
|
1300
1402
|
this.pending.clear();
|
|
1301
1403
|
this.runRoutes.clear();
|
|
1302
|
-
this.recordRoutes.clear();
|
|
1303
1404
|
this.lastPartialHandle = void 0;
|
|
1405
|
+
this.killLeakedAliveChild(detail);
|
|
1406
|
+
if (this.state !== "unavailable" && this.state !== "disposed") {
|
|
1407
|
+
this.state = "exited";
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
/**
|
|
1411
|
+
* [H1 U2 / 红线①收割链] 非主动死亡且引擎已死的路径,在镜像置死**之前**补发收割
|
|
1412
|
+
* 信号:孤儿真因 = 引擎意外死时宿主未发收割信号(原先 alreadyDead 分支直接跳过
|
|
1413
|
+
* killProcessTree),任务子进程留在无主进程组继续写 session 文件。插入点约束:
|
|
1414
|
+
* - 位置 = mirror.killAll() 置死清空之前——Windows 通道依赖镜像快照过滤活孤儿
|
|
1415
|
+
* (置死后全量项 killed=true 不可过滤);
|
|
1416
|
+
* - 条件 = !intentionalKill && alreadyDead——主动 dispose 链已有两层杀(引擎
|
|
1417
|
+
* killAllActiveChildren SIGTERM+30s + 宿主 killAll 组杀 5s 升级),跳过即不叠加、
|
|
1418
|
+
* 优雅窗零压缩、pi trap-flush 零截断;进程仍活的路径(握手失败重建)由
|
|
1419
|
+
* killLeakedAliveChild 组杀兜底承接。
|
|
1420
|
+
*/
|
|
1421
|
+
reapOrphansAfterUnexpectedDeath(detail) {
|
|
1422
|
+
if (!this.intentionalKill && this.child !== void 0) {
|
|
1423
|
+
const dying = this.child;
|
|
1424
|
+
const alreadyDead = dying.exitCode !== null || dying.signalCode !== null;
|
|
1425
|
+
if (alreadyDead && dying.pid !== void 0) {
|
|
1426
|
+
if (process.platform === "win32") {
|
|
1427
|
+
this.reapOrphansViaMirrorSnapshot(detail);
|
|
1428
|
+
} else {
|
|
1429
|
+
killProcessTree(
|
|
1430
|
+
dying.pid,
|
|
1431
|
+
`reap orphaned children after unexpected engine death (${detail})`,
|
|
1432
|
+
{ engineId: this.engineId }
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
/**
|
|
1439
|
+
* 握手失败路径(版本越界/握手超时/启动僵死)引擎进程仍活着:必须组杀,否则
|
|
1440
|
+
* crash 重建循环每次 spawn 泄漏一个常驻孤儿(测试机实测积累 370+)。主动杀
|
|
1441
|
+
* (intentionalKill=true)与进程已死(exitCode/signalCode 非空)跳过重复发信号。
|
|
1442
|
+
*/
|
|
1443
|
+
killLeakedAliveChild(detail) {
|
|
1304
1444
|
if (this.child !== void 0) {
|
|
1305
1445
|
const child = this.child;
|
|
1306
1446
|
this.child = void 0;
|
|
@@ -1310,8 +1450,30 @@ var EngineClient = class {
|
|
|
1310
1450
|
}
|
|
1311
1451
|
child.removeAllListeners();
|
|
1312
1452
|
}
|
|
1313
|
-
|
|
1314
|
-
|
|
1453
|
+
}
|
|
1454
|
+
/**
|
|
1455
|
+
* [H1 U2 / 红线①Windows 通道] 镜像快照收割活孤儿:镜像快照同步抓清单(置死前,
|
|
1456
|
+
* 全量项含引擎一生历史死 pid——镜像对已退出子进程只改 state 不删项、唯一清空点 =
|
|
1457
|
+
* killAll)→ 过滤活孤儿(state === "running" && !killed,活孤儿 ≤ 并发数)→ 逐 pid
|
|
1458
|
+
* **异步 spawn** taskkill /T /F fire-and-forget(树根 = 活着的任务子进程自身,树杀
|
|
1459
|
+
* 有效;快照同步抓、杀动作异步发——与 POSIX fire-and-forget 对称;禁 spawnSync:
|
|
1460
|
+
* 逐个同步 N×10s 上界会冻结 onEngineExit 同步回调、推迟 pending reject(Continuation
|
|
1461
|
+
* 失败通知链源头)与宿主事件循环)。
|
|
1462
|
+
*/
|
|
1463
|
+
reapOrphansViaMirrorSnapshot(detail) {
|
|
1464
|
+
const liveOrphans = this.mirror.snapshot().filter((e) => e.state === "running" && !e.killed);
|
|
1465
|
+
if (liveOrphans.length === 0) return;
|
|
1466
|
+
logger10.warn(
|
|
1467
|
+
`[engine-client:${this.engineId}] reaping ${liveOrphans.length} orphaned task child(ren) via mirror snapshot after unexpected engine death (${detail})`
|
|
1468
|
+
);
|
|
1469
|
+
for (const entry of liveOrphans) {
|
|
1470
|
+
const killer = (0, import_node_child_process3.spawn)("taskkill", ["/PID", String(entry.pid), "/T", "/F"], { stdio: "ignore" });
|
|
1471
|
+
killer.unref();
|
|
1472
|
+
killer.on("error", (err) => {
|
|
1473
|
+
logger10.debug(
|
|
1474
|
+
`[engine-client:${this.engineId}] taskkill spawn failed for orphan pid ${entry.pid} (${err.message})`
|
|
1475
|
+
);
|
|
1476
|
+
});
|
|
1315
1477
|
}
|
|
1316
1478
|
}
|
|
1317
1479
|
// ── pidfile ──────────────────────────────────────────────────────────────
|
|
@@ -1326,7 +1488,7 @@ var EngineClient = class {
|
|
|
1326
1488
|
`);
|
|
1327
1489
|
return true;
|
|
1328
1490
|
} catch (err) {
|
|
1329
|
-
|
|
1491
|
+
logger10.debug(
|
|
1330
1492
|
`[engine-client:${this.engineId}] stdin write failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1331
1493
|
);
|
|
1332
1494
|
return false;
|
|
@@ -1349,8 +1511,19 @@ function truncate(text, max) {
|
|
|
1349
1511
|
}
|
|
1350
1512
|
|
|
1351
1513
|
// src/execution/engine/client/remote-engine.ts
|
|
1514
|
+
var import_node_os2 = require("os");
|
|
1515
|
+
var import_node_path4 = require("path");
|
|
1352
1516
|
var import_subagent_engine_sdk8 = require("@zhushanwen/subagent-engine-sdk");
|
|
1353
1517
|
|
|
1518
|
+
// src/execution/path-encoding.ts
|
|
1519
|
+
var path2 = __toESM(require("path"), 1);
|
|
1520
|
+
function encodeCwd(cwd) {
|
|
1521
|
+
return "--" + cwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-") + "--";
|
|
1522
|
+
}
|
|
1523
|
+
function getSubagentSessionDir(agentDir, mainCwd) {
|
|
1524
|
+
return path2.join(agentDir, "subagents", encodeCwd(mainCwd), "sessions");
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1354
1527
|
// src/execution/engine/common/capability-gate.ts
|
|
1355
1528
|
var import_subagent_engine_sdk7 = require("@zhushanwen/subagent-engine-sdk");
|
|
1356
1529
|
|
|
@@ -1365,7 +1538,7 @@ var EngineError = class extends Error {
|
|
|
1365
1538
|
this.code = code;
|
|
1366
1539
|
this.recovery = recovery;
|
|
1367
1540
|
}
|
|
1368
|
-
/** 结构化投影(
|
|
1541
|
+
/** 结构化投影(code/message 与 GUI 警告条共用形态)。 */
|
|
1369
1542
|
toStructured() {
|
|
1370
1543
|
return { code: this.code, message: this.message, recovery: this.recovery };
|
|
1371
1544
|
}
|
|
@@ -1514,22 +1687,6 @@ var RemoteEngine = class {
|
|
|
1514
1687
|
unregister();
|
|
1515
1688
|
}
|
|
1516
1689
|
}
|
|
1517
|
-
async interact(handle, action) {
|
|
1518
|
-
await this.opts.client.ensureConnected();
|
|
1519
|
-
const result = await this.opts.client.request("interact", {
|
|
1520
|
-
handle: handle.data,
|
|
1521
|
-
action
|
|
1522
|
-
});
|
|
1523
|
-
return result;
|
|
1524
|
-
}
|
|
1525
|
-
/**
|
|
1526
|
-
* [W3 v1.x] chat 轮次反向通道路由注册(recordId 键,EnginePort 可选面实现):
|
|
1527
|
-
* interact 续聊轮的 streamDelta / roundLifecycle 分发目标。薄委托 EngineClient
|
|
1528
|
-
* 的 recordRoutes(键分发见 reverse-router.ts)。
|
|
1529
|
-
*/
|
|
1530
|
-
registerChatRoundRoute(recordId, route) {
|
|
1531
|
-
return this.opts.client.registerRecordRoute(recordId, route);
|
|
1532
|
-
}
|
|
1533
1690
|
/** 协议 read(dataDir 必填——引擎数据根,构造注入)。 */
|
|
1534
1691
|
async read(handle) {
|
|
1535
1692
|
await this.opts.client.ensureConnected();
|
|
@@ -1561,6 +1718,15 @@ function isTransientRunFailure(err) {
|
|
|
1561
1718
|
if (!(err instanceof import_subagent_engine_sdk8.EngineSdkError)) return false;
|
|
1562
1719
|
return err.code === "engine_crashed" || err.code === "engine_request_timeout" || err.code === "engine_handshake_timeout";
|
|
1563
1720
|
}
|
|
1721
|
+
var PI_AGENT_DIR_ENV = "PI_CODING_AGENT_DIR";
|
|
1722
|
+
var PI_ROOT_CWD_ENV = "PI_SUBAGENT_ROOT_CWD";
|
|
1723
|
+
function deriveHostSubagentSessionDir() {
|
|
1724
|
+
const agentDir = process.env[PI_AGENT_DIR_ENV];
|
|
1725
|
+
const resolvedAgentDir = agentDir !== void 0 && agentDir !== "" ? agentDir : (0, import_node_path4.join)((0, import_node_os2.homedir)(), ".pi", "agent");
|
|
1726
|
+
const rootCwd = process.env[PI_ROOT_CWD_ENV];
|
|
1727
|
+
const resolvedRootCwd = rootCwd !== void 0 && rootCwd !== "" ? rootCwd : process.cwd();
|
|
1728
|
+
return getSubagentSessionDir(resolvedAgentDir, resolvedRootCwd);
|
|
1729
|
+
}
|
|
1564
1730
|
function buildRunParams(task, ctx, runId) {
|
|
1565
1731
|
const ctxModelRef = ctx.ctxModel ? `${ctx.ctxModel.provider}/${ctx.ctxModel.id}` : void 0;
|
|
1566
1732
|
return {
|
|
@@ -1573,9 +1739,16 @@ function buildRunParams(task, ctx, runId) {
|
|
|
1573
1739
|
schemaEnv: ctx.schemaEnv ?? task.schemaEnv,
|
|
1574
1740
|
ctxModel: ctxModelRef,
|
|
1575
1741
|
engineFallback: ctx.engineFallback,
|
|
1576
|
-
streamMode: ctx.stream !== void 0 ? "stream" : void 0
|
|
1742
|
+
streamMode: ctx.stream !== void 0 ? "stream" : void 0,
|
|
1743
|
+
// [F6] 根 session id(relay 归属键 SESSION_ID 权威源)——undefined 不上 wire
|
|
1744
|
+
//(additive 语义,与顶层 chat 参数同写法)。
|
|
1745
|
+
...ctx.sessionRootId !== void 0 ? { sessionRootId: ctx.sessionRootId } : {},
|
|
1746
|
+
// [Option C 协议化] 权威 subagent session 目录(Gate B S6):宿主注入值优先,
|
|
1747
|
+
// 缺省同源 env 推导(deriveHostSubagentSessionDir)——恒有值恒上 wire,引擎
|
|
1748
|
+
// 据此组装 --session-dir 不自推导(引擎本地推导降级 [LEGACY] fallback)。
|
|
1749
|
+
sessionDir: ctx.sessionDir ?? deriveHostSubagentSessionDir()
|
|
1577
1750
|
},
|
|
1578
|
-
...ctx.
|
|
1751
|
+
...ctx.resume !== void 0 ? { resume: ctx.resume } : {}
|
|
1579
1752
|
};
|
|
1580
1753
|
}
|
|
1581
1754
|
function buildRunRouteHandlers(ctx) {
|
|
@@ -1583,9 +1756,7 @@ function buildRunRouteHandlers(ctx) {
|
|
|
1583
1756
|
onEvent: (event) => ctx.onEvent?.(event),
|
|
1584
1757
|
onStreamDelta: (delta) => ctx.stream?.onDelta(delta),
|
|
1585
1758
|
onPoolResolved: (poolKey) => ctx.onPoolResolved?.(poolKey),
|
|
1586
|
-
onHandleReady: (partial) => ctx.onHandleReady?.(partial)
|
|
1587
|
-
// [W3 v1.x] 首轮(run 会话形态)轮次生命周期帧(runId 键)→ 宿主消费口。
|
|
1588
|
-
onRoundLifecycle: (phase) => ctx.onRoundLifecycle?.(phase)
|
|
1759
|
+
onHandleReady: (partial) => ctx.onHandleReady?.(partial)
|
|
1589
1760
|
};
|
|
1590
1761
|
}
|
|
1591
1762
|
function wireAbortSignal(client, runId, ctx) {
|
|
@@ -1675,11 +1846,11 @@ function getHostUiRequestEndpoint() {
|
|
|
1675
1846
|
}
|
|
1676
1847
|
|
|
1677
1848
|
// src/execution/engine/engine-inspect-package.ts
|
|
1678
|
-
var
|
|
1849
|
+
var logger11 = getLogger("subagents");
|
|
1679
1850
|
function readEnginePackageJson(pkgDir) {
|
|
1680
1851
|
let raw;
|
|
1681
1852
|
try {
|
|
1682
|
-
raw = fs.readFileSync(
|
|
1853
|
+
raw = fs.readFileSync(path3.join(pkgDir, "package.json"), "utf8");
|
|
1683
1854
|
} catch (err) {
|
|
1684
1855
|
return { status: "skip", reason: `package.json unreadable: ${errorMessage(err)}` };
|
|
1685
1856
|
}
|
|
@@ -1754,12 +1925,12 @@ function parseOptionalDisplayFields(m, id) {
|
|
|
1754
1925
|
if (typeof rawDisplayName === "string" && rawDisplayName.trim() !== "") {
|
|
1755
1926
|
displayName = rawDisplayName;
|
|
1756
1927
|
} else {
|
|
1757
|
-
|
|
1928
|
+
logger11.warn(`[engine-discovery] engine '${id}': displayName must be a non-empty string \u2014 ignoring`);
|
|
1758
1929
|
}
|
|
1759
1930
|
}
|
|
1760
1931
|
const rawDescription = m["description"];
|
|
1761
1932
|
if (rawDescription !== void 0 && typeof rawDescription !== "string") {
|
|
1762
|
-
|
|
1933
|
+
logger11.warn(`[engine-discovery] engine '${id}': description must be a string \u2014 ignoring`);
|
|
1763
1934
|
}
|
|
1764
1935
|
return displayName;
|
|
1765
1936
|
}
|
|
@@ -1847,19 +2018,19 @@ function errorMessage(err) {
|
|
|
1847
2018
|
|
|
1848
2019
|
// src/execution/engine/engine-discovery-roots.ts
|
|
1849
2020
|
var fs2 = __toESM(require("fs"), 1);
|
|
1850
|
-
var
|
|
1851
|
-
var
|
|
2021
|
+
var path4 = __toESM(require("path"), 1);
|
|
2022
|
+
var logger12 = getLogger("subagents");
|
|
1852
2023
|
var ENGINE_ROOTS_ENV = "XYZ_AGENT_ENGINE_ROOTS";
|
|
1853
2024
|
function parseEngineRootsEnv(env) {
|
|
1854
2025
|
const raw = env[ENGINE_ROOTS_ENV];
|
|
1855
2026
|
if (raw === void 0 || raw.trim() === "") return [];
|
|
1856
2027
|
const out = [];
|
|
1857
2028
|
const seen = /* @__PURE__ */ new Set();
|
|
1858
|
-
for (const part of raw.split(
|
|
2029
|
+
for (const part of raw.split(path4.delimiter)) {
|
|
1859
2030
|
const dir = part.trim();
|
|
1860
2031
|
if (dir === "") continue;
|
|
1861
|
-
if (!
|
|
1862
|
-
|
|
2032
|
+
if (!path4.isAbsolute(dir)) {
|
|
2033
|
+
logger12.warn(
|
|
1863
2034
|
`[engine-discovery] ${ENGINE_ROOTS_ENV} entry '${dir}' is not an absolute path \u2014 dropping entry`
|
|
1864
2035
|
);
|
|
1865
2036
|
continue;
|
|
@@ -1873,11 +2044,11 @@ function parseEngineRootsEnv(env) {
|
|
|
1873
2044
|
function deriveNodeModuleRoots(argvEntry = process.argv[1]) {
|
|
1874
2045
|
if (!argvEntry || argvEntry.trim() === "") return [];
|
|
1875
2046
|
const roots = [];
|
|
1876
|
-
let dir =
|
|
2047
|
+
let dir = path4.dirname(path4.resolve(argvEntry));
|
|
1877
2048
|
while (true) {
|
|
1878
|
-
const nm =
|
|
2049
|
+
const nm = path4.join(dir, "node_modules");
|
|
1879
2050
|
if (isDirectory(nm)) roots.push(nm);
|
|
1880
|
-
const parent =
|
|
2051
|
+
const parent = path4.dirname(dir);
|
|
1881
2052
|
if (parent === dir) break;
|
|
1882
2053
|
dir = parent;
|
|
1883
2054
|
}
|
|
@@ -1892,8 +2063,8 @@ function scanEngineRoot(root, visit) {
|
|
|
1892
2063
|
}
|
|
1893
2064
|
for (const item of entries) {
|
|
1894
2065
|
if (!item.isDirectory()) continue;
|
|
1895
|
-
const dir =
|
|
1896
|
-
if (isFile(
|
|
2066
|
+
const dir = path4.join(root, item.name);
|
|
2067
|
+
if (isFile(path4.join(dir, "package.json"))) {
|
|
1897
2068
|
visit(dir);
|
|
1898
2069
|
continue;
|
|
1899
2070
|
}
|
|
@@ -1905,8 +2076,8 @@ function scanEngineRoot(root, visit) {
|
|
|
1905
2076
|
}
|
|
1906
2077
|
for (const child of sub) {
|
|
1907
2078
|
if (!child.isDirectory()) continue;
|
|
1908
|
-
const subDir =
|
|
1909
|
-
if (isFile(
|
|
2079
|
+
const subDir = path4.join(dir, child.name);
|
|
2080
|
+
if (isFile(path4.join(subDir, "package.json"))) visit(subDir);
|
|
1910
2081
|
}
|
|
1911
2082
|
}
|
|
1912
2083
|
}
|
|
@@ -1927,10 +2098,10 @@ function isFile(p) {
|
|
|
1927
2098
|
|
|
1928
2099
|
// src/execution/engine/config.ts
|
|
1929
2100
|
var fs3 = __toESM(require("fs"), 1);
|
|
1930
|
-
var
|
|
1931
|
-
var
|
|
2101
|
+
var path5 = __toESM(require("path"), 1);
|
|
2102
|
+
var logger13 = getLogger("subagents");
|
|
1932
2103
|
function readExplicitEngines(agentDir) {
|
|
1933
|
-
const configPath =
|
|
2104
|
+
const configPath = path5.join(agentDir, "subagents", "config.json");
|
|
1934
2105
|
let parsed;
|
|
1935
2106
|
try {
|
|
1936
2107
|
parsed = JSON.parse(fs3.readFileSync(configPath, "utf8"));
|
|
@@ -1941,7 +2112,7 @@ function readExplicitEngines(agentDir) {
|
|
|
1941
2112
|
const engines = parsed["engines"];
|
|
1942
2113
|
if (typeof engines !== "object" || engines === null || Array.isArray(engines)) {
|
|
1943
2114
|
if (engines !== void 0) {
|
|
1944
|
-
|
|
2115
|
+
logger13.warn(
|
|
1945
2116
|
`[engine-discovery] config.json engines section must be an object keyed by engine id, got ${jsonKindOf(engines)} \u2014 ignoring L3 explicit engines`
|
|
1946
2117
|
);
|
|
1947
2118
|
}
|
|
@@ -1969,7 +2140,7 @@ function sanitizeExplicitEntry(id, raw) {
|
|
|
1969
2140
|
}
|
|
1970
2141
|
function requireEntryObject(id, raw) {
|
|
1971
2142
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
1972
|
-
|
|
2143
|
+
logger13.warn(
|
|
1973
2144
|
`[engine-discovery] config.json engines.${id} must be an object, got ${jsonKindOf(raw)} \u2014 skipping entry`
|
|
1974
2145
|
);
|
|
1975
2146
|
return void 0;
|
|
@@ -1978,7 +2149,7 @@ function requireEntryObject(id, raw) {
|
|
|
1978
2149
|
}
|
|
1979
2150
|
function requireCommand(id, command) {
|
|
1980
2151
|
if (typeof command !== "string" || command.trim() === "") {
|
|
1981
|
-
|
|
2152
|
+
logger13.warn(
|
|
1982
2153
|
`[engine-discovery] config.json engines.${id}.command is required (non-empty string) \u2014 skipping entry`
|
|
1983
2154
|
);
|
|
1984
2155
|
return void 0;
|
|
@@ -1987,7 +2158,7 @@ function requireCommand(id, command) {
|
|
|
1987
2158
|
}
|
|
1988
2159
|
function normalizeArgs(id, args) {
|
|
1989
2160
|
if (args !== void 0 && !isStringArray(args)) {
|
|
1990
|
-
|
|
2161
|
+
logger13.warn(
|
|
1991
2162
|
`[engine-discovery] config.json engines.${id}.args must be a string array \u2014 ignoring args`
|
|
1992
2163
|
);
|
|
1993
2164
|
}
|
|
@@ -1995,7 +2166,7 @@ function normalizeArgs(id, args) {
|
|
|
1995
2166
|
}
|
|
1996
2167
|
function normalizeConfig(id, config) {
|
|
1997
2168
|
if (config !== void 0 && !isStringRecord(config)) {
|
|
1998
|
-
|
|
2169
|
+
logger13.warn(
|
|
1999
2170
|
`[engine-discovery] config.json engines.${id}.config must be a Record<string, string> \u2014 ignoring config`
|
|
2000
2171
|
);
|
|
2001
2172
|
}
|
|
@@ -2003,7 +2174,7 @@ function normalizeConfig(id, config) {
|
|
|
2003
2174
|
}
|
|
2004
2175
|
function normalizeCwd(id, cwd) {
|
|
2005
2176
|
if (cwd !== void 0 && typeof cwd !== "string") {
|
|
2006
|
-
|
|
2177
|
+
logger13.warn(
|
|
2007
2178
|
`[engine-discovery] config.json engines.${id}.cwd must be a string \u2014 ignoring cwd`
|
|
2008
2179
|
);
|
|
2009
2180
|
}
|
|
@@ -2023,7 +2194,7 @@ function jsonKindOf(value) {
|
|
|
2023
2194
|
}
|
|
2024
2195
|
|
|
2025
2196
|
// src/execution/engine/registry.ts
|
|
2026
|
-
var
|
|
2197
|
+
var logger14 = getLogger("subagents");
|
|
2027
2198
|
var ENGINE_REGISTRY_SLOT_KEY = /* @__PURE__ */ Symbol.for("@zhushanwen/pi-subagent-workflow.engineRegistry");
|
|
2028
2199
|
function getRegistrySlot() {
|
|
2029
2200
|
let slot = Reflect.get(globalThis, ENGINE_REGISTRY_SLOT_KEY);
|
|
@@ -2037,12 +2208,12 @@ function triggerEngineDispose(engine, source) {
|
|
|
2037
2208
|
if (typeof engine.dispose !== "function") return;
|
|
2038
2209
|
try {
|
|
2039
2210
|
engine.dispose().then(void 0, (err) => {
|
|
2040
|
-
|
|
2211
|
+
logger14.warn(
|
|
2041
2212
|
`[engine-registry] engine '${engine.id}' dispose rejected (${source}, best-effort continue): ${err instanceof Error ? err.message : String(err)}`
|
|
2042
2213
|
);
|
|
2043
2214
|
});
|
|
2044
2215
|
} catch (err) {
|
|
2045
|
-
|
|
2216
|
+
logger14.warn(
|
|
2046
2217
|
`[engine-registry] engine '${engine.id}' dispose threw synchronously (${source}, best-effort continue): ${err instanceof Error ? err.message : String(err)}`
|
|
2047
2218
|
);
|
|
2048
2219
|
}
|
|
@@ -2059,14 +2230,14 @@ function hasEngine(id) {
|
|
|
2059
2230
|
}
|
|
2060
2231
|
|
|
2061
2232
|
// src/execution/engine/engine-discovery-scan.ts
|
|
2062
|
-
var
|
|
2233
|
+
var logger15 = getLogger("subagents");
|
|
2063
2234
|
function scanEngines(opts) {
|
|
2064
2235
|
const env = opts.env ?? process.env;
|
|
2065
2236
|
const result = { discovered: [], skipped: [], unusable: [] };
|
|
2066
2237
|
const present = (entry) => {
|
|
2067
2238
|
const idx = result.discovered.findIndex((d) => d.id === entry.id);
|
|
2068
2239
|
if (idx >= 0) {
|
|
2069
|
-
|
|
2240
|
+
logger15.debug(
|
|
2070
2241
|
`[engine-discovery] engine id '${entry.id}' from '${entry.source}' overrides earlier discovery from '${result.discovered[idx].source}' \u2014 same-id override`
|
|
2071
2242
|
);
|
|
2072
2243
|
result.discovered[idx] = entry;
|
|
@@ -2078,13 +2249,13 @@ function scanEngines(opts) {
|
|
|
2078
2249
|
const inspection = inspectEnginePackage(pkgDir, source, opts.hostKind, env);
|
|
2079
2250
|
if (inspection.status === "skip") {
|
|
2080
2251
|
if (!inspection.reason.includes("not an engine package")) {
|
|
2081
|
-
|
|
2252
|
+
logger15.warn(`[engine-discovery] skipping ${pkgDir} (${source}): ${inspection.reason}`);
|
|
2082
2253
|
result.skipped.push({ pkgDir, reason: inspection.reason });
|
|
2083
2254
|
}
|
|
2084
2255
|
return;
|
|
2085
2256
|
}
|
|
2086
2257
|
if (inspection.status === "unusable") {
|
|
2087
|
-
|
|
2258
|
+
logger15.warn(`[engine-discovery] engine unavailable: ${inspection.reason}`);
|
|
2088
2259
|
result.unusable.push({ pkgDir, id: inspection.id, reason: inspection.reason });
|
|
2089
2260
|
return;
|
|
2090
2261
|
}
|
|
@@ -2109,7 +2280,7 @@ function scanEngines(opts) {
|
|
|
2109
2280
|
const command = resolveExplicitCommand(entry.command, env);
|
|
2110
2281
|
if (command === void 0) {
|
|
2111
2282
|
const reason = `engine '${id}' (config.json engines.${id}): command '${entry.command}' not found or not executable`;
|
|
2112
|
-
|
|
2283
|
+
logger15.warn(`[engine-discovery] ${reason} \u2014 not registering`);
|
|
2113
2284
|
result.unusable.push({ pkgDir: `config.json engines.${id}`, id, reason });
|
|
2114
2285
|
continue;
|
|
2115
2286
|
}
|
|
@@ -2126,7 +2297,7 @@ function discoverAndRegisterEngines(opts) {
|
|
|
2126
2297
|
registerEngineDescriptor(entry.id, entry.descriptor);
|
|
2127
2298
|
loadedDiscoveryIdSet.add(entry.id);
|
|
2128
2299
|
if (existed) {
|
|
2129
|
-
|
|
2300
|
+
logger15.debug(
|
|
2130
2301
|
`[engine-discovery] engine '${entry.id}' descriptor overwritten in registry (source '${entry.source}') \u2014 same-id override`
|
|
2131
2302
|
);
|
|
2132
2303
|
}
|
|
@@ -2143,20 +2314,20 @@ function ensureEngineDiscovered(id, opts) {
|
|
|
2143
2314
|
}
|
|
2144
2315
|
function resolveExplicitCommand(command, env) {
|
|
2145
2316
|
if (command.includes("/") || command.includes("\\")) {
|
|
2146
|
-
const p =
|
|
2317
|
+
const p = path6.isAbsolute(command) ? command : path6.resolve(command);
|
|
2147
2318
|
return canExecute(p) ? p : void 0;
|
|
2148
2319
|
}
|
|
2149
2320
|
const pathVar = env["PATH"] ?? env["Path"] ?? "";
|
|
2150
|
-
for (const dir of pathVar.split(
|
|
2321
|
+
for (const dir of pathVar.split(path6.delimiter)) {
|
|
2151
2322
|
if (dir === "") continue;
|
|
2152
|
-
const p =
|
|
2323
|
+
const p = path6.join(dir, command);
|
|
2153
2324
|
if (canExecute(p)) return p;
|
|
2154
2325
|
}
|
|
2155
2326
|
return void 0;
|
|
2156
2327
|
}
|
|
2157
2328
|
function buildExplicitDescriptor(id, command, entry, opts) {
|
|
2158
2329
|
const env = opts.env ?? process.env;
|
|
2159
|
-
|
|
2330
|
+
logger15.debug(
|
|
2160
2331
|
`[engine-discovery] engine '${id}' registered from config.json engines section with conservative capabilities (no manifest)`
|
|
2161
2332
|
);
|
|
2162
2333
|
const caps = { ...CONSERVATIVE_CAPABILITIES };
|