@ouro.bot/cli 0.1.0-alpha.3 → 0.1.0-alpha.300
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/README.md +188 -190
- package/SerpentGuide.ouro/agent.json +82 -0
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +6 -3
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +2 -2
- package/SerpentGuide.ouro/psyche/identities/python.md +31 -0
- package/assets/ouroboros.png +0 -0
- package/changelog.json +1888 -0
- package/dist/arc/attention-types.js +8 -0
- package/dist/arc/cares.js +140 -0
- package/dist/arc/episodes.js +117 -0
- package/dist/arc/intentions.js +133 -0
- package/dist/arc/json-store.js +117 -0
- package/dist/arc/obligations.js +237 -0
- package/dist/arc/packets.js +203 -0
- package/dist/arc/presence.js +185 -0
- package/dist/heart/active-work.js +832 -0
- package/dist/heart/agent-entry.js +37 -2
- package/dist/heart/attachments/image-normalize.js +194 -0
- package/dist/heart/attachments/materialize.js +230 -0
- package/dist/heart/attachments/render.js +29 -0
- package/dist/heart/attachments/store.js +103 -0
- package/dist/heart/attachments/types.js +138 -0
- package/dist/heart/auth/auth-flow.js +456 -0
- package/dist/heart/bridges/manager.js +358 -0
- package/dist/heart/bridges/state-machine.js +135 -0
- package/dist/heart/bridges/store.js +123 -0
- package/dist/heart/bundle-state.js +168 -0
- package/dist/heart/commitments.js +111 -0
- package/dist/heart/config-registry.js +304 -0
- package/dist/heart/config.js +123 -28
- package/dist/heart/core.js +738 -191
- package/dist/heart/cross-chat-delivery.js +131 -0
- package/dist/heart/daemon/agent-config-check.js +149 -0
- package/dist/heart/daemon/agent-discovery.js +157 -0
- package/dist/heart/daemon/agent-service.js +360 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/cli-defaults.js +574 -0
- package/dist/heart/daemon/cli-exec.js +1945 -0
- package/dist/heart/daemon/cli-parse.js +808 -0
- package/dist/heart/daemon/cli-render.js +503 -0
- package/dist/heart/daemon/cli-types.js +8 -0
- package/dist/heart/daemon/daemon-cli.js +29 -673
- package/dist/heart/daemon/daemon-entry.js +259 -8
- package/dist/heart/daemon/daemon-health.js +136 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +231 -0
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +745 -18
- package/dist/heart/daemon/health-monitor.js +38 -0
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +206 -0
- package/dist/heart/daemon/inner-status.js +89 -0
- package/dist/heart/daemon/launchd.js +171 -0
- package/dist/heart/daemon/log-tailer.js +82 -12
- package/dist/heart/daemon/logs-prune.js +105 -0
- package/dist/heart/daemon/message-router.js +17 -8
- package/dist/heart/daemon/os-cron-deps.js +134 -0
- package/dist/heart/daemon/ouro-bot-entry.js +1 -1
- package/dist/heart/daemon/process-manager.js +181 -1
- package/dist/heart/daemon/pulse.js +463 -0
- package/dist/heart/daemon/run-hooks.js +39 -0
- package/dist/heart/daemon/runtime-logging.js +70 -15
- package/dist/heart/daemon/runtime-metadata.js +219 -0
- package/dist/heart/daemon/runtime-mode.js +67 -0
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sense-manager.js +335 -0
- package/dist/heart/daemon/session-id-resolver.js +131 -0
- package/dist/heart/daemon/skill-management-installer.js +94 -0
- package/dist/heart/daemon/socket-client.js +307 -0
- package/dist/heart/daemon/task-scheduler.js +3 -25
- package/dist/heart/daemon/thoughts.js +510 -0
- package/dist/heart/delegation.js +62 -0
- package/dist/heart/habits/habit-migration.js +181 -0
- package/dist/heart/habits/habit-parser.js +140 -0
- package/dist/heart/habits/habit-scheduler.js +371 -0
- package/dist/heart/hatch/hatch-animation.js +35 -0
- package/dist/heart/{daemon → hatch}/hatch-flow.js +31 -130
- package/dist/heart/{daemon → hatch}/hatch-specialist.js +7 -2
- package/dist/heart/hatch/specialist-orchestrator.js +129 -0
- package/dist/heart/hatch/specialist-prompt.js +101 -0
- package/dist/heart/hatch/specialist-tools.js +283 -0
- package/dist/heart/identity.js +241 -54
- package/dist/heart/kicks.js +2 -20
- package/dist/heart/mcp/mcp-server.js +653 -0
- package/dist/heart/migrate-config.js +127 -0
- package/dist/heart/model-capabilities.js +59 -0
- package/dist/heart/outlook/outlook-http.js +439 -0
- package/dist/heart/outlook/outlook-read.js +1618 -0
- package/dist/heart/outlook/outlook-render.js +1032 -0
- package/dist/heart/outlook/outlook-types.js +7 -0
- package/dist/heart/outlook/outlook-view.js +194 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/provider-failover.js +88 -0
- package/dist/heart/provider-ping.js +162 -0
- package/dist/heart/providers/anthropic-token.js +163 -0
- package/dist/heart/providers/anthropic.js +181 -41
- package/dist/heart/providers/azure.js +98 -11
- package/dist/heart/providers/error-classification.js +63 -0
- package/dist/heart/providers/github-copilot.js +136 -0
- package/dist/heart/providers/minimax-vlm.js +189 -0
- package/dist/heart/providers/minimax.js +23 -5
- package/dist/heart/providers/openai-codex.js +33 -22
- package/dist/heart/sense-truth.js +61 -0
- package/dist/heart/session-activity.js +172 -0
- package/dist/heart/session-recall.js +216 -0
- package/dist/heart/start-of-turn-packet.js +339 -0
- package/dist/heart/streaming.js +120 -33
- package/dist/heart/sync.js +332 -0
- package/dist/heart/target-resolution.js +127 -0
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/tool-activity-callbacks.js +36 -0
- package/dist/heart/tool-description.js +135 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-context.js +358 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/heart/versioning/ouro-bot-global-installer.js +128 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +5 -4
- package/dist/heart/versioning/ouro-path-installer.js +221 -0
- package/dist/heart/{daemon → versioning}/ouro-uti.js +11 -2
- package/dist/heart/versioning/ouro-version-manager.js +295 -0
- package/dist/heart/versioning/staged-restart.js +146 -0
- package/dist/heart/versioning/update-checker.js +113 -0
- package/dist/heart/versioning/update-hooks.js +142 -0
- package/dist/heart/versioning/wrapper-publish-guard.js +86 -0
- package/dist/mind/associative-recall.js +135 -66
- package/dist/mind/bundle-manifest.js +77 -1
- package/dist/mind/context.js +125 -14
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +84 -96
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/first-impressions.js +16 -2
- package/dist/mind/friends/channel.js +64 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/resolver.js +37 -0
- package/dist/mind/friends/store-file.js +58 -3
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +9 -1
- package/dist/mind/journal-index.js +161 -0
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/pending.js +76 -9
- package/dist/mind/phrases.js +1 -0
- package/dist/mind/prompt.js +1011 -107
- package/dist/mind/scrutiny.js +173 -0
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/cli-logging.js +22 -3
- package/dist/nerves/coverage/audit.js +1 -1
- package/dist/nerves/coverage/file-completeness.js +59 -5
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +224 -4
- package/dist/nerves/observation.js +20 -0
- package/dist/nerves/redact.js +79 -0
- package/dist/nerves/runtime.js +5 -1
- package/dist/outlook-ui/assets/index-Ck8agNeO.js +61 -0
- package/dist/outlook-ui/assets/index-LwChZTgL.css +1 -0
- package/dist/outlook-ui/index.html +15 -0
- package/dist/repertoire/ado-client.js +17 -56
- package/dist/repertoire/ado-semantic.js +11 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +319 -0
- package/dist/repertoire/bundle-templates.js +72 -0
- package/dist/repertoire/bw-installer.js +79 -0
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/context-pack.js +330 -0
- package/dist/repertoire/coding/feedback.js +301 -0
- package/dist/repertoire/coding/index.js +4 -1
- package/dist/repertoire/coding/manager.js +220 -13
- package/dist/repertoire/coding/spawner.js +58 -12
- package/dist/repertoire/coding/tools.js +209 -7
- package/dist/repertoire/commerce-errors.js +109 -0
- package/dist/repertoire/commerce-self-test.js +156 -0
- package/dist/repertoire/credential-access.js +527 -0
- package/dist/repertoire/data/ado-endpoints.json +188 -0
- package/dist/repertoire/duffel-client.js +185 -0
- package/dist/repertoire/github-client.js +14 -55
- package/dist/repertoire/graph-client.js +11 -52
- package/dist/repertoire/guardrails.js +375 -0
- package/dist/repertoire/mcp-client.js +255 -0
- package/dist/repertoire/mcp-manager.js +305 -0
- package/dist/repertoire/mcp-tools.js +63 -0
- package/dist/repertoire/shell-sessions.js +133 -0
- package/dist/repertoire/skills.js +14 -23
- package/dist/repertoire/stripe-client.js +131 -0
- package/dist/repertoire/tasks/board.js +43 -5
- package/dist/repertoire/tasks/fix.js +182 -0
- package/dist/repertoire/tasks/index.js +28 -10
- package/dist/repertoire/tasks/lifecycle.js +2 -2
- package/dist/repertoire/tasks/parser.js +3 -2
- package/dist/repertoire/tasks/scanner.js +194 -37
- package/dist/repertoire/tasks/transitions.js +11 -28
- package/dist/repertoire/tool-results.js +29 -0
- package/dist/repertoire/tools-attachments.js +316 -0
- package/dist/repertoire/tools-base.js +45 -707
- package/dist/repertoire/tools-bluebubbles.js +94 -0
- package/dist/repertoire/tools-bridge.js +141 -0
- package/dist/repertoire/tools-bundle.js +984 -0
- package/dist/repertoire/tools-config.js +185 -0
- package/dist/repertoire/tools-continuity.js +248 -0
- package/dist/repertoire/tools-credential.js +182 -0
- package/dist/repertoire/tools-files.js +342 -0
- package/dist/repertoire/tools-flight.js +224 -0
- package/dist/repertoire/tools-flow.js +105 -0
- package/dist/repertoire/tools-github.js +1 -7
- package/dist/repertoire/tools-memory.js +374 -0
- package/dist/repertoire/tools-session.js +739 -0
- package/dist/repertoire/tools-shell.js +120 -0
- package/dist/repertoire/tools-stripe.js +180 -0
- package/dist/repertoire/tools-surface.js +243 -0
- package/dist/repertoire/tools-teams.js +64 -61
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-user-profile.js +144 -0
- package/dist/repertoire/tools-vault.js +110 -0
- package/dist/repertoire/tools.js +149 -98
- package/dist/repertoire/travel-api-client.js +360 -0
- package/dist/repertoire/user-profile.js +118 -0
- package/dist/repertoire/vault-setup.js +241 -0
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/attention-queue.js +116 -0
- package/dist/senses/bluebubbles/attachment-cache.js +53 -0
- package/dist/senses/bluebubbles/attachment-download.js +137 -0
- package/dist/senses/bluebubbles/client.js +568 -0
- package/dist/senses/bluebubbles/entry.js +13 -0
- package/dist/senses/bluebubbles/inbound-log.js +113 -0
- package/dist/senses/bluebubbles/index.js +1427 -0
- package/dist/senses/bluebubbles/media.js +390 -0
- package/dist/senses/bluebubbles/model.js +282 -0
- package/dist/senses/bluebubbles/mutation-log.js +116 -0
- package/dist/senses/bluebubbles/replay.js +129 -0
- package/dist/senses/bluebubbles/runtime-state.js +109 -0
- package/dist/senses/bluebubbles/session-cleanup.js +72 -0
- package/dist/senses/cli/bracketed-paste.js +82 -0
- package/dist/senses/cli/image-paste.js +287 -0
- package/dist/senses/cli/image-ref-navigation.js +75 -0
- package/dist/senses/cli/ink-app.js +156 -0
- package/dist/senses/cli/inline-diff.js +64 -0
- package/dist/senses/cli/input-keys.js +174 -0
- package/dist/senses/cli/kill-ring.js +86 -0
- package/dist/senses/cli/message-list.js +51 -0
- package/dist/senses/cli/ouro-tui.js +595 -0
- package/dist/senses/cli/spinner-imperative.js +135 -0
- package/dist/senses/cli/spinner.js +101 -0
- package/dist/senses/cli/status-line.js +60 -0
- package/dist/senses/cli/streaming-markdown.js +526 -0
- package/dist/senses/cli/tool-display.js +83 -0
- package/dist/senses/cli/tool-render.js +85 -0
- package/dist/senses/cli/tui-store.js +240 -0
- package/dist/senses/cli/virtual-list.js +35 -0
- package/dist/senses/cli-entry.js +1 -1
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +772 -264
- package/dist/senses/commands.js +65 -1
- package/dist/senses/continuity.js +94 -0
- package/dist/senses/habit-turn-message.js +108 -0
- package/dist/senses/inner-dialog-worker.js +112 -19
- package/dist/senses/inner-dialog.js +633 -86
- package/dist/senses/pipeline.js +560 -0
- package/dist/senses/shared-turn.js +195 -0
- package/dist/senses/surface-tool.js +68 -0
- package/dist/senses/teams.js +814 -196
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +25 -9
- package/skills/agent-commerce.md +106 -0
- package/skills/browser-navigation.md +110 -0
- package/skills/commerce-setup-guide.md +116 -0
- package/skills/commerce-setup.md +84 -0
- package/skills/configure-dev-tools.md +81 -0
- package/skills/travel-planning.md +138 -0
- package/AdoptionSpecialist.ouro/agent.json +0 -20
- package/AdoptionSpecialist.ouro/psyche/identities/python.md +0 -1
- package/dist/heart/daemon/subagent-installer.js +0 -125
- package/dist/inner-worker-entry.js +0 -4
- package/subagents/README.md +0 -73
- package/subagents/work-doer.md +0 -233
- package/subagents/work-merger.md +0 -624
- package/subagents/work-planner.md +0 -373
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
|
@@ -51,15 +51,49 @@ class DaemonProcessManager {
|
|
|
51
51
|
now;
|
|
52
52
|
setTimeoutFn;
|
|
53
53
|
clearTimeoutFn;
|
|
54
|
+
cooldownRecoveryMs;
|
|
55
|
+
maxCooldownRetries;
|
|
56
|
+
existsSyncFn;
|
|
57
|
+
configCheckFn;
|
|
58
|
+
onSnapshotChangeFn;
|
|
59
|
+
/**
|
|
60
|
+
* Notify the snapshot-change observer (if registered). Swallows any
|
|
61
|
+
* errors from the observer so process lifecycle code never fails
|
|
62
|
+
* because the observer threw.
|
|
63
|
+
*/
|
|
64
|
+
notifySnapshotChange(snapshot) {
|
|
65
|
+
if (!this.onSnapshotChangeFn)
|
|
66
|
+
return;
|
|
67
|
+
try {
|
|
68
|
+
this.onSnapshotChangeFn(snapshot);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
(0, runtime_1.emitNervesEvent)({
|
|
72
|
+
level: "warn",
|
|
73
|
+
component: "daemon",
|
|
74
|
+
event: "daemon.snapshot_change_observer_error",
|
|
75
|
+
message: "snapshot-change observer threw",
|
|
76
|
+
meta: {
|
|
77
|
+
agent: snapshot.name,
|
|
78
|
+
error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error),
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
54
83
|
constructor(options) {
|
|
55
84
|
this.maxRestartsPerHour = options.maxRestartsPerHour ?? 10;
|
|
56
85
|
this.stabilityThresholdMs = options.stabilityThresholdMs ?? 60_000;
|
|
57
86
|
this.initialBackoffMs = options.initialBackoffMs ?? 1_000;
|
|
58
87
|
this.maxBackoffMs = options.maxBackoffMs ?? 60_000;
|
|
88
|
+
this.cooldownRecoveryMs = options.cooldownRecoveryMs ?? 5 * 60 * 1_000;
|
|
89
|
+
this.maxCooldownRetries = options.maxCooldownRetries ?? 3;
|
|
59
90
|
this.spawnFn = options.spawn ?? ((command, args, spawnOptions) => (0, child_process_1.spawn)(command, args, spawnOptions));
|
|
60
91
|
this.now = options.now ?? (() => Date.now());
|
|
61
92
|
this.setTimeoutFn = options.setTimeoutFn ?? ((cb, delay) => setTimeout(cb, delay));
|
|
62
93
|
this.clearTimeoutFn = options.clearTimeoutFn ?? ((timer) => clearTimeout(timer));
|
|
94
|
+
this.existsSyncFn = options.existsSync ?? null;
|
|
95
|
+
this.configCheckFn = options.configCheck ?? null;
|
|
96
|
+
this.onSnapshotChangeFn = options.onSnapshotChange ?? null;
|
|
63
97
|
for (const agent of options.agents) {
|
|
64
98
|
this.agents.set(agent.name, {
|
|
65
99
|
config: agent,
|
|
@@ -67,6 +101,9 @@ class DaemonProcessManager {
|
|
|
67
101
|
restartTimer: null,
|
|
68
102
|
crashTimestamps: [],
|
|
69
103
|
stopRequested: false,
|
|
104
|
+
cooldownTimer: null,
|
|
105
|
+
cooldownRetryCount: 0,
|
|
106
|
+
fastCrashCount: 0,
|
|
70
107
|
snapshot: {
|
|
71
108
|
name: agent.name,
|
|
72
109
|
channel: agent.channel,
|
|
@@ -76,6 +113,10 @@ class DaemonProcessManager {
|
|
|
76
113
|
startedAt: null,
|
|
77
114
|
lastCrashAt: null,
|
|
78
115
|
backoffMs: this.initialBackoffMs,
|
|
116
|
+
lastExitCode: null,
|
|
117
|
+
lastSignal: null,
|
|
118
|
+
errorReason: null,
|
|
119
|
+
fixHint: null,
|
|
79
120
|
},
|
|
80
121
|
});
|
|
81
122
|
}
|
|
@@ -94,14 +135,62 @@ class DaemonProcessManager {
|
|
|
94
135
|
this.clearRestartTimer(state);
|
|
95
136
|
state.stopRequested = false;
|
|
96
137
|
state.snapshot.status = "starting";
|
|
138
|
+
if (this.configCheckFn) {
|
|
139
|
+
const result = this.configCheckFn(agent);
|
|
140
|
+
if (!result.ok) {
|
|
141
|
+
state.snapshot.status = "crashed";
|
|
142
|
+
// Surface the error and fix to the snapshot so sibling agents can
|
|
143
|
+
// read it via the pulse. Without this, the diagnosis stayed
|
|
144
|
+
// trapped in the nerves event and stderr — visible to humans
|
|
145
|
+
// running `ouro status` or grepping logs, but invisible to
|
|
146
|
+
// peer agents trying to coordinate around the broken state.
|
|
147
|
+
state.snapshot.errorReason = result.error ?? "agent config validation failed";
|
|
148
|
+
state.snapshot.fixHint = result.fix ?? null;
|
|
149
|
+
(0, runtime_1.emitNervesEvent)({
|
|
150
|
+
level: "error",
|
|
151
|
+
component: "daemon",
|
|
152
|
+
event: "daemon.agent_config_invalid",
|
|
153
|
+
message: result.error ?? "agent config validation failed",
|
|
154
|
+
meta: { agent, fix: result.fix ?? null },
|
|
155
|
+
});
|
|
156
|
+
process.stderr.write(`[daemon] ${agent}: ${result.error}\n` +
|
|
157
|
+
(result.fix ? ` Fix: ${result.fix}\n` : ""));
|
|
158
|
+
this.notifySnapshotChange(state.snapshot);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
// Config check passed — clear any prior error so the pulse stops
|
|
162
|
+
// reporting the broken state. This is the recovery path: the user
|
|
163
|
+
// fixed their secrets/config, the next startAgent attempt sees a
|
|
164
|
+
// valid config, and the pulse goes quiet.
|
|
165
|
+
state.snapshot.errorReason = null;
|
|
166
|
+
state.snapshot.fixHint = null;
|
|
167
|
+
}
|
|
97
168
|
const runCwd = (0, identity_1.getRepoRoot)();
|
|
98
169
|
const entryScript = path.join((0, identity_1.getRepoRoot)(), "dist", state.config.entry);
|
|
99
|
-
|
|
170
|
+
if (this.existsSyncFn && !this.existsSyncFn(entryScript)) {
|
|
171
|
+
state.snapshot.status = "crashed";
|
|
172
|
+
(0, runtime_1.emitNervesEvent)({
|
|
173
|
+
level: "error",
|
|
174
|
+
component: "daemon",
|
|
175
|
+
event: "daemon.agent_entry_missing",
|
|
176
|
+
message: "agent entry script does not exist — cannot spawn. Run 'ouro daemon install' from the correct location.",
|
|
177
|
+
meta: { agent, entryScript },
|
|
178
|
+
});
|
|
179
|
+
this.notifySnapshotChange(state.snapshot);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const args = [entryScript, "--agent", state.config.agentArg ?? agent, ...(state.config.args ?? [])];
|
|
100
183
|
const child = this.spawnFn("node", args, {
|
|
101
184
|
cwd: runCwd,
|
|
102
185
|
env: state.config.env ? { ...process.env, ...state.config.env } : process.env,
|
|
103
186
|
stdio: ["ignore", "ignore", "ignore", "ipc"],
|
|
104
187
|
});
|
|
188
|
+
/* v8 ignore next 7 -- defensive: spawn should always return a ChildProcess @preserve */
|
|
189
|
+
if (!child) {
|
|
190
|
+
state.snapshot.status = "crashed";
|
|
191
|
+
(0, runtime_1.emitNervesEvent)({ level: "error", component: "daemon", event: "daemon.agent_spawn_failed", message: "spawn returned null", meta: { agent } });
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
105
194
|
state.process = child;
|
|
106
195
|
state.snapshot.status = "running";
|
|
107
196
|
state.snapshot.pid = child.pid ?? null;
|
|
@@ -112,6 +201,18 @@ class DaemonProcessManager {
|
|
|
112
201
|
message: "daemon started managed agent process",
|
|
113
202
|
meta: { agent, pid: child.pid ?? null, cwd: runCwd },
|
|
114
203
|
});
|
|
204
|
+
this.notifySnapshotChange(state.snapshot);
|
|
205
|
+
/* v8 ignore start — child process error handler; requires real spawn to trigger */
|
|
206
|
+
child.on("error", (err) => {
|
|
207
|
+
(0, runtime_1.emitNervesEvent)({
|
|
208
|
+
level: "warn",
|
|
209
|
+
component: "daemon",
|
|
210
|
+
event: "daemon.agent_process_error",
|
|
211
|
+
message: "managed agent process emitted error",
|
|
212
|
+
meta: { agent, error: err.message },
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
/* v8 ignore stop */
|
|
115
216
|
child.once("exit", (code, signal) => {
|
|
116
217
|
this.onExit(state, code, signal);
|
|
117
218
|
});
|
|
@@ -119,10 +220,12 @@ class DaemonProcessManager {
|
|
|
119
220
|
async stopAgent(agent) {
|
|
120
221
|
const state = this.requireAgent(agent);
|
|
121
222
|
this.clearRestartTimer(state);
|
|
223
|
+
this.clearCooldownTimer(state);
|
|
122
224
|
state.stopRequested = true;
|
|
123
225
|
if (!state.process) {
|
|
124
226
|
state.snapshot.status = "stopped";
|
|
125
227
|
state.snapshot.pid = null;
|
|
228
|
+
this.notifySnapshotChange(state.snapshot);
|
|
126
229
|
return;
|
|
127
230
|
}
|
|
128
231
|
const child = state.process;
|
|
@@ -141,6 +244,7 @@ class DaemonProcessManager {
|
|
|
141
244
|
meta: { agent },
|
|
142
245
|
});
|
|
143
246
|
}
|
|
247
|
+
this.notifySnapshotChange(state.snapshot);
|
|
144
248
|
}
|
|
145
249
|
async restartAgent(agent) {
|
|
146
250
|
await this.stopAgent(agent);
|
|
@@ -179,6 +283,8 @@ class DaemonProcessManager {
|
|
|
179
283
|
return;
|
|
180
284
|
state.process = null;
|
|
181
285
|
state.snapshot.pid = null;
|
|
286
|
+
state.snapshot.lastExitCode = code;
|
|
287
|
+
state.snapshot.lastSignal = signal;
|
|
182
288
|
const crashed = !state.stopRequested && code !== 0;
|
|
183
289
|
const now = this.now();
|
|
184
290
|
const startedAt = state.snapshot.startedAt ? Date.parse(state.snapshot.startedAt) : now;
|
|
@@ -195,13 +301,45 @@ class DaemonProcessManager {
|
|
|
195
301
|
if (runDuration >= this.stabilityThresholdMs) {
|
|
196
302
|
state.snapshot.backoffMs = this.initialBackoffMs;
|
|
197
303
|
}
|
|
304
|
+
this.notifySnapshotChange(state.snapshot);
|
|
198
305
|
return;
|
|
199
306
|
}
|
|
200
307
|
state.snapshot.lastCrashAt = new Date(now).toISOString();
|
|
308
|
+
// Fast-crash detection: if the agent dies within 5 seconds of starting, it's likely
|
|
309
|
+
// a configuration issue (missing credentials, bad provider, etc.) not a transient failure.
|
|
310
|
+
// After 3 consecutive fast crashes, stop retrying and mark as config-failed.
|
|
311
|
+
const FAST_CRASH_THRESHOLD_MS = 5000;
|
|
312
|
+
const FAST_CRASH_MAX = 3;
|
|
313
|
+
if (runDuration < FAST_CRASH_THRESHOLD_MS) {
|
|
314
|
+
state.fastCrashCount = state.fastCrashCount + 1;
|
|
315
|
+
if (state.fastCrashCount >= FAST_CRASH_MAX) {
|
|
316
|
+
state.snapshot.status = "crashed";
|
|
317
|
+
// Capture the fast-crash diagnosis on the snapshot so it surfaces
|
|
318
|
+
// via the pulse. The error message is prescriptive: it tells the
|
|
319
|
+
// user (and their sibling agents) exactly what to do.
|
|
320
|
+
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).`;
|
|
321
|
+
state.snapshot.fixHint = `Fix the config and run \`ouro up\` to restart, or check daemon logs for the underlying error.`;
|
|
322
|
+
(0, runtime_1.emitNervesEvent)({
|
|
323
|
+
level: "error",
|
|
324
|
+
component: "daemon",
|
|
325
|
+
event: "daemon.agent_config_failure",
|
|
326
|
+
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.`,
|
|
327
|
+
meta: { agent: state.config.name, fastCrashCount: state.fastCrashCount, avgRunDurationMs: runDuration },
|
|
328
|
+
});
|
|
329
|
+
this.notifySnapshotChange(state.snapshot);
|
|
330
|
+
return; // Don't schedule cooldown recovery — this needs human/agent intervention
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
// Reset fast-crash counter on a stable run
|
|
335
|
+
state.fastCrashCount = 0;
|
|
336
|
+
}
|
|
201
337
|
state.crashTimestamps = state.crashTimestamps.filter((crashTs) => crashTs >= startOfHour(now));
|
|
202
338
|
state.crashTimestamps.push(now);
|
|
203
339
|
if (state.crashTimestamps.length > this.maxRestartsPerHour) {
|
|
204
340
|
state.snapshot.status = "crashed";
|
|
341
|
+
state.snapshot.errorReason = `agent exceeded restart limit (${this.maxRestartsPerHour}/hr) — entering cooldown`;
|
|
342
|
+
state.snapshot.fixHint = "investigate why the agent keeps crashing; cooldown will retry shortly";
|
|
205
343
|
(0, runtime_1.emitNervesEvent)({
|
|
206
344
|
level: "error",
|
|
207
345
|
component: "daemon",
|
|
@@ -209,6 +347,8 @@ class DaemonProcessManager {
|
|
|
209
347
|
message: "managed agent exceeded restart limit and is marked crashed",
|
|
210
348
|
meta: { agent: state.config.name, maxRestartsPerHour: this.maxRestartsPerHour },
|
|
211
349
|
});
|
|
350
|
+
this.notifySnapshotChange(state.snapshot);
|
|
351
|
+
this.scheduleCooldownRecovery(state);
|
|
212
352
|
return;
|
|
213
353
|
}
|
|
214
354
|
state.snapshot.status = "starting";
|
|
@@ -219,6 +359,7 @@ class DaemonProcessManager {
|
|
|
219
359
|
state.restartTimer = this.setTimeoutFn(() => {
|
|
220
360
|
void this.startAgent(state.config.name);
|
|
221
361
|
}, waitMs);
|
|
362
|
+
this.notifySnapshotChange(state.snapshot);
|
|
222
363
|
}
|
|
223
364
|
clearRestartTimer(state) {
|
|
224
365
|
if (state.restartTimer === null)
|
|
@@ -226,6 +367,45 @@ class DaemonProcessManager {
|
|
|
226
367
|
this.clearTimeoutFn(state.restartTimer);
|
|
227
368
|
state.restartTimer = null;
|
|
228
369
|
}
|
|
370
|
+
scheduleCooldownRecovery(state) {
|
|
371
|
+
if (state.cooldownRetryCount >= this.maxCooldownRetries) {
|
|
372
|
+
(0, runtime_1.emitNervesEvent)({
|
|
373
|
+
level: "error",
|
|
374
|
+
component: "daemon",
|
|
375
|
+
event: "daemon.agent_permanent_failure",
|
|
376
|
+
message: "managed agent exhausted all cooldown retries — permanently stopped",
|
|
377
|
+
meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount, maxCooldownRetries: this.maxCooldownRetries },
|
|
378
|
+
});
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
this.clearCooldownTimer(state);
|
|
382
|
+
state.cooldownTimer = this.setTimeoutFn(() => {
|
|
383
|
+
state.cooldownRetryCount += 1;
|
|
384
|
+
state.crashTimestamps = [];
|
|
385
|
+
state.snapshot.backoffMs = this.initialBackoffMs;
|
|
386
|
+
state.snapshot.status = "starting";
|
|
387
|
+
state.snapshot.restartCount += 1;
|
|
388
|
+
(0, runtime_1.emitNervesEvent)({
|
|
389
|
+
component: "daemon",
|
|
390
|
+
event: "daemon.agent_cooldown_recovery",
|
|
391
|
+
message: "attempting cooldown recovery for managed agent",
|
|
392
|
+
meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount },
|
|
393
|
+
});
|
|
394
|
+
void this.startAgent(state.config.name);
|
|
395
|
+
}, this.cooldownRecoveryMs);
|
|
396
|
+
(0, runtime_1.emitNervesEvent)({
|
|
397
|
+
component: "daemon",
|
|
398
|
+
event: "daemon.agent_cooldown_scheduled",
|
|
399
|
+
message: `scheduled cooldown recovery in ${this.cooldownRecoveryMs}ms`,
|
|
400
|
+
meta: { agent: state.config.name, cooldownRecoveryMs: this.cooldownRecoveryMs, cooldownRetryCount: state.cooldownRetryCount },
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
clearCooldownTimer(state) {
|
|
404
|
+
if (state.cooldownTimer === null)
|
|
405
|
+
return;
|
|
406
|
+
this.clearTimeoutFn(state.cooldownTimer);
|
|
407
|
+
state.cooldownTimer = null;
|
|
408
|
+
}
|
|
229
409
|
requireAgent(agent) {
|
|
230
410
|
const state = this.agents.get(agent);
|
|
231
411
|
if (!state) {
|