@ouro.bot/cli 0.1.0-alpha.66 → 0.1.0-alpha.660
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 +4209 -13
- package/dist/a2a/card.js +56 -0
- package/dist/a2a/client.js +143 -0
- package/dist/a2a/config.js +50 -0
- package/dist/a2a/onboarding.js +111 -0
- package/dist/a2a/server.js +498 -0
- package/dist/a2a/task-store.js +69 -0
- package/dist/a2a/types.js +3 -0
- package/dist/arc/attention-types.js +8 -0
- package/dist/arc/cares.js +144 -0
- package/dist/arc/episodes.js +118 -0
- package/dist/arc/evolution.js +487 -0
- package/dist/arc/flight-recorder.js +369 -0
- package/dist/arc/intentions.js +134 -0
- package/dist/arc/json-store.js +117 -0
- package/dist/arc/obligations.js +292 -0
- package/dist/arc/packets.js +288 -0
- package/dist/arc/presence.js +185 -0
- package/dist/arc/task-lifecycle.js +57 -0
- package/dist/commerce/store.js +755 -0
- package/dist/commerce/types.js +3 -0
- package/dist/heart/active-work.js +860 -43
- 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/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/awaiting/await-alert.js +146 -0
- package/dist/heart/awaiting/await-expiry.js +108 -0
- package/dist/heart/awaiting/await-loader.js +91 -0
- package/dist/heart/awaiting/await-parser.js +141 -0
- package/dist/heart/awaiting/await-runtime-state.js +100 -0
- package/dist/heart/awaiting/await-scheduler.js +377 -0
- package/dist/heart/background-operations.js +281 -0
- package/dist/heart/bridges/manager.js +137 -17
- package/dist/heart/bridges/store.js +14 -2
- package/dist/heart/bundle-state.js +168 -0
- package/dist/heart/commitments.js +135 -0
- package/dist/heart/config-registry.js +331 -0
- package/dist/heart/config.js +118 -119
- package/dist/heart/context-loss-gauntlet.js +354 -0
- package/dist/heart/core.js +1123 -247
- 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 +523 -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 +780 -0
- package/dist/heart/daemon/cli-desk.js +322 -0
- package/dist/heart/daemon/cli-exec.js +7767 -0
- package/dist/heart/daemon/cli-help.js +558 -0
- package/dist/heart/daemon/cli-parse.js +1688 -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 -1750
- package/dist/heart/daemon/daemon-entry.js +485 -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 +937 -74
- 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 +135 -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 +78 -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 +79 -10
- package/dist/heart/daemon/logs-prune.js +110 -0
- package/dist/heart/daemon/mcp-canary.js +297 -0
- package/dist/heart/daemon/message-router.js +6 -2
- package/dist/heart/daemon/migrate-to-desk.js +848 -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/plugin-cli.js +432 -0
- package/dist/heart/daemon/process-manager.js +511 -40
- 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 +35 -14
- 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 +564 -38
- package/dist/heart/daemon/session-id-resolver.js +131 -0
- package/dist/heart/daemon/skill-management-installer.js +1 -1
- 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 +117 -39
- package/dist/heart/daemon/terminal-ui.js +499 -0
- package/dist/heart/daemon/thoughts.js +229 -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 -4
- package/dist/heart/habits/habit-migration.js +189 -0
- package/dist/heart/habits/habit-parser.js +203 -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 +40 -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 +45 -18
- package/dist/heart/identity.js +174 -57
- package/dist/heart/kept-notes.js +289 -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 +67 -0
- package/dist/heart/mailbox/mailbox-http-response.js +7 -0
- package/dist/heart/mailbox/mailbox-http-routes.js +250 -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 +32 -0
- package/dist/heart/mailbox/mailbox-types.js +27 -0
- package/dist/heart/mailbox/mailbox-view.js +197 -0
- package/dist/heart/mailbox/readers/agent-machine.js +418 -0
- package/dist/heart/mailbox/readers/continuity-readers.js +324 -0
- package/dist/heart/mailbox/readers/mail.js +375 -0
- package/dist/heart/mailbox/readers/runtime-readers.js +728 -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 +696 -0
- package/dist/heart/migrate-config.js +100 -0
- package/dist/heart/model-capabilities.js +19 -0
- package/dist/heart/orientation-frame.js +217 -0
- package/dist/heart/platform.js +81 -0
- package/dist/heart/provider-attempt.js +134 -0
- package/dist/heart/provider-binding-resolver.js +272 -0
- package/dist/heart/provider-credentials.js +425 -0
- package/dist/heart/provider-failover.js +311 -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 +23 -11
- 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-token.js +349 -0
- 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 +17 -4
- package/dist/heart/session-activity.js +48 -24
- package/dist/heart/session-events.js +1133 -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 +133 -0
- package/dist/heart/start-of-turn-packet.js +351 -0
- package/dist/heart/streaming.js +44 -27
- package/dist/heart/structured-output.js +196 -0
- 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 +155 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-context.js +430 -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 +409 -0
- package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
- package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
- package/dist/heart/versioning/update-hooks.js +154 -0
- package/dist/heart/work-card.js +386 -0
- package/dist/mailbox-ui/assets/index-B-V9vRQ0.js +61 -0
- package/dist/mailbox-ui/assets/index-BOZbGbkL.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 +715 -0
- package/dist/mailroom/body-cache.js +61 -0
- package/dist/mailroom/core.js +788 -0
- package/dist/mailroom/entry.js +160 -0
- package/dist/mailroom/file-store.js +568 -0
- package/dist/mailroom/mbox-import.js +393 -0
- package/dist/mailroom/migration.js +164 -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 +334 -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 +21 -2
- package/dist/mind/context.js +250 -101
- package/dist/mind/desk-section.js +362 -0
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +68 -77
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/friends/channel.js +48 -0
- package/dist/mind/friends/resolver.js +67 -4
- package/dist/mind/friends/store-file.js +61 -4
- package/dist/mind/friends/types.js +2 -2
- package/dist/mind/{associative-recall.js → note-search.js} +47 -58
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/pending.js +6 -1
- package/dist/mind/prompt-refresh.js +3 -2
- package/dist/mind/prompt.js +1015 -140
- package/dist/mind/provenance-trust.js +26 -0
- package/dist/mind/record-paths.js +312 -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 +139 -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 +16 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +1041 -0
- package/dist/repertoire/bundle-templates.js +71 -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 +331 -0
- package/dist/repertoire/coding/feedback.js +197 -30
- package/dist/repertoire/coding/manager.js +166 -10
- package/dist/repertoire/coding/spawner.js +55 -9
- package/dist/repertoire/coding/tools.js +219 -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/desk/classifier.js +362 -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 +159 -25
- package/dist/repertoire/mcp-client.js +295 -0
- package/dist/repertoire/mcp-manager.js +434 -0
- package/dist/repertoire/mcp-tools.js +83 -0
- package/dist/repertoire/plugin-mcp.js +175 -0
- package/dist/repertoire/plugins.js +253 -0
- package/dist/repertoire/shell-sessions.js +133 -0
- package/dist/repertoire/skills.js +48 -4
- package/dist/repertoire/stripe-client.js +131 -0
- package/dist/repertoire/tool-results.js +29 -0
- package/dist/repertoire/tools-a2a.js +283 -0
- package/dist/repertoire/tools-attachments.js +317 -0
- package/dist/repertoire/tools-awaiting.js +372 -0
- package/dist/repertoire/tools-base.js +63 -1082
- package/dist/repertoire/tools-bluebubbles.js +2 -0
- package/dist/repertoire/tools-bridge.js +144 -0
- package/dist/repertoire/tools-bundle.js +993 -0
- package/dist/repertoire/tools-commerce.js +253 -0
- package/dist/repertoire/tools-config.js +186 -0
- package/dist/repertoire/tools-continuity.js +252 -0
- package/dist/repertoire/tools-credential.js +383 -0
- package/dist/repertoire/tools-evolution.js +527 -0
- package/dist/repertoire/tools-files.js +344 -0
- package/dist/repertoire/tools-flight.js +290 -0
- package/dist/repertoire/tools-flow.js +119 -0
- package/dist/repertoire/tools-github.js +3 -8
- package/dist/repertoire/tools-mail.js +1975 -0
- package/dist/repertoire/tools-notes.js +418 -0
- package/dist/repertoire/tools-obligations.js +143 -0
- package/dist/repertoire/tools-orientation.js +31 -0
- package/dist/repertoire/tools-record.js +469 -0
- package/dist/repertoire/tools-runtime.js +150 -0
- package/dist/repertoire/tools-session.js +766 -0
- package/dist/repertoire/tools-shell.js +120 -0
- package/dist/repertoire/tools-stripe.js +224 -0
- package/dist/repertoire/tools-surface.js +344 -0
- package/dist/repertoire/tools-teams.js +12 -39
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-trip.js +982 -0
- package/dist/repertoire/tools-user-profile.js +146 -0
- package/dist/repertoire/tools-vault.js +40 -0
- package/dist/repertoire/tools-voice.js +145 -0
- package/dist/repertoire/tools.js +243 -79
- 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/a2a-entry.js +78 -0
- package/dist/senses/attention-queue.js +186 -0
- package/dist/senses/await-turn-message.js +58 -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 +2737 -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 +517 -204
- package/dist/senses/commands.js +66 -3
- package/dist/senses/habit-turn-message.js +122 -0
- package/dist/senses/inner-dialog-worker.js +303 -22
- package/dist/senses/inner-dialog.js +525 -41
- package/dist/senses/mail-entry.js +66 -0
- package/dist/senses/mail.js +379 -0
- package/dist/senses/pipeline.js +857 -180
- package/dist/senses/proactive-content-guard.js +51 -0
- package/dist/senses/shared-turn.js +419 -0
- package/dist/senses/surface-tool.js +108 -0
- package/dist/senses/teams-entry.js +60 -8
- package/dist/senses/teams.js +390 -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 +807 -0
- package/dist/senses/voice/twilio-phone.js +5079 -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/dist/util/frontmatter.js +69 -0
- package/package.json +55 -12
- package/skills/agent-commerce.md +113 -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/update-hooks.js +0 -138
- package/dist/heart/safe-workspace.js +0 -228
- package/dist/heart/session-recall.js +0 -116
- package/dist/repertoire/tasks/board.js +0 -134
- package/dist/repertoire/tasks/index.js +0 -224
- package/dist/repertoire/tasks/lifecycle.js +0 -80
- package/dist/repertoire/tasks/middleware.js +0 -65
- package/dist/repertoire/tasks/parser.js +0 -173
- package/dist/repertoire/tasks/scanner.js +0 -132
- package/dist/repertoire/tasks/transitions.js +0 -144
- 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 -7
- /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/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +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
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.DaemonProcessManager = void 0;
|
|
36
|
+
exports.DaemonProcessManager = exports.RESPAWN_GUARD_WINDOW_MS = exports.RESPAWN_GUARD_MAX_RESTARTS = void 0;
|
|
37
37
|
const child_process_1 = require("child_process");
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const identity_1 = require("../identity");
|
|
@@ -41,6 +41,19 @@ const runtime_1 = require("../../nerves/runtime");
|
|
|
41
41
|
function startOfHour(ms) {
|
|
42
42
|
return ms - 60 * 60 * 1000;
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Respawn-loop guard: refuse `restartAgent` if we've already orchestrated
|
|
46
|
+
* RESPAWN_GUARD_MAX_RESTARTS in the past RESPAWN_GUARD_WINDOW_MS.
|
|
47
|
+
*
|
|
48
|
+
* Calibrated for the 2026-05-11 BB sense incident: a misconfigured probe
|
|
49
|
+
* was triggering `restartAgent` every ~60s for hours. Five restarts in
|
|
50
|
+
* 10 minutes is well above the rate of legitimate operational restarts
|
|
51
|
+
* (a single human-initiated `ouro down && ouro up` produces one) and well
|
|
52
|
+
* below the rate of a death spiral (60/hr ⇒ 10/10min).
|
|
53
|
+
*/
|
|
54
|
+
exports.RESPAWN_GUARD_MAX_RESTARTS = 5;
|
|
55
|
+
exports.RESPAWN_GUARD_WINDOW_MS = 10 * 60_000;
|
|
56
|
+
const MAX_PENDING_IPC_MESSAGES = 20;
|
|
44
57
|
class DaemonProcessManager {
|
|
45
58
|
agents = new Map();
|
|
46
59
|
maxRestartsPerHour;
|
|
@@ -51,24 +64,111 @@ class DaemonProcessManager {
|
|
|
51
64
|
now;
|
|
52
65
|
setTimeoutFn;
|
|
53
66
|
clearTimeoutFn;
|
|
67
|
+
cooldownRecoveryMs;
|
|
68
|
+
maxCooldownRetries;
|
|
69
|
+
startupStaleAfterMs;
|
|
54
70
|
existsSyncFn;
|
|
71
|
+
configCheckFn;
|
|
72
|
+
statusWriterFn;
|
|
73
|
+
onSnapshotChangeFn;
|
|
74
|
+
/**
|
|
75
|
+
* Notify the snapshot-change observer (if registered). Swallows any
|
|
76
|
+
* errors from the observer so process lifecycle code never fails
|
|
77
|
+
* because the observer threw.
|
|
78
|
+
*/
|
|
79
|
+
notifySnapshotChange(snapshot) {
|
|
80
|
+
if (!this.onSnapshotChangeFn)
|
|
81
|
+
return;
|
|
82
|
+
try {
|
|
83
|
+
this.onSnapshotChangeFn(snapshot);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
(0, runtime_1.emitNervesEvent)({
|
|
87
|
+
level: "warn",
|
|
88
|
+
component: "daemon",
|
|
89
|
+
event: "daemon.snapshot_change_observer_error",
|
|
90
|
+
message: "snapshot-change observer threw",
|
|
91
|
+
meta: {
|
|
92
|
+
agent: snapshot.name,
|
|
93
|
+
error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error),
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
writeStatus(agent, text) {
|
|
99
|
+
try {
|
|
100
|
+
this.statusWriterFn(text);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
(0, runtime_1.emitNervesEvent)({
|
|
104
|
+
level: "warn",
|
|
105
|
+
component: "daemon",
|
|
106
|
+
event: "daemon.status_writer_error",
|
|
107
|
+
message: "daemon status writer threw",
|
|
108
|
+
meta: {
|
|
109
|
+
agent,
|
|
110
|
+
error: error instanceof Error ? error.message : String(error),
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
currentTimeMs() {
|
|
116
|
+
const value = this.now();
|
|
117
|
+
return Number.isFinite(value) ? value : Date.now();
|
|
118
|
+
}
|
|
119
|
+
isStartAttemptCurrent(state, attemptId) {
|
|
120
|
+
return state.startAttemptId === attemptId;
|
|
121
|
+
}
|
|
122
|
+
markConfigCheckFailed(state, errorReason, fixHint) {
|
|
123
|
+
const agent = state.config.name;
|
|
124
|
+
state.snapshot.status = "crashed";
|
|
125
|
+
state.snapshot.errorReason = errorReason;
|
|
126
|
+
state.snapshot.fixHint = fixHint;
|
|
127
|
+
(0, runtime_1.emitNervesEvent)({
|
|
128
|
+
level: "error",
|
|
129
|
+
component: "daemon",
|
|
130
|
+
event: "daemon.agent_config_invalid",
|
|
131
|
+
message: errorReason,
|
|
132
|
+
meta: { agent, fix: fixHint },
|
|
133
|
+
});
|
|
134
|
+
this.writeStatus(agent, `[daemon] ${agent}: ${errorReason}\n` +
|
|
135
|
+
(fixHint ? ` Fix: ${fixHint}\n` : ""));
|
|
136
|
+
this.notifySnapshotChange(state.snapshot);
|
|
137
|
+
}
|
|
55
138
|
constructor(options) {
|
|
56
139
|
this.maxRestartsPerHour = options.maxRestartsPerHour ?? 10;
|
|
57
140
|
this.stabilityThresholdMs = options.stabilityThresholdMs ?? 60_000;
|
|
58
141
|
this.initialBackoffMs = options.initialBackoffMs ?? 1_000;
|
|
59
142
|
this.maxBackoffMs = options.maxBackoffMs ?? 60_000;
|
|
143
|
+
this.cooldownRecoveryMs = options.cooldownRecoveryMs ?? 5 * 60 * 1_000;
|
|
144
|
+
this.maxCooldownRetries = options.maxCooldownRetries ?? 3;
|
|
145
|
+
this.startupStaleAfterMs = options.startupStaleAfterMs ?? 45_000;
|
|
60
146
|
this.spawnFn = options.spawn ?? ((command, args, spawnOptions) => (0, child_process_1.spawn)(command, args, spawnOptions));
|
|
61
147
|
this.now = options.now ?? (() => Date.now());
|
|
62
148
|
this.setTimeoutFn = options.setTimeoutFn ?? ((cb, delay) => setTimeout(cb, delay));
|
|
63
149
|
this.clearTimeoutFn = options.clearTimeoutFn ?? ((timer) => clearTimeout(timer));
|
|
64
150
|
this.existsSyncFn = options.existsSync ?? null;
|
|
151
|
+
this.configCheckFn = options.configCheck ?? null;
|
|
152
|
+
this.statusWriterFn = options.statusWriter ?? ((text) => {
|
|
153
|
+
process.stderr.write(text);
|
|
154
|
+
});
|
|
155
|
+
this.onSnapshotChangeFn = options.onSnapshotChange ?? null;
|
|
65
156
|
for (const agent of options.agents) {
|
|
66
157
|
this.agents.set(agent.name, {
|
|
67
158
|
config: agent,
|
|
68
159
|
process: null,
|
|
160
|
+
startInFlight: false,
|
|
161
|
+
startAttemptedAtMs: null,
|
|
162
|
+
startAttemptId: 0,
|
|
163
|
+
pendingIpcMessages: [],
|
|
69
164
|
restartTimer: null,
|
|
70
165
|
crashTimestamps: [],
|
|
166
|
+
orchestratedRestartTimestamps: [],
|
|
167
|
+
respawnLoopTripped: false,
|
|
71
168
|
stopRequested: false,
|
|
169
|
+
cooldownTimer: null,
|
|
170
|
+
cooldownRetryCount: 0,
|
|
171
|
+
fastCrashCount: 0,
|
|
72
172
|
snapshot: {
|
|
73
173
|
name: agent.name,
|
|
74
174
|
channel: agent.channel,
|
|
@@ -78,6 +178,10 @@ class DaemonProcessManager {
|
|
|
78
178
|
startedAt: null,
|
|
79
179
|
lastCrashAt: null,
|
|
80
180
|
backoffMs: this.initialBackoffMs,
|
|
181
|
+
lastExitCode: null,
|
|
182
|
+
lastSignal: null,
|
|
183
|
+
errorReason: null,
|
|
184
|
+
fixHint: null,
|
|
81
185
|
},
|
|
82
186
|
});
|
|
83
187
|
}
|
|
@@ -89,53 +193,235 @@ class DaemonProcessManager {
|
|
|
89
193
|
}
|
|
90
194
|
}
|
|
91
195
|
}
|
|
92
|
-
|
|
196
|
+
triggerAutoStartAgents() {
|
|
197
|
+
for (const state of this.agents.values()) {
|
|
198
|
+
if (!state.config.autoStart)
|
|
199
|
+
continue;
|
|
200
|
+
void this.startAgent(state.config.name).catch((error) => {
|
|
201
|
+
const errorReason = error instanceof Error ? error.message : String(error);
|
|
202
|
+
this.markConfigCheckFailed(state, `agent startup threw before the worker could run: ${errorReason}`, "Run 'ouro doctor' for diagnostics, then retry 'ouro up'.");
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
async startAgent(agent, options = {}) {
|
|
93
207
|
const state = this.requireAgent(agent);
|
|
94
|
-
if (state.process)
|
|
208
|
+
if (state.process || state.startInFlight)
|
|
95
209
|
return;
|
|
96
|
-
|
|
97
|
-
state.
|
|
98
|
-
state.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
state.
|
|
210
|
+
const attemptId = state.startAttemptId + 1;
|
|
211
|
+
state.startAttemptId = attemptId;
|
|
212
|
+
state.startInFlight = true;
|
|
213
|
+
state.startAttemptedAtMs = this.currentTimeMs();
|
|
214
|
+
try {
|
|
215
|
+
this.clearRestartTimer(state);
|
|
216
|
+
state.stopRequested = false;
|
|
217
|
+
state.snapshot.status = "starting";
|
|
218
|
+
if (this.configCheckFn && options.skipConfigCheck !== true) {
|
|
219
|
+
let result;
|
|
220
|
+
try {
|
|
221
|
+
result = await this.configCheckFn(agent);
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
const errorReason = error instanceof Error ? error.message : String(error);
|
|
225
|
+
this.markConfigCheckFailed(state, `agent config validation threw: ${errorReason}`, "Run 'ouro doctor' for diagnostics, then retry 'ouro up'.");
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (!this.isStartAttemptCurrent(state, attemptId))
|
|
229
|
+
return;
|
|
230
|
+
if (state.stopRequested) {
|
|
231
|
+
state.snapshot.status = "stopped";
|
|
232
|
+
state.snapshot.pid = null;
|
|
233
|
+
this.notifySnapshotChange(state.snapshot);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (result.skip) {
|
|
237
|
+
state.snapshot.status = "stopped";
|
|
238
|
+
state.snapshot.errorReason = null;
|
|
239
|
+
state.snapshot.fixHint = null;
|
|
240
|
+
(0, runtime_1.emitNervesEvent)({
|
|
241
|
+
component: "daemon",
|
|
242
|
+
event: "daemon.agent_config_skipped",
|
|
243
|
+
message: result.error ?? "agent start skipped by config check",
|
|
244
|
+
meta: { agent, fix: result.fix ?? null },
|
|
245
|
+
});
|
|
246
|
+
this.notifySnapshotChange(state.snapshot);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (!result.ok) {
|
|
250
|
+
this.markConfigCheckFailed(state, result.error ?? "agent config validation failed", result.fix ?? null);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
/* v8 ignore next -- defensive duplicate stale-start guard before spawn preparation @preserve */
|
|
254
|
+
if (!this.isStartAttemptCurrent(state, attemptId))
|
|
255
|
+
return;
|
|
256
|
+
// Config check passed — clear any prior error so the pulse stops
|
|
257
|
+
// reporting the broken state. This is the recovery path: the user
|
|
258
|
+
// fixed their secrets/config, the next startAgent attempt sees a
|
|
259
|
+
// valid config, and the pulse goes quiet.
|
|
260
|
+
state.snapshot.errorReason = null;
|
|
261
|
+
state.snapshot.fixHint = null;
|
|
262
|
+
}
|
|
263
|
+
if (options.skipConfigCheck === true) {
|
|
264
|
+
state.snapshot.errorReason = null;
|
|
265
|
+
state.snapshot.fixHint = null;
|
|
266
|
+
}
|
|
267
|
+
const runCwd = (0, identity_1.getRepoRoot)();
|
|
268
|
+
const entryScript = path.join((0, identity_1.getRepoRoot)(), "dist", state.config.entry);
|
|
269
|
+
if (this.existsSyncFn && !this.existsSyncFn(entryScript)) {
|
|
270
|
+
state.snapshot.status = "crashed";
|
|
271
|
+
(0, runtime_1.emitNervesEvent)({
|
|
272
|
+
level: "error",
|
|
273
|
+
component: "daemon",
|
|
274
|
+
event: "daemon.agent_entry_missing",
|
|
275
|
+
message: "agent entry script does not exist — cannot spawn. Run 'ouro daemon install' from the correct location.",
|
|
276
|
+
meta: { agent, entryScript },
|
|
277
|
+
});
|
|
278
|
+
this.notifySnapshotChange(state.snapshot);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
/* v8 ignore next -- defensive duplicate stale-start guard immediately before spawn @preserve */
|
|
282
|
+
if (!this.isStartAttemptCurrent(state, attemptId))
|
|
283
|
+
return;
|
|
284
|
+
if (state.stopRequested) {
|
|
285
|
+
state.snapshot.status = "stopped";
|
|
286
|
+
state.snapshot.pid = null;
|
|
287
|
+
this.notifySnapshotChange(state.snapshot);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
const agentArgs = state.config.getArgs?.() ?? state.config.args ?? [];
|
|
291
|
+
const args = [entryScript, "--agent", state.config.agentArg ?? agent, ...agentArgs];
|
|
292
|
+
const child = this.spawnFn("node", args, {
|
|
293
|
+
cwd: runCwd,
|
|
294
|
+
env: state.config.env ? { ...process.env, ...state.config.env } : process.env,
|
|
295
|
+
stdio: ["ignore", "ignore", "ignore", "ipc"],
|
|
296
|
+
});
|
|
297
|
+
/* v8 ignore next 7 -- defensive: spawn should always return a ChildProcess @preserve */
|
|
298
|
+
if (!child) {
|
|
299
|
+
state.snapshot.status = "crashed";
|
|
300
|
+
(0, runtime_1.emitNervesEvent)({ level: "error", component: "daemon", event: "daemon.agent_spawn_failed", message: "spawn returned null", meta: { agent } });
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (!this.isStartAttemptCurrent(state, attemptId)) {
|
|
304
|
+
try {
|
|
305
|
+
child.kill("SIGTERM");
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
(0, runtime_1.emitNervesEvent)({
|
|
309
|
+
level: "warn",
|
|
310
|
+
component: "daemon",
|
|
311
|
+
event: "daemon.agent_stop_error",
|
|
312
|
+
message: "failed to stop stale managed agent startup",
|
|
313
|
+
meta: { agent },
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
state.process = child;
|
|
319
|
+
state.snapshot.status = "running";
|
|
320
|
+
state.snapshot.pid = child.pid ?? null;
|
|
321
|
+
state.snapshot.startedAt = new Date(this.currentTimeMs()).toISOString();
|
|
322
|
+
const bootstrap = state.config.getRuntimeCredentialBootstrap?.() ?? null;
|
|
323
|
+
if (bootstrap) {
|
|
324
|
+
const message = {
|
|
325
|
+
type: "ouro.runtimeCredentialBootstrap",
|
|
326
|
+
agentName: bootstrap.agentName,
|
|
327
|
+
};
|
|
328
|
+
if (bootstrap.runtimeConfig)
|
|
329
|
+
message.runtimeConfig = bootstrap.runtimeConfig;
|
|
330
|
+
if (bootstrap.machineRuntimeConfig)
|
|
331
|
+
message.machineRuntimeConfig = bootstrap.machineRuntimeConfig;
|
|
332
|
+
if (bootstrap.machineId)
|
|
333
|
+
message.machineId = bootstrap.machineId;
|
|
334
|
+
if (bootstrap.providerCredentialRecords)
|
|
335
|
+
message.providerCredentialRecords = bootstrap.providerCredentialRecords;
|
|
336
|
+
try {
|
|
337
|
+
child.send?.(message);
|
|
338
|
+
(0, runtime_1.emitNervesEvent)({
|
|
339
|
+
component: "daemon",
|
|
340
|
+
event: "daemon.agent_runtime_credentials_bootstrap_sent",
|
|
341
|
+
message: "sent runtime credential bootstrap to managed agent process",
|
|
342
|
+
meta: {
|
|
343
|
+
agent,
|
|
344
|
+
runtimeConfig: !!bootstrap.runtimeConfig,
|
|
345
|
+
machineRuntimeConfig: !!bootstrap.machineRuntimeConfig,
|
|
346
|
+
providerCredentialRecords: bootstrap.providerCredentialRecords?.length ?? 0,
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
(0, runtime_1.emitNervesEvent)({
|
|
352
|
+
level: "warn",
|
|
353
|
+
component: "daemon",
|
|
354
|
+
event: "daemon.agent_ipc_send_error",
|
|
355
|
+
message: "failed to send runtime credential bootstrap to managed agent process",
|
|
356
|
+
meta: { agent, error: error instanceof Error ? error.message : String(error) },
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const pendingIpcMessages = state.pendingIpcMessages.splice(0);
|
|
361
|
+
for (const message of pendingIpcMessages) {
|
|
362
|
+
try {
|
|
363
|
+
child.send?.(message);
|
|
364
|
+
(0, runtime_1.emitNervesEvent)({
|
|
365
|
+
component: "daemon",
|
|
366
|
+
event: "daemon.agent_pending_ipc_flushed",
|
|
367
|
+
message: "flushed pending IPC message to managed agent",
|
|
368
|
+
meta: { agent },
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
catch (error) {
|
|
372
|
+
(0, runtime_1.emitNervesEvent)({
|
|
373
|
+
level: "warn",
|
|
374
|
+
component: "daemon",
|
|
375
|
+
event: "daemon.agent_ipc_send_error",
|
|
376
|
+
message: "failed to flush pending IPC message to managed agent",
|
|
377
|
+
meta: { agent, error: error instanceof Error ? error.message : String(error) },
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
}
|
|
103
381
|
(0, runtime_1.emitNervesEvent)({
|
|
104
|
-
level: "error",
|
|
105
382
|
component: "daemon",
|
|
106
|
-
event: "daemon.
|
|
107
|
-
message: "
|
|
108
|
-
meta: { agent,
|
|
383
|
+
event: "daemon.agent_started",
|
|
384
|
+
message: "daemon started managed agent process",
|
|
385
|
+
meta: { agent, pid: child.pid ?? null, cwd: runCwd },
|
|
386
|
+
});
|
|
387
|
+
this.notifySnapshotChange(state.snapshot);
|
|
388
|
+
/* v8 ignore start — child process error handler; requires real spawn to trigger */
|
|
389
|
+
child.on("error", (err) => {
|
|
390
|
+
(0, runtime_1.emitNervesEvent)({
|
|
391
|
+
level: "warn",
|
|
392
|
+
component: "daemon",
|
|
393
|
+
event: "daemon.agent_process_error",
|
|
394
|
+
message: "managed agent process emitted error",
|
|
395
|
+
meta: { agent, error: err.message },
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
/* v8 ignore stop */
|
|
399
|
+
child.once("exit", (code, signal) => {
|
|
400
|
+
this.onExit(state, child, code, signal);
|
|
109
401
|
});
|
|
110
|
-
return;
|
|
111
402
|
}
|
|
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
|
-
});
|
|
403
|
+
finally {
|
|
404
|
+
if (this.isStartAttemptCurrent(state, attemptId)) {
|
|
405
|
+
state.startInFlight = false;
|
|
406
|
+
state.startAttemptedAtMs = null;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
131
409
|
}
|
|
132
410
|
async stopAgent(agent) {
|
|
133
411
|
const state = this.requireAgent(agent);
|
|
134
412
|
this.clearRestartTimer(state);
|
|
413
|
+
this.clearCooldownTimer(state);
|
|
135
414
|
state.stopRequested = true;
|
|
415
|
+
state.pendingIpcMessages = [];
|
|
416
|
+
// NOTE: do not touch state.respawnLoopTripped / orchestratedRestartTimestamps
|
|
417
|
+
// here. restartAgent calls stopAgent internally; clearing the guard here
|
|
418
|
+
// would reset the window every cycle and defeat the loop-detection. The
|
|
419
|
+
// guard self-clears when timestamps age out of the window (handled inside
|
|
420
|
+
// restartAgent at the prune step).
|
|
136
421
|
if (!state.process) {
|
|
137
422
|
state.snapshot.status = "stopped";
|
|
138
423
|
state.snapshot.pid = null;
|
|
424
|
+
this.notifySnapshotChange(state.snapshot);
|
|
139
425
|
return;
|
|
140
426
|
}
|
|
141
427
|
const child = state.process;
|
|
@@ -154,20 +440,127 @@ class DaemonProcessManager {
|
|
|
154
440
|
meta: { agent },
|
|
155
441
|
});
|
|
156
442
|
}
|
|
443
|
+
this.notifySnapshotChange(state.snapshot);
|
|
157
444
|
}
|
|
158
|
-
async restartAgent(agent) {
|
|
445
|
+
async restartAgent(agent, options = {}) {
|
|
446
|
+
const state = this.requireAgent(agent);
|
|
447
|
+
// Respawn-loop guard: prune timestamps outside the window, then check
|
|
448
|
+
// whether we've already restarted this agent too many times in it.
|
|
449
|
+
const now = this.now();
|
|
450
|
+
const windowStart = now - exports.RESPAWN_GUARD_WINDOW_MS;
|
|
451
|
+
state.orchestratedRestartTimestamps = state.orchestratedRestartTimestamps.filter((ts) => ts >= windowStart);
|
|
452
|
+
// If the window is now empty, the trip naturally self-clears. That means
|
|
453
|
+
// after RESPAWN_GUARD_WINDOW_MS of no restart attempts, the daemon is
|
|
454
|
+
// willing to try again (e.g. for a fresh health probe failure that has
|
|
455
|
+
// nothing to do with the original loop).
|
|
456
|
+
if (state.respawnLoopTripped && state.orchestratedRestartTimestamps.length === 0) {
|
|
457
|
+
state.respawnLoopTripped = false;
|
|
458
|
+
state.snapshot.errorReason = null;
|
|
459
|
+
state.snapshot.fixHint = null;
|
|
460
|
+
(0, runtime_1.emitNervesEvent)({
|
|
461
|
+
component: "daemon",
|
|
462
|
+
event: "daemon.agent_respawn_loop_cleared",
|
|
463
|
+
message: "respawn-loop guard cleared by window-aging",
|
|
464
|
+
meta: { agent, windowMs: exports.RESPAWN_GUARD_WINDOW_MS },
|
|
465
|
+
});
|
|
466
|
+
this.notifySnapshotChange(state.snapshot);
|
|
467
|
+
}
|
|
468
|
+
if (state.respawnLoopTripped) {
|
|
469
|
+
(0, runtime_1.emitNervesEvent)({
|
|
470
|
+
level: "error",
|
|
471
|
+
component: "daemon",
|
|
472
|
+
event: "daemon.agent_respawn_loop_blocked",
|
|
473
|
+
message: "refused agent restart — respawn-loop guard tripped; manual intervention required",
|
|
474
|
+
meta: {
|
|
475
|
+
agent,
|
|
476
|
+
recentRestartCount: state.orchestratedRestartTimestamps.length,
|
|
477
|
+
windowMs: exports.RESPAWN_GUARD_WINDOW_MS,
|
|
478
|
+
},
|
|
479
|
+
});
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
if (state.orchestratedRestartTimestamps.length >= exports.RESPAWN_GUARD_MAX_RESTARTS) {
|
|
483
|
+
state.respawnLoopTripped = true;
|
|
484
|
+
state.snapshot.errorReason = `respawn loop detected: ${exports.RESPAWN_GUARD_MAX_RESTARTS}+ restarts in ${Math.round(exports.RESPAWN_GUARD_WINDOW_MS / 60_000)}min — refusing further restarts`;
|
|
485
|
+
state.snapshot.fixHint = "investigate the root cause then run `ouro up` to resume";
|
|
486
|
+
(0, runtime_1.emitNervesEvent)({
|
|
487
|
+
level: "error",
|
|
488
|
+
component: "daemon",
|
|
489
|
+
event: "daemon.agent_respawn_loop_tripped",
|
|
490
|
+
message: "respawn-loop guard tripped; further restarts blocked",
|
|
491
|
+
meta: {
|
|
492
|
+
agent,
|
|
493
|
+
restartCount: state.orchestratedRestartTimestamps.length,
|
|
494
|
+
windowMs: exports.RESPAWN_GUARD_WINDOW_MS,
|
|
495
|
+
maxRestarts: exports.RESPAWN_GUARD_MAX_RESTARTS,
|
|
496
|
+
},
|
|
497
|
+
});
|
|
498
|
+
this.notifySnapshotChange(state.snapshot);
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
state.orchestratedRestartTimestamps.push(now);
|
|
502
|
+
if (state.startInFlight && !state.process) {
|
|
503
|
+
const startedAt = state.startAttemptedAtMs;
|
|
504
|
+
/* v8 ignore next -- defensive: startInFlight always records a start timestamp @preserve */
|
|
505
|
+
const elapsedMs = startedAt === null ? 0 : this.currentTimeMs() - startedAt;
|
|
506
|
+
if (elapsedMs < this.startupStaleAfterMs) {
|
|
507
|
+
(0, runtime_1.emitNervesEvent)({
|
|
508
|
+
component: "daemon",
|
|
509
|
+
event: "daemon.agent_restart_deferred",
|
|
510
|
+
message: "managed agent restart skipped while startup is already in flight",
|
|
511
|
+
meta: { agent, elapsedMs, startupStaleAfterMs: this.startupStaleAfterMs },
|
|
512
|
+
});
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
(0, runtime_1.emitNervesEvent)({
|
|
516
|
+
level: "warn",
|
|
517
|
+
component: "daemon",
|
|
518
|
+
event: "daemon.agent_startup_stale_recovered",
|
|
519
|
+
message: "managed agent startup was stale; clearing the pending attempt before restart",
|
|
520
|
+
meta: { agent, elapsedMs, startupStaleAfterMs: this.startupStaleAfterMs },
|
|
521
|
+
});
|
|
522
|
+
state.startAttemptId += 1;
|
|
523
|
+
state.startInFlight = false;
|
|
524
|
+
state.startAttemptedAtMs = null;
|
|
525
|
+
}
|
|
159
526
|
await this.stopAgent(agent);
|
|
160
|
-
await this.startAgent(agent);
|
|
527
|
+
await this.startAgent(agent, options);
|
|
161
528
|
}
|
|
162
529
|
async stopAll() {
|
|
163
530
|
for (const state of this.agents.values()) {
|
|
164
531
|
await this.stopAgent(state.config.name);
|
|
165
532
|
}
|
|
166
533
|
}
|
|
534
|
+
resetAgentFailureState(agent) {
|
|
535
|
+
const state = this.requireAgent(agent);
|
|
536
|
+
this.clearRestartTimer(state);
|
|
537
|
+
this.clearCooldownTimer(state);
|
|
538
|
+
state.cooldownRetryCount = 0;
|
|
539
|
+
state.crashTimestamps = [];
|
|
540
|
+
state.fastCrashCount = 0;
|
|
541
|
+
state.respawnLoopTripped = false;
|
|
542
|
+
state.orchestratedRestartTimestamps = [];
|
|
543
|
+
state.snapshot.errorReason = null;
|
|
544
|
+
state.snapshot.fixHint = null;
|
|
545
|
+
this.notifySnapshotChange(state.snapshot);
|
|
546
|
+
}
|
|
167
547
|
sendToAgent(agent, message) {
|
|
168
548
|
const state = this.requireAgent(agent);
|
|
169
|
-
if (!state.process)
|
|
549
|
+
if (!state.process) {
|
|
550
|
+
if (state.startInFlight) {
|
|
551
|
+
if (state.pendingIpcMessages.length >= MAX_PENDING_IPC_MESSAGES) {
|
|
552
|
+
state.pendingIpcMessages.shift();
|
|
553
|
+
}
|
|
554
|
+
state.pendingIpcMessages.push(message);
|
|
555
|
+
(0, runtime_1.emitNervesEvent)({
|
|
556
|
+
component: "daemon",
|
|
557
|
+
event: "daemon.agent_ipc_queued_during_startup",
|
|
558
|
+
message: "queued IPC message while managed agent startup is in flight",
|
|
559
|
+
meta: { agent, queuedCount: state.pendingIpcMessages.length },
|
|
560
|
+
});
|
|
561
|
+
}
|
|
170
562
|
return;
|
|
563
|
+
}
|
|
171
564
|
try {
|
|
172
565
|
state.process.send(message);
|
|
173
566
|
}
|
|
@@ -187,13 +580,17 @@ class DaemonProcessManager {
|
|
|
187
580
|
listAgentSnapshots() {
|
|
188
581
|
return [...this.agents.values()].map((state) => state.snapshot);
|
|
189
582
|
}
|
|
190
|
-
onExit(state, code, signal) {
|
|
191
|
-
if (
|
|
583
|
+
onExit(state, child, code, signal) {
|
|
584
|
+
if (state.process !== child)
|
|
192
585
|
return;
|
|
193
586
|
state.process = null;
|
|
587
|
+
state.startInFlight = false;
|
|
588
|
+
state.startAttemptedAtMs = null;
|
|
194
589
|
state.snapshot.pid = null;
|
|
590
|
+
state.snapshot.lastExitCode = code;
|
|
591
|
+
state.snapshot.lastSignal = signal;
|
|
195
592
|
const crashed = !state.stopRequested && code !== 0;
|
|
196
|
-
const now = this.
|
|
593
|
+
const now = this.currentTimeMs();
|
|
197
594
|
const startedAt = state.snapshot.startedAt ? Date.parse(state.snapshot.startedAt) : now;
|
|
198
595
|
const runDuration = Math.max(0, now - startedAt);
|
|
199
596
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -208,13 +605,45 @@ class DaemonProcessManager {
|
|
|
208
605
|
if (runDuration >= this.stabilityThresholdMs) {
|
|
209
606
|
state.snapshot.backoffMs = this.initialBackoffMs;
|
|
210
607
|
}
|
|
608
|
+
this.notifySnapshotChange(state.snapshot);
|
|
211
609
|
return;
|
|
212
610
|
}
|
|
213
611
|
state.snapshot.lastCrashAt = new Date(now).toISOString();
|
|
612
|
+
// Fast-crash detection: if the agent dies within 5 seconds of starting, it's likely
|
|
613
|
+
// a configuration issue (missing credentials, bad provider, etc.) not a transient failure.
|
|
614
|
+
// After 3 consecutive fast crashes, stop retrying and mark as config-failed.
|
|
615
|
+
const FAST_CRASH_THRESHOLD_MS = 5000;
|
|
616
|
+
const FAST_CRASH_MAX = 3;
|
|
617
|
+
if (runDuration < FAST_CRASH_THRESHOLD_MS) {
|
|
618
|
+
state.fastCrashCount = state.fastCrashCount + 1;
|
|
619
|
+
if (state.fastCrashCount >= FAST_CRASH_MAX) {
|
|
620
|
+
state.snapshot.status = "crashed";
|
|
621
|
+
// Capture the fast-crash diagnosis on the snapshot so it surfaces
|
|
622
|
+
// via the pulse. The error message is prescriptive: it tells the
|
|
623
|
+
// user (and their sibling agents) exactly what to do.
|
|
624
|
+
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).`;
|
|
625
|
+
state.snapshot.fixHint = `Fix the config and run \`ouro up\` to restart, or check daemon logs for the underlying error.`;
|
|
626
|
+
(0, runtime_1.emitNervesEvent)({
|
|
627
|
+
level: "error",
|
|
628
|
+
component: "daemon",
|
|
629
|
+
event: "daemon.agent_config_failure",
|
|
630
|
+
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.`,
|
|
631
|
+
meta: { agent: state.config.name, fastCrashCount: state.fastCrashCount, avgRunDurationMs: runDuration },
|
|
632
|
+
});
|
|
633
|
+
this.notifySnapshotChange(state.snapshot);
|
|
634
|
+
return; // Don't schedule cooldown recovery — this needs human/agent intervention
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
else {
|
|
638
|
+
// Reset fast-crash counter on a stable run
|
|
639
|
+
state.fastCrashCount = 0;
|
|
640
|
+
}
|
|
214
641
|
state.crashTimestamps = state.crashTimestamps.filter((crashTs) => crashTs >= startOfHour(now));
|
|
215
642
|
state.crashTimestamps.push(now);
|
|
216
643
|
if (state.crashTimestamps.length > this.maxRestartsPerHour) {
|
|
217
644
|
state.snapshot.status = "crashed";
|
|
645
|
+
state.snapshot.errorReason = `agent exceeded restart limit (${this.maxRestartsPerHour}/hr) — entering cooldown`;
|
|
646
|
+
state.snapshot.fixHint = "investigate why the agent keeps crashing; cooldown will retry shortly";
|
|
218
647
|
(0, runtime_1.emitNervesEvent)({
|
|
219
648
|
level: "error",
|
|
220
649
|
component: "daemon",
|
|
@@ -222,6 +651,8 @@ class DaemonProcessManager {
|
|
|
222
651
|
message: "managed agent exceeded restart limit and is marked crashed",
|
|
223
652
|
meta: { agent: state.config.name, maxRestartsPerHour: this.maxRestartsPerHour },
|
|
224
653
|
});
|
|
654
|
+
this.notifySnapshotChange(state.snapshot);
|
|
655
|
+
this.scheduleCooldownRecovery(state);
|
|
225
656
|
return;
|
|
226
657
|
}
|
|
227
658
|
state.snapshot.status = "starting";
|
|
@@ -232,6 +663,7 @@ class DaemonProcessManager {
|
|
|
232
663
|
state.restartTimer = this.setTimeoutFn(() => {
|
|
233
664
|
void this.startAgent(state.config.name);
|
|
234
665
|
}, waitMs);
|
|
666
|
+
this.notifySnapshotChange(state.snapshot);
|
|
235
667
|
}
|
|
236
668
|
clearRestartTimer(state) {
|
|
237
669
|
if (state.restartTimer === null)
|
|
@@ -239,6 +671,45 @@ class DaemonProcessManager {
|
|
|
239
671
|
this.clearTimeoutFn(state.restartTimer);
|
|
240
672
|
state.restartTimer = null;
|
|
241
673
|
}
|
|
674
|
+
scheduleCooldownRecovery(state) {
|
|
675
|
+
if (state.cooldownRetryCount >= this.maxCooldownRetries) {
|
|
676
|
+
(0, runtime_1.emitNervesEvent)({
|
|
677
|
+
level: "error",
|
|
678
|
+
component: "daemon",
|
|
679
|
+
event: "daemon.agent_permanent_failure",
|
|
680
|
+
message: "managed agent exhausted all cooldown retries — permanently stopped",
|
|
681
|
+
meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount, maxCooldownRetries: this.maxCooldownRetries },
|
|
682
|
+
});
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
this.clearCooldownTimer(state);
|
|
686
|
+
state.cooldownTimer = this.setTimeoutFn(() => {
|
|
687
|
+
state.cooldownRetryCount += 1;
|
|
688
|
+
state.crashTimestamps = [];
|
|
689
|
+
state.snapshot.backoffMs = this.initialBackoffMs;
|
|
690
|
+
state.snapshot.status = "starting";
|
|
691
|
+
state.snapshot.restartCount += 1;
|
|
692
|
+
(0, runtime_1.emitNervesEvent)({
|
|
693
|
+
component: "daemon",
|
|
694
|
+
event: "daemon.agent_cooldown_recovery",
|
|
695
|
+
message: "attempting cooldown recovery for managed agent",
|
|
696
|
+
meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount },
|
|
697
|
+
});
|
|
698
|
+
void this.startAgent(state.config.name);
|
|
699
|
+
}, this.cooldownRecoveryMs);
|
|
700
|
+
(0, runtime_1.emitNervesEvent)({
|
|
701
|
+
component: "daemon",
|
|
702
|
+
event: "daemon.agent_cooldown_scheduled",
|
|
703
|
+
message: `scheduled cooldown recovery in ${this.cooldownRecoveryMs}ms`,
|
|
704
|
+
meta: { agent: state.config.name, cooldownRecoveryMs: this.cooldownRecoveryMs, cooldownRetryCount: state.cooldownRetryCount },
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
clearCooldownTimer(state) {
|
|
708
|
+
if (state.cooldownTimer === null)
|
|
709
|
+
return;
|
|
710
|
+
this.clearTimeoutFn(state.cooldownTimer);
|
|
711
|
+
state.cooldownTimer = null;
|
|
712
|
+
}
|
|
242
713
|
requireAgent(agent) {
|
|
243
714
|
const state = this.agents.get(agent);
|
|
244
715
|
if (!state) {
|