@ouro.bot/cli 0.1.0-alpha.728 → 0.1.0-alpha.729

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/changelog.json CHANGED
@@ -1,6 +1,13 @@
1
1
  {
2
2
  "_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
3
3
  "versions": [
4
+ {
5
+ "version": "0.1.0-alpha.729",
6
+ "changes": [
7
+ "Respawn long-lived auto-start workers after unsolicited clean exits so managed BlueBubbles listeners recover without a full daemon restart.",
8
+ "Bound repeated clean-exit loops with cause-neutral diagnostics and keep explicit stop, whole-daemon stop, and manual restart free of stale respawns."
9
+ ]
10
+ },
4
11
  {
5
12
  "version": "0.1.0-alpha.728",
6
13
  "changes": [
@@ -672,26 +672,29 @@ class DaemonProcessManager {
672
672
  state.snapshot.errorReason = null;
673
673
  state.snapshot.fixHint = null;
674
674
  }
675
- const crashed = !state.stopRequested && code !== 0;
675
+ const unexpectedExit = !state.stopRequested;
676
+ const crashed = unexpectedExit && code !== 0;
676
677
  const now = this.currentTimeMs();
677
678
  const startedAt = state.snapshot.startedAt ? Date.parse(state.snapshot.startedAt) : now;
678
679
  const runDuration = Math.max(0, now - startedAt);
679
680
  (0, runtime_1.emitNervesEvent)({
680
- level: crashed ? "warn" : "info",
681
+ level: unexpectedExit ? "warn" : "info",
681
682
  component: "daemon",
682
683
  event: "daemon.agent_exit",
683
684
  message: "managed agent process exited",
684
- meta: { agent: state.config.name, code, signal, crashed, runDurationMs: runDuration },
685
+ meta: { agent: state.config.name, code, signal, crashed, unexpectedExit, runDurationMs: runDuration },
685
686
  });
686
- if (!crashed) {
687
+ if (runDuration >= this.stabilityThresholdMs) {
688
+ state.snapshot.backoffMs = this.initialBackoffMs;
689
+ }
690
+ if (!unexpectedExit) {
687
691
  state.snapshot.status = "stopped";
688
- if (runDuration >= this.stabilityThresholdMs) {
689
- state.snapshot.backoffMs = this.initialBackoffMs;
690
- }
691
692
  this.notifySnapshotChange(state.snapshot);
692
693
  return;
693
694
  }
694
- state.snapshot.lastCrashAt = new Date(now).toISOString();
695
+ if (crashed) {
696
+ state.snapshot.lastCrashAt = new Date(now).toISOString();
697
+ }
695
698
  if (state.config.autoStart === false) {
696
699
  state.snapshot.status = "stopped";
697
700
  state.snapshot.errorReason = "non-autostart worker exited unexpectedly; not respawning";
@@ -699,46 +702,45 @@ class DaemonProcessManager {
699
702
  this.notifySnapshotChange(state.snapshot);
700
703
  return;
701
704
  }
702
- // Fast-crash detection: if the agent dies within 5 seconds of starting, it's likely
703
- // a configuration issue (missing credentials, bad provider, etc.) not a transient failure.
704
- // After 3 consecutive fast crashes, stop retrying and mark as config-failed.
705
+ // Fast-exit detection: repeated early exits, including clean exit-code-0
706
+ // terminations, indicate a startup/configuration or lifecycle fault rather
707
+ // than a stable long-lived worker. Bound them without claiming a crash.
705
708
  const FAST_CRASH_THRESHOLD_MS = 5000;
706
709
  const FAST_CRASH_MAX = 3;
707
710
  if (runDuration < FAST_CRASH_THRESHOLD_MS) {
708
711
  state.fastCrashCount = state.fastCrashCount + 1;
709
712
  if (state.fastCrashCount >= FAST_CRASH_MAX) {
710
713
  state.snapshot.status = "crashed";
711
- // Capture the fast-crash diagnosis on the snapshot so it surfaces
712
- // via the pulse. The error message is prescriptive: it tells the
713
- // user (and their sibling agents) exactly what to do.
714
- state.snapshot.errorReason = `agent crashed ${FAST_CRASH_MAX} times within ${FAST_CRASH_THRESHOLD_MS}ms of startup — likely a configuration issue (missing credentials, bad provider).`;
715
- state.snapshot.fixHint = `Fix the config and run \`ouro up\` to restart, or check daemon logs for the underlying error.`;
714
+ // Capture the fast-exit diagnosis on the snapshot so it surfaces via
715
+ // the pulse with a prescriptive, cause-neutral repair path.
716
+ state.snapshot.errorReason = `agent exited unexpectedly ${FAST_CRASH_MAX} times within ${FAST_CRASH_THRESHOLD_MS}ms of startup — likely a startup/configuration or lifecycle issue.`;
717
+ state.snapshot.fixHint = `Inspect daemon logs and lifecycle/configuration, then run \`ouro up\` to restart.`;
716
718
  (0, runtime_1.emitNervesEvent)({
717
719
  level: "error",
718
720
  component: "daemon",
719
721
  event: "daemon.agent_config_failure",
720
- message: `agent crashed ${FAST_CRASH_MAX} times within ${FAST_CRASH_THRESHOLD_MS}ms of startup — likely a configuration issue (missing credentials, bad provider). Not retrying. Fix the config and run \`ouro up\` to restart.`,
721
- meta: { agent: state.config.name, fastCrashCount: state.fastCrashCount, avgRunDurationMs: runDuration },
722
+ message: `agent exited unexpectedly ${FAST_CRASH_MAX} times within ${FAST_CRASH_THRESHOLD_MS}ms of startup. Not retrying. Inspect lifecycle/configuration and run \`ouro up\` to restart.`,
723
+ meta: { agent: state.config.name, fastUnexpectedExitCount: state.fastCrashCount, avgRunDurationMs: runDuration },
722
724
  });
723
725
  this.notifySnapshotChange(state.snapshot);
724
726
  return; // Don't schedule cooldown recovery — this needs human/agent intervention
725
727
  }
726
728
  }
727
729
  else {
728
- // Reset fast-crash counter on a stable run
730
+ // Reset the fast unexpected-exit counter on a stable run.
729
731
  state.fastCrashCount = 0;
730
732
  }
731
733
  state.crashTimestamps = state.crashTimestamps.filter((crashTs) => crashTs >= startOfHour(now));
732
734
  state.crashTimestamps.push(now);
733
735
  if (state.crashTimestamps.length > this.maxRestartsPerHour) {
734
736
  state.snapshot.status = "crashed";
735
- state.snapshot.errorReason = `agent exceeded restart limit (${this.maxRestartsPerHour}/hr) — entering cooldown`;
736
- state.snapshot.fixHint = "investigate why the agent keeps crashing; cooldown will retry shortly";
737
+ state.snapshot.errorReason = `agent exceeded unexpected-exit restart limit (${this.maxRestartsPerHour}/hr) — entering cooldown`;
738
+ state.snapshot.fixHint = "investigate why the agent keeps exiting unexpectedly; cooldown will retry shortly";
737
739
  (0, runtime_1.emitNervesEvent)({
738
740
  level: "error",
739
741
  component: "daemon",
740
742
  event: "daemon.agent_restart_exhausted",
741
- message: "managed agent exceeded restart limit and is marked crashed",
743
+ message: "managed agent exceeded unexpected-exit restart limit and is marked crashed",
742
744
  meta: { agent: state.config.name, maxRestartsPerHour: this.maxRestartsPerHour },
743
745
  });
744
746
  this.notifySnapshotChange(state.snapshot);
@@ -488,7 +488,7 @@ function readBlueBubblesRuntimeFacts(agent, bundlesRoot, snapshot) {
488
488
  ...proofFacts,
489
489
  lastFailure: "listener process is not running",
490
490
  failureLayer: "listener",
491
- recoveryAction: "daemon health monitor will restart the BlueBubbles listener when its probe fails",
491
+ recoveryAction: "daemon process manager will restart the BlueBubbles listener after an unsolicited exit",
492
492
  };
493
493
  }
494
494
  if (state.upstreamStatus === "error") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.728",
3
+ "version": "0.1.0-alpha.729",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },