@proagentstore/cli 0.4.101 → 0.4.102
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.
|
@@ -424,8 +424,9 @@ export class HeadlessSession {
|
|
|
424
424
|
}
|
|
425
425
|
if (this.procAlive)
|
|
426
426
|
return;
|
|
427
|
-
// A new process has read none of what the old one was sent.
|
|
427
|
+
// A new process has read none of what the old one was sent, and owns none of its half-read line.
|
|
428
428
|
this.pendingInputs = [];
|
|
429
|
+
this.buf = "";
|
|
429
430
|
const args = this.adapter.buildLaunchArgs(this.cmdArgs, this.claudeSessionId);
|
|
430
431
|
const proc = spawn(this.cmdBin, args, {
|
|
431
432
|
cwd: this.config.workDir,
|
|
@@ -446,8 +447,17 @@ export class HeadlessSession {
|
|
|
446
447
|
});
|
|
447
448
|
// Swallow EPIPE when writing to a process that just exited (one-shot turn).
|
|
448
449
|
proc.stdin?.on("error", () => { });
|
|
449
|
-
|
|
450
|
+
// FENCED to this process (#1019). A stopped process can outlive its replacement — it may ignore the
|
|
451
|
+
// signal, or flush buffered output on its way out — and its late `result` would otherwise be read as
|
|
452
|
+
// the NEW process's turn end: crediting the new turn's input as consumed, filing its report and
|
|
453
|
+
// marking it idle while it is still working.
|
|
454
|
+
proc.stdout?.on("data", (d) => {
|
|
455
|
+
if (this.proc === proc)
|
|
456
|
+
this.onStdout(d.toString("utf8"));
|
|
457
|
+
});
|
|
450
458
|
proc.stderr?.on("data", (d) => {
|
|
459
|
+
if (this.proc !== proc)
|
|
460
|
+
return;
|
|
451
461
|
this.lastOutputAt = Date.now();
|
|
452
462
|
this.sawOutputSinceInput = true;
|
|
453
463
|
const text = d.toString("utf8").trim();
|
|
@@ -591,6 +601,7 @@ export class HeadlessSession {
|
|
|
591
601
|
}
|
|
592
602
|
}
|
|
593
603
|
this.proc = proc;
|
|
604
|
+
this.buf = ""; // the replaced turn's half-written line is not this turn's (#1019)
|
|
594
605
|
// Without an 'error' listener a spawn failure (binary not on PATH) is an uncaught
|
|
595
606
|
// exception that takes the whole runner down, not just this session.
|
|
596
607
|
proc.on("error", (err) => {
|
|
@@ -604,8 +615,15 @@ export class HeadlessSession {
|
|
|
604
615
|
// real reason instead of retrying a binary that does not exist.
|
|
605
616
|
this.spawnFailed = true;
|
|
606
617
|
});
|
|
607
|
-
|
|
608
|
-
proc.
|
|
618
|
+
// Fenced like the persistent process (#1019): a turn this one replaced must not speak for it.
|
|
619
|
+
proc.stdout?.on("data", (d) => {
|
|
620
|
+
if (this.proc === proc)
|
|
621
|
+
this.onStdout(d.toString());
|
|
622
|
+
});
|
|
623
|
+
proc.stderr?.on("data", (d) => {
|
|
624
|
+
if (this.proc === proc)
|
|
625
|
+
this.onStdout(d.toString());
|
|
626
|
+
});
|
|
609
627
|
// The enforced ceiling (#391). `unref` so a pending timer can never keep the runner's
|
|
610
628
|
// event loop alive past its own shutdown.
|
|
611
629
|
const maxTurnMs = this.config.maxTurnMs ?? MAX_ONE_SHOT_TURN_MS;
|