@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
- proc.stdout?.on("data", (d) => this.onStdout(d.toString("utf8")));
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
- proc.stdout?.on("data", (d) => this.onStdout(d.toString()));
608
- proc.stderr?.on("data", (d) => this.onStdout(d.toString()));
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proagentstore/cli",
3
- "version": "0.4.101",
3
+ "version": "0.4.102",
4
4
  "description": "CLI for creating, publishing, and running ProAgentStore agents",
5
5
  "license": "MIT",
6
6
  "type": "module",