@ouro.bot/cli 0.1.0-alpha.59 → 0.1.0-alpha.591
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 +127 -23
- package/RepairGuide.ouro/agent.json +5 -0
- package/RepairGuide.ouro/psyche/IDENTITY.md +19 -0
- package/RepairGuide.ouro/psyche/SOUL.md +55 -0
- package/RepairGuide.ouro/skills/diagnose-broken-remote.md +63 -0
- package/RepairGuide.ouro/skills/diagnose-stacked-typed-issues.md +35 -0
- package/RepairGuide.ouro/skills/diagnose-sync-blocked.md +54 -0
- package/RepairGuide.ouro/skills/diagnose-vault-expired.md +60 -0
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +4 -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 +3811 -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 +193 -0
- package/dist/arc/presence.js +185 -0
- package/dist/arc/task-lifecycle.js +65 -0
- package/dist/heart/active-work.js +837 -26
- package/dist/heart/agent-entry.js +69 -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 +479 -0
- package/dist/heart/background-operations.js +281 -0
- package/dist/heart/bundle-state.js +168 -0
- package/dist/heart/commitments.js +111 -0
- package/dist/heart/config-registry.js +322 -0
- package/dist/heart/config.js +114 -118
- package/dist/heart/core.js +909 -246
- package/dist/heart/cross-chat-delivery.js +3 -18
- package/dist/heart/daemon/agent-config-check.js +419 -0
- package/dist/heart/daemon/agent-discovery.js +102 -3
- package/dist/heart/daemon/agent-service.js +522 -0
- package/dist/heart/daemon/agentic-repair.js +547 -0
- package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
- package/dist/heart/daemon/boot-sync-probe.js +197 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/cli-defaults.js +776 -0
- package/dist/heart/daemon/cli-exec.js +7559 -0
- package/dist/heart/daemon/cli-help.js +498 -0
- package/dist/heart/daemon/cli-parse.js +1592 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +763 -0
- package/dist/heart/daemon/cli-types.js +8 -0
- package/dist/heart/daemon/connect-bay.js +323 -0
- package/dist/heart/daemon/daemon-cli.js +29 -1703
- package/dist/heart/daemon/daemon-entry.js +387 -2
- package/dist/heart/daemon/daemon-health.js +176 -0
- package/dist/heart/daemon/daemon-rollup.js +57 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +88 -13
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +815 -70
- package/dist/heart/daemon/dns-workflow.js +394 -0
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +873 -0
- package/dist/heart/daemon/health-monitor.js +122 -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/human-command-screens.js +234 -0
- package/dist/heart/daemon/human-readiness.js +114 -0
- package/dist/heart/daemon/inner-status.js +89 -0
- package/dist/heart/daemon/interactive-repair.js +394 -0
- package/dist/heart/daemon/launchd.js +37 -8
- package/dist/heart/daemon/log-tailer.js +78 -9
- package/dist/heart/daemon/logs-prune.js +110 -0
- package/dist/heart/daemon/mcp-canary.js +297 -0
- package/dist/heart/daemon/os-cron-deps.js +135 -0
- package/dist/heart/daemon/os-cron.js +14 -12
- 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 +375 -33
- package/dist/heart/daemon/provider-discovery.js +137 -0
- package/dist/heart/daemon/provider-ping-progress.js +83 -0
- package/dist/heart/daemon/pulse.js +475 -0
- package/dist/heart/daemon/readiness-repair.js +365 -0
- package/dist/heart/daemon/run-hooks.js +2 -0
- package/dist/heart/daemon/runtime-logging.js +10 -2
- package/dist/heart/daemon/runtime-metadata.js +2 -30
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sense-manager.js +462 -38
- 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 +158 -11
- package/dist/heart/daemon/stale-bundle-prune.js +96 -0
- package/dist/heart/daemon/startup-tui.js +330 -0
- package/dist/heart/daemon/task-scheduler.js +3 -25
- package/dist/heart/daemon/terminal-ui.js +499 -0
- package/dist/heart/daemon/thoughts.js +162 -17
- package/dist/heart/daemon/up-progress.js +366 -0
- package/dist/heart/daemon/vault-items.js +56 -0
- package/dist/heart/delegation.js +1 -1
- package/dist/heart/habits/habit-migration.js +189 -0
- package/dist/heart/habits/habit-parser.js +140 -0
- package/dist/heart/habits/habit-runtime-state.js +100 -0
- package/dist/heart/habits/habit-scheduler.js +372 -0
- package/dist/heart/{daemon → hatch}/hatch-flow.js +32 -56
- package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
- package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
- package/dist/heart/{daemon → hatch}/specialist-tools.js +35 -12
- package/dist/heart/identity.js +166 -55
- 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/mail-import-discovery.js +353 -0
- package/dist/heart/mailbox/mailbox-http-hooks.js +66 -0
- package/dist/heart/mailbox/mailbox-http-response.js +7 -0
- package/dist/heart/mailbox/mailbox-http-routes.js +246 -0
- package/dist/heart/mailbox/mailbox-http-static.js +103 -0
- package/dist/heart/mailbox/mailbox-http-transport.js +116 -0
- package/dist/heart/mailbox/mailbox-http.js +99 -0
- package/dist/heart/mailbox/mailbox-read.js +31 -0
- package/dist/heart/mailbox/mailbox-types.js +27 -0
- package/dist/heart/mailbox/mailbox-view.js +195 -0
- package/dist/heart/mailbox/readers/agent-machine.js +382 -0
- package/dist/heart/mailbox/readers/continuity-readers.js +338 -0
- package/dist/heart/mailbox/readers/mail.js +362 -0
- package/dist/heart/mailbox/readers/runtime-readers.js +651 -0
- package/dist/heart/mailbox/readers/sessions.js +232 -0
- package/dist/heart/mailbox/readers/shared.js +111 -0
- package/dist/heart/mcp/mcp-server.js +656 -0
- package/dist/heart/migrate-config.js +100 -0
- package/dist/heart/model-capabilities.js +19 -0
- package/dist/heart/platform.js +81 -0
- package/dist/heart/provider-attempt.js +134 -0
- package/dist/heart/provider-binding-resolver.js +267 -0
- package/dist/heart/provider-credentials.js +425 -0
- package/dist/heart/provider-failover.js +301 -0
- package/dist/heart/provider-models.js +81 -0
- package/dist/heart/provider-ping.js +262 -0
- package/dist/heart/provider-readiness-cache.js +40 -0
- package/dist/heart/provider-visibility.js +188 -0
- package/dist/heart/providers/anthropic-token.js +131 -0
- package/dist/heart/providers/anthropic.js +139 -52
- package/dist/heart/providers/azure.js +97 -13
- package/dist/heart/providers/error-classification.js +127 -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 +26 -8
- package/dist/heart/providers/openai-codex.js +55 -40
- package/dist/heart/runtime-capability-check.js +170 -0
- package/dist/heart/runtime-credentials.js +367 -0
- package/dist/heart/runtime-cwd.js +87 -0
- package/dist/heart/sense-truth.js +13 -4
- package/dist/heart/session-activity.js +43 -22
- package/dist/heart/session-events.js +1149 -0
- package/dist/heart/session-playback-cli-main.js +5 -0
- package/dist/heart/session-playback-cli.js +36 -0
- package/dist/heart/session-playback.js +231 -0
- package/dist/heart/session-stats-cli-main.js +5 -0
- package/dist/heart/session-stats.js +182 -0
- package/dist/heart/session-transcript.js +243 -0
- package/dist/heart/start-of-turn-packet.js +345 -0
- package/dist/heart/streaming.js +44 -27
- package/dist/heart/sync-classification.js +176 -0
- package/dist/heart/sync.js +449 -0
- package/dist/heart/target-resolution.js +9 -5
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/timeouts.js +101 -0
- package/dist/heart/tool-activity-callbacks.js +59 -0
- package/dist/heart/tool-description.js +143 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-context.js +421 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
- package/dist/heart/versioning/ouro-path-installer.js +426 -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 +6 -1
- package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
- package/dist/mailbox-ui/assets/index-B-461hes.js +61 -0
- package/dist/mailbox-ui/assets/index-BPr5vNuM.css +1 -0
- package/dist/mailbox-ui/index.html +15 -0
- package/dist/mailroom/attention.js +167 -0
- package/dist/mailroom/autonomy.js +209 -0
- package/dist/mailroom/blob-store.js +674 -0
- package/dist/mailroom/body-cache.js +61 -0
- package/dist/mailroom/core.js +720 -0
- package/dist/mailroom/entry.js +160 -0
- package/dist/mailroom/file-store.js +430 -0
- package/dist/mailroom/mbox-import.js +383 -0
- package/dist/mailroom/outbound.js +380 -0
- package/dist/mailroom/policy.js +263 -0
- package/dist/mailroom/reader.js +233 -0
- package/dist/mailroom/search-cache.js +268 -0
- package/dist/mailroom/search-relevance.js +319 -0
- package/dist/mailroom/smtp-ingress.js +176 -0
- package/dist/mailroom/source-state.js +176 -0
- package/dist/mailroom/thread.js +109 -0
- package/dist/mailroom/travel-extract.js +89 -0
- package/dist/mind/bundle-manifest.js +7 -1
- package/dist/mind/context.js +165 -101
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +62 -75
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/friends/channel.js +39 -0
- package/dist/mind/friends/resolver.js +54 -2
- package/dist/mind/friends/store-file.js +39 -3
- package/dist/mind/friends/types.js +2 -2
- 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 +4 -0
- package/dist/mind/prompt-refresh.js +3 -2
- package/dist/mind/prompt.js +1039 -135
- 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 +129 -5
- 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/review/cli-main.js +5 -0
- package/dist/nerves/review/cli.js +156 -0
- package/dist/nerves/review/core.js +152 -0
- package/dist/nerves/runtime.js +5 -1
- 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 +997 -0
- package/dist/repertoire/bundle-templates.js +72 -0
- package/dist/repertoire/bw-installer.js +180 -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 +178 -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 +396 -0
- package/dist/repertoire/mcp-client.js +295 -0
- package/dist/repertoire/mcp-manager.js +362 -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 +31 -5
- package/dist/repertoire/tasks/fix.js +182 -0
- package/dist/repertoire/tasks/index.js +16 -4
- 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 +47 -1082
- package/dist/repertoire/tools-bluebubbles.js +1 -0
- package/dist/repertoire/tools-bridge.js +142 -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 +381 -0
- package/dist/repertoire/tools-files.js +342 -0
- package/dist/repertoire/tools-flight.js +224 -0
- package/dist/repertoire/tools-flow.js +119 -0
- package/dist/repertoire/tools-github.js +1 -7
- package/dist/repertoire/tools-mail.js +1911 -0
- package/dist/repertoire/tools-notes.js +421 -0
- package/dist/repertoire/tools-session.js +809 -0
- package/dist/repertoire/tools-shell.js +120 -0
- package/dist/repertoire/tools-stripe.js +180 -0
- package/dist/repertoire/tools-surface.js +345 -0
- package/dist/repertoire/tools-teams.js +9 -39
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-trip.js +604 -0
- package/dist/repertoire/tools-user-profile.js +144 -0
- package/dist/repertoire/tools-vault.js +40 -0
- package/dist/repertoire/tools-voice.js +144 -0
- package/dist/repertoire/tools.js +115 -103
- package/dist/repertoire/travel-api-client.js +360 -0
- package/dist/repertoire/user-profile.js +131 -0
- package/dist/repertoire/vault-setup.js +246 -0
- package/dist/repertoire/vault-unlock.js +594 -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/active-turns.js +216 -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} +219 -18
- package/dist/senses/bluebubbles/entry.js +77 -0
- package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +20 -3
- package/dist/senses/bluebubbles/index.js +2487 -0
- package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
- package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +33 -12
- package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +3 -3
- package/dist/senses/bluebubbles/processed-log.js +133 -0
- package/dist/senses/bluebubbles/replay.js +137 -0
- package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
- package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
- package/dist/senses/bluebubbles-meta-guard.js +40 -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 +607 -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 +85 -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 +100 -0
- package/dist/senses/cli.js +516 -204
- package/dist/senses/commands.js +66 -3
- package/dist/senses/habit-turn-message.js +108 -0
- package/dist/senses/inner-dialog-worker.js +175 -21
- package/dist/senses/inner-dialog.js +330 -27
- package/dist/senses/mail-entry.js +66 -0
- package/dist/senses/mail.js +379 -0
- package/dist/senses/pipeline.js +654 -181
- package/dist/senses/proactive-content-guard.js +51 -0
- package/dist/senses/shared-turn.js +392 -0
- package/dist/senses/surface-tool.js +70 -0
- package/dist/senses/teams-entry.js +60 -8
- package/dist/senses/teams.js +387 -98
- package/dist/senses/trust-gate.js +100 -5
- package/dist/senses/voice/audio-playback.js +237 -0
- package/dist/senses/voice/audio-routing.js +119 -0
- package/dist/senses/voice/elevenlabs.js +202 -0
- package/dist/senses/voice/floor-control.js +431 -0
- package/dist/senses/voice/floor-controller.js +115 -0
- package/dist/senses/voice/golden-path.js +116 -0
- package/dist/senses/voice/index.js +29 -0
- package/dist/senses/voice/meeting.js +113 -0
- package/dist/senses/voice/outbound.js +190 -0
- package/dist/senses/voice/phone.js +33 -0
- package/dist/senses/voice/playback.js +139 -0
- package/dist/senses/voice/realtime-eval.js +496 -0
- package/dist/senses/voice/realtime-trace.js +531 -0
- package/dist/senses/voice/transcript.js +70 -0
- package/dist/senses/voice/turn.js +191 -0
- package/dist/senses/voice/twilio-phone-runtime.js +794 -0
- package/dist/senses/voice/twilio-phone.js +5016 -0
- package/dist/senses/voice/types.js +2 -0
- package/dist/senses/voice/whisper.js +161 -0
- package/dist/senses/voice-entry.js +81 -0
- package/dist/senses/voice-realtime-eval-command.js +99 -0
- package/dist/senses/voice-realtime-eval-entry.js +21 -0
- package/dist/senses/voice-twilio-entry.js +87 -0
- package/dist/trips/core.js +138 -0
- package/dist/trips/store.js +265 -0
- package/package.json +42 -7
- package/skills/agent-commerce.md +106 -0
- package/skills/browser-navigation.md +117 -0
- package/skills/commerce-setup-guide.md +116 -0
- package/skills/commerce-setup.md +84 -0
- package/skills/configure-dev-tools.md +99 -0
- package/skills/travel-planning.md +138 -0
- package/dist/heart/daemon/auth-flow.js +0 -351
- package/dist/heart/daemon/ouro-path-installer.js +0 -178
- package/dist/heart/daemon/subagent-installer.js +0 -166
- package/dist/heart/safe-workspace.js +0 -228
- package/dist/heart/session-recall.js +0 -116
- package/dist/mind/associative-recall.js +0 -209
- package/dist/senses/bluebubbles-entry.js +0 -13
- package/dist/senses/bluebubbles.js +0 -1177
- package/dist/senses/debug-activity.js +0 -148
- package/subagents/README.md +0 -86
- package/subagents/work-doer.md +0 -237
- package/subagents/work-merger.md +0 -618
- package/subagents/work-planner.md +0 -390
- /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
|
@@ -43,6 +43,7 @@ exports.crontabLine = crontabLine;
|
|
|
43
43
|
const os = __importStar(require("os"));
|
|
44
44
|
const runtime_1 = require("../../nerves/runtime");
|
|
45
45
|
const PLIST_PREFIX = "bot.ouro.";
|
|
46
|
+
const DAEMON_PLIST_FILENAME = "bot.ouro.daemon.plist";
|
|
46
47
|
function plistLabel(job) {
|
|
47
48
|
return `${PLIST_PREFIX}${job.agent}.${job.taskId}`;
|
|
48
49
|
}
|
|
@@ -80,7 +81,7 @@ function scheduleToCalendarInterval(schedule) {
|
|
|
80
81
|
result.Month = parseInt(month, 10);
|
|
81
82
|
return Object.keys(result).length > 0 ? result : null;
|
|
82
83
|
}
|
|
83
|
-
function generatePlistXml(job) {
|
|
84
|
+
function generatePlistXml(job, envPath) {
|
|
84
85
|
const label = plistLabel(job);
|
|
85
86
|
const seconds = cadenceToSeconds(job.schedule);
|
|
86
87
|
const calendar = seconds === null ? scheduleToCalendarInterval(job.schedule) : null;
|
|
@@ -97,7 +98,7 @@ function generatePlistXml(job) {
|
|
|
97
98
|
else {
|
|
98
99
|
triggerXml = ` <key>StartInterval</key>\n <integer>1800</integer>`;
|
|
99
100
|
}
|
|
100
|
-
|
|
101
|
+
const lines = [
|
|
101
102
|
`<?xml version="1.0" encoding="UTF-8"?>`,
|
|
102
103
|
`<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">`,
|
|
103
104
|
`<plist version="1.0">`,
|
|
@@ -110,14 +111,12 @@ function generatePlistXml(job) {
|
|
|
110
111
|
...job.command.split(" ").slice(1).map((arg) => ` <string>${arg}</string>`),
|
|
111
112
|
` </array>`,
|
|
112
113
|
triggerXml,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
` <key>
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
``,
|
|
120
|
-
].join("\n");
|
|
114
|
+
];
|
|
115
|
+
if (envPath) {
|
|
116
|
+
lines.push(` <key>EnvironmentVariables</key>`, ` <dict>`, ` <key>PATH</key>`, ` <string>${envPath}</string>`, ` </dict>`);
|
|
117
|
+
}
|
|
118
|
+
lines.push(` <key>StandardOutPath</key>`, ` <string>/tmp/${label}.stdout.log</string>`, ` <key>StandardErrorPath</key>`, ` <string>/tmp/${label}.stderr.log</string>`, `</dict>`, `</plist>`, ``);
|
|
119
|
+
return lines.join("\n");
|
|
121
120
|
}
|
|
122
121
|
class LaunchdCronManager {
|
|
123
122
|
deps;
|
|
@@ -148,7 +147,7 @@ class LaunchdCronManager {
|
|
|
148
147
|
const label = plistLabel(job);
|
|
149
148
|
const filename = `${label}.plist`;
|
|
150
149
|
const fullPath = `${this.launchAgentsDir}/${filename}`;
|
|
151
|
-
const xml = generatePlistXml(job);
|
|
150
|
+
const xml = generatePlistXml(job, this.deps.envPath);
|
|
152
151
|
try {
|
|
153
152
|
this.deps.exec(`launchctl unload "${fullPath}"`);
|
|
154
153
|
}
|
|
@@ -178,7 +177,9 @@ class LaunchdCronManager {
|
|
|
178
177
|
listPlistFiles() {
|
|
179
178
|
if (!this.deps.existsFile(this.launchAgentsDir))
|
|
180
179
|
return [];
|
|
181
|
-
return this.deps.listDir(this.launchAgentsDir).filter((f) => f.startsWith(PLIST_PREFIX) &&
|
|
180
|
+
return this.deps.listDir(this.launchAgentsDir).filter((f) => f.startsWith(PLIST_PREFIX) &&
|
|
181
|
+
f.endsWith(".plist") &&
|
|
182
|
+
f !== DAEMON_PLIST_FILENAME);
|
|
182
183
|
}
|
|
183
184
|
}
|
|
184
185
|
exports.LaunchdCronManager = LaunchdCronManager;
|
|
@@ -248,6 +249,7 @@ function createOsCronManager(options = {}) {
|
|
|
248
249
|
listDir: () => [],
|
|
249
250
|
mkdirp: () => { },
|
|
250
251
|
homeDir: os.homedir(),
|
|
252
|
+
envPath: process.env.PATH ?? "",
|
|
251
253
|
};
|
|
252
254
|
/* v8 ignore stop */
|
|
253
255
|
return new LaunchdCronManager(deps);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
const runtime_1 = require("../../nerves/runtime");
|
|
5
5
|
const runtime_logging_1 = require("./runtime-logging");
|
|
6
|
-
const ouro_bot_wrapper_1 = require("
|
|
6
|
+
const ouro_bot_wrapper_1 = require("../versioning/ouro-bot-wrapper");
|
|
7
7
|
(0, runtime_logging_1.configureDaemonRuntimeLogger)("ouro-bot");
|
|
8
8
|
(0, runtime_1.emitNervesEvent)({
|
|
9
9
|
component: "daemon",
|
|
@@ -12,12 +12,14 @@ const ouro_bot_wrapper_1 = require("./ouro-bot-wrapper");
|
|
|
12
12
|
meta: { args: process.argv.slice(2) },
|
|
13
13
|
});
|
|
14
14
|
void (0, ouro_bot_wrapper_1.runOuroBotWrapper)(process.argv.slice(2)).catch((error) => {
|
|
15
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
16
|
+
process.stderr.write(`${message}\n`);
|
|
15
17
|
(0, runtime_1.emitNervesEvent)({
|
|
16
18
|
level: "error",
|
|
17
19
|
component: "daemon",
|
|
18
20
|
event: "daemon.ouro_bot_entry_error",
|
|
19
21
|
message: "ouro.bot wrapper entrypoint failed",
|
|
20
|
-
meta: { error:
|
|
22
|
+
meta: { error: message },
|
|
21
23
|
});
|
|
22
24
|
process.exit(1);
|
|
23
25
|
});
|
|
@@ -12,12 +12,14 @@ const runtime_logging_1 = require("./runtime-logging");
|
|
|
12
12
|
meta: { args: process.argv.slice(2) },
|
|
13
13
|
});
|
|
14
14
|
void (0, daemon_cli_1.runOuroCli)(process.argv.slice(2)).catch((error) => {
|
|
15
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
16
|
+
process.stderr.write(`${message}\n`);
|
|
15
17
|
(0, runtime_1.emitNervesEvent)({
|
|
16
18
|
level: "error",
|
|
17
19
|
component: "daemon",
|
|
18
20
|
event: "daemon.cli_entry_error",
|
|
19
21
|
message: "ouro CLI entrypoint failed",
|
|
20
|
-
meta: { error:
|
|
22
|
+
meta: { error: message },
|
|
21
23
|
});
|
|
22
24
|
process.exit(1);
|
|
23
25
|
});
|
|
@@ -51,24 +51,108 @@ class DaemonProcessManager {
|
|
|
51
51
|
now;
|
|
52
52
|
setTimeoutFn;
|
|
53
53
|
clearTimeoutFn;
|
|
54
|
+
cooldownRecoveryMs;
|
|
55
|
+
maxCooldownRetries;
|
|
56
|
+
startupStaleAfterMs;
|
|
54
57
|
existsSyncFn;
|
|
58
|
+
configCheckFn;
|
|
59
|
+
statusWriterFn;
|
|
60
|
+
onSnapshotChangeFn;
|
|
61
|
+
/**
|
|
62
|
+
* Notify the snapshot-change observer (if registered). Swallows any
|
|
63
|
+
* errors from the observer so process lifecycle code never fails
|
|
64
|
+
* because the observer threw.
|
|
65
|
+
*/
|
|
66
|
+
notifySnapshotChange(snapshot) {
|
|
67
|
+
if (!this.onSnapshotChangeFn)
|
|
68
|
+
return;
|
|
69
|
+
try {
|
|
70
|
+
this.onSnapshotChangeFn(snapshot);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
(0, runtime_1.emitNervesEvent)({
|
|
74
|
+
level: "warn",
|
|
75
|
+
component: "daemon",
|
|
76
|
+
event: "daemon.snapshot_change_observer_error",
|
|
77
|
+
message: "snapshot-change observer threw",
|
|
78
|
+
meta: {
|
|
79
|
+
agent: snapshot.name,
|
|
80
|
+
error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error),
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
writeStatus(agent, text) {
|
|
86
|
+
try {
|
|
87
|
+
this.statusWriterFn(text);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
(0, runtime_1.emitNervesEvent)({
|
|
91
|
+
level: "warn",
|
|
92
|
+
component: "daemon",
|
|
93
|
+
event: "daemon.status_writer_error",
|
|
94
|
+
message: "daemon status writer threw",
|
|
95
|
+
meta: {
|
|
96
|
+
agent,
|
|
97
|
+
error: error instanceof Error ? error.message : String(error),
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
currentTimeMs() {
|
|
103
|
+
const value = this.now();
|
|
104
|
+
return Number.isFinite(value) ? value : Date.now();
|
|
105
|
+
}
|
|
106
|
+
isStartAttemptCurrent(state, attemptId) {
|
|
107
|
+
return state.startAttemptId === attemptId;
|
|
108
|
+
}
|
|
109
|
+
markConfigCheckFailed(state, errorReason, fixHint) {
|
|
110
|
+
const agent = state.config.name;
|
|
111
|
+
state.snapshot.status = "crashed";
|
|
112
|
+
state.snapshot.errorReason = errorReason;
|
|
113
|
+
state.snapshot.fixHint = fixHint;
|
|
114
|
+
(0, runtime_1.emitNervesEvent)({
|
|
115
|
+
level: "error",
|
|
116
|
+
component: "daemon",
|
|
117
|
+
event: "daemon.agent_config_invalid",
|
|
118
|
+
message: errorReason,
|
|
119
|
+
meta: { agent, fix: fixHint },
|
|
120
|
+
});
|
|
121
|
+
this.writeStatus(agent, `[daemon] ${agent}: ${errorReason}\n` +
|
|
122
|
+
(fixHint ? ` Fix: ${fixHint}\n` : ""));
|
|
123
|
+
this.notifySnapshotChange(state.snapshot);
|
|
124
|
+
}
|
|
55
125
|
constructor(options) {
|
|
56
126
|
this.maxRestartsPerHour = options.maxRestartsPerHour ?? 10;
|
|
57
127
|
this.stabilityThresholdMs = options.stabilityThresholdMs ?? 60_000;
|
|
58
128
|
this.initialBackoffMs = options.initialBackoffMs ?? 1_000;
|
|
59
129
|
this.maxBackoffMs = options.maxBackoffMs ?? 60_000;
|
|
130
|
+
this.cooldownRecoveryMs = options.cooldownRecoveryMs ?? 5 * 60 * 1_000;
|
|
131
|
+
this.maxCooldownRetries = options.maxCooldownRetries ?? 3;
|
|
132
|
+
this.startupStaleAfterMs = options.startupStaleAfterMs ?? 45_000;
|
|
60
133
|
this.spawnFn = options.spawn ?? ((command, args, spawnOptions) => (0, child_process_1.spawn)(command, args, spawnOptions));
|
|
61
134
|
this.now = options.now ?? (() => Date.now());
|
|
62
135
|
this.setTimeoutFn = options.setTimeoutFn ?? ((cb, delay) => setTimeout(cb, delay));
|
|
63
136
|
this.clearTimeoutFn = options.clearTimeoutFn ?? ((timer) => clearTimeout(timer));
|
|
64
137
|
this.existsSyncFn = options.existsSync ?? null;
|
|
138
|
+
this.configCheckFn = options.configCheck ?? null;
|
|
139
|
+
this.statusWriterFn = options.statusWriter ?? ((text) => {
|
|
140
|
+
process.stderr.write(text);
|
|
141
|
+
});
|
|
142
|
+
this.onSnapshotChangeFn = options.onSnapshotChange ?? null;
|
|
65
143
|
for (const agent of options.agents) {
|
|
66
144
|
this.agents.set(agent.name, {
|
|
67
145
|
config: agent,
|
|
68
146
|
process: null,
|
|
147
|
+
startInFlight: false,
|
|
148
|
+
startAttemptedAtMs: null,
|
|
149
|
+
startAttemptId: 0,
|
|
69
150
|
restartTimer: null,
|
|
70
151
|
crashTimestamps: [],
|
|
71
152
|
stopRequested: false,
|
|
153
|
+
cooldownTimer: null,
|
|
154
|
+
cooldownRetryCount: 0,
|
|
155
|
+
fastCrashCount: 0,
|
|
72
156
|
snapshot: {
|
|
73
157
|
name: agent.name,
|
|
74
158
|
channel: agent.channel,
|
|
@@ -78,6 +162,10 @@ class DaemonProcessManager {
|
|
|
78
162
|
startedAt: null,
|
|
79
163
|
lastCrashAt: null,
|
|
80
164
|
backoffMs: this.initialBackoffMs,
|
|
165
|
+
lastExitCode: null,
|
|
166
|
+
lastSignal: null,
|
|
167
|
+
errorReason: null,
|
|
168
|
+
fixHint: null,
|
|
81
169
|
},
|
|
82
170
|
});
|
|
83
171
|
}
|
|
@@ -89,53 +177,203 @@ class DaemonProcessManager {
|
|
|
89
177
|
}
|
|
90
178
|
}
|
|
91
179
|
}
|
|
180
|
+
triggerAutoStartAgents() {
|
|
181
|
+
for (const state of this.agents.values()) {
|
|
182
|
+
if (!state.config.autoStart)
|
|
183
|
+
continue;
|
|
184
|
+
void this.startAgent(state.config.name).catch((error) => {
|
|
185
|
+
const errorReason = error instanceof Error ? error.message : String(error);
|
|
186
|
+
this.markConfigCheckFailed(state, `agent startup threw before the worker could run: ${errorReason}`, "Run 'ouro doctor' for diagnostics, then retry 'ouro up'.");
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
92
190
|
async startAgent(agent) {
|
|
93
191
|
const state = this.requireAgent(agent);
|
|
94
|
-
if (state.process)
|
|
192
|
+
if (state.process || state.startInFlight)
|
|
95
193
|
return;
|
|
96
|
-
|
|
97
|
-
state.
|
|
98
|
-
state.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
state.
|
|
194
|
+
const attemptId = state.startAttemptId + 1;
|
|
195
|
+
state.startAttemptId = attemptId;
|
|
196
|
+
state.startInFlight = true;
|
|
197
|
+
state.startAttemptedAtMs = this.currentTimeMs();
|
|
198
|
+
try {
|
|
199
|
+
this.clearRestartTimer(state);
|
|
200
|
+
state.stopRequested = false;
|
|
201
|
+
state.snapshot.status = "starting";
|
|
202
|
+
if (this.configCheckFn) {
|
|
203
|
+
let result;
|
|
204
|
+
try {
|
|
205
|
+
result = await this.configCheckFn(agent);
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
const errorReason = error instanceof Error ? error.message : String(error);
|
|
209
|
+
this.markConfigCheckFailed(state, `agent config validation threw: ${errorReason}`, "Run 'ouro doctor' for diagnostics, then retry 'ouro up'.");
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (!this.isStartAttemptCurrent(state, attemptId))
|
|
213
|
+
return;
|
|
214
|
+
if (state.stopRequested) {
|
|
215
|
+
state.snapshot.status = "stopped";
|
|
216
|
+
state.snapshot.pid = null;
|
|
217
|
+
this.notifySnapshotChange(state.snapshot);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (result.skip) {
|
|
221
|
+
state.snapshot.status = "stopped";
|
|
222
|
+
state.snapshot.errorReason = null;
|
|
223
|
+
state.snapshot.fixHint = null;
|
|
224
|
+
(0, runtime_1.emitNervesEvent)({
|
|
225
|
+
component: "daemon",
|
|
226
|
+
event: "daemon.agent_config_skipped",
|
|
227
|
+
message: result.error ?? "agent start skipped by config check",
|
|
228
|
+
meta: { agent, fix: result.fix ?? null },
|
|
229
|
+
});
|
|
230
|
+
this.notifySnapshotChange(state.snapshot);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (!result.ok) {
|
|
234
|
+
this.markConfigCheckFailed(state, result.error ?? "agent config validation failed", result.fix ?? null);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
/* v8 ignore next -- defensive duplicate stale-start guard before spawn preparation @preserve */
|
|
238
|
+
if (!this.isStartAttemptCurrent(state, attemptId))
|
|
239
|
+
return;
|
|
240
|
+
// Config check passed — clear any prior error so the pulse stops
|
|
241
|
+
// reporting the broken state. This is the recovery path: the user
|
|
242
|
+
// fixed their secrets/config, the next startAgent attempt sees a
|
|
243
|
+
// valid config, and the pulse goes quiet.
|
|
244
|
+
state.snapshot.errorReason = null;
|
|
245
|
+
state.snapshot.fixHint = null;
|
|
246
|
+
}
|
|
247
|
+
const runCwd = (0, identity_1.getRepoRoot)();
|
|
248
|
+
const entryScript = path.join((0, identity_1.getRepoRoot)(), "dist", state.config.entry);
|
|
249
|
+
if (this.existsSyncFn && !this.existsSyncFn(entryScript)) {
|
|
250
|
+
state.snapshot.status = "crashed";
|
|
251
|
+
(0, runtime_1.emitNervesEvent)({
|
|
252
|
+
level: "error",
|
|
253
|
+
component: "daemon",
|
|
254
|
+
event: "daemon.agent_entry_missing",
|
|
255
|
+
message: "agent entry script does not exist — cannot spawn. Run 'ouro daemon install' from the correct location.",
|
|
256
|
+
meta: { agent, entryScript },
|
|
257
|
+
});
|
|
258
|
+
this.notifySnapshotChange(state.snapshot);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
/* v8 ignore next -- defensive duplicate stale-start guard immediately before spawn @preserve */
|
|
262
|
+
if (!this.isStartAttemptCurrent(state, attemptId))
|
|
263
|
+
return;
|
|
264
|
+
if (state.stopRequested) {
|
|
265
|
+
state.snapshot.status = "stopped";
|
|
266
|
+
state.snapshot.pid = null;
|
|
267
|
+
this.notifySnapshotChange(state.snapshot);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const args = [entryScript, "--agent", state.config.agentArg ?? agent, ...(state.config.args ?? [])];
|
|
271
|
+
const child = this.spawnFn("node", args, {
|
|
272
|
+
cwd: runCwd,
|
|
273
|
+
env: state.config.env ? { ...process.env, ...state.config.env } : process.env,
|
|
274
|
+
stdio: ["ignore", "ignore", "ignore", "ipc"],
|
|
275
|
+
});
|
|
276
|
+
/* v8 ignore next 7 -- defensive: spawn should always return a ChildProcess @preserve */
|
|
277
|
+
if (!child) {
|
|
278
|
+
state.snapshot.status = "crashed";
|
|
279
|
+
(0, runtime_1.emitNervesEvent)({ level: "error", component: "daemon", event: "daemon.agent_spawn_failed", message: "spawn returned null", meta: { agent } });
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (!this.isStartAttemptCurrent(state, attemptId)) {
|
|
283
|
+
try {
|
|
284
|
+
child.kill("SIGTERM");
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
(0, runtime_1.emitNervesEvent)({
|
|
288
|
+
level: "warn",
|
|
289
|
+
component: "daemon",
|
|
290
|
+
event: "daemon.agent_stop_error",
|
|
291
|
+
message: "failed to stop stale managed agent startup",
|
|
292
|
+
meta: { agent },
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
state.process = child;
|
|
298
|
+
state.snapshot.status = "running";
|
|
299
|
+
state.snapshot.pid = child.pid ?? null;
|
|
300
|
+
state.snapshot.startedAt = new Date(this.currentTimeMs()).toISOString();
|
|
301
|
+
const bootstrap = state.config.getRuntimeCredentialBootstrap?.() ?? null;
|
|
302
|
+
if (bootstrap) {
|
|
303
|
+
const message = {
|
|
304
|
+
type: "ouro.runtimeCredentialBootstrap",
|
|
305
|
+
agentName: bootstrap.agentName,
|
|
306
|
+
};
|
|
307
|
+
if (bootstrap.runtimeConfig)
|
|
308
|
+
message.runtimeConfig = bootstrap.runtimeConfig;
|
|
309
|
+
if (bootstrap.machineRuntimeConfig)
|
|
310
|
+
message.machineRuntimeConfig = bootstrap.machineRuntimeConfig;
|
|
311
|
+
if (bootstrap.machineId)
|
|
312
|
+
message.machineId = bootstrap.machineId;
|
|
313
|
+
if (bootstrap.providerCredentialRecords)
|
|
314
|
+
message.providerCredentialRecords = bootstrap.providerCredentialRecords;
|
|
315
|
+
try {
|
|
316
|
+
child.send?.(message);
|
|
317
|
+
(0, runtime_1.emitNervesEvent)({
|
|
318
|
+
component: "daemon",
|
|
319
|
+
event: "daemon.agent_runtime_credentials_bootstrap_sent",
|
|
320
|
+
message: "sent runtime credential bootstrap to managed agent process",
|
|
321
|
+
meta: {
|
|
322
|
+
agent,
|
|
323
|
+
runtimeConfig: !!bootstrap.runtimeConfig,
|
|
324
|
+
machineRuntimeConfig: !!bootstrap.machineRuntimeConfig,
|
|
325
|
+
providerCredentialRecords: bootstrap.providerCredentialRecords?.length ?? 0,
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
(0, runtime_1.emitNervesEvent)({
|
|
331
|
+
level: "warn",
|
|
332
|
+
component: "daemon",
|
|
333
|
+
event: "daemon.agent_ipc_send_error",
|
|
334
|
+
message: "failed to send runtime credential bootstrap to managed agent process",
|
|
335
|
+
meta: { agent, error: error instanceof Error ? error.message : String(error) },
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
103
339
|
(0, runtime_1.emitNervesEvent)({
|
|
104
|
-
level: "error",
|
|
105
340
|
component: "daemon",
|
|
106
|
-
event: "daemon.
|
|
107
|
-
message: "
|
|
108
|
-
meta: { agent,
|
|
341
|
+
event: "daemon.agent_started",
|
|
342
|
+
message: "daemon started managed agent process",
|
|
343
|
+
meta: { agent, pid: child.pid ?? null, cwd: runCwd },
|
|
344
|
+
});
|
|
345
|
+
this.notifySnapshotChange(state.snapshot);
|
|
346
|
+
/* v8 ignore start — child process error handler; requires real spawn to trigger */
|
|
347
|
+
child.on("error", (err) => {
|
|
348
|
+
(0, runtime_1.emitNervesEvent)({
|
|
349
|
+
level: "warn",
|
|
350
|
+
component: "daemon",
|
|
351
|
+
event: "daemon.agent_process_error",
|
|
352
|
+
message: "managed agent process emitted error",
|
|
353
|
+
meta: { agent, error: err.message },
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
/* v8 ignore stop */
|
|
357
|
+
child.once("exit", (code, signal) => {
|
|
358
|
+
this.onExit(state, code, signal);
|
|
109
359
|
});
|
|
110
|
-
return;
|
|
111
360
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
state.process = child;
|
|
119
|
-
state.snapshot.status = "running";
|
|
120
|
-
state.snapshot.pid = child.pid ?? null;
|
|
121
|
-
state.snapshot.startedAt = new Date(this.now()).toISOString();
|
|
122
|
-
(0, runtime_1.emitNervesEvent)({
|
|
123
|
-
component: "daemon",
|
|
124
|
-
event: "daemon.agent_started",
|
|
125
|
-
message: "daemon started managed agent process",
|
|
126
|
-
meta: { agent, pid: child.pid ?? null, cwd: runCwd },
|
|
127
|
-
});
|
|
128
|
-
child.once("exit", (code, signal) => {
|
|
129
|
-
this.onExit(state, code, signal);
|
|
130
|
-
});
|
|
361
|
+
finally {
|
|
362
|
+
if (this.isStartAttemptCurrent(state, attemptId)) {
|
|
363
|
+
state.startInFlight = false;
|
|
364
|
+
state.startAttemptedAtMs = null;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
131
367
|
}
|
|
132
368
|
async stopAgent(agent) {
|
|
133
369
|
const state = this.requireAgent(agent);
|
|
134
370
|
this.clearRestartTimer(state);
|
|
371
|
+
this.clearCooldownTimer(state);
|
|
135
372
|
state.stopRequested = true;
|
|
136
373
|
if (!state.process) {
|
|
137
374
|
state.snapshot.status = "stopped";
|
|
138
375
|
state.snapshot.pid = null;
|
|
376
|
+
this.notifySnapshotChange(state.snapshot);
|
|
139
377
|
return;
|
|
140
378
|
}
|
|
141
379
|
const child = state.process;
|
|
@@ -154,8 +392,34 @@ class DaemonProcessManager {
|
|
|
154
392
|
meta: { agent },
|
|
155
393
|
});
|
|
156
394
|
}
|
|
395
|
+
this.notifySnapshotChange(state.snapshot);
|
|
157
396
|
}
|
|
158
397
|
async restartAgent(agent) {
|
|
398
|
+
const state = this.requireAgent(agent);
|
|
399
|
+
if (state.startInFlight && !state.process) {
|
|
400
|
+
const startedAt = state.startAttemptedAtMs;
|
|
401
|
+
/* v8 ignore next -- defensive: startInFlight always records a start timestamp @preserve */
|
|
402
|
+
const elapsedMs = startedAt === null ? 0 : this.currentTimeMs() - startedAt;
|
|
403
|
+
if (elapsedMs < this.startupStaleAfterMs) {
|
|
404
|
+
(0, runtime_1.emitNervesEvent)({
|
|
405
|
+
component: "daemon",
|
|
406
|
+
event: "daemon.agent_restart_deferred",
|
|
407
|
+
message: "managed agent restart skipped while startup is already in flight",
|
|
408
|
+
meta: { agent, elapsedMs, startupStaleAfterMs: this.startupStaleAfterMs },
|
|
409
|
+
});
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
(0, runtime_1.emitNervesEvent)({
|
|
413
|
+
level: "warn",
|
|
414
|
+
component: "daemon",
|
|
415
|
+
event: "daemon.agent_startup_stale_recovered",
|
|
416
|
+
message: "managed agent startup was stale; clearing the pending attempt before restart",
|
|
417
|
+
meta: { agent, elapsedMs, startupStaleAfterMs: this.startupStaleAfterMs },
|
|
418
|
+
});
|
|
419
|
+
state.startAttemptId += 1;
|
|
420
|
+
state.startInFlight = false;
|
|
421
|
+
state.startAttemptedAtMs = null;
|
|
422
|
+
}
|
|
159
423
|
await this.stopAgent(agent);
|
|
160
424
|
await this.startAgent(agent);
|
|
161
425
|
}
|
|
@@ -191,9 +455,13 @@ class DaemonProcessManager {
|
|
|
191
455
|
if (!state.process)
|
|
192
456
|
return;
|
|
193
457
|
state.process = null;
|
|
458
|
+
state.startInFlight = false;
|
|
459
|
+
state.startAttemptedAtMs = null;
|
|
194
460
|
state.snapshot.pid = null;
|
|
461
|
+
state.snapshot.lastExitCode = code;
|
|
462
|
+
state.snapshot.lastSignal = signal;
|
|
195
463
|
const crashed = !state.stopRequested && code !== 0;
|
|
196
|
-
const now = this.
|
|
464
|
+
const now = this.currentTimeMs();
|
|
197
465
|
const startedAt = state.snapshot.startedAt ? Date.parse(state.snapshot.startedAt) : now;
|
|
198
466
|
const runDuration = Math.max(0, now - startedAt);
|
|
199
467
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -208,13 +476,45 @@ class DaemonProcessManager {
|
|
|
208
476
|
if (runDuration >= this.stabilityThresholdMs) {
|
|
209
477
|
state.snapshot.backoffMs = this.initialBackoffMs;
|
|
210
478
|
}
|
|
479
|
+
this.notifySnapshotChange(state.snapshot);
|
|
211
480
|
return;
|
|
212
481
|
}
|
|
213
482
|
state.snapshot.lastCrashAt = new Date(now).toISOString();
|
|
483
|
+
// Fast-crash detection: if the agent dies within 5 seconds of starting, it's likely
|
|
484
|
+
// a configuration issue (missing credentials, bad provider, etc.) not a transient failure.
|
|
485
|
+
// After 3 consecutive fast crashes, stop retrying and mark as config-failed.
|
|
486
|
+
const FAST_CRASH_THRESHOLD_MS = 5000;
|
|
487
|
+
const FAST_CRASH_MAX = 3;
|
|
488
|
+
if (runDuration < FAST_CRASH_THRESHOLD_MS) {
|
|
489
|
+
state.fastCrashCount = state.fastCrashCount + 1;
|
|
490
|
+
if (state.fastCrashCount >= FAST_CRASH_MAX) {
|
|
491
|
+
state.snapshot.status = "crashed";
|
|
492
|
+
// Capture the fast-crash diagnosis on the snapshot so it surfaces
|
|
493
|
+
// via the pulse. The error message is prescriptive: it tells the
|
|
494
|
+
// user (and their sibling agents) exactly what to do.
|
|
495
|
+
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).`;
|
|
496
|
+
state.snapshot.fixHint = `Fix the config and run \`ouro up\` to restart, or check daemon logs for the underlying error.`;
|
|
497
|
+
(0, runtime_1.emitNervesEvent)({
|
|
498
|
+
level: "error",
|
|
499
|
+
component: "daemon",
|
|
500
|
+
event: "daemon.agent_config_failure",
|
|
501
|
+
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.`,
|
|
502
|
+
meta: { agent: state.config.name, fastCrashCount: state.fastCrashCount, avgRunDurationMs: runDuration },
|
|
503
|
+
});
|
|
504
|
+
this.notifySnapshotChange(state.snapshot);
|
|
505
|
+
return; // Don't schedule cooldown recovery — this needs human/agent intervention
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
// Reset fast-crash counter on a stable run
|
|
510
|
+
state.fastCrashCount = 0;
|
|
511
|
+
}
|
|
214
512
|
state.crashTimestamps = state.crashTimestamps.filter((crashTs) => crashTs >= startOfHour(now));
|
|
215
513
|
state.crashTimestamps.push(now);
|
|
216
514
|
if (state.crashTimestamps.length > this.maxRestartsPerHour) {
|
|
217
515
|
state.snapshot.status = "crashed";
|
|
516
|
+
state.snapshot.errorReason = `agent exceeded restart limit (${this.maxRestartsPerHour}/hr) — entering cooldown`;
|
|
517
|
+
state.snapshot.fixHint = "investigate why the agent keeps crashing; cooldown will retry shortly";
|
|
218
518
|
(0, runtime_1.emitNervesEvent)({
|
|
219
519
|
level: "error",
|
|
220
520
|
component: "daemon",
|
|
@@ -222,6 +522,8 @@ class DaemonProcessManager {
|
|
|
222
522
|
message: "managed agent exceeded restart limit and is marked crashed",
|
|
223
523
|
meta: { agent: state.config.name, maxRestartsPerHour: this.maxRestartsPerHour },
|
|
224
524
|
});
|
|
525
|
+
this.notifySnapshotChange(state.snapshot);
|
|
526
|
+
this.scheduleCooldownRecovery(state);
|
|
225
527
|
return;
|
|
226
528
|
}
|
|
227
529
|
state.snapshot.status = "starting";
|
|
@@ -232,6 +534,7 @@ class DaemonProcessManager {
|
|
|
232
534
|
state.restartTimer = this.setTimeoutFn(() => {
|
|
233
535
|
void this.startAgent(state.config.name);
|
|
234
536
|
}, waitMs);
|
|
537
|
+
this.notifySnapshotChange(state.snapshot);
|
|
235
538
|
}
|
|
236
539
|
clearRestartTimer(state) {
|
|
237
540
|
if (state.restartTimer === null)
|
|
@@ -239,6 +542,45 @@ class DaemonProcessManager {
|
|
|
239
542
|
this.clearTimeoutFn(state.restartTimer);
|
|
240
543
|
state.restartTimer = null;
|
|
241
544
|
}
|
|
545
|
+
scheduleCooldownRecovery(state) {
|
|
546
|
+
if (state.cooldownRetryCount >= this.maxCooldownRetries) {
|
|
547
|
+
(0, runtime_1.emitNervesEvent)({
|
|
548
|
+
level: "error",
|
|
549
|
+
component: "daemon",
|
|
550
|
+
event: "daemon.agent_permanent_failure",
|
|
551
|
+
message: "managed agent exhausted all cooldown retries — permanently stopped",
|
|
552
|
+
meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount, maxCooldownRetries: this.maxCooldownRetries },
|
|
553
|
+
});
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
this.clearCooldownTimer(state);
|
|
557
|
+
state.cooldownTimer = this.setTimeoutFn(() => {
|
|
558
|
+
state.cooldownRetryCount += 1;
|
|
559
|
+
state.crashTimestamps = [];
|
|
560
|
+
state.snapshot.backoffMs = this.initialBackoffMs;
|
|
561
|
+
state.snapshot.status = "starting";
|
|
562
|
+
state.snapshot.restartCount += 1;
|
|
563
|
+
(0, runtime_1.emitNervesEvent)({
|
|
564
|
+
component: "daemon",
|
|
565
|
+
event: "daemon.agent_cooldown_recovery",
|
|
566
|
+
message: "attempting cooldown recovery for managed agent",
|
|
567
|
+
meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount },
|
|
568
|
+
});
|
|
569
|
+
void this.startAgent(state.config.name);
|
|
570
|
+
}, this.cooldownRecoveryMs);
|
|
571
|
+
(0, runtime_1.emitNervesEvent)({
|
|
572
|
+
component: "daemon",
|
|
573
|
+
event: "daemon.agent_cooldown_scheduled",
|
|
574
|
+
message: `scheduled cooldown recovery in ${this.cooldownRecoveryMs}ms`,
|
|
575
|
+
meta: { agent: state.config.name, cooldownRecoveryMs: this.cooldownRecoveryMs, cooldownRetryCount: state.cooldownRetryCount },
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
clearCooldownTimer(state) {
|
|
579
|
+
if (state.cooldownTimer === null)
|
|
580
|
+
return;
|
|
581
|
+
this.clearTimeoutFn(state.cooldownTimer);
|
|
582
|
+
state.cooldownTimer = null;
|
|
583
|
+
}
|
|
242
584
|
requireAgent(agent) {
|
|
243
585
|
const state = this.agents.get(agent);
|
|
244
586
|
if (!state) {
|