@zhushanwen/subagent-core 0.8.0 → 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-OL5BK4VN.js → chunk-LZJOEDGQ.js} +455 -203
- 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 +216 -155
- 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 +7686 -5894
- package/dist/index.d.cts +1876 -1360
- package/dist/index.d.ts +1876 -1360
- package/dist/index.js +7048 -5493
- package/dist.bundle/index.cjs +7747 -5957
- package/package.json +4 -4
- package/src/__tests__/manifest-store.test.ts +16 -15
- package/src/__tests__/robustness-medium-batch1.test.ts +10 -14
- package/src/__tests__/robustness-medium-batch2.test.ts +7 -2
- 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__/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__/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__/engine-discovery-scan.test.ts +0 -3
- package/src/execution/engine/__tests__/engine-discovery.test.ts +0 -3
- 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 +81 -25
- 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 +19 -41
- package/src/execution/engine/host/pi-host-binding.ts +5 -5
- package/src/execution/engine/port.ts +34 -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/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 -2980
- 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 +1 -1
- package/src/index.ts +6 -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-BXLA8y-z.d.cts +0 -1624
- package/dist/engine-discovery-scan-BXLA8y-z.d.ts +0 -1624
- 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
|
}
|
|
@@ -399,7 +414,7 @@ var SpawnedChildrenMirror = class {
|
|
|
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
|
}
|
|
@@ -905,17 +909,15 @@ function hasLiveProcessHandleCore(recordId) {
|
|
|
905
909
|
}
|
|
906
910
|
|
|
907
911
|
// src/execution/lifecycle-manager.ts
|
|
908
|
-
var
|
|
909
|
-
var
|
|
910
|
-
var
|
|
912
|
+
var logger9 = getLogger("subagents");
|
|
913
|
+
var MS_PER_SECOND2 = 1e3;
|
|
914
|
+
var SECONDS_PER_MINUTE2 = 60;
|
|
911
915
|
var IDLE_TIMEOUT_MINUTES = 5;
|
|
912
|
-
var DEFAULT_IDLE_TIMEOUT_MS = IDLE_TIMEOUT_MINUTES *
|
|
916
|
+
var DEFAULT_IDLE_TIMEOUT_MS = IDLE_TIMEOUT_MINUTES * SECONDS_PER_MINUTE2 * MS_PER_SECOND2;
|
|
913
917
|
var idleTimers = /* @__PURE__ */ new Map();
|
|
914
918
|
function hasIdleTimer(recordId) {
|
|
915
919
|
return idleTimers.has(recordId);
|
|
916
920
|
}
|
|
917
|
-
var ACTIVATE_LOCK_TIMEOUT_SECONDS = 30;
|
|
918
|
-
var ACTIVATE_LOCK_TIMEOUT_MS = ACTIVATE_LOCK_TIMEOUT_SECONDS * MS_PER_SECOND;
|
|
919
921
|
|
|
920
922
|
// src/execution/lifecycle-predicates.ts
|
|
921
923
|
function hasLiveProcessHandle(recordId) {
|
|
@@ -944,7 +946,7 @@ function countInFlight() {
|
|
|
944
946
|
}
|
|
945
947
|
|
|
946
948
|
// src/execution/engine/client/engine-client.ts
|
|
947
|
-
var
|
|
949
|
+
var logger10 = (0, import_subagent_engine_sdk6.getLogger)("subagents");
|
|
948
950
|
function bridgeMirrorEventToCoreMirror(event, mirror) {
|
|
949
951
|
if (event.recordId === void 0 || event.pid === void 0) return;
|
|
950
952
|
const entry = mirror.getEntry(event.pid);
|
|
@@ -986,9 +988,6 @@ var EngineClient = class {
|
|
|
986
988
|
nextRequestId = 1;
|
|
987
989
|
pending = /* @__PURE__ */ new Map();
|
|
988
990
|
runRoutes = /* @__PURE__ */ new Map();
|
|
989
|
-
/** [W3 v1.x] chat 轮次路由(recordId 键,D1-A——续聊轮无 runId);注册经
|
|
990
|
-
* RemoteEngine.registerChatRoundRoute,record 终态注销。 */
|
|
991
|
-
recordRoutes = /* @__PURE__ */ new Map();
|
|
992
991
|
stderrTail = "";
|
|
993
992
|
unavailableReason;
|
|
994
993
|
connectInFlight;
|
|
@@ -1008,7 +1007,6 @@ var EngineClient = class {
|
|
|
1008
1007
|
permissionHandler: opts.permissionHandler,
|
|
1009
1008
|
log: opts.log,
|
|
1010
1009
|
runRoutes: this.runRoutes,
|
|
1011
|
-
recordRoutes: this.recordRoutes,
|
|
1012
1010
|
mirror: this.mirror,
|
|
1013
1011
|
setPartialHandle: (partial) => {
|
|
1014
1012
|
this.lastPartialHandle = partial;
|
|
@@ -1082,19 +1080,19 @@ var EngineClient = class {
|
|
|
1082
1080
|
matchesEngineCmdline: this.opts.engineCmdlineMatcher ?? defaultEngineCmdlineMatcher(this.opts.command)
|
|
1083
1081
|
});
|
|
1084
1082
|
if (sweep.killed.length > 0) {
|
|
1085
|
-
|
|
1083
|
+
logger10.warn(
|
|
1086
1084
|
`[engine-client:${this.engineId}] startup sweep killed stale orphan engine pids: ${sweep.killed.join(",")}`
|
|
1087
1085
|
);
|
|
1088
1086
|
}
|
|
1089
1087
|
for (const removed of sweep.removed) {
|
|
1090
|
-
|
|
1088
|
+
logger10.debug(`[engine-client:${this.engineId}] swept stale pidfile ${removed.file}: ${removed.reason}`);
|
|
1091
1089
|
}
|
|
1092
1090
|
}
|
|
1093
1091
|
let attempt = 0;
|
|
1094
1092
|
while (attempt <= import_subagent_engine_sdk6.CRASH_REBUILD_MAX_ATTEMPTS) {
|
|
1095
1093
|
if (attempt > 0) {
|
|
1096
1094
|
const backoff = import_subagent_engine_sdk6.CRASH_REBUILD_BACKOFF_MS[attempt - 1];
|
|
1097
|
-
|
|
1095
|
+
logger10.warn(
|
|
1098
1096
|
`[engine-client:${this.engineId}] rebuild attempt ${attempt}/${import_subagent_engine_sdk6.CRASH_REBUILD_MAX_ATTEMPTS} after ${backoff}ms backoff`
|
|
1099
1097
|
);
|
|
1100
1098
|
await delay(backoff);
|
|
@@ -1127,7 +1125,7 @@ var EngineClient = class {
|
|
|
1127
1125
|
markUnavailable(reason) {
|
|
1128
1126
|
this.state = "unavailable";
|
|
1129
1127
|
this.unavailableReason = reason;
|
|
1130
|
-
|
|
1128
|
+
logger10.error(`[engine-client:${this.engineId}] marked unavailable: ${reason.message}`);
|
|
1131
1129
|
}
|
|
1132
1130
|
/** spawn 引擎 CLI + initialize 握手 + 版本协商 + 诊断留痕。 */
|
|
1133
1131
|
async spawnAndInitialize() {
|
|
@@ -1157,13 +1155,13 @@ var EngineClient = class {
|
|
|
1157
1155
|
});
|
|
1158
1156
|
const child = this.child;
|
|
1159
1157
|
child.on("error", (err) => {
|
|
1160
|
-
|
|
1158
|
+
logger10.warn(`[engine-client:${this.engineId}] spawn error: ${err.message}`);
|
|
1161
1159
|
this.appendStderrTail(`spawn error: ${err.message}
|
|
1162
1160
|
`);
|
|
1163
1161
|
});
|
|
1164
1162
|
for (const pipe of ["stdin", "stdout", "stderr"]) {
|
|
1165
1163
|
child[pipe]?.on("error", (err) => {
|
|
1166
|
-
|
|
1164
|
+
logger10.debug(`[engine-client:${this.engineId}] ${pipe} pipe error (engine died concurrently = expected): ${err.message}`);
|
|
1167
1165
|
});
|
|
1168
1166
|
}
|
|
1169
1167
|
child.stdout?.setEncoding("utf-8");
|
|
@@ -1232,7 +1230,7 @@ var EngineClient = class {
|
|
|
1232
1230
|
try {
|
|
1233
1231
|
frame = JSON.parse(line);
|
|
1234
1232
|
} catch {
|
|
1235
|
-
|
|
1233
|
+
logger10.warn(
|
|
1236
1234
|
`[engine-client:${this.engineId}] dropped non-NDJSON stdout line (protocol contract: stdout is NDJSON-only): ${truncate(line, FRAME_ECHO_MAX_CHARS)}`
|
|
1237
1235
|
);
|
|
1238
1236
|
return;
|
|
@@ -1249,7 +1247,7 @@ var EngineClient = class {
|
|
|
1249
1247
|
this.onReverseRequest(frame);
|
|
1250
1248
|
return;
|
|
1251
1249
|
}
|
|
1252
|
-
|
|
1250
|
+
logger10.warn(
|
|
1253
1251
|
`[engine-client:${this.engineId}] dropped unrecognized frame: ${truncate(line, FRAME_ECHO_MAX_CHARS)}`
|
|
1254
1252
|
);
|
|
1255
1253
|
}
|
|
@@ -1330,14 +1328,6 @@ var EngineClient = class {
|
|
|
1330
1328
|
if (this.runRoutes.get(runId) === route) this.runRoutes.delete(runId);
|
|
1331
1329
|
};
|
|
1332
1330
|
}
|
|
1333
|
-
/** [W3 v1.x] 注册 chat 轮次路由(recordId 键);分发见 reverse-router.ts
|
|
1334
|
-
* (与 runRoutes 同构,键空间互斥:runId=run 帧分配 / recordId=宿主铸造)。 */
|
|
1335
|
-
registerRecordRoute(recordId, route) {
|
|
1336
|
-
this.recordRoutes.set(recordId, route);
|
|
1337
|
-
return () => {
|
|
1338
|
-
if (this.recordRoutes.get(recordId) === route) this.recordRoutes.delete(recordId);
|
|
1339
|
-
};
|
|
1340
|
-
}
|
|
1341
1331
|
/** 健康检查(ADR-0047:静默 ≠ 卡死,不据此杀任务)。 */
|
|
1342
1332
|
async ping() {
|
|
1343
1333
|
await this.ensureConnected();
|
|
@@ -1373,7 +1363,7 @@ var EngineClient = class {
|
|
|
1373
1363
|
try {
|
|
1374
1364
|
await this.request("dispose", {}, { timeoutMs: DISPOSE_GRACE_MS });
|
|
1375
1365
|
} catch (err) {
|
|
1376
|
-
|
|
1366
|
+
logger10.debug(
|
|
1377
1367
|
`[engine-client:${this.engineId}] dispose frame failed, falling back to kill chain: ${err instanceof Error ? err.message : String(err)}`
|
|
1378
1368
|
);
|
|
1379
1369
|
}
|
|
@@ -1385,18 +1375,20 @@ var EngineClient = class {
|
|
|
1385
1375
|
onEngineExit(code, signal) {
|
|
1386
1376
|
const detail = signal !== null ? `signal ${signal}` : `exit code ${code}`;
|
|
1387
1377
|
if (this.intentionalKill) {
|
|
1388
|
-
|
|
1378
|
+
logger10.debug(`[engine-client:${this.engineId}] engine exited after intentional kill (${detail}) \u2014 expected`);
|
|
1389
1379
|
} else {
|
|
1390
|
-
|
|
1380
|
+
logger10.warn(`[engine-client:${this.engineId}] engine process exited unexpectedly (${detail})`);
|
|
1391
1381
|
}
|
|
1392
1382
|
this.teardownProcess(detail);
|
|
1393
1383
|
this.state = "exited";
|
|
1394
1384
|
}
|
|
1395
1385
|
/**
|
|
1396
|
-
* 进程死亡 /
|
|
1397
|
-
*
|
|
1386
|
+
* 进程死亡 / 失败后的统一清理(收割/组杀分支各成子函数,此处只做顺序编排):
|
|
1387
|
+
* 孤儿收割(镜像置死前)→ 镜像置死(失效语义 2+3)→ pidfile 删 → 在途请求
|
|
1388
|
+
* engine_crashed(附 stderr 尾)→ run 路由清空 → 活引擎组杀兜底。
|
|
1398
1389
|
*/
|
|
1399
1390
|
teardownProcess(detail) {
|
|
1391
|
+
this.reapOrphansAfterUnexpectedDeath(detail);
|
|
1400
1392
|
this.mirror.killAll();
|
|
1401
1393
|
if (this.pidfileWritten !== void 0) {
|
|
1402
1394
|
removePidfile(this.pidfileWritten);
|
|
@@ -1409,8 +1401,46 @@ var EngineClient = class {
|
|
|
1409
1401
|
}
|
|
1410
1402
|
this.pending.clear();
|
|
1411
1403
|
this.runRoutes.clear();
|
|
1412
|
-
this.recordRoutes.clear();
|
|
1413
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) {
|
|
1414
1444
|
if (this.child !== void 0) {
|
|
1415
1445
|
const child = this.child;
|
|
1416
1446
|
this.child = void 0;
|
|
@@ -1420,8 +1450,30 @@ var EngineClient = class {
|
|
|
1420
1450
|
}
|
|
1421
1451
|
child.removeAllListeners();
|
|
1422
1452
|
}
|
|
1423
|
-
|
|
1424
|
-
|
|
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
|
+
});
|
|
1425
1477
|
}
|
|
1426
1478
|
}
|
|
1427
1479
|
// ── pidfile ──────────────────────────────────────────────────────────────
|
|
@@ -1436,7 +1488,7 @@ var EngineClient = class {
|
|
|
1436
1488
|
`);
|
|
1437
1489
|
return true;
|
|
1438
1490
|
} catch (err) {
|
|
1439
|
-
|
|
1491
|
+
logger10.debug(
|
|
1440
1492
|
`[engine-client:${this.engineId}] stdin write failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1441
1493
|
);
|
|
1442
1494
|
return false;
|
|
@@ -1459,8 +1511,19 @@ function truncate(text, max) {
|
|
|
1459
1511
|
}
|
|
1460
1512
|
|
|
1461
1513
|
// src/execution/engine/client/remote-engine.ts
|
|
1514
|
+
var import_node_os2 = require("os");
|
|
1515
|
+
var import_node_path4 = require("path");
|
|
1462
1516
|
var import_subagent_engine_sdk8 = require("@zhushanwen/subagent-engine-sdk");
|
|
1463
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
|
+
|
|
1464
1527
|
// src/execution/engine/common/capability-gate.ts
|
|
1465
1528
|
var import_subagent_engine_sdk7 = require("@zhushanwen/subagent-engine-sdk");
|
|
1466
1529
|
|
|
@@ -1475,7 +1538,7 @@ var EngineError = class extends Error {
|
|
|
1475
1538
|
this.code = code;
|
|
1476
1539
|
this.recovery = recovery;
|
|
1477
1540
|
}
|
|
1478
|
-
/** 结构化投影(
|
|
1541
|
+
/** 结构化投影(code/message 与 GUI 警告条共用形态)。 */
|
|
1479
1542
|
toStructured() {
|
|
1480
1543
|
return { code: this.code, message: this.message, recovery: this.recovery };
|
|
1481
1544
|
}
|
|
@@ -1624,22 +1687,6 @@ var RemoteEngine = class {
|
|
|
1624
1687
|
unregister();
|
|
1625
1688
|
}
|
|
1626
1689
|
}
|
|
1627
|
-
async interact(handle, action) {
|
|
1628
|
-
await this.opts.client.ensureConnected();
|
|
1629
|
-
const result = await this.opts.client.request("interact", {
|
|
1630
|
-
handle: handle.data,
|
|
1631
|
-
action
|
|
1632
|
-
});
|
|
1633
|
-
return result;
|
|
1634
|
-
}
|
|
1635
|
-
/**
|
|
1636
|
-
* [W3 v1.x] chat 轮次反向通道路由注册(recordId 键,EnginePort 可选面实现):
|
|
1637
|
-
* interact 续聊轮的 streamDelta / roundLifecycle 分发目标。薄委托 EngineClient
|
|
1638
|
-
* 的 recordRoutes(键分发见 reverse-router.ts)。
|
|
1639
|
-
*/
|
|
1640
|
-
registerChatRoundRoute(recordId, route) {
|
|
1641
|
-
return this.opts.client.registerRecordRoute(recordId, route);
|
|
1642
|
-
}
|
|
1643
1690
|
/** 协议 read(dataDir 必填——引擎数据根,构造注入)。 */
|
|
1644
1691
|
async read(handle) {
|
|
1645
1692
|
await this.opts.client.ensureConnected();
|
|
@@ -1671,6 +1718,15 @@ function isTransientRunFailure(err) {
|
|
|
1671
1718
|
if (!(err instanceof import_subagent_engine_sdk8.EngineSdkError)) return false;
|
|
1672
1719
|
return err.code === "engine_crashed" || err.code === "engine_request_timeout" || err.code === "engine_handshake_timeout";
|
|
1673
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
|
+
}
|
|
1674
1730
|
function buildRunParams(task, ctx, runId) {
|
|
1675
1731
|
const ctxModelRef = ctx.ctxModel ? `${ctx.ctxModel.provider}/${ctx.ctxModel.id}` : void 0;
|
|
1676
1732
|
return {
|
|
@@ -1683,9 +1739,16 @@ function buildRunParams(task, ctx, runId) {
|
|
|
1683
1739
|
schemaEnv: ctx.schemaEnv ?? task.schemaEnv,
|
|
1684
1740
|
ctxModel: ctxModelRef,
|
|
1685
1741
|
engineFallback: ctx.engineFallback,
|
|
1686
|
-
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()
|
|
1687
1750
|
},
|
|
1688
|
-
...ctx.
|
|
1751
|
+
...ctx.resume !== void 0 ? { resume: ctx.resume } : {}
|
|
1689
1752
|
};
|
|
1690
1753
|
}
|
|
1691
1754
|
function buildRunRouteHandlers(ctx) {
|
|
@@ -1693,9 +1756,7 @@ function buildRunRouteHandlers(ctx) {
|
|
|
1693
1756
|
onEvent: (event) => ctx.onEvent?.(event),
|
|
1694
1757
|
onStreamDelta: (delta) => ctx.stream?.onDelta(delta),
|
|
1695
1758
|
onPoolResolved: (poolKey) => ctx.onPoolResolved?.(poolKey),
|
|
1696
|
-
onHandleReady: (partial) => ctx.onHandleReady?.(partial)
|
|
1697
|
-
// [W3 v1.x] 首轮(run 会话形态)轮次生命周期帧(runId 键)→ 宿主消费口。
|
|
1698
|
-
onRoundLifecycle: (phase) => ctx.onRoundLifecycle?.(phase)
|
|
1759
|
+
onHandleReady: (partial) => ctx.onHandleReady?.(partial)
|
|
1699
1760
|
};
|
|
1700
1761
|
}
|
|
1701
1762
|
function wireAbortSignal(client, runId, ctx) {
|
|
@@ -1785,11 +1846,11 @@ function getHostUiRequestEndpoint() {
|
|
|
1785
1846
|
}
|
|
1786
1847
|
|
|
1787
1848
|
// src/execution/engine/engine-inspect-package.ts
|
|
1788
|
-
var
|
|
1849
|
+
var logger11 = getLogger("subagents");
|
|
1789
1850
|
function readEnginePackageJson(pkgDir) {
|
|
1790
1851
|
let raw;
|
|
1791
1852
|
try {
|
|
1792
|
-
raw = fs.readFileSync(
|
|
1853
|
+
raw = fs.readFileSync(path3.join(pkgDir, "package.json"), "utf8");
|
|
1793
1854
|
} catch (err) {
|
|
1794
1855
|
return { status: "skip", reason: `package.json unreadable: ${errorMessage(err)}` };
|
|
1795
1856
|
}
|
|
@@ -1864,12 +1925,12 @@ function parseOptionalDisplayFields(m, id) {
|
|
|
1864
1925
|
if (typeof rawDisplayName === "string" && rawDisplayName.trim() !== "") {
|
|
1865
1926
|
displayName = rawDisplayName;
|
|
1866
1927
|
} else {
|
|
1867
|
-
|
|
1928
|
+
logger11.warn(`[engine-discovery] engine '${id}': displayName must be a non-empty string \u2014 ignoring`);
|
|
1868
1929
|
}
|
|
1869
1930
|
}
|
|
1870
1931
|
const rawDescription = m["description"];
|
|
1871
1932
|
if (rawDescription !== void 0 && typeof rawDescription !== "string") {
|
|
1872
|
-
|
|
1933
|
+
logger11.warn(`[engine-discovery] engine '${id}': description must be a string \u2014 ignoring`);
|
|
1873
1934
|
}
|
|
1874
1935
|
return displayName;
|
|
1875
1936
|
}
|
|
@@ -1957,19 +2018,19 @@ function errorMessage(err) {
|
|
|
1957
2018
|
|
|
1958
2019
|
// src/execution/engine/engine-discovery-roots.ts
|
|
1959
2020
|
var fs2 = __toESM(require("fs"), 1);
|
|
1960
|
-
var
|
|
1961
|
-
var
|
|
2021
|
+
var path4 = __toESM(require("path"), 1);
|
|
2022
|
+
var logger12 = getLogger("subagents");
|
|
1962
2023
|
var ENGINE_ROOTS_ENV = "XYZ_AGENT_ENGINE_ROOTS";
|
|
1963
2024
|
function parseEngineRootsEnv(env) {
|
|
1964
2025
|
const raw = env[ENGINE_ROOTS_ENV];
|
|
1965
2026
|
if (raw === void 0 || raw.trim() === "") return [];
|
|
1966
2027
|
const out = [];
|
|
1967
2028
|
const seen = /* @__PURE__ */ new Set();
|
|
1968
|
-
for (const part of raw.split(
|
|
2029
|
+
for (const part of raw.split(path4.delimiter)) {
|
|
1969
2030
|
const dir = part.trim();
|
|
1970
2031
|
if (dir === "") continue;
|
|
1971
|
-
if (!
|
|
1972
|
-
|
|
2032
|
+
if (!path4.isAbsolute(dir)) {
|
|
2033
|
+
logger12.warn(
|
|
1973
2034
|
`[engine-discovery] ${ENGINE_ROOTS_ENV} entry '${dir}' is not an absolute path \u2014 dropping entry`
|
|
1974
2035
|
);
|
|
1975
2036
|
continue;
|
|
@@ -1983,11 +2044,11 @@ function parseEngineRootsEnv(env) {
|
|
|
1983
2044
|
function deriveNodeModuleRoots(argvEntry = process.argv[1]) {
|
|
1984
2045
|
if (!argvEntry || argvEntry.trim() === "") return [];
|
|
1985
2046
|
const roots = [];
|
|
1986
|
-
let dir =
|
|
2047
|
+
let dir = path4.dirname(path4.resolve(argvEntry));
|
|
1987
2048
|
while (true) {
|
|
1988
|
-
const nm =
|
|
2049
|
+
const nm = path4.join(dir, "node_modules");
|
|
1989
2050
|
if (isDirectory(nm)) roots.push(nm);
|
|
1990
|
-
const parent =
|
|
2051
|
+
const parent = path4.dirname(dir);
|
|
1991
2052
|
if (parent === dir) break;
|
|
1992
2053
|
dir = parent;
|
|
1993
2054
|
}
|
|
@@ -2002,8 +2063,8 @@ function scanEngineRoot(root, visit) {
|
|
|
2002
2063
|
}
|
|
2003
2064
|
for (const item of entries) {
|
|
2004
2065
|
if (!item.isDirectory()) continue;
|
|
2005
|
-
const dir =
|
|
2006
|
-
if (isFile(
|
|
2066
|
+
const dir = path4.join(root, item.name);
|
|
2067
|
+
if (isFile(path4.join(dir, "package.json"))) {
|
|
2007
2068
|
visit(dir);
|
|
2008
2069
|
continue;
|
|
2009
2070
|
}
|
|
@@ -2015,8 +2076,8 @@ function scanEngineRoot(root, visit) {
|
|
|
2015
2076
|
}
|
|
2016
2077
|
for (const child of sub) {
|
|
2017
2078
|
if (!child.isDirectory()) continue;
|
|
2018
|
-
const subDir =
|
|
2019
|
-
if (isFile(
|
|
2079
|
+
const subDir = path4.join(dir, child.name);
|
|
2080
|
+
if (isFile(path4.join(subDir, "package.json"))) visit(subDir);
|
|
2020
2081
|
}
|
|
2021
2082
|
}
|
|
2022
2083
|
}
|
|
@@ -2037,10 +2098,10 @@ function isFile(p) {
|
|
|
2037
2098
|
|
|
2038
2099
|
// src/execution/engine/config.ts
|
|
2039
2100
|
var fs3 = __toESM(require("fs"), 1);
|
|
2040
|
-
var
|
|
2041
|
-
var
|
|
2101
|
+
var path5 = __toESM(require("path"), 1);
|
|
2102
|
+
var logger13 = getLogger("subagents");
|
|
2042
2103
|
function readExplicitEngines(agentDir) {
|
|
2043
|
-
const configPath =
|
|
2104
|
+
const configPath = path5.join(agentDir, "subagents", "config.json");
|
|
2044
2105
|
let parsed;
|
|
2045
2106
|
try {
|
|
2046
2107
|
parsed = JSON.parse(fs3.readFileSync(configPath, "utf8"));
|
|
@@ -2051,7 +2112,7 @@ function readExplicitEngines(agentDir) {
|
|
|
2051
2112
|
const engines = parsed["engines"];
|
|
2052
2113
|
if (typeof engines !== "object" || engines === null || Array.isArray(engines)) {
|
|
2053
2114
|
if (engines !== void 0) {
|
|
2054
|
-
|
|
2115
|
+
logger13.warn(
|
|
2055
2116
|
`[engine-discovery] config.json engines section must be an object keyed by engine id, got ${jsonKindOf(engines)} \u2014 ignoring L3 explicit engines`
|
|
2056
2117
|
);
|
|
2057
2118
|
}
|
|
@@ -2079,7 +2140,7 @@ function sanitizeExplicitEntry(id, raw) {
|
|
|
2079
2140
|
}
|
|
2080
2141
|
function requireEntryObject(id, raw) {
|
|
2081
2142
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
2082
|
-
|
|
2143
|
+
logger13.warn(
|
|
2083
2144
|
`[engine-discovery] config.json engines.${id} must be an object, got ${jsonKindOf(raw)} \u2014 skipping entry`
|
|
2084
2145
|
);
|
|
2085
2146
|
return void 0;
|
|
@@ -2088,7 +2149,7 @@ function requireEntryObject(id, raw) {
|
|
|
2088
2149
|
}
|
|
2089
2150
|
function requireCommand(id, command) {
|
|
2090
2151
|
if (typeof command !== "string" || command.trim() === "") {
|
|
2091
|
-
|
|
2152
|
+
logger13.warn(
|
|
2092
2153
|
`[engine-discovery] config.json engines.${id}.command is required (non-empty string) \u2014 skipping entry`
|
|
2093
2154
|
);
|
|
2094
2155
|
return void 0;
|
|
@@ -2097,7 +2158,7 @@ function requireCommand(id, command) {
|
|
|
2097
2158
|
}
|
|
2098
2159
|
function normalizeArgs(id, args) {
|
|
2099
2160
|
if (args !== void 0 && !isStringArray(args)) {
|
|
2100
|
-
|
|
2161
|
+
logger13.warn(
|
|
2101
2162
|
`[engine-discovery] config.json engines.${id}.args must be a string array \u2014 ignoring args`
|
|
2102
2163
|
);
|
|
2103
2164
|
}
|
|
@@ -2105,7 +2166,7 @@ function normalizeArgs(id, args) {
|
|
|
2105
2166
|
}
|
|
2106
2167
|
function normalizeConfig(id, config) {
|
|
2107
2168
|
if (config !== void 0 && !isStringRecord(config)) {
|
|
2108
|
-
|
|
2169
|
+
logger13.warn(
|
|
2109
2170
|
`[engine-discovery] config.json engines.${id}.config must be a Record<string, string> \u2014 ignoring config`
|
|
2110
2171
|
);
|
|
2111
2172
|
}
|
|
@@ -2113,7 +2174,7 @@ function normalizeConfig(id, config) {
|
|
|
2113
2174
|
}
|
|
2114
2175
|
function normalizeCwd(id, cwd) {
|
|
2115
2176
|
if (cwd !== void 0 && typeof cwd !== "string") {
|
|
2116
|
-
|
|
2177
|
+
logger13.warn(
|
|
2117
2178
|
`[engine-discovery] config.json engines.${id}.cwd must be a string \u2014 ignoring cwd`
|
|
2118
2179
|
);
|
|
2119
2180
|
}
|
|
@@ -2133,7 +2194,7 @@ function jsonKindOf(value) {
|
|
|
2133
2194
|
}
|
|
2134
2195
|
|
|
2135
2196
|
// src/execution/engine/registry.ts
|
|
2136
|
-
var
|
|
2197
|
+
var logger14 = getLogger("subagents");
|
|
2137
2198
|
var ENGINE_REGISTRY_SLOT_KEY = /* @__PURE__ */ Symbol.for("@zhushanwen/pi-subagent-workflow.engineRegistry");
|
|
2138
2199
|
function getRegistrySlot() {
|
|
2139
2200
|
let slot = Reflect.get(globalThis, ENGINE_REGISTRY_SLOT_KEY);
|
|
@@ -2147,12 +2208,12 @@ function triggerEngineDispose(engine, source) {
|
|
|
2147
2208
|
if (typeof engine.dispose !== "function") return;
|
|
2148
2209
|
try {
|
|
2149
2210
|
engine.dispose().then(void 0, (err) => {
|
|
2150
|
-
|
|
2211
|
+
logger14.warn(
|
|
2151
2212
|
`[engine-registry] engine '${engine.id}' dispose rejected (${source}, best-effort continue): ${err instanceof Error ? err.message : String(err)}`
|
|
2152
2213
|
);
|
|
2153
2214
|
});
|
|
2154
2215
|
} catch (err) {
|
|
2155
|
-
|
|
2216
|
+
logger14.warn(
|
|
2156
2217
|
`[engine-registry] engine '${engine.id}' dispose threw synchronously (${source}, best-effort continue): ${err instanceof Error ? err.message : String(err)}`
|
|
2157
2218
|
);
|
|
2158
2219
|
}
|
|
@@ -2169,14 +2230,14 @@ function hasEngine(id) {
|
|
|
2169
2230
|
}
|
|
2170
2231
|
|
|
2171
2232
|
// src/execution/engine/engine-discovery-scan.ts
|
|
2172
|
-
var
|
|
2233
|
+
var logger15 = getLogger("subagents");
|
|
2173
2234
|
function scanEngines(opts) {
|
|
2174
2235
|
const env = opts.env ?? process.env;
|
|
2175
2236
|
const result = { discovered: [], skipped: [], unusable: [] };
|
|
2176
2237
|
const present = (entry) => {
|
|
2177
2238
|
const idx = result.discovered.findIndex((d) => d.id === entry.id);
|
|
2178
2239
|
if (idx >= 0) {
|
|
2179
|
-
|
|
2240
|
+
logger15.debug(
|
|
2180
2241
|
`[engine-discovery] engine id '${entry.id}' from '${entry.source}' overrides earlier discovery from '${result.discovered[idx].source}' \u2014 same-id override`
|
|
2181
2242
|
);
|
|
2182
2243
|
result.discovered[idx] = entry;
|
|
@@ -2188,13 +2249,13 @@ function scanEngines(opts) {
|
|
|
2188
2249
|
const inspection = inspectEnginePackage(pkgDir, source, opts.hostKind, env);
|
|
2189
2250
|
if (inspection.status === "skip") {
|
|
2190
2251
|
if (!inspection.reason.includes("not an engine package")) {
|
|
2191
|
-
|
|
2252
|
+
logger15.warn(`[engine-discovery] skipping ${pkgDir} (${source}): ${inspection.reason}`);
|
|
2192
2253
|
result.skipped.push({ pkgDir, reason: inspection.reason });
|
|
2193
2254
|
}
|
|
2194
2255
|
return;
|
|
2195
2256
|
}
|
|
2196
2257
|
if (inspection.status === "unusable") {
|
|
2197
|
-
|
|
2258
|
+
logger15.warn(`[engine-discovery] engine unavailable: ${inspection.reason}`);
|
|
2198
2259
|
result.unusable.push({ pkgDir, id: inspection.id, reason: inspection.reason });
|
|
2199
2260
|
return;
|
|
2200
2261
|
}
|
|
@@ -2219,7 +2280,7 @@ function scanEngines(opts) {
|
|
|
2219
2280
|
const command = resolveExplicitCommand(entry.command, env);
|
|
2220
2281
|
if (command === void 0) {
|
|
2221
2282
|
const reason = `engine '${id}' (config.json engines.${id}): command '${entry.command}' not found or not executable`;
|
|
2222
|
-
|
|
2283
|
+
logger15.warn(`[engine-discovery] ${reason} \u2014 not registering`);
|
|
2223
2284
|
result.unusable.push({ pkgDir: `config.json engines.${id}`, id, reason });
|
|
2224
2285
|
continue;
|
|
2225
2286
|
}
|
|
@@ -2236,7 +2297,7 @@ function discoverAndRegisterEngines(opts) {
|
|
|
2236
2297
|
registerEngineDescriptor(entry.id, entry.descriptor);
|
|
2237
2298
|
loadedDiscoveryIdSet.add(entry.id);
|
|
2238
2299
|
if (existed) {
|
|
2239
|
-
|
|
2300
|
+
logger15.debug(
|
|
2240
2301
|
`[engine-discovery] engine '${entry.id}' descriptor overwritten in registry (source '${entry.source}') \u2014 same-id override`
|
|
2241
2302
|
);
|
|
2242
2303
|
}
|
|
@@ -2253,20 +2314,20 @@ function ensureEngineDiscovered(id, opts) {
|
|
|
2253
2314
|
}
|
|
2254
2315
|
function resolveExplicitCommand(command, env) {
|
|
2255
2316
|
if (command.includes("/") || command.includes("\\")) {
|
|
2256
|
-
const p =
|
|
2317
|
+
const p = path6.isAbsolute(command) ? command : path6.resolve(command);
|
|
2257
2318
|
return canExecute(p) ? p : void 0;
|
|
2258
2319
|
}
|
|
2259
2320
|
const pathVar = env["PATH"] ?? env["Path"] ?? "";
|
|
2260
|
-
for (const dir of pathVar.split(
|
|
2321
|
+
for (const dir of pathVar.split(path6.delimiter)) {
|
|
2261
2322
|
if (dir === "") continue;
|
|
2262
|
-
const p =
|
|
2323
|
+
const p = path6.join(dir, command);
|
|
2263
2324
|
if (canExecute(p)) return p;
|
|
2264
2325
|
}
|
|
2265
2326
|
return void 0;
|
|
2266
2327
|
}
|
|
2267
2328
|
function buildExplicitDescriptor(id, command, entry, opts) {
|
|
2268
2329
|
const env = opts.env ?? process.env;
|
|
2269
|
-
|
|
2330
|
+
logger15.debug(
|
|
2270
2331
|
`[engine-discovery] engine '${id}' registered from config.json engines section with conservative capabilities (no manifest)`
|
|
2271
2332
|
);
|
|
2272
2333
|
const caps = { ...CONSERVATIVE_CAPABILITIES };
|