@ouro.bot/cli 0.1.0-alpha.40 → 0.1.0-alpha.401
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 +109 -14
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +2 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
- package/changelog.json +2465 -6
- 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 +193 -0
- package/dist/arc/presence.js +185 -0
- package/dist/arc/task-lifecycle.js +65 -0
- package/dist/heart/active-work.js +832 -0
- package/dist/heart/agent-entry.js +58 -3
- package/dist/heart/attachments/image-normalize.js +194 -0
- package/dist/heart/attachments/materialize.js +97 -0
- package/dist/heart/attachments/originals.js +88 -0
- package/dist/heart/attachments/render.js +29 -0
- package/dist/heart/attachments/sources/adapter.js +2 -0
- package/dist/heart/attachments/sources/bluebubbles.js +156 -0
- package/dist/heart/attachments/sources/cli-local-file.js +78 -0
- package/dist/heart/attachments/sources/index.js +16 -0
- package/dist/heart/attachments/store.js +103 -0
- package/dist/heart/attachments/types.js +93 -0
- package/dist/heart/auth/auth-flow.js +417 -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 +101 -128
- package/dist/heart/core.js +801 -217
- package/dist/heart/cross-chat-delivery.js +131 -0
- package/dist/heart/daemon/agent-config-check.js +397 -0
- package/dist/heart/daemon/agent-discovery.js +79 -3
- package/dist/heart/daemon/agent-service.js +360 -0
- package/dist/heart/daemon/agentic-repair.js +213 -0
- package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/cli-defaults.js +599 -0
- package/dist/heart/daemon/cli-exec.js +3616 -0
- package/dist/heart/daemon/cli-help.js +396 -0
- package/dist/heart/daemon/cli-parse.js +1118 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +560 -0
- package/dist/heart/daemon/cli-types.js +8 -0
- package/dist/heart/daemon/daemon-cli.js +28 -1582
- package/dist/heart/daemon/daemon-entry.js +356 -3
- package/dist/heart/daemon/daemon-health.js +141 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +157 -12
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +684 -58
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +419 -0
- package/dist/heart/daemon/health-monitor.js +79 -1
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
- package/dist/heart/daemon/http-health-probe.js +80 -0
- package/dist/heart/daemon/inner-status.js +89 -0
- package/dist/heart/daemon/interactive-repair.js +209 -0
- package/dist/heart/daemon/launchd.js +46 -9
- 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 +4 -2
- package/dist/heart/daemon/ouro-entry.js +3 -1
- package/dist/heart/daemon/process-manager.js +201 -0
- package/dist/heart/daemon/provider-discovery.js +137 -0
- package/dist/heart/daemon/pulse.js +475 -0
- package/dist/heart/daemon/readiness-repair.js +216 -0
- package/dist/heart/daemon/run-hooks.js +2 -0
- package/dist/heart/daemon/runtime-logging.js +67 -16
- package/dist/heart/daemon/runtime-metadata.js +73 -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 +119 -30
- 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/stale-bundle-prune.js +96 -0
- package/dist/heart/daemon/startup-tui.js +237 -0
- package/dist/heart/daemon/task-scheduler.js +3 -25
- package/dist/heart/daemon/thoughts.js +510 -0
- package/dist/heart/daemon/up-progress.js +135 -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/{daemon → hatch}/hatch-flow.js +53 -117
- package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
- package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
- package/dist/heart/{daemon → hatch}/specialist-tools.js +33 -12
- package/dist/heart/identity.js +161 -65
- package/dist/heart/kept-notes.js +357 -0
- package/dist/heart/kicks.js +1 -1
- package/dist/heart/machine-identity.js +161 -0
- package/dist/heart/mcp/mcp-server.js +653 -0
- package/dist/heart/migrate-config.js +100 -0
- package/dist/heart/model-capabilities.js +59 -0
- package/dist/heart/outlook/outlook-http-hooks.js +64 -0
- package/dist/heart/outlook/outlook-http-response.js +7 -0
- package/dist/heart/outlook/outlook-http-routes.js +232 -0
- package/dist/heart/outlook/outlook-http-static.js +99 -0
- package/dist/heart/outlook/outlook-http-transport.js +116 -0
- package/dist/heart/outlook/outlook-http.js +99 -0
- package/dist/heart/outlook/outlook-read.js +28 -0
- package/dist/heart/outlook/outlook-types.js +27 -0
- package/dist/heart/outlook/outlook-view.js +195 -0
- package/dist/heart/outlook/readers/agent-machine.js +359 -0
- package/dist/heart/outlook/readers/continuity-readers.js +332 -0
- package/dist/heart/outlook/readers/runtime-readers.js +660 -0
- package/dist/heart/outlook/readers/sessions.js +232 -0
- package/dist/heart/outlook/readers/shared.js +111 -0
- package/dist/heart/platform.js +81 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/provider-attempt.js +133 -0
- package/dist/heart/provider-binding-resolver.js +239 -0
- package/dist/heart/provider-credentials.js +383 -0
- package/dist/heart/provider-failover.js +266 -0
- package/dist/heart/provider-models.js +81 -0
- package/dist/heart/provider-ping.js +237 -0
- package/dist/heart/provider-state.js +216 -0
- package/dist/heart/provider-visibility.js +186 -0
- package/dist/heart/providers/anthropic-token.js +131 -0
- package/dist/heart/providers/anthropic.js +193 -55
- package/dist/heart/providers/azure.js +103 -12
- package/dist/heart/providers/error-classification.js +63 -0
- package/dist/heart/providers/github-copilot.js +145 -0
- package/dist/heart/providers/minimax-vlm.js +189 -0
- package/dist/heart/providers/minimax.js +29 -7
- package/dist/heart/providers/openai-codex.js +39 -29
- package/dist/heart/runtime-credentials.js +181 -0
- package/dist/heart/session-activity.js +190 -0
- package/dist/heart/session-events.js +855 -0
- package/dist/heart/session-transcript.js +167 -0
- package/dist/heart/start-of-turn-packet.js +345 -0
- package/dist/heart/streaming.js +36 -27
- 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 +351 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +1 -1
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
- package/dist/heart/versioning/ouro-path-installer.js +301 -0
- package/dist/heart/versioning/ouro-version-manager.js +295 -0
- package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
- package/dist/heart/{daemon → versioning}/update-checker.js +12 -2
- package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
- package/dist/mind/bundle-manifest.js +7 -1
- package/dist/mind/context.js +134 -87
- 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 +14 -1
- package/dist/mind/friends/channel.js +21 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/resolver.js +38 -1
- package/dist/mind/friends/store-file.js +39 -3
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +1 -1
- package/dist/mind/journal-index.js +161 -0
- package/dist/mind/note-search.js +268 -0
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/pending.js +66 -7
- package/dist/mind/prompt-refresh.js +3 -2
- package/dist/mind/prompt.js +946 -167
- package/dist/mind/provenance-trust.js +26 -0
- package/dist/mind/scrutiny.js +173 -0
- package/dist/nerves/cli-logging.js +7 -1
- package/dist/nerves/coverage/audit-rules.js +15 -6
- package/dist/nerves/coverage/audit.js +28 -2
- package/dist/nerves/coverage/cli.js +1 -1
- package/dist/nerves/coverage/contract.js +5 -5
- package/dist/nerves/coverage/file-completeness.js +83 -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-BAcU08c-.css +1 -0
- package/dist/outlook-ui/assets/index-D7l3l4vY.js +61 -0
- package/dist/outlook-ui/index.html +15 -0
- package/dist/repertoire/ado-client.js +15 -56
- package/dist/repertoire/ado-semantic.js +11 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +519 -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 +197 -30
- package/dist/repertoire/coding/manager.js +158 -9
- package/dist/repertoire/coding/spawner.js +55 -9
- package/dist/repertoire/coding/tools.js +170 -7
- package/dist/repertoire/commerce-errors.js +109 -0
- package/dist/repertoire/commerce-self-test.js +156 -0
- package/dist/repertoire/credential-access.js +107 -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 +371 -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 +15 -24
- 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 +26 -1
- 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 +16 -78
- package/dist/repertoire/tool-results.js +29 -0
- package/dist/repertoire/tools-attachments.js +317 -0
- package/dist/repertoire/tools-base.js +42 -687
- package/dist/repertoire/tools-bluebubbles.js +1 -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-notes.js +376 -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 +9 -39
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-user-profile.js +144 -0
- package/dist/repertoire/tools-vault.js +40 -0
- package/dist/repertoire/tools.js +144 -113
- package/dist/repertoire/travel-api-client.js +360 -0
- package/dist/repertoire/user-profile.js +118 -0
- package/dist/repertoire/vault-setup.js +246 -0
- package/dist/repertoire/vault-unlock.js +382 -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 → bluebubbles/client.js} +260 -9
- package/dist/senses/bluebubbles/entry.js +70 -0
- package/dist/senses/bluebubbles/inbound-log.js +113 -0
- package/dist/senses/bluebubbles/index.js +1620 -0
- package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -70
- package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +33 -12
- package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +45 -3
- package/dist/senses/bluebubbles/replay.js +129 -0
- package/dist/senses/bluebubbles/runtime-state.js +109 -0
- package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
- 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 +605 -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 +60 -8
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +526 -211
- package/dist/senses/commands.js +66 -3
- 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 +600 -95
- package/dist/senses/pipeline.js +539 -61
- package/dist/senses/proactive-content-guard.js +51 -0
- package/dist/senses/shared-turn.js +205 -0
- package/dist/senses/surface-tool.js +68 -0
- package/dist/senses/teams-entry.js +60 -8
- package/dist/senses/teams.js +569 -237
- package/dist/senses/trust-gate.js +5 -5
- package/package.json +28 -7
- 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 +101 -0
- package/skills/travel-planning.md +134 -0
- package/dist/heart/daemon/ouro-path-installer.js +0 -178
- package/dist/heart/daemon/subagent-installer.js +0 -134
- package/dist/mind/associative-recall.js +0 -197
- package/dist/senses/bluebubbles-entry.js +0 -11
- package/dist/senses/bluebubbles.js +0 -832
- package/dist/senses/debug-activity.js +0 -127
- package/subagents/README.md +0 -60
- package/subagents/work-doer.md +0 -235
- package/subagents/work-merger.md +0 -618
- package/subagents/work-planner.md +0 -382
- /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/monty.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/python.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-snake.md +0 -0
- /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
- /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
- /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
- /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
|
@@ -51,15 +51,70 @@ class DaemonProcessManager {
|
|
|
51
51
|
now;
|
|
52
52
|
setTimeoutFn;
|
|
53
53
|
clearTimeoutFn;
|
|
54
|
+
cooldownRecoveryMs;
|
|
55
|
+
maxCooldownRetries;
|
|
56
|
+
existsSyncFn;
|
|
57
|
+
configCheckFn;
|
|
58
|
+
statusWriterFn;
|
|
59
|
+
onSnapshotChangeFn;
|
|
60
|
+
/**
|
|
61
|
+
* Notify the snapshot-change observer (if registered). Swallows any
|
|
62
|
+
* errors from the observer so process lifecycle code never fails
|
|
63
|
+
* because the observer threw.
|
|
64
|
+
*/
|
|
65
|
+
notifySnapshotChange(snapshot) {
|
|
66
|
+
if (!this.onSnapshotChangeFn)
|
|
67
|
+
return;
|
|
68
|
+
try {
|
|
69
|
+
this.onSnapshotChangeFn(snapshot);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
(0, runtime_1.emitNervesEvent)({
|
|
73
|
+
level: "warn",
|
|
74
|
+
component: "daemon",
|
|
75
|
+
event: "daemon.snapshot_change_observer_error",
|
|
76
|
+
message: "snapshot-change observer threw",
|
|
77
|
+
meta: {
|
|
78
|
+
agent: snapshot.name,
|
|
79
|
+
error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error),
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
writeStatus(agent, text) {
|
|
85
|
+
try {
|
|
86
|
+
this.statusWriterFn(text);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
(0, runtime_1.emitNervesEvent)({
|
|
90
|
+
level: "warn",
|
|
91
|
+
component: "daemon",
|
|
92
|
+
event: "daemon.status_writer_error",
|
|
93
|
+
message: "daemon status writer threw",
|
|
94
|
+
meta: {
|
|
95
|
+
agent,
|
|
96
|
+
error: error instanceof Error ? error.message : String(error),
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
54
101
|
constructor(options) {
|
|
55
102
|
this.maxRestartsPerHour = options.maxRestartsPerHour ?? 10;
|
|
56
103
|
this.stabilityThresholdMs = options.stabilityThresholdMs ?? 60_000;
|
|
57
104
|
this.initialBackoffMs = options.initialBackoffMs ?? 1_000;
|
|
58
105
|
this.maxBackoffMs = options.maxBackoffMs ?? 60_000;
|
|
106
|
+
this.cooldownRecoveryMs = options.cooldownRecoveryMs ?? 5 * 60 * 1_000;
|
|
107
|
+
this.maxCooldownRetries = options.maxCooldownRetries ?? 3;
|
|
59
108
|
this.spawnFn = options.spawn ?? ((command, args, spawnOptions) => (0, child_process_1.spawn)(command, args, spawnOptions));
|
|
60
109
|
this.now = options.now ?? (() => Date.now());
|
|
61
110
|
this.setTimeoutFn = options.setTimeoutFn ?? ((cb, delay) => setTimeout(cb, delay));
|
|
62
111
|
this.clearTimeoutFn = options.clearTimeoutFn ?? ((timer) => clearTimeout(timer));
|
|
112
|
+
this.existsSyncFn = options.existsSync ?? null;
|
|
113
|
+
this.configCheckFn = options.configCheck ?? null;
|
|
114
|
+
this.statusWriterFn = options.statusWriter ?? ((text) => {
|
|
115
|
+
process.stderr.write(text);
|
|
116
|
+
});
|
|
117
|
+
this.onSnapshotChangeFn = options.onSnapshotChange ?? null;
|
|
63
118
|
for (const agent of options.agents) {
|
|
64
119
|
this.agents.set(agent.name, {
|
|
65
120
|
config: agent,
|
|
@@ -67,6 +122,9 @@ class DaemonProcessManager {
|
|
|
67
122
|
restartTimer: null,
|
|
68
123
|
crashTimestamps: [],
|
|
69
124
|
stopRequested: false,
|
|
125
|
+
cooldownTimer: null,
|
|
126
|
+
cooldownRetryCount: 0,
|
|
127
|
+
fastCrashCount: 0,
|
|
70
128
|
snapshot: {
|
|
71
129
|
name: agent.name,
|
|
72
130
|
channel: agent.channel,
|
|
@@ -76,6 +134,10 @@ class DaemonProcessManager {
|
|
|
76
134
|
startedAt: null,
|
|
77
135
|
lastCrashAt: null,
|
|
78
136
|
backoffMs: this.initialBackoffMs,
|
|
137
|
+
lastExitCode: null,
|
|
138
|
+
lastSignal: null,
|
|
139
|
+
errorReason: null,
|
|
140
|
+
fixHint: null,
|
|
79
141
|
},
|
|
80
142
|
});
|
|
81
143
|
}
|
|
@@ -94,14 +156,62 @@ class DaemonProcessManager {
|
|
|
94
156
|
this.clearRestartTimer(state);
|
|
95
157
|
state.stopRequested = false;
|
|
96
158
|
state.snapshot.status = "starting";
|
|
159
|
+
if (this.configCheckFn) {
|
|
160
|
+
const result = await this.configCheckFn(agent);
|
|
161
|
+
if (!result.ok) {
|
|
162
|
+
state.snapshot.status = "crashed";
|
|
163
|
+
// Surface the error and fix to the snapshot so sibling agents can
|
|
164
|
+
// read it via the pulse. Without this, the diagnosis stayed
|
|
165
|
+
// trapped in the nerves event and stderr — visible to humans
|
|
166
|
+
// running `ouro status` or grepping logs, but invisible to
|
|
167
|
+
// peer agents trying to coordinate around the broken state.
|
|
168
|
+
state.snapshot.errorReason = result.error ?? "agent config validation failed";
|
|
169
|
+
state.snapshot.fixHint = result.fix ?? null;
|
|
170
|
+
(0, runtime_1.emitNervesEvent)({
|
|
171
|
+
level: "error",
|
|
172
|
+
component: "daemon",
|
|
173
|
+
event: "daemon.agent_config_invalid",
|
|
174
|
+
message: result.error ?? "agent config validation failed",
|
|
175
|
+
meta: { agent, fix: result.fix ?? null },
|
|
176
|
+
});
|
|
177
|
+
this.writeStatus(agent, `[daemon] ${agent}: ${result.error}\n` +
|
|
178
|
+
(result.fix ? ` Fix: ${result.fix}\n` : ""));
|
|
179
|
+
this.notifySnapshotChange(state.snapshot);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
// Config check passed — clear any prior error so the pulse stops
|
|
183
|
+
// reporting the broken state. This is the recovery path: the user
|
|
184
|
+
// fixed their secrets/config, the next startAgent attempt sees a
|
|
185
|
+
// valid config, and the pulse goes quiet.
|
|
186
|
+
state.snapshot.errorReason = null;
|
|
187
|
+
state.snapshot.fixHint = null;
|
|
188
|
+
}
|
|
97
189
|
const runCwd = (0, identity_1.getRepoRoot)();
|
|
98
190
|
const entryScript = path.join((0, identity_1.getRepoRoot)(), "dist", state.config.entry);
|
|
191
|
+
if (this.existsSyncFn && !this.existsSyncFn(entryScript)) {
|
|
192
|
+
state.snapshot.status = "crashed";
|
|
193
|
+
(0, runtime_1.emitNervesEvent)({
|
|
194
|
+
level: "error",
|
|
195
|
+
component: "daemon",
|
|
196
|
+
event: "daemon.agent_entry_missing",
|
|
197
|
+
message: "agent entry script does not exist — cannot spawn. Run 'ouro daemon install' from the correct location.",
|
|
198
|
+
meta: { agent, entryScript },
|
|
199
|
+
});
|
|
200
|
+
this.notifySnapshotChange(state.snapshot);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
99
203
|
const args = [entryScript, "--agent", state.config.agentArg ?? agent, ...(state.config.args ?? [])];
|
|
100
204
|
const child = this.spawnFn("node", args, {
|
|
101
205
|
cwd: runCwd,
|
|
102
206
|
env: state.config.env ? { ...process.env, ...state.config.env } : process.env,
|
|
103
207
|
stdio: ["ignore", "ignore", "ignore", "ipc"],
|
|
104
208
|
});
|
|
209
|
+
/* v8 ignore next 7 -- defensive: spawn should always return a ChildProcess @preserve */
|
|
210
|
+
if (!child) {
|
|
211
|
+
state.snapshot.status = "crashed";
|
|
212
|
+
(0, runtime_1.emitNervesEvent)({ level: "error", component: "daemon", event: "daemon.agent_spawn_failed", message: "spawn returned null", meta: { agent } });
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
105
215
|
state.process = child;
|
|
106
216
|
state.snapshot.status = "running";
|
|
107
217
|
state.snapshot.pid = child.pid ?? null;
|
|
@@ -112,6 +222,18 @@ class DaemonProcessManager {
|
|
|
112
222
|
message: "daemon started managed agent process",
|
|
113
223
|
meta: { agent, pid: child.pid ?? null, cwd: runCwd },
|
|
114
224
|
});
|
|
225
|
+
this.notifySnapshotChange(state.snapshot);
|
|
226
|
+
/* v8 ignore start — child process error handler; requires real spawn to trigger */
|
|
227
|
+
child.on("error", (err) => {
|
|
228
|
+
(0, runtime_1.emitNervesEvent)({
|
|
229
|
+
level: "warn",
|
|
230
|
+
component: "daemon",
|
|
231
|
+
event: "daemon.agent_process_error",
|
|
232
|
+
message: "managed agent process emitted error",
|
|
233
|
+
meta: { agent, error: err.message },
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
/* v8 ignore stop */
|
|
115
237
|
child.once("exit", (code, signal) => {
|
|
116
238
|
this.onExit(state, code, signal);
|
|
117
239
|
});
|
|
@@ -119,10 +241,12 @@ class DaemonProcessManager {
|
|
|
119
241
|
async stopAgent(agent) {
|
|
120
242
|
const state = this.requireAgent(agent);
|
|
121
243
|
this.clearRestartTimer(state);
|
|
244
|
+
this.clearCooldownTimer(state);
|
|
122
245
|
state.stopRequested = true;
|
|
123
246
|
if (!state.process) {
|
|
124
247
|
state.snapshot.status = "stopped";
|
|
125
248
|
state.snapshot.pid = null;
|
|
249
|
+
this.notifySnapshotChange(state.snapshot);
|
|
126
250
|
return;
|
|
127
251
|
}
|
|
128
252
|
const child = state.process;
|
|
@@ -141,6 +265,7 @@ class DaemonProcessManager {
|
|
|
141
265
|
meta: { agent },
|
|
142
266
|
});
|
|
143
267
|
}
|
|
268
|
+
this.notifySnapshotChange(state.snapshot);
|
|
144
269
|
}
|
|
145
270
|
async restartAgent(agent) {
|
|
146
271
|
await this.stopAgent(agent);
|
|
@@ -179,6 +304,8 @@ class DaemonProcessManager {
|
|
|
179
304
|
return;
|
|
180
305
|
state.process = null;
|
|
181
306
|
state.snapshot.pid = null;
|
|
307
|
+
state.snapshot.lastExitCode = code;
|
|
308
|
+
state.snapshot.lastSignal = signal;
|
|
182
309
|
const crashed = !state.stopRequested && code !== 0;
|
|
183
310
|
const now = this.now();
|
|
184
311
|
const startedAt = state.snapshot.startedAt ? Date.parse(state.snapshot.startedAt) : now;
|
|
@@ -195,13 +322,45 @@ class DaemonProcessManager {
|
|
|
195
322
|
if (runDuration >= this.stabilityThresholdMs) {
|
|
196
323
|
state.snapshot.backoffMs = this.initialBackoffMs;
|
|
197
324
|
}
|
|
325
|
+
this.notifySnapshotChange(state.snapshot);
|
|
198
326
|
return;
|
|
199
327
|
}
|
|
200
328
|
state.snapshot.lastCrashAt = new Date(now).toISOString();
|
|
329
|
+
// Fast-crash detection: if the agent dies within 5 seconds of starting, it's likely
|
|
330
|
+
// a configuration issue (missing credentials, bad provider, etc.) not a transient failure.
|
|
331
|
+
// After 3 consecutive fast crashes, stop retrying and mark as config-failed.
|
|
332
|
+
const FAST_CRASH_THRESHOLD_MS = 5000;
|
|
333
|
+
const FAST_CRASH_MAX = 3;
|
|
334
|
+
if (runDuration < FAST_CRASH_THRESHOLD_MS) {
|
|
335
|
+
state.fastCrashCount = state.fastCrashCount + 1;
|
|
336
|
+
if (state.fastCrashCount >= FAST_CRASH_MAX) {
|
|
337
|
+
state.snapshot.status = "crashed";
|
|
338
|
+
// Capture the fast-crash diagnosis on the snapshot so it surfaces
|
|
339
|
+
// via the pulse. The error message is prescriptive: it tells the
|
|
340
|
+
// user (and their sibling agents) exactly what to do.
|
|
341
|
+
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).`;
|
|
342
|
+
state.snapshot.fixHint = `Fix the config and run \`ouro up\` to restart, or check daemon logs for the underlying error.`;
|
|
343
|
+
(0, runtime_1.emitNervesEvent)({
|
|
344
|
+
level: "error",
|
|
345
|
+
component: "daemon",
|
|
346
|
+
event: "daemon.agent_config_failure",
|
|
347
|
+
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.`,
|
|
348
|
+
meta: { agent: state.config.name, fastCrashCount: state.fastCrashCount, avgRunDurationMs: runDuration },
|
|
349
|
+
});
|
|
350
|
+
this.notifySnapshotChange(state.snapshot);
|
|
351
|
+
return; // Don't schedule cooldown recovery — this needs human/agent intervention
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
// Reset fast-crash counter on a stable run
|
|
356
|
+
state.fastCrashCount = 0;
|
|
357
|
+
}
|
|
201
358
|
state.crashTimestamps = state.crashTimestamps.filter((crashTs) => crashTs >= startOfHour(now));
|
|
202
359
|
state.crashTimestamps.push(now);
|
|
203
360
|
if (state.crashTimestamps.length > this.maxRestartsPerHour) {
|
|
204
361
|
state.snapshot.status = "crashed";
|
|
362
|
+
state.snapshot.errorReason = `agent exceeded restart limit (${this.maxRestartsPerHour}/hr) — entering cooldown`;
|
|
363
|
+
state.snapshot.fixHint = "investigate why the agent keeps crashing; cooldown will retry shortly";
|
|
205
364
|
(0, runtime_1.emitNervesEvent)({
|
|
206
365
|
level: "error",
|
|
207
366
|
component: "daemon",
|
|
@@ -209,6 +368,8 @@ class DaemonProcessManager {
|
|
|
209
368
|
message: "managed agent exceeded restart limit and is marked crashed",
|
|
210
369
|
meta: { agent: state.config.name, maxRestartsPerHour: this.maxRestartsPerHour },
|
|
211
370
|
});
|
|
371
|
+
this.notifySnapshotChange(state.snapshot);
|
|
372
|
+
this.scheduleCooldownRecovery(state);
|
|
212
373
|
return;
|
|
213
374
|
}
|
|
214
375
|
state.snapshot.status = "starting";
|
|
@@ -219,6 +380,7 @@ class DaemonProcessManager {
|
|
|
219
380
|
state.restartTimer = this.setTimeoutFn(() => {
|
|
220
381
|
void this.startAgent(state.config.name);
|
|
221
382
|
}, waitMs);
|
|
383
|
+
this.notifySnapshotChange(state.snapshot);
|
|
222
384
|
}
|
|
223
385
|
clearRestartTimer(state) {
|
|
224
386
|
if (state.restartTimer === null)
|
|
@@ -226,6 +388,45 @@ class DaemonProcessManager {
|
|
|
226
388
|
this.clearTimeoutFn(state.restartTimer);
|
|
227
389
|
state.restartTimer = null;
|
|
228
390
|
}
|
|
391
|
+
scheduleCooldownRecovery(state) {
|
|
392
|
+
if (state.cooldownRetryCount >= this.maxCooldownRetries) {
|
|
393
|
+
(0, runtime_1.emitNervesEvent)({
|
|
394
|
+
level: "error",
|
|
395
|
+
component: "daemon",
|
|
396
|
+
event: "daemon.agent_permanent_failure",
|
|
397
|
+
message: "managed agent exhausted all cooldown retries — permanently stopped",
|
|
398
|
+
meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount, maxCooldownRetries: this.maxCooldownRetries },
|
|
399
|
+
});
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
this.clearCooldownTimer(state);
|
|
403
|
+
state.cooldownTimer = this.setTimeoutFn(() => {
|
|
404
|
+
state.cooldownRetryCount += 1;
|
|
405
|
+
state.crashTimestamps = [];
|
|
406
|
+
state.snapshot.backoffMs = this.initialBackoffMs;
|
|
407
|
+
state.snapshot.status = "starting";
|
|
408
|
+
state.snapshot.restartCount += 1;
|
|
409
|
+
(0, runtime_1.emitNervesEvent)({
|
|
410
|
+
component: "daemon",
|
|
411
|
+
event: "daemon.agent_cooldown_recovery",
|
|
412
|
+
message: "attempting cooldown recovery for managed agent",
|
|
413
|
+
meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount },
|
|
414
|
+
});
|
|
415
|
+
void this.startAgent(state.config.name);
|
|
416
|
+
}, this.cooldownRecoveryMs);
|
|
417
|
+
(0, runtime_1.emitNervesEvent)({
|
|
418
|
+
component: "daemon",
|
|
419
|
+
event: "daemon.agent_cooldown_scheduled",
|
|
420
|
+
message: `scheduled cooldown recovery in ${this.cooldownRecoveryMs}ms`,
|
|
421
|
+
meta: { agent: state.config.name, cooldownRecoveryMs: this.cooldownRecoveryMs, cooldownRetryCount: state.cooldownRetryCount },
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
clearCooldownTimer(state) {
|
|
425
|
+
if (state.cooldownTimer === null)
|
|
426
|
+
return;
|
|
427
|
+
this.clearTimeoutFn(state.cooldownTimer);
|
|
428
|
+
state.cooldownTimer = null;
|
|
429
|
+
}
|
|
229
430
|
requireAgent(agent) {
|
|
230
431
|
const state = this.agents.get(agent);
|
|
231
432
|
if (!state) {
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared provider discovery for repair.
|
|
4
|
+
*
|
|
5
|
+
* Runtime repair only trusts the agent vault. First-run conveniences may still
|
|
6
|
+
* inspect env vars before credentials are stored, but once an agent exists the
|
|
7
|
+
* vault is the source of truth.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.scanEnvVarCredentials = scanEnvVarCredentials;
|
|
11
|
+
exports.discoverInstalledAgentCredentials = discoverInstalledAgentCredentials;
|
|
12
|
+
exports.describeDiscoveredCredentialSource = describeDiscoveredCredentialSource;
|
|
13
|
+
exports.discoverWorkingProvider = discoverWorkingProvider;
|
|
14
|
+
const identity_1 = require("../identity");
|
|
15
|
+
const provider_credentials_1 = require("../provider-credentials");
|
|
16
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
17
|
+
/**
|
|
18
|
+
* Scan environment variables for API keys during first-run bootstrap.
|
|
19
|
+
* This does not participate in runtime provider repair.
|
|
20
|
+
*/
|
|
21
|
+
function scanEnvVarCredentials(env) {
|
|
22
|
+
const results = [];
|
|
23
|
+
for (const [provider, desc] of Object.entries(identity_1.PROVIDER_CREDENTIALS)) {
|
|
24
|
+
const cred = {};
|
|
25
|
+
for (const [envVar, credKey] of Object.entries(desc.envVars)) {
|
|
26
|
+
const value = env[envVar];
|
|
27
|
+
if (value) {
|
|
28
|
+
cred[credKey] = value;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const hasRequired = desc.required.some((key) => !!cred[key]);
|
|
32
|
+
if (hasRequired) {
|
|
33
|
+
results.push({
|
|
34
|
+
provider,
|
|
35
|
+
agentName: "env",
|
|
36
|
+
credentials: cred,
|
|
37
|
+
providerConfig: { ...cred },
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return results;
|
|
42
|
+
}
|
|
43
|
+
function stringifyProviderFields(fields) {
|
|
44
|
+
const result = {};
|
|
45
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
46
|
+
result[key] = String(value);
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
function discoveredFromVaultRecord(record, agentName = "vault") {
|
|
51
|
+
return {
|
|
52
|
+
provider: record.provider,
|
|
53
|
+
agentName,
|
|
54
|
+
credentials: stringifyProviderFields(record.credentials),
|
|
55
|
+
providerConfig: stringifyProviderFields(record.config),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
async function discoverInstalledAgentCredentials(agentNames) {
|
|
59
|
+
const discovered = [];
|
|
60
|
+
for (const agentName of agentNames) {
|
|
61
|
+
if (agentName === "SerpentGuide")
|
|
62
|
+
continue;
|
|
63
|
+
const poolResult = await (0, provider_credentials_1.refreshProviderCredentialPool)(agentName, { preserveCachedOnFailure: true });
|
|
64
|
+
if (!poolResult.ok)
|
|
65
|
+
continue;
|
|
66
|
+
for (const record of Object.values(poolResult.pool.providers)) {
|
|
67
|
+
if (!record)
|
|
68
|
+
continue;
|
|
69
|
+
discovered.push(discoveredFromVaultRecord(record, agentName));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return discovered;
|
|
73
|
+
}
|
|
74
|
+
function describeDiscoveredCredentialSource(credential, envVar) {
|
|
75
|
+
if (credential.agentName === "env") {
|
|
76
|
+
return envVar ? `from env: $${envVar}` : "from env";
|
|
77
|
+
}
|
|
78
|
+
return `from ${credential.agentName}'s vault`;
|
|
79
|
+
}
|
|
80
|
+
async function discoverWorkingProvider(deps) {
|
|
81
|
+
const poolResult = await (0, provider_credentials_1.refreshProviderCredentialPool)(deps.agentName);
|
|
82
|
+
if (!poolResult.ok) {
|
|
83
|
+
(0, runtime_1.emitNervesEvent)({
|
|
84
|
+
level: "warn",
|
|
85
|
+
component: "daemon",
|
|
86
|
+
event: "daemon.provider_discovery_none",
|
|
87
|
+
message: "provider discovery could not read agent vault",
|
|
88
|
+
meta: { agentName: deps.agentName, reason: poolResult.reason },
|
|
89
|
+
});
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const candidates = Object.entries(poolResult.pool.providers)
|
|
93
|
+
.map(([, record]) => discoveredFromVaultRecord(record));
|
|
94
|
+
if (candidates.length === 0) {
|
|
95
|
+
(0, runtime_1.emitNervesEvent)({
|
|
96
|
+
level: "info",
|
|
97
|
+
component: "daemon",
|
|
98
|
+
event: "daemon.provider_discovery_none",
|
|
99
|
+
message: "no provider credentials found in agent vault",
|
|
100
|
+
meta: { agentName: deps.agentName },
|
|
101
|
+
});
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
for (const candidate of candidates) {
|
|
105
|
+
const config = { ...candidate.providerConfig, ...candidate.credentials };
|
|
106
|
+
(0, runtime_1.emitNervesEvent)({
|
|
107
|
+
level: "info",
|
|
108
|
+
component: "daemon",
|
|
109
|
+
event: "daemon.provider_discovery_ping",
|
|
110
|
+
message: `pinging provider: ${candidate.provider}`,
|
|
111
|
+
meta: { agentName: deps.agentName, provider: candidate.provider, source: candidate.agentName },
|
|
112
|
+
});
|
|
113
|
+
const result = await deps.pingProvider(candidate.provider, config);
|
|
114
|
+
if (result.ok) {
|
|
115
|
+
(0, runtime_1.emitNervesEvent)({
|
|
116
|
+
level: "info",
|
|
117
|
+
component: "daemon",
|
|
118
|
+
event: "daemon.provider_discovery_ok",
|
|
119
|
+
message: `provider discovery succeeded: ${candidate.provider}`,
|
|
120
|
+
meta: { agentName: deps.agentName, provider: candidate.provider, source: candidate.agentName },
|
|
121
|
+
});
|
|
122
|
+
return {
|
|
123
|
+
provider: candidate.provider,
|
|
124
|
+
credentials: candidate.credentials,
|
|
125
|
+
providerConfig: candidate.providerConfig,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
(0, runtime_1.emitNervesEvent)({
|
|
130
|
+
level: "warn",
|
|
131
|
+
component: "daemon",
|
|
132
|
+
event: "daemon.provider_discovery_all_failed",
|
|
133
|
+
message: "all vault provider candidates failed ping",
|
|
134
|
+
meta: { agentName: deps.agentName, candidateCount: candidates.length },
|
|
135
|
+
});
|
|
136
|
+
return null;
|
|
137
|
+
}
|