@ouro.bot/cli 0.1.0-alpha.9 → 0.1.0-alpha.900
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/LICENSE +21 -0
- package/README.md +249 -176
- 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/SerpentGuide.ouro/agent.json +83 -0
- package/SerpentGuide.ouro/psyche/SOUL.md +25 -0
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +2 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
- package/assets/bluebubbles-host +264 -0
- package/assets/ouroboros.png +0 -0
- package/assets/sanctuary-host-launcher.sh +16 -0
- package/changelog.json +6224 -0
- package/deploy/unraid/Dockerfile +58 -0
- package/deploy/unraid/README.txt +2997 -0
- package/deploy/unraid/audit-container-spec.sh +4 -0
- package/deploy/unraid/container-runtime.json +1 -0
- package/deploy/unraid/docker-man-template-transaction.mjs +711 -0
- package/deploy/unraid/docker-man-template-xml.cjs +105 -0
- package/deploy/unraid/migrate-sanctuary-bundle.mjs +60 -0
- package/deploy/unraid/ouro-events/bootstrap-spool.sh +92 -0
- package/deploy/unraid/ouro-events/emit-event.mjs +310 -0
- package/deploy/unraid/ouro-events/emit-usenet-event.sh +54 -0
- package/deploy/unraid/ouro-events/install-usenet-guard.sh +435 -0
- package/deploy/unraid/ouro-events/usenet-health.sh +176 -0
- package/deploy/unraid/sanctuary-acceptance-adapter.sh +43 -0
- package/deploy/unraid/sanctuary-acceptance-contract.json +198 -0
- package/deploy/unraid/sanctuary-acceptance-harness.sh +23 -0
- package/deploy/unraid/sanctuary-authority-installation.json +36 -0
- package/deploy/unraid/sanctuary-authority-service.sh +30 -0
- package/deploy/unraid/sanctuary-butler-upgrade.mjs +1254 -0
- package/deploy/unraid/sanctuary-deployment-target.mjs +710 -0
- package/deploy/unraid/sanctuary-replay-gate.mjs +1614 -0
- package/deploy/unraid/sanctuary-safe-rename.py +24 -0
- package/deploy/unraid/sanctuary-unit16-host-broker.mjs +2086 -0
- package/deploy/unraid/sanctuary-unit16-run.sh +560 -0
- package/deploy/unraid/sanctuary-unit18-target-audit.sh +27 -0
- package/deploy/unraid/sanctuary.ouro/agent.json +66 -0
- package/deploy/unraid/sanctuary.ouro/arc/README.md +3 -0
- package/deploy/unraid/sanctuary.ouro/awaiting/house-care-sweep.md +17 -0
- package/deploy/unraid/sanctuary.ouro/bundle-meta.json +5 -0
- package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +8 -0
- package/deploy/unraid/sanctuary.ouro/mcp/media-mcp.mjs +2231 -0
- package/deploy/unraid/sanctuary.ouro/provider-readiness.json +11 -0
- package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +5 -0
- package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +5 -0
- package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +27 -0
- package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +27 -0
- package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +22 -0
- package/deploy/unraid/sanctuary.ouro/state/policy/steward.json +7 -0
- package/deploy/unraid/sanctuary.ouro/tool-profiles.json +232 -0
- package/deploy/unraid/sanctuary.xml +30 -0
- package/dist/a2a/card.js +57 -0
- package/dist/a2a/client.js +250 -0
- package/dist/a2a/config.js +50 -0
- package/dist/a2a/delegated-command-grants.js +93 -0
- package/dist/a2a/delegated-command.js +197 -0
- package/dist/a2a/delegation-stores.js +56 -0
- package/dist/a2a/did-resolution.js +93 -0
- package/dist/a2a/escalation-grants.js +84 -0
- package/dist/a2a/escalation-wiring.js +60 -0
- package/dist/a2a/identity.js +258 -0
- package/dist/a2a/inbound-share.js +110 -0
- package/dist/a2a/mission-result-wire.js +196 -0
- package/dist/a2a/onboarding.js +133 -0
- package/dist/a2a/operator-trust.js +382 -0
- package/dist/a2a/outbox-client.js +57 -0
- package/dist/a2a/outbox-store.js +188 -0
- package/dist/a2a/outbox-wire.js +95 -0
- package/dist/a2a/pin-store.js +132 -0
- package/dist/a2a/replay-grant-holders.js +33 -0
- package/dist/a2a/replay-harness.js +188 -0
- package/dist/a2a/resolution-proof.js +53 -0
- package/dist/a2a/sealed-chat.js +113 -0
- package/dist/a2a/seen-ledger.js +120 -0
- package/dist/a2a/server.js +771 -0
- package/dist/a2a/task-store.js +69 -0
- package/dist/a2a/transport.js +45 -0
- package/dist/a2a/trust-commands.js +203 -0
- package/dist/a2a/trusted-files.js +185 -0
- package/dist/a2a/types.js +3 -0
- package/dist/arc/attention-types.js +8 -0
- package/dist/arc/cares.js +360 -0
- package/dist/arc/episodes.js +118 -0
- package/dist/arc/evolution.js +490 -0
- package/dist/arc/flight-recorder.js +1034 -0
- package/dist/arc/intentions.js +134 -0
- package/dist/arc/json-store.js +117 -0
- package/dist/arc/obligations.js +386 -0
- package/dist/arc/packets.js +336 -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 +1009 -0
- package/dist/heart/agent-entry.js +252 -3
- package/dist/heart/approval-files.js +113 -0
- package/dist/heart/approval-store.js +960 -0
- 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 +18 -0
- package/dist/heart/attachments/sources/telegram.js +71 -0
- package/dist/heart/attachments/store.js +132 -0
- package/dist/heart/attachments/types.js +93 -0
- package/dist/heart/auth/auth-flow.js +495 -0
- package/dist/heart/autonomy-budget.js +478 -0
- package/dist/heart/awaiting/a2a-await-delivery.js +109 -0
- package/dist/heart/awaiting/await-alert.js +152 -0
- package/dist/heart/awaiting/await-expiry.js +143 -0
- package/dist/heart/awaiting/await-loader.js +91 -0
- package/dist/heart/awaiting/await-parser.js +152 -0
- package/dist/heart/awaiting/await-runtime-state.js +111 -0
- package/dist/heart/awaiting/await-scheduler.js +391 -0
- package/dist/heart/background-operations.js +281 -0
- package/dist/heart/bridges/manager.js +478 -0
- package/dist/heart/bridges/state-machine.js +135 -0
- package/dist/heart/bridges/store.js +135 -0
- 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 +225 -137
- package/dist/heart/context-loss-gauntlet.js +387 -0
- package/dist/heart/context-loss-sentinel.js +1157 -0
- package/dist/heart/core.js +2442 -343
- package/dist/heart/cross-chat-delivery.js +133 -0
- package/dist/heart/daemon/agent-config-check.js +451 -0
- package/dist/heart/daemon/agent-discovery.js +355 -0
- package/dist/heart/daemon/agent-home.js +319 -0
- package/dist/heart/daemon/agent-service.js +542 -0
- package/dist/heart/daemon/agentic-repair.js +547 -0
- package/dist/heart/daemon/await-private-wake.js +59 -0
- package/dist/heart/daemon/bluebubbles-cli-integration.js +71 -0
- package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
- package/dist/heart/daemon/bluebubbles-host-protocol.js +433 -0
- package/dist/heart/daemon/bluebubbles-host.js +273 -0
- package/dist/heart/daemon/boot-sync-probe.js +197 -0
- package/dist/heart/daemon/cadence.js +180 -0
- package/dist/heart/daemon/cli-defaults.js +859 -0
- package/dist/heart/daemon/cli-desk.js +322 -0
- package/dist/heart/daemon/cli-exec.js +9003 -0
- package/dist/heart/daemon/cli-help.js +729 -0
- package/dist/heart/daemon/cli-parse.js +3140 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +804 -0
- package/dist/heart/daemon/cli-types.js +8 -0
- package/dist/heart/daemon/connect-bay.js +323 -0
- package/dist/heart/daemon/container-credential-bootstrap.js +273 -0
- package/dist/heart/daemon/container-healthcheck.js +112 -0
- package/dist/heart/daemon/container-runtime.js +97 -0
- package/dist/heart/daemon/container-spec-auditor-main.js +171 -0
- package/dist/heart/daemon/container-spec-auditor.js +235 -0
- package/dist/heart/daemon/daemon-bootstrap-startup.js +115 -0
- package/dist/heart/daemon/daemon-cli.js +30 -882
- package/dist/heart/daemon/daemon-entry.js +1002 -13
- package/dist/heart/daemon/daemon-health.js +186 -0
- package/dist/heart/daemon/daemon-rollup.js +57 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +287 -0
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +2213 -28
- package/dist/heart/daemon/dns-workflow.js +394 -0
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +1755 -0
- package/dist/heart/daemon/freshness.js +345 -0
- package/dist/heart/daemon/habit-private-wake.js +61 -0
- package/dist/heart/daemon/health-monitor.js +142 -11
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +226 -0
- 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 +188 -0
- package/dist/heart/daemon/log-tailer.js +82 -12
- package/dist/heart/daemon/logs-prune.js +132 -0
- package/dist/heart/daemon/mcp-canary.js +524 -0
- package/dist/heart/daemon/message-router.js +80 -11
- 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 +22 -14
- 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 +766 -54
- package/dist/heart/daemon/provider-discovery.js +137 -0
- package/dist/heart/daemon/provider-ping-progress.js +83 -0
- package/dist/heart/daemon/prunable-bundle.js +144 -0
- package/dist/heart/daemon/pulse.js +511 -0
- package/dist/heart/daemon/readiness-repair.js +365 -0
- package/dist/heart/daemon/run-hooks.js +39 -0
- package/dist/heart/daemon/runtime-logging.js +92 -16
- package/dist/heart/daemon/runtime-metadata.js +191 -0
- package/dist/{repertoire/tasks/lifecycle.js → heart/daemon/runtime-mode.js} +37 -39
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sanctuary-acceptance-adapter.js +2654 -0
- package/dist/heart/daemon/sanctuary-acceptance-harness.js +1830 -0
- package/dist/heart/daemon/sanctuary-acceptance-marker.js +267 -0
- package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +955 -0
- package/dist/heart/daemon/sanctuary-authority-codec.js +86 -0
- package/dist/heart/daemon/sanctuary-authority-epoch.js +281 -0
- package/dist/heart/daemon/sanctuary-authority-installation.js +161 -0
- package/dist/heart/daemon/sanctuary-authority-ledger.js +241 -0
- package/dist/heart/daemon/sanctuary-authority-root-lifecycle.js +1267 -0
- package/dist/heart/daemon/sanctuary-authority-vault-migration.js +79 -0
- package/dist/heart/daemon/sanctuary-bundle-migration.js +730 -0
- package/dist/heart/daemon/sanctuary-host-authority.js +1008 -0
- package/dist/heart/daemon/sanctuary-host-detached-supervisor.js +493 -0
- package/dist/heart/daemon/sanctuary-host-executor.js +670 -0
- package/dist/heart/daemon/sanctuary-host-linux-kernel.js +334 -0
- package/dist/heart/daemon/sanctuary-host-supervisor-entry.js +207 -0
- package/dist/heart/daemon/sanctuary-host-supervisor.js +95 -0
- package/dist/heart/daemon/sanctuary-package-management.js +103 -0
- package/dist/heart/daemon/sanctuary-scheduler-liveness.js +227 -0
- package/dist/heart/daemon/sanctuary-scheduler-origin.js +132 -0
- package/dist/heart/daemon/sanctuary-telegram-authority-entry.js +475 -0
- package/dist/heart/daemon/sanctuary-telegram-authority-gateway.js +585 -0
- package/dist/heart/daemon/sanctuary-telegram-authority-service.js +676 -0
- package/dist/heart/daemon/sense-manager.js +975 -0
- package/dist/heart/daemon/session-id-resolver.js +131 -0
- package/dist/heart/daemon/skill-management-installer.js +94 -0
- package/dist/heart/daemon/socket-client.js +370 -0
- package/dist/heart/daemon/stale-bundle-prune.js +113 -0
- package/dist/heart/daemon/startup-tui.js +339 -0
- package/dist/heart/daemon/supercronic-supervisor.js +282 -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 +591 -0
- package/dist/heart/daemon/up-progress.js +366 -0
- package/dist/heart/daemon/vault-items.js +56 -0
- package/dist/heart/delegation.js +59 -0
- package/dist/heart/external-events/router.js +1516 -0
- package/dist/heart/failure-reports.js +335 -0
- package/dist/heart/file-lock.js +109 -0
- package/dist/heart/frontend-approval-runtime.js +506 -0
- package/dist/heart/frontend-journal.js +215 -0
- package/dist/heart/frontend-session-service.js +237 -0
- package/dist/heart/frontend-socket-client.js +164 -0
- package/dist/heart/frontend-socket.js +400 -0
- package/dist/heart/habits/habit-cancel.js +810 -0
- package/dist/heart/habits/habit-lifecycle.js +1327 -0
- package/dist/heart/habits/habit-migration.js +194 -0
- package/dist/heart/habits/habit-parser.js +278 -0
- package/dist/heart/habits/habit-runtime-state.js +93 -0
- package/dist/heart/habits/habit-scheduler.js +653 -0
- package/dist/heart/habits/habit-session-summary.js +318 -0
- package/dist/heart/habits/habit-session.js +636 -0
- package/dist/heart/{daemon → hatch}/hatch-animation.js +10 -3
- package/dist/heart/{daemon → hatch}/hatch-flow.js +57 -139
- package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
- package/dist/heart/hatch/specialist-orchestrator.js +129 -0
- package/dist/heart/hatch/specialist-prompt.js +102 -0
- package/dist/heart/hatch/specialist-tools.js +318 -0
- package/dist/heart/identity.js +312 -67
- package/dist/heart/kept-notes.js +289 -0
- package/dist/heart/kicks.js +2 -20
- package/dist/heart/machine-identity.js +175 -0
- package/dist/heart/mail-import-discovery.js +356 -0
- package/dist/heart/mailbox/mailbox-http-hooks.js +98 -0
- package/dist/heart/mailbox/mailbox-http-response.js +7 -0
- package/dist/heart/mailbox/mailbox-http-routes.js +398 -0
- package/dist/heart/mailbox/mailbox-http-static.js +103 -0
- package/dist/heart/mailbox/mailbox-http-transport.js +272 -0
- package/dist/heart/mailbox/mailbox-http.js +99 -0
- package/dist/heart/mailbox/mailbox-read.js +39 -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 +428 -0
- package/dist/heart/mailbox/readers/continuity-readers.js +329 -0
- package/dist/heart/mailbox/readers/mail.js +375 -0
- package/dist/heart/mailbox/readers/runtime-readers.js +823 -0
- package/dist/heart/mailbox/readers/sessions.js +231 -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 +59 -0
- package/dist/heart/orientation-frame.js +345 -0
- package/dist/heart/platform.js +81 -0
- package/dist/heart/primitives.js +1 -1
- package/dist/heart/private-runtime/decision-renderer.js +158 -0
- package/dist/heart/private-runtime/index.js +18 -0
- package/dist/heart/private-runtime/ledger.js +221 -0
- package/dist/heart/private-runtime/policy.js +260 -0
- package/dist/heart/private-runtime/types.js +2 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/provider-attempt.js +137 -0
- package/dist/heart/provider-binding-resolver.js +273 -0
- package/dist/heart/provider-credentials.js +449 -0
- package/dist/heart/provider-failover.js +311 -0
- package/dist/heart/provider-models.js +96 -0
- package/dist/heart/provider-ping.js +265 -0
- package/dist/heart/provider-readiness-cache.js +119 -0
- package/dist/heart/provider-visibility.js +188 -0
- package/dist/heart/providers/anthropic-token.js +131 -0
- package/dist/heart/providers/anthropic.js +236 -58
- package/dist/heart/providers/azure.js +104 -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 +29 -7
- package/dist/heart/providers/openai-codex-token.js +349 -0
- package/dist/heart/providers/openai-codex.js +63 -39
- package/dist/heart/providers/openai-compatible.js +177 -0
- package/dist/heart/reply-style.js +56 -0
- package/dist/heart/run-ledger.js +247 -0
- package/dist/heart/runtime-capability-check.js +170 -0
- package/dist/heart/runtime-credentials.js +607 -0
- package/dist/heart/runtime-cwd.js +87 -0
- package/dist/heart/sense-truth.js +81 -0
- package/dist/heart/session-activity.js +237 -0
- package/dist/heart/session-events.js +1504 -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-redaction-repair-cli-main.js +31 -0
- package/dist/heart/session-redaction-repair.js +552 -0
- package/dist/heart/session-stats-cli-main.js +5 -0
- package/dist/heart/session-stats.js +183 -0
- package/dist/heart/session-transcript.js +133 -0
- package/dist/heart/shepherd-config.js +113 -0
- package/dist/heart/source-grounding.js +283 -0
- package/dist/heart/start-of-turn-packet.js +372 -0
- package/dist/heart/steward-policy.js +656 -0
- package/dist/heart/streaming.js +682 -207
- package/dist/heart/structured-output.js +196 -0
- package/dist/heart/sync-classification.js +176 -0
- package/dist/heart/sync.js +505 -0
- package/dist/heart/target-resolution.js +127 -0
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/timeouts.js +101 -0
- package/dist/heart/tool-activity-callbacks.js +59 -0
- package/dist/heart/tool-approval.js +382 -0
- package/dist/heart/tool-description.js +155 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +206 -0
- package/dist/heart/turn-context.js +494 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/heart/turn-execution-lease.js +30 -0
- package/dist/heart/versioning/ouro-bot-global-installer.js +129 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
- package/dist/heart/versioning/ouro-path-installer.js +432 -0
- package/dist/heart/versioning/ouro-recovery-launcher.js +76 -0
- package/dist/heart/{daemon → versioning}/ouro-uti.js +11 -2
- package/dist/heart/versioning/ouro-version-manager.js +409 -0
- package/dist/heart/versioning/staged-restart.js +146 -0
- package/dist/heart/versioning/update-checker.js +116 -0
- package/dist/heart/versioning/update-hooks.js +154 -0
- package/dist/heart/versioning/version-intent.js +114 -0
- package/dist/heart/versioning/wrapper-publish-guard.js +86 -0
- package/dist/heart/work-card.js +380 -0
- package/dist/mailbox-ui/assets/index-Cyv4mw3Y.js +1 -0
- package/dist/mailbox-ui/assets/index-Du_9G9WO.css +1 -0
- package/dist/mailbox-ui/assets/vendor-CcN1XpQ9.js +61 -0
- package/dist/mailbox-ui/index.html +16 -0
- package/dist/mailroom/attention.js +167 -0
- package/dist/mailroom/autonomy.js +209 -0
- package/dist/mailroom/blob-store.js +889 -0
- package/dist/mailroom/body-cache.js +61 -0
- package/dist/mailroom/cache-sync-cli.js +58 -0
- package/dist/mailroom/core.js +803 -0
- package/dist/mailroom/entry.js +160 -0
- package/dist/mailroom/file-store.js +568 -0
- package/dist/mailroom/hosted-cache-sync.js +384 -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 +292 -0
- package/dist/mailroom/search-cache.js +513 -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 +93 -1
- package/dist/mind/context.js +318 -104
- package/dist/mind/desk-section.js +362 -0
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +90 -98
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/first-impressions.js +16 -2
- package/dist/mind/{associative-recall.js → note-search.js} +61 -60
- package/dist/mind/obligation-steering.js +227 -0
- package/dist/mind/pending.js +103 -10
- package/dist/mind/phrases.js +1 -0
- package/dist/mind/prompt-budget.js +479 -0
- package/dist/mind/prompt-refresh.js +3 -2
- package/dist/mind/prompt.js +1417 -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/mind/session-transaction.js +564 -0
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/cli-logging.js +22 -3
- 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/coverage/run-artifacts.js +56 -36
- package/dist/nerves/coverage/source-scanner.js +4 -4
- package/dist/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +305 -9
- 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 +159 -0
- package/dist/nerves/runtime.js +17 -1
- package/dist/repertoire/ado-client.js +17 -56
- package/dist/repertoire/ado-semantic.js +16 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +1191 -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 +322 -0
- package/dist/repertoire/coding/index.js +18 -3
- package/dist/repertoire/coding/manager.js +228 -14
- package/dist/repertoire/coding/spawner.js +61 -14
- package/dist/repertoire/coding/tools.js +279 -12
- package/dist/repertoire/coding/workbench-client.js +272 -0
- package/dist/repertoire/coding/workbench-manager.js +346 -0
- package/dist/repertoire/commerce-errors.js +109 -0
- package/dist/repertoire/commerce-self-test.js +156 -0
- package/dist/repertoire/credential-access.js +182 -0
- package/dist/repertoire/data/ado-endpoints.json +188 -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 +1232 -0
- package/dist/repertoire/house-sweep.js +469 -0
- package/dist/repertoire/mcp-client.js +307 -0
- package/dist/repertoire/mcp-manager.js +483 -0
- package/dist/repertoire/mcp-tools.js +132 -0
- package/dist/repertoire/mcp-write-guard.js +46 -0
- package/dist/repertoire/plugin-mcp.js +175 -0
- package/dist/repertoire/plugins.js +253 -0
- package/dist/repertoire/relationship-authorization.js +322 -0
- package/dist/repertoire/sanctuary-container-policy.js +46 -0
- package/dist/repertoire/shell-sessions.js +134 -0
- package/dist/repertoire/skills.js +45 -24
- package/dist/repertoire/stripe-client.js +131 -0
- package/dist/repertoire/tool-arguments.js +182 -0
- package/dist/repertoire/tool-results.js +29 -0
- package/dist/repertoire/tools-a2a.js +699 -0
- package/dist/repertoire/tools-attachments.js +322 -0
- package/dist/repertoire/tools-awaiting.js +870 -0
- package/dist/repertoire/tools-base.js +84 -706
- package/dist/repertoire/tools-bluebubbles.js +95 -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 +575 -0
- package/dist/repertoire/tools-credential.js +383 -0
- package/dist/repertoire/tools-escalation.js +84 -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-habits.js +103 -0
- package/dist/repertoire/tools-house-care.js +174 -0
- package/dist/repertoire/tools-mail.js +1828 -0
- package/dist/repertoire/tools-notes.js +496 -0
- package/dist/repertoire/tools-obligations.js +182 -0
- package/dist/repertoire/tools-orientation.js +31 -0
- package/dist/repertoire/tools-record.js +482 -0
- package/dist/repertoire/tools-rsvp.js +139 -0
- package/dist/repertoire/tools-runtime.js +238 -0
- package/dist/repertoire/tools-sanctuary-host.js +202 -0
- package/dist/repertoire/tools-session.js +1133 -0
- package/dist/repertoire/tools-shell.js +149 -0
- package/dist/repertoire/tools-shepherd.js +180 -0
- package/dist/repertoire/tools-steward-policy.js +147 -0
- package/dist/repertoire/tools-stripe.js +224 -0
- package/dist/repertoire/tools-surface.js +369 -0
- package/dist/repertoire/tools-teams.js +67 -61
- package/dist/repertoire/tools-telegram-contacts.js +37 -0
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-trip.js +982 -0
- package/dist/repertoire/tools-unraid.js +556 -0
- package/dist/repertoire/tools-user-profile.js +146 -0
- package/dist/repertoire/tools-vault.js +40 -0
- package/dist/repertoire/tools-voice-local.js +198 -0
- package/dist/repertoire/tools-voice.js +145 -0
- package/dist/repertoire/tools.js +534 -123
- package/dist/repertoire/travel-api-client.js +360 -0
- package/dist/repertoire/unraid-client.js +155 -0
- package/dist/repertoire/unraid-restart.js +339 -0
- package/dist/repertoire/user-profile.js +131 -0
- package/dist/repertoire/vault-setup.js +246 -0
- package/dist/repertoire/vault-unlock.js +686 -0
- package/dist/rsvp/aisleplanner-client.js +354 -0
- package/dist/rsvp/cli.js +926 -0
- package/dist/rsvp/config.js +327 -0
- package/dist/rsvp/cutover.js +433 -0
- package/dist/rsvp/diagnostics.js +232 -0
- package/dist/rsvp/diff-renderer.js +176 -0
- package/dist/rsvp/habit-policy.js +110 -0
- package/dist/rsvp/habit-stage.js +123 -0
- package/dist/rsvp/incident-bundle.js +97 -0
- package/dist/rsvp/migration.js +204 -0
- package/dist/rsvp/native-habit-runner.js +630 -0
- package/dist/rsvp/outbound-state.js +628 -0
- package/dist/rsvp/query.js +118 -0
- package/dist/rsvp/replay.js +233 -0
- package/dist/rsvp/snapshot.js +232 -0
- package/dist/rsvp/spend-ledger.js +175 -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 +132 -0
- package/dist/senses/acp-server.js +386 -0
- package/dist/senses/attention-queue.js +186 -0
- package/dist/senses/await-turn-message.js +65 -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 +799 -0
- package/dist/senses/bluebubbles/context-packet.js +207 -0
- package/dist/senses/bluebubbles/context-smoke.js +144 -0
- package/dist/senses/bluebubbles/entry.js +90 -0
- package/dist/senses/bluebubbles/inbound-log.js +127 -0
- package/dist/senses/bluebubbles/index.js +3841 -0
- package/dist/senses/bluebubbles/latest-turn.js +311 -0
- package/dist/senses/bluebubbles/media.js +389 -0
- package/dist/senses/bluebubbles/model.js +392 -0
- package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +57 -6
- package/dist/senses/bluebubbles/outbound-state.js +308 -0
- package/dist/senses/bluebubbles/processed-log.js +133 -0
- package/dist/senses/bluebubbles/reaction-policy.js +47 -0
- package/dist/senses/bluebubbles/replay.js +284 -0
- package/dist/senses/bluebubbles/runtime-state.js +137 -0
- package/dist/senses/bluebubbles/semantic-receipts.js +1986 -0
- package/dist/senses/bluebubbles/session-cleanup.js +72 -0
- package/dist/senses/bluebubbles/webhook-registration.js +228 -0
- package/dist/senses/bluebubbles-meta-guard.js +39 -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 +241 -0
- package/dist/senses/cli/virtual-list.js +35 -0
- package/dist/senses/cli-entry.js +60 -8
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +814 -281
- package/dist/senses/commands.js +66 -3
- package/dist/senses/context-packet-ledger.js +371 -0
- package/dist/senses/context-packets.js +154 -0
- package/dist/senses/continuity.js +94 -0
- package/dist/senses/habit-lifecycle-guard.js +24 -0
- package/dist/senses/habit-turn-message.js +160 -0
- package/dist/senses/ingress-evidence.js +2 -0
- package/dist/senses/mail-entry.js +66 -0
- package/dist/senses/mail.js +524 -0
- package/dist/senses/pipeline.js +1236 -0
- package/dist/senses/private-runtime-worker.js +859 -0
- package/dist/senses/private-runtime.js +1627 -0
- package/dist/senses/proactive-content-guard.js +51 -0
- package/dist/senses/root-host-approval-port.js +345 -0
- package/dist/senses/root-host-approval-runtime.js +393 -0
- package/dist/senses/sanctuary-authority-resident.js +102 -0
- package/dist/senses/sanctuary-download-credit-presentation.js +31 -0
- package/dist/senses/sanctuary-full-visibility-contract.js +302 -0
- package/dist/senses/sanctuary-grounding.js +118 -0
- package/dist/senses/sanctuary-health-acceptance-probe-entry.js +12 -0
- package/dist/senses/sanctuary-health-acceptance-probe.js +774 -0
- package/dist/senses/sanctuary-health-runner.js +104 -0
- package/dist/senses/sanctuary-health.js +422 -0
- package/dist/senses/sanctuary-install-state-contract.js +169 -0
- package/dist/senses/sanctuary-interactive-control.js +410 -0
- package/dist/senses/sanctuary-media-catalog-contract.js +297 -0
- package/dist/senses/sanctuary-media-optimization.js +514 -0
- package/dist/senses/sanctuary-runtime.js +369 -0
- package/dist/senses/sanctuary-sab.js +85 -0
- package/dist/senses/sanctuary-storage-optimization-contract.js +32 -0
- package/dist/senses/shared-turn.js +793 -0
- package/dist/senses/shepherd/client.js +196 -0
- package/dist/senses/shepherd/cmux.js +332 -0
- package/dist/senses/shepherd/herdr.js +311 -0
- package/dist/senses/shepherd/host.js +14 -0
- package/dist/senses/shepherd/judge.js +98 -0
- package/dist/senses/shepherd/redact.js +37 -0
- package/dist/senses/shepherd/returns.js +79 -0
- package/dist/senses/shepherd/sense.js +288 -0
- package/dist/senses/shepherd-entry.js +78 -0
- package/dist/senses/surface-tool.js +109 -0
- package/dist/senses/teams-entry.js +60 -8
- package/dist/senses/teams.js +956 -211
- package/dist/senses/telegram-admission.js +718 -0
- package/dist/senses/telegram-approval-runtime.js +793 -0
- package/dist/senses/telegram-attachments.js +186 -0
- package/dist/senses/telegram-audit-ledger.js +220 -0
- package/dist/senses/telegram-authority-transport.js +231 -0
- package/dist/senses/telegram-client.js +1749 -0
- package/dist/senses/telegram-effect-adapter.js +900 -0
- package/dist/senses/telegram-entry.js +83 -0
- package/dist/senses/telegram.js +2351 -0
- package/dist/senses/trust-gate.js +207 -2
- package/dist/senses/voice/audio-playback.js +237 -0
- package/dist/senses/voice/audio-routing.js +119 -0
- package/dist/senses/voice/call-auth.js +250 -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/local-audio-control.js +344 -0
- package/dist/senses/voice/local-audio-devices.js +516 -0
- package/dist/senses/voice/local-audio-disclosure.js +174 -0
- package/dist/senses/voice/local-audio-identity.js +103 -0
- package/dist/senses/voice/local-audio-join.js +325 -0
- package/dist/senses/voice/local-audio-mute.js +199 -0
- package/dist/senses/voice/local-audio-notify.js +121 -0
- package/dist/senses/voice/local-audio-prompts.js +116 -0
- package/dist/senses/voice/local-audio-transport.js +427 -0
- package/dist/senses/voice/local-audio.js +281 -0
- package/dist/senses/voice/local-media-stream.js +145 -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/process-guards.js +51 -0
- package/dist/senses/voice/realtime-eval.js +496 -0
- package/dist/senses/voice/realtime-tool-schema.js +195 -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 +836 -0
- package/dist/senses/voice/twilio-phone.js +5454 -0
- package/dist/senses/voice/types.js +2 -0
- package/dist/senses/voice/whisper.js +161 -0
- package/dist/senses/voice-entry.js +83 -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/npm-shrinkwrap.json +8100 -0
- package/package.json +83 -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/AdoptionSpecialist.ouro/agent.json +0 -20
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +0 -22
- package/dist/heart/daemon/specialist-orchestrator.js +0 -160
- package/dist/heart/daemon/specialist-prompt.js +0 -47
- package/dist/heart/daemon/specialist-session.js +0 -142
- package/dist/heart/daemon/specialist-tools.js +0 -132
- package/dist/heart/daemon/subagent-installer.js +0 -125
- package/dist/inner-worker-entry.js +0 -4
- package/dist/mind/friends/channel.js +0 -49
- package/dist/mind/friends/resolver.js +0 -84
- package/dist/mind/friends/store-file.js +0 -171
- package/dist/mind/friends/store.js +0 -4
- package/dist/mind/friends/tokens.js +0 -26
- package/dist/mind/friends/types.js +0 -21
- package/dist/repertoire/tasks/board.js +0 -122
- package/dist/repertoire/tasks/index.js +0 -210
- 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 -145
- package/dist/senses/bluebubbles-client.js +0 -279
- package/dist/senses/bluebubbles-entry.js +0 -11
- package/dist/senses/bluebubbles-model.js +0 -253
- package/dist/senses/bluebubbles.js +0 -332
- package/dist/senses/inner-dialog-worker.js +0 -61
- package/dist/senses/inner-dialog.js +0 -231
- package/subagents/README.md +0 -73
- package/subagents/work-doer.md +0 -233
- package/subagents/work-merger.md +0 -624
- package/subagents/work-planner.md +0 -373
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/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
|
@@ -33,16 +33,34 @@ 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.PRIVATE_RUNTIME_DISPATCH_ACK_TIMEOUT_MS = exports.RESPAWN_GUARD_WINDOW_MS = exports.RESPAWN_GUARD_MAX_RESTARTS = void 0;
|
|
37
37
|
const child_process_1 = require("child_process");
|
|
38
|
+
const crypto_1 = require("crypto");
|
|
38
39
|
const path = __importStar(require("path"));
|
|
39
40
|
const identity_1 = require("../identity");
|
|
40
41
|
const runtime_1 = require("../../nerves/runtime");
|
|
41
42
|
function startOfHour(ms) {
|
|
42
43
|
return ms - 60 * 60 * 1000;
|
|
43
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Respawn-loop guard: refuse `restartAgent` if we've already orchestrated
|
|
47
|
+
* RESPAWN_GUARD_MAX_RESTARTS in the past RESPAWN_GUARD_WINDOW_MS.
|
|
48
|
+
*
|
|
49
|
+
* Calibrated for the 2026-05-11 BB sense incident: a misconfigured probe
|
|
50
|
+
* was triggering `restartAgent` every ~60s for hours. Five restarts in
|
|
51
|
+
* 10 minutes is well above the rate of legitimate operational restarts
|
|
52
|
+
* (a single human-initiated `ouro down && ouro up` produces one) and well
|
|
53
|
+
* below the rate of a death spiral (60/hr ⇒ 10/10min).
|
|
54
|
+
*/
|
|
55
|
+
exports.RESPAWN_GUARD_MAX_RESTARTS = 5;
|
|
56
|
+
exports.RESPAWN_GUARD_WINDOW_MS = 10 * 60_000;
|
|
57
|
+
const MAX_PENDING_IPC_MESSAGES = 20;
|
|
58
|
+
exports.PRIVATE_RUNTIME_DISPATCH_ACK_TIMEOUT_MS = 8 * 60_000;
|
|
44
59
|
class DaemonProcessManager {
|
|
45
60
|
agents = new Map();
|
|
61
|
+
/** `stopAll()` is terminal for a manager instance. Once set, it fences
|
|
62
|
+
* detached restart/start continuations until the owning daemon exits. */
|
|
63
|
+
shutdownRequested = false;
|
|
46
64
|
maxRestartsPerHour;
|
|
47
65
|
stabilityThresholdMs;
|
|
48
66
|
initialBackoffMs;
|
|
@@ -51,31 +69,130 @@ class DaemonProcessManager {
|
|
|
51
69
|
now;
|
|
52
70
|
setTimeoutFn;
|
|
53
71
|
clearTimeoutFn;
|
|
72
|
+
cooldownRecoveryMs;
|
|
73
|
+
maxCooldownRetries;
|
|
74
|
+
startupStaleAfterMs;
|
|
75
|
+
stopTimeoutMs;
|
|
76
|
+
existsSyncFn;
|
|
77
|
+
configCheckFn;
|
|
78
|
+
statusWriterFn;
|
|
79
|
+
onSnapshotChangeFn;
|
|
80
|
+
/**
|
|
81
|
+
* Notify the snapshot-change observer (if registered). Swallows any
|
|
82
|
+
* errors from the observer so process lifecycle code never fails
|
|
83
|
+
* because the observer threw.
|
|
84
|
+
*/
|
|
85
|
+
notifySnapshotChange(snapshot) {
|
|
86
|
+
if (!this.onSnapshotChangeFn)
|
|
87
|
+
return;
|
|
88
|
+
try {
|
|
89
|
+
this.onSnapshotChangeFn(snapshot);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
(0, runtime_1.emitNervesEvent)({
|
|
93
|
+
level: "warn",
|
|
94
|
+
component: "daemon",
|
|
95
|
+
event: "daemon.snapshot_change_observer_error",
|
|
96
|
+
message: "snapshot-change observer threw",
|
|
97
|
+
meta: {
|
|
98
|
+
agent: snapshot.name,
|
|
99
|
+
error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error),
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
writeStatus(agent, text) {
|
|
105
|
+
try {
|
|
106
|
+
this.statusWriterFn(text);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
(0, runtime_1.emitNervesEvent)({
|
|
110
|
+
level: "warn",
|
|
111
|
+
component: "daemon",
|
|
112
|
+
event: "daemon.status_writer_error",
|
|
113
|
+
message: "daemon status writer threw",
|
|
114
|
+
meta: {
|
|
115
|
+
agent,
|
|
116
|
+
error: error instanceof Error ? error.message : String(error),
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
currentTimeMs() {
|
|
122
|
+
const value = this.now();
|
|
123
|
+
return Number.isFinite(value) ? value : Date.now();
|
|
124
|
+
}
|
|
125
|
+
isStartAttemptCurrent(state, attemptId) {
|
|
126
|
+
return state.startAttemptId === attemptId;
|
|
127
|
+
}
|
|
128
|
+
markConfigCheckFailed(state, errorReason, fixHint) {
|
|
129
|
+
const agent = state.config.name;
|
|
130
|
+
state.snapshot.status = "crashed";
|
|
131
|
+
state.snapshot.errorReason = errorReason;
|
|
132
|
+
state.snapshot.fixHint = fixHint;
|
|
133
|
+
(0, runtime_1.emitNervesEvent)({
|
|
134
|
+
level: "error",
|
|
135
|
+
component: "daemon",
|
|
136
|
+
event: "daemon.agent_config_invalid",
|
|
137
|
+
message: errorReason,
|
|
138
|
+
meta: { agent, fix: fixHint },
|
|
139
|
+
});
|
|
140
|
+
this.writeStatus(agent, `[daemon] ${agent}: ${errorReason}\n` +
|
|
141
|
+
(fixHint ? ` Fix: ${fixHint}\n` : ""));
|
|
142
|
+
this.notifySnapshotChange(state.snapshot);
|
|
143
|
+
}
|
|
54
144
|
constructor(options) {
|
|
55
145
|
this.maxRestartsPerHour = options.maxRestartsPerHour ?? 10;
|
|
56
146
|
this.stabilityThresholdMs = options.stabilityThresholdMs ?? 60_000;
|
|
57
147
|
this.initialBackoffMs = options.initialBackoffMs ?? 1_000;
|
|
58
148
|
this.maxBackoffMs = options.maxBackoffMs ?? 60_000;
|
|
149
|
+
this.cooldownRecoveryMs = options.cooldownRecoveryMs ?? 5 * 60 * 1_000;
|
|
150
|
+
this.maxCooldownRetries = options.maxCooldownRetries ?? 3;
|
|
151
|
+
this.startupStaleAfterMs = options.startupStaleAfterMs ?? 45_000;
|
|
152
|
+
this.stopTimeoutMs = options.stopTimeoutMs ?? 4_000;
|
|
59
153
|
this.spawnFn = options.spawn ?? ((command, args, spawnOptions) => (0, child_process_1.spawn)(command, args, spawnOptions));
|
|
60
154
|
this.now = options.now ?? (() => Date.now());
|
|
61
155
|
this.setTimeoutFn = options.setTimeoutFn ?? ((cb, delay) => setTimeout(cb, delay));
|
|
62
156
|
this.clearTimeoutFn = options.clearTimeoutFn ?? ((timer) => clearTimeout(timer));
|
|
157
|
+
this.existsSyncFn = options.existsSync ?? null;
|
|
158
|
+
this.configCheckFn = options.configCheck ?? null;
|
|
159
|
+
this.statusWriterFn = options.statusWriter ?? ((text) => {
|
|
160
|
+
process.stderr.write(text);
|
|
161
|
+
});
|
|
162
|
+
this.onSnapshotChangeFn = options.onSnapshotChange ?? null;
|
|
63
163
|
for (const agent of options.agents) {
|
|
64
164
|
this.agents.set(agent.name, {
|
|
65
165
|
config: agent,
|
|
66
166
|
process: null,
|
|
167
|
+
startInFlight: false,
|
|
168
|
+
startAttemptedAtMs: null,
|
|
169
|
+
startAttemptId: 0,
|
|
170
|
+
pendingIpcMessages: [],
|
|
171
|
+
pendingDispatches: new Map(),
|
|
67
172
|
restartTimer: null,
|
|
68
173
|
crashTimestamps: [],
|
|
174
|
+
orchestratedRestartTimestamps: [],
|
|
175
|
+
respawnLoopTripped: false,
|
|
69
176
|
stopRequested: false,
|
|
177
|
+
cooldownTimer: null,
|
|
178
|
+
cooldownRetryCount: 0,
|
|
179
|
+
fastCrashCount: 0,
|
|
180
|
+
stopFailureActive: false,
|
|
181
|
+
displacedReason: null,
|
|
70
182
|
snapshot: {
|
|
71
183
|
name: agent.name,
|
|
72
184
|
channel: agent.channel,
|
|
185
|
+
autoStart: agent.autoStart,
|
|
73
186
|
status: "stopped",
|
|
74
187
|
pid: null,
|
|
75
188
|
restartCount: 0,
|
|
76
189
|
startedAt: null,
|
|
77
190
|
lastCrashAt: null,
|
|
78
191
|
backoffMs: this.initialBackoffMs,
|
|
192
|
+
lastExitCode: null,
|
|
193
|
+
lastSignal: null,
|
|
194
|
+
errorReason: null,
|
|
195
|
+
fixHint: null,
|
|
79
196
|
},
|
|
80
197
|
});
|
|
81
198
|
}
|
|
@@ -87,74 +204,508 @@ class DaemonProcessManager {
|
|
|
87
204
|
}
|
|
88
205
|
}
|
|
89
206
|
}
|
|
90
|
-
|
|
207
|
+
triggerAutoStartAgents() {
|
|
208
|
+
for (const state of this.agents.values()) {
|
|
209
|
+
if (!state.config.autoStart)
|
|
210
|
+
continue;
|
|
211
|
+
void this.startAgent(state.config.name).catch((error) => {
|
|
212
|
+
const errorReason = error instanceof Error ? error.message : String(error);
|
|
213
|
+
this.markConfigCheckFailed(state, `agent startup threw before the worker could run: ${errorReason}`, "Run 'ouro doctor' for diagnostics, then retry 'ouro up'.");
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async startAgent(agent, options = {}) {
|
|
91
218
|
const state = this.requireAgent(agent);
|
|
92
|
-
if (
|
|
219
|
+
if (this.shutdownRequested)
|
|
93
220
|
return;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
state.
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
221
|
+
if (state.displacedReason !== null)
|
|
222
|
+
return;
|
|
223
|
+
if (state.process || state.startInFlight)
|
|
224
|
+
return;
|
|
225
|
+
const attemptId = state.startAttemptId + 1;
|
|
226
|
+
state.startAttemptId = attemptId;
|
|
227
|
+
state.startInFlight = true;
|
|
228
|
+
state.startAttemptedAtMs = this.currentTimeMs();
|
|
229
|
+
try {
|
|
230
|
+
this.clearRestartTimer(state);
|
|
231
|
+
state.stopRequested = false;
|
|
232
|
+
state.snapshot.status = "starting";
|
|
233
|
+
if (this.configCheckFn && options.skipConfigCheck !== true) {
|
|
234
|
+
let result;
|
|
235
|
+
try {
|
|
236
|
+
result = await this.configCheckFn(agent);
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
const errorReason = error instanceof Error ? error.message : String(error);
|
|
240
|
+
this.markConfigCheckFailed(state, `agent config validation threw: ${errorReason}`, "Run 'ouro doctor' for diagnostics, then retry 'ouro up'.");
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (!this.isStartAttemptCurrent(state, attemptId))
|
|
244
|
+
return;
|
|
245
|
+
if (state.stopRequested) {
|
|
246
|
+
state.snapshot.status = "stopped";
|
|
247
|
+
state.snapshot.pid = null;
|
|
248
|
+
this.notifySnapshotChange(state.snapshot);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
if (result.skip) {
|
|
252
|
+
state.snapshot.status = "stopped";
|
|
253
|
+
state.snapshot.errorReason = null;
|
|
254
|
+
state.snapshot.fixHint = null;
|
|
255
|
+
(0, runtime_1.emitNervesEvent)({
|
|
256
|
+
component: "daemon",
|
|
257
|
+
event: "daemon.agent_config_skipped",
|
|
258
|
+
message: result.error ?? "agent start skipped by config check",
|
|
259
|
+
meta: { agent, fix: result.fix ?? null },
|
|
260
|
+
});
|
|
261
|
+
this.notifySnapshotChange(state.snapshot);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
if (!result.ok) {
|
|
265
|
+
this.markConfigCheckFailed(state, result.error ?? "agent config validation failed", result.fix ?? null);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
/* v8 ignore next -- defensive duplicate stale-start guard before spawn preparation @preserve */
|
|
269
|
+
if (!this.isStartAttemptCurrent(state, attemptId))
|
|
270
|
+
return;
|
|
271
|
+
// Config check passed — clear any prior error so the pulse stops
|
|
272
|
+
// reporting the broken state. This is the recovery path: the user
|
|
273
|
+
// fixed their secrets/config, the next startAgent attempt sees a
|
|
274
|
+
// valid config, and the pulse goes quiet.
|
|
275
|
+
state.snapshot.errorReason = null;
|
|
276
|
+
state.snapshot.fixHint = null;
|
|
277
|
+
}
|
|
278
|
+
if (options.skipConfigCheck === true) {
|
|
279
|
+
state.snapshot.errorReason = null;
|
|
280
|
+
state.snapshot.fixHint = null;
|
|
281
|
+
}
|
|
282
|
+
const runCwd = (0, identity_1.getRepoRoot)();
|
|
283
|
+
const entryScript = path.join((0, identity_1.getRepoRoot)(), "dist", state.config.entry);
|
|
284
|
+
if (this.existsSyncFn && !this.existsSyncFn(entryScript)) {
|
|
285
|
+
state.snapshot.status = "crashed";
|
|
286
|
+
(0, runtime_1.emitNervesEvent)({
|
|
287
|
+
level: "error",
|
|
288
|
+
component: "daemon",
|
|
289
|
+
event: "daemon.agent_entry_missing",
|
|
290
|
+
message: "agent entry script does not exist — cannot spawn. Run 'ouro daemon install' from the correct location.",
|
|
291
|
+
meta: { agent, entryScript },
|
|
292
|
+
});
|
|
293
|
+
this.notifySnapshotChange(state.snapshot);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
/* v8 ignore next -- defensive duplicate stale-start guard immediately before spawn @preserve */
|
|
297
|
+
if (!this.isStartAttemptCurrent(state, attemptId))
|
|
298
|
+
return;
|
|
299
|
+
if (state.stopRequested) {
|
|
300
|
+
state.snapshot.status = "stopped";
|
|
301
|
+
state.snapshot.pid = null;
|
|
302
|
+
this.notifySnapshotChange(state.snapshot);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const agentArgs = state.config.getArgs?.() ?? state.config.args ?? [];
|
|
306
|
+
const args = [entryScript, "--agent", state.config.agentArg ?? agent, ...agentArgs];
|
|
307
|
+
const child = this.spawnFn("node", args, {
|
|
308
|
+
cwd: runCwd,
|
|
309
|
+
env: state.config.env ? { ...process.env, ...state.config.env } : process.env,
|
|
310
|
+
stdio: ["ignore", "ignore", "ignore", "ipc"],
|
|
311
|
+
});
|
|
312
|
+
/* v8 ignore next 7 -- defensive: spawn should always return a ChildProcess @preserve */
|
|
313
|
+
if (!child) {
|
|
314
|
+
state.snapshot.status = "crashed";
|
|
315
|
+
(0, runtime_1.emitNervesEvent)({ level: "error", component: "daemon", event: "daemon.agent_spawn_failed", message: "spawn returned null", meta: { agent } });
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (!this.isStartAttemptCurrent(state, attemptId)) {
|
|
319
|
+
try {
|
|
320
|
+
child.kill("SIGTERM");
|
|
321
|
+
}
|
|
322
|
+
catch {
|
|
323
|
+
(0, runtime_1.emitNervesEvent)({
|
|
324
|
+
level: "warn",
|
|
325
|
+
component: "daemon",
|
|
326
|
+
event: "daemon.agent_stop_error",
|
|
327
|
+
message: "failed to stop stale managed agent startup",
|
|
328
|
+
meta: { agent },
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
state.process = child;
|
|
334
|
+
state.snapshot.status = "running";
|
|
335
|
+
state.snapshot.pid = child.pid ?? null;
|
|
336
|
+
state.snapshot.startedAt = new Date(this.currentTimeMs()).toISOString();
|
|
337
|
+
child.on("message", (message) => {
|
|
338
|
+
if (state.process !== child || !message || typeof message !== "object")
|
|
339
|
+
return;
|
|
340
|
+
const result = message;
|
|
341
|
+
if (result.type !== "ouro.privateRuntimeDispatchResult" || typeof result.dispatchId !== "string")
|
|
342
|
+
return;
|
|
343
|
+
const pending = state.pendingDispatches.get(result.dispatchId);
|
|
344
|
+
if (!pending || pending.child !== child)
|
|
345
|
+
return;
|
|
346
|
+
state.pendingDispatches.delete(result.dispatchId);
|
|
347
|
+
this.clearTimeoutFn(pending.timer);
|
|
348
|
+
if (result.ok === true)
|
|
349
|
+
pending.resolve();
|
|
350
|
+
else
|
|
351
|
+
pending.reject(new Error(typeof result.error === "string" && result.error.trim() ? result.error.slice(0, 1_000) : "private-runtime dispatch failed"));
|
|
352
|
+
});
|
|
353
|
+
child.once("disconnect", () => {
|
|
354
|
+
if (state.process !== child)
|
|
355
|
+
return;
|
|
356
|
+
this.rejectPendingDispatchesForChild(state, child, new Error(`Managed agent '${agent}' disconnected before acknowledged dispatch completed.`));
|
|
357
|
+
});
|
|
358
|
+
const bootstrap = state.config.getRuntimeCredentialBootstrap?.() ?? null;
|
|
359
|
+
if (bootstrap) {
|
|
360
|
+
const message = {
|
|
361
|
+
type: "ouro.runtimeCredentialBootstrap",
|
|
362
|
+
agentName: bootstrap.agentName,
|
|
363
|
+
};
|
|
364
|
+
if (bootstrap.runtimeConfig)
|
|
365
|
+
message.runtimeConfig = bootstrap.runtimeConfig;
|
|
366
|
+
if (bootstrap.machineRuntimeConfig)
|
|
367
|
+
message.machineRuntimeConfig = bootstrap.machineRuntimeConfig;
|
|
368
|
+
if (bootstrap.machineId)
|
|
369
|
+
message.machineId = bootstrap.machineId;
|
|
370
|
+
if (bootstrap.providerCredentialRecords)
|
|
371
|
+
message.providerCredentialRecords = bootstrap.providerCredentialRecords;
|
|
372
|
+
try {
|
|
373
|
+
child.send?.(message);
|
|
374
|
+
(0, runtime_1.emitNervesEvent)({
|
|
375
|
+
component: "daemon",
|
|
376
|
+
event: "daemon.agent_runtime_credentials_bootstrap_sent",
|
|
377
|
+
message: "sent runtime credential bootstrap to managed agent process",
|
|
378
|
+
meta: {
|
|
379
|
+
agent,
|
|
380
|
+
runtimeConfig: !!bootstrap.runtimeConfig,
|
|
381
|
+
machineRuntimeConfig: !!bootstrap.machineRuntimeConfig,
|
|
382
|
+
providerCredentialRecords: bootstrap.providerCredentialRecords?.length ?? 0,
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
(0, runtime_1.emitNervesEvent)({
|
|
388
|
+
level: "warn",
|
|
389
|
+
component: "daemon",
|
|
390
|
+
event: "daemon.agent_ipc_send_error",
|
|
391
|
+
message: "failed to send runtime credential bootstrap to managed agent process",
|
|
392
|
+
meta: { agent, error: error instanceof Error ? error.message : String(error) },
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
const pendingIpcMessages = state.pendingIpcMessages.splice(0);
|
|
397
|
+
for (const pendingMessage of pendingIpcMessages) {
|
|
398
|
+
const { message, dispatchId } = pendingMessage;
|
|
399
|
+
if (dispatchId) {
|
|
400
|
+
const pending = state.pendingDispatches.get(dispatchId);
|
|
401
|
+
if (!pending)
|
|
402
|
+
continue;
|
|
403
|
+
pending.child = child;
|
|
404
|
+
this.sendAcknowledgedMessage(state, child, dispatchId, message);
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
try {
|
|
408
|
+
child.send?.(message);
|
|
409
|
+
(0, runtime_1.emitNervesEvent)({
|
|
410
|
+
component: "daemon",
|
|
411
|
+
event: "daemon.agent_pending_ipc_flushed",
|
|
412
|
+
message: "flushed pending IPC message to managed agent",
|
|
413
|
+
meta: { agent },
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
catch (error) {
|
|
417
|
+
(0, runtime_1.emitNervesEvent)({
|
|
418
|
+
level: "warn",
|
|
419
|
+
component: "daemon",
|
|
420
|
+
event: "daemon.agent_ipc_send_error",
|
|
421
|
+
message: "failed to flush pending IPC message to managed agent",
|
|
422
|
+
meta: { agent, error: error instanceof Error ? error.message : String(error) },
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
(0, runtime_1.emitNervesEvent)({
|
|
427
|
+
component: "daemon",
|
|
428
|
+
event: "daemon.agent_started",
|
|
429
|
+
message: "daemon started managed agent process",
|
|
430
|
+
meta: { agent, pid: child.pid ?? null, cwd: runCwd },
|
|
431
|
+
});
|
|
432
|
+
this.notifySnapshotChange(state.snapshot);
|
|
433
|
+
/* v8 ignore start — child process error handler; requires real spawn to trigger */
|
|
434
|
+
child.on("error", (err) => {
|
|
435
|
+
(0, runtime_1.emitNervesEvent)({
|
|
436
|
+
level: "warn",
|
|
437
|
+
component: "daemon",
|
|
438
|
+
event: "daemon.agent_process_error",
|
|
439
|
+
message: "managed agent process emitted error",
|
|
440
|
+
meta: { agent, error: err.message },
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
/* v8 ignore stop */
|
|
444
|
+
child.once("exit", (code, signal) => {
|
|
445
|
+
this.onExit(state, child, code, signal);
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
finally {
|
|
449
|
+
if (this.isStartAttemptCurrent(state, attemptId)) {
|
|
450
|
+
state.startInFlight = false;
|
|
451
|
+
state.startAttemptedAtMs = null;
|
|
452
|
+
if (!state.process) {
|
|
453
|
+
this.rejectPendingDispatchesForChild(state, null, new Error(`Managed agent '${agent}' startup ended before acknowledged dispatch completed.`));
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
118
457
|
}
|
|
119
458
|
async stopAgent(agent) {
|
|
120
459
|
const state = this.requireAgent(agent);
|
|
121
460
|
this.clearRestartTimer(state);
|
|
461
|
+
this.clearCooldownTimer(state);
|
|
122
462
|
state.stopRequested = true;
|
|
463
|
+
for (const [dispatchId] of state.pendingDispatches) {
|
|
464
|
+
this.rejectPendingDispatch(state, dispatchId, new Error(`Managed agent '${agent}' stopped before acknowledged dispatch completed.`));
|
|
465
|
+
}
|
|
466
|
+
state.pendingIpcMessages = [];
|
|
467
|
+
// NOTE: do not touch state.respawnLoopTripped / orchestratedRestartTimestamps
|
|
468
|
+
// here. restartAgent calls stopAgent internally; clearing the guard here
|
|
469
|
+
// would reset the window every cycle and defeat the loop-detection. The
|
|
470
|
+
// guard self-clears when timestamps age out of the window (handled inside
|
|
471
|
+
// restartAgent at the prune step).
|
|
123
472
|
if (!state.process) {
|
|
124
473
|
state.snapshot.status = "stopped";
|
|
125
474
|
state.snapshot.pid = null;
|
|
475
|
+
this.notifySnapshotChange(state.snapshot);
|
|
126
476
|
return;
|
|
127
477
|
}
|
|
128
478
|
const child = state.process;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
479
|
+
const childPid = child.pid ?? null;
|
|
480
|
+
const childPidLabel = childPid === null ? "unknown" : String(childPid);
|
|
481
|
+
await new Promise((resolve, reject) => {
|
|
482
|
+
let settled = false;
|
|
483
|
+
let timeout = null;
|
|
484
|
+
const finish = (error) => {
|
|
485
|
+
/* v8 ignore next -- defensive idempotence if an injected timer fires after cancellation @preserve */
|
|
486
|
+
if (settled)
|
|
487
|
+
return;
|
|
488
|
+
settled = true;
|
|
489
|
+
child.removeListener("exit", onExit);
|
|
490
|
+
if (timeout !== null)
|
|
491
|
+
this.clearTimeoutFn(timeout);
|
|
492
|
+
if (error)
|
|
493
|
+
reject(error);
|
|
494
|
+
else
|
|
495
|
+
resolve();
|
|
496
|
+
};
|
|
497
|
+
const onExit = () => finish();
|
|
498
|
+
child.once("exit", onExit);
|
|
499
|
+
let signalAccepted = false;
|
|
500
|
+
try {
|
|
501
|
+
signalAccepted = child.kill("SIGTERM");
|
|
502
|
+
}
|
|
503
|
+
catch {
|
|
504
|
+
const errorReason = "failed to send SIGTERM to managed agent; worker remains tracked";
|
|
505
|
+
state.stopFailureActive = true;
|
|
506
|
+
state.snapshot.errorReason = errorReason;
|
|
507
|
+
state.snapshot.fixHint = `Inspect managed worker PID ${childPidLabel} before retrying the stop or restart.`;
|
|
508
|
+
(0, runtime_1.emitNervesEvent)({
|
|
509
|
+
level: "error",
|
|
510
|
+
component: "daemon",
|
|
511
|
+
event: "daemon.agent_stop_error",
|
|
512
|
+
message: errorReason,
|
|
513
|
+
meta: { agent, pid: childPid },
|
|
514
|
+
});
|
|
515
|
+
this.notifySnapshotChange(state.snapshot);
|
|
516
|
+
finish(new Error(errorReason));
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
if (!signalAccepted) {
|
|
520
|
+
const errorReason = "managed agent rejected SIGTERM; worker remains tracked";
|
|
521
|
+
state.stopFailureActive = true;
|
|
522
|
+
state.snapshot.errorReason = errorReason;
|
|
523
|
+
state.snapshot.fixHint = `Inspect managed worker PID ${childPidLabel} before retrying the stop or restart.`;
|
|
524
|
+
(0, runtime_1.emitNervesEvent)({
|
|
525
|
+
level: "error",
|
|
526
|
+
component: "daemon",
|
|
527
|
+
event: "daemon.agent_stop_error",
|
|
528
|
+
message: errorReason,
|
|
529
|
+
meta: { agent, pid: childPid },
|
|
530
|
+
});
|
|
531
|
+
this.notifySnapshotChange(state.snapshot);
|
|
532
|
+
finish(new Error(errorReason));
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
if (settled)
|
|
536
|
+
return;
|
|
537
|
+
timeout = this.setTimeoutFn(() => {
|
|
538
|
+
if (settled)
|
|
539
|
+
return;
|
|
540
|
+
const errorReason = `managed agent did not exit within ${this.stopTimeoutMs}ms; replacement was not started`;
|
|
541
|
+
state.stopFailureActive = true;
|
|
542
|
+
state.snapshot.errorReason = errorReason;
|
|
543
|
+
state.snapshot.fixHint = `Inspect managed worker PID ${childPidLabel}; retry only after that worker has exited.`;
|
|
544
|
+
(0, runtime_1.emitNervesEvent)({
|
|
545
|
+
level: "error",
|
|
546
|
+
component: "daemon",
|
|
547
|
+
event: "daemon.agent_stop_timeout",
|
|
548
|
+
message: errorReason,
|
|
549
|
+
meta: { agent, pid: childPid, timeoutMs: this.stopTimeoutMs },
|
|
550
|
+
});
|
|
551
|
+
this.notifySnapshotChange(state.snapshot);
|
|
552
|
+
finish(new Error(errorReason));
|
|
553
|
+
}, this.stopTimeoutMs);
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Stop an agent whose home moved to another machine and keep it stopped:
|
|
558
|
+
* it is parked (health checks no longer restart it) and no later start,
|
|
559
|
+
* wake or restart runs it again in this daemon. `ouro up` replaces the
|
|
560
|
+
* daemon with one that does not manage it at all.
|
|
561
|
+
*/
|
|
562
|
+
async displaceAgent(agent, reason) {
|
|
563
|
+
const state = this.requireAgent(agent);
|
|
564
|
+
if (state.displacedReason !== null)
|
|
565
|
+
return;
|
|
566
|
+
state.displacedReason = reason;
|
|
567
|
+
state.snapshot.autoStart = false;
|
|
568
|
+
state.snapshot.errorReason = reason;
|
|
569
|
+
state.snapshot.fixHint = "Run 'ouro up' on this machine to stop managing it, or 'ouro move <agent> here' to take it back.";
|
|
570
|
+
(0, runtime_1.emitNervesEvent)({
|
|
571
|
+
level: "warn",
|
|
572
|
+
component: "daemon",
|
|
573
|
+
event: "daemon.agent_displaced",
|
|
574
|
+
message: "stopping agent homed on another machine",
|
|
575
|
+
meta: { agent, reason },
|
|
576
|
+
});
|
|
577
|
+
this.notifySnapshotChange(state.snapshot);
|
|
578
|
+
await this.stopAgent(agent);
|
|
579
|
+
}
|
|
580
|
+
async restartAgent(agent, options = {}) {
|
|
581
|
+
const state = this.requireAgent(agent);
|
|
582
|
+
if (this.shutdownRequested)
|
|
583
|
+
return;
|
|
584
|
+
// Respawn-loop guard: prune timestamps outside the window, then check
|
|
585
|
+
// whether we've already restarted this agent too many times in it.
|
|
586
|
+
const now = this.now();
|
|
587
|
+
const windowStart = now - exports.RESPAWN_GUARD_WINDOW_MS;
|
|
588
|
+
state.orchestratedRestartTimestamps = state.orchestratedRestartTimestamps.filter((ts) => ts >= windowStart);
|
|
589
|
+
// If the window is now empty, the trip naturally self-clears. That means
|
|
590
|
+
// after RESPAWN_GUARD_WINDOW_MS of no restart attempts, the daemon is
|
|
591
|
+
// willing to try again (e.g. for a fresh health probe failure that has
|
|
592
|
+
// nothing to do with the original loop).
|
|
593
|
+
if (state.respawnLoopTripped && state.orchestratedRestartTimestamps.length === 0) {
|
|
594
|
+
state.respawnLoopTripped = false;
|
|
595
|
+
state.snapshot.errorReason = null;
|
|
596
|
+
state.snapshot.fixHint = null;
|
|
597
|
+
(0, runtime_1.emitNervesEvent)({
|
|
598
|
+
component: "daemon",
|
|
599
|
+
event: "daemon.agent_respawn_loop_cleared",
|
|
600
|
+
message: "respawn-loop guard cleared by window-aging",
|
|
601
|
+
meta: { agent, windowMs: exports.RESPAWN_GUARD_WINDOW_MS },
|
|
602
|
+
});
|
|
603
|
+
this.notifySnapshotChange(state.snapshot);
|
|
134
604
|
}
|
|
135
|
-
|
|
605
|
+
if (state.respawnLoopTripped) {
|
|
606
|
+
(0, runtime_1.emitNervesEvent)({
|
|
607
|
+
level: "error",
|
|
608
|
+
component: "daemon",
|
|
609
|
+
event: "daemon.agent_respawn_loop_blocked",
|
|
610
|
+
message: "refused agent restart — respawn-loop guard tripped; manual intervention required",
|
|
611
|
+
meta: {
|
|
612
|
+
agent,
|
|
613
|
+
recentRestartCount: state.orchestratedRestartTimestamps.length,
|
|
614
|
+
windowMs: exports.RESPAWN_GUARD_WINDOW_MS,
|
|
615
|
+
},
|
|
616
|
+
});
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
if (state.orchestratedRestartTimestamps.length >= exports.RESPAWN_GUARD_MAX_RESTARTS) {
|
|
620
|
+
state.respawnLoopTripped = true;
|
|
621
|
+
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`;
|
|
622
|
+
state.snapshot.fixHint = "investigate the root cause then run `ouro up` to resume";
|
|
623
|
+
(0, runtime_1.emitNervesEvent)({
|
|
624
|
+
level: "error",
|
|
625
|
+
component: "daemon",
|
|
626
|
+
event: "daemon.agent_respawn_loop_tripped",
|
|
627
|
+
message: "respawn-loop guard tripped; further restarts blocked",
|
|
628
|
+
meta: {
|
|
629
|
+
agent,
|
|
630
|
+
restartCount: state.orchestratedRestartTimestamps.length,
|
|
631
|
+
windowMs: exports.RESPAWN_GUARD_WINDOW_MS,
|
|
632
|
+
maxRestarts: exports.RESPAWN_GUARD_MAX_RESTARTS,
|
|
633
|
+
},
|
|
634
|
+
});
|
|
635
|
+
this.notifySnapshotChange(state.snapshot);
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
state.orchestratedRestartTimestamps.push(now);
|
|
639
|
+
if (state.startInFlight && !state.process) {
|
|
640
|
+
const startedAt = state.startAttemptedAtMs;
|
|
641
|
+
/* v8 ignore next -- defensive: startInFlight always records a start timestamp @preserve */
|
|
642
|
+
const elapsedMs = startedAt === null ? 0 : this.currentTimeMs() - startedAt;
|
|
643
|
+
if (elapsedMs < this.startupStaleAfterMs) {
|
|
644
|
+
(0, runtime_1.emitNervesEvent)({
|
|
645
|
+
component: "daemon",
|
|
646
|
+
event: "daemon.agent_restart_deferred",
|
|
647
|
+
message: "managed agent restart skipped while startup is already in flight",
|
|
648
|
+
meta: { agent, elapsedMs, startupStaleAfterMs: this.startupStaleAfterMs },
|
|
649
|
+
});
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
136
652
|
(0, runtime_1.emitNervesEvent)({
|
|
137
653
|
level: "warn",
|
|
138
654
|
component: "daemon",
|
|
139
|
-
event: "daemon.
|
|
140
|
-
message: "
|
|
141
|
-
meta: { agent },
|
|
655
|
+
event: "daemon.agent_startup_stale_recovered",
|
|
656
|
+
message: "managed agent startup was stale; clearing the pending attempt before restart",
|
|
657
|
+
meta: { agent, elapsedMs, startupStaleAfterMs: this.startupStaleAfterMs },
|
|
142
658
|
});
|
|
659
|
+
state.startAttemptId += 1;
|
|
660
|
+
state.startInFlight = false;
|
|
661
|
+
state.startAttemptedAtMs = null;
|
|
143
662
|
}
|
|
144
|
-
}
|
|
145
|
-
async restartAgent(agent) {
|
|
146
663
|
await this.stopAgent(agent);
|
|
147
|
-
await this.startAgent(agent);
|
|
664
|
+
await this.startAgent(agent, options);
|
|
148
665
|
}
|
|
149
666
|
async stopAll() {
|
|
150
|
-
|
|
151
|
-
|
|
667
|
+
this.shutdownRequested = true;
|
|
668
|
+
const states = [...this.agents.values()];
|
|
669
|
+
const results = await Promise.allSettled(states.map((state) => this.stopAgent(state.config.name)));
|
|
670
|
+
const failures = results.flatMap((result, index) => result.status === "rejected"
|
|
671
|
+
? [{ agent: states[index].config.name, reason: result.reason }]
|
|
672
|
+
: []);
|
|
673
|
+
if (failures.length > 0) {
|
|
674
|
+
throw new AggregateError(failures.map((failure) => failure.reason), `failed to stop ${failures.length} managed worker(s): ${failures.map((failure) => failure.agent).join(", ")}`);
|
|
152
675
|
}
|
|
153
676
|
}
|
|
677
|
+
resetAgentFailureState(agent) {
|
|
678
|
+
const state = this.requireAgent(agent);
|
|
679
|
+
this.clearRestartTimer(state);
|
|
680
|
+
this.clearCooldownTimer(state);
|
|
681
|
+
state.cooldownRetryCount = 0;
|
|
682
|
+
state.crashTimestamps = [];
|
|
683
|
+
state.fastCrashCount = 0;
|
|
684
|
+
state.respawnLoopTripped = false;
|
|
685
|
+
state.orchestratedRestartTimestamps = [];
|
|
686
|
+
state.snapshot.errorReason = null;
|
|
687
|
+
state.snapshot.fixHint = null;
|
|
688
|
+
this.notifySnapshotChange(state.snapshot);
|
|
689
|
+
}
|
|
154
690
|
sendToAgent(agent, message) {
|
|
155
691
|
const state = this.requireAgent(agent);
|
|
156
|
-
if (!state.process)
|
|
692
|
+
if (!state.process) {
|
|
693
|
+
if (state.startInFlight) {
|
|
694
|
+
if (state.pendingIpcMessages.length >= MAX_PENDING_IPC_MESSAGES) {
|
|
695
|
+
const evicted = state.pendingIpcMessages.shift();
|
|
696
|
+
if (evicted?.dispatchId)
|
|
697
|
+
this.rejectPendingDispatch(state, evicted.dispatchId, new Error(`Managed agent '${agent}' startup IPC queue evicted acknowledged dispatch.`));
|
|
698
|
+
}
|
|
699
|
+
state.pendingIpcMessages.push({ message });
|
|
700
|
+
(0, runtime_1.emitNervesEvent)({
|
|
701
|
+
component: "daemon",
|
|
702
|
+
event: "daemon.agent_ipc_queued_during_startup",
|
|
703
|
+
message: "queued IPC message while managed agent startup is in flight",
|
|
704
|
+
meta: { agent, queuedCount: state.pendingIpcMessages.length },
|
|
705
|
+
});
|
|
706
|
+
}
|
|
157
707
|
return;
|
|
708
|
+
}
|
|
158
709
|
try {
|
|
159
710
|
state.process.send(message);
|
|
160
711
|
}
|
|
@@ -168,47 +719,168 @@ class DaemonProcessManager {
|
|
|
168
719
|
});
|
|
169
720
|
}
|
|
170
721
|
}
|
|
722
|
+
dispatchToAgent(agent, message, options = {}) {
|
|
723
|
+
const state = this.requireAgent(agent);
|
|
724
|
+
const dispatchId = (0, crypto_1.randomUUID)();
|
|
725
|
+
const timeoutMs = options.timeoutMs ?? exports.PRIVATE_RUNTIME_DISPATCH_ACK_TIMEOUT_MS;
|
|
726
|
+
return new Promise((resolve, reject) => {
|
|
727
|
+
const timer = this.setTimeoutFn(() => {
|
|
728
|
+
const pending = state.pendingDispatches.get(dispatchId);
|
|
729
|
+
if (!pending)
|
|
730
|
+
return;
|
|
731
|
+
state.pendingDispatches.delete(dispatchId);
|
|
732
|
+
if (pending.child && state.process === pending.child) {
|
|
733
|
+
try {
|
|
734
|
+
pending.child.send?.({ type: "ouro.privateRuntimeDispatchCancel", dispatchId });
|
|
735
|
+
}
|
|
736
|
+
catch { /* cancellation is best effort */ }
|
|
737
|
+
}
|
|
738
|
+
reject(new Error(`Private-runtime dispatch timed out after ${timeoutMs}ms.`));
|
|
739
|
+
}, timeoutMs);
|
|
740
|
+
const child = state.process?.connected === false ? null : state.process;
|
|
741
|
+
state.pendingDispatches.set(dispatchId, { child, timer, resolve, reject });
|
|
742
|
+
if (child) {
|
|
743
|
+
this.sendAcknowledgedMessage(state, child, dispatchId, message);
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
if (state.startInFlight) {
|
|
747
|
+
if (state.pendingIpcMessages.length >= MAX_PENDING_IPC_MESSAGES) {
|
|
748
|
+
const evicted = state.pendingIpcMessages.shift();
|
|
749
|
+
if (evicted?.dispatchId)
|
|
750
|
+
this.rejectPendingDispatch(state, evicted.dispatchId, new Error(`Managed agent '${agent}' startup IPC queue evicted acknowledged dispatch.`));
|
|
751
|
+
}
|
|
752
|
+
state.pendingIpcMessages.push({ message, dispatchId });
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
this.rejectPendingDispatch(state, dispatchId, new Error(`Managed agent '${agent}' is unavailable for acknowledged dispatch.`));
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
rejectPendingDispatch(state, dispatchId, error) {
|
|
759
|
+
const pending = state.pendingDispatches.get(dispatchId);
|
|
760
|
+
if (!pending)
|
|
761
|
+
return;
|
|
762
|
+
state.pendingDispatches.delete(dispatchId);
|
|
763
|
+
this.clearTimeoutFn(pending.timer);
|
|
764
|
+
pending.reject(error);
|
|
765
|
+
}
|
|
766
|
+
rejectPendingDispatchesForChild(state, child, error) {
|
|
767
|
+
for (const [dispatchId, pending] of state.pendingDispatches) {
|
|
768
|
+
if (pending.child === child)
|
|
769
|
+
this.rejectPendingDispatch(state, dispatchId, error);
|
|
770
|
+
}
|
|
771
|
+
if (child === null)
|
|
772
|
+
state.pendingIpcMessages = state.pendingIpcMessages.filter((pending) => pending.dispatchId === undefined);
|
|
773
|
+
}
|
|
774
|
+
sendAcknowledgedMessage(state, child, dispatchId, message) {
|
|
775
|
+
try {
|
|
776
|
+
child.send?.({ ...message, dispatchId }, (error) => {
|
|
777
|
+
if (!error)
|
|
778
|
+
return;
|
|
779
|
+
const pending = state.pendingDispatches.get(dispatchId);
|
|
780
|
+
if (!pending || pending.child !== child)
|
|
781
|
+
return;
|
|
782
|
+
this.rejectPendingDispatch(state, dispatchId, error);
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
catch (error) {
|
|
786
|
+
this.rejectPendingDispatch(state, dispatchId, error instanceof Error ? error : new Error(String(error)));
|
|
787
|
+
}
|
|
788
|
+
}
|
|
171
789
|
getAgentSnapshot(agent) {
|
|
172
790
|
return this.agents.get(agent)?.snapshot;
|
|
173
791
|
}
|
|
174
792
|
listAgentSnapshots() {
|
|
175
793
|
return [...this.agents.values()].map((state) => state.snapshot);
|
|
176
794
|
}
|
|
177
|
-
onExit(state, code, signal) {
|
|
178
|
-
|
|
795
|
+
onExit(state, child, code, signal) {
|
|
796
|
+
/* v8 ignore next -- defensive: replacement cannot start before this child's one-shot exit listener drains @preserve */
|
|
797
|
+
if (state.process !== child)
|
|
179
798
|
return;
|
|
799
|
+
this.rejectPendingDispatchesForChild(state, child, new Error(`Managed agent '${state.config.name}' exited before acknowledged dispatch completed.`));
|
|
180
800
|
state.process = null;
|
|
801
|
+
state.startInFlight = false;
|
|
802
|
+
state.startAttemptedAtMs = null;
|
|
181
803
|
state.snapshot.pid = null;
|
|
182
|
-
|
|
183
|
-
|
|
804
|
+
state.snapshot.lastExitCode = code;
|
|
805
|
+
state.snapshot.lastSignal = signal;
|
|
806
|
+
if (state.stopFailureActive) {
|
|
807
|
+
state.stopFailureActive = false;
|
|
808
|
+
state.snapshot.errorReason = null;
|
|
809
|
+
state.snapshot.fixHint = null;
|
|
810
|
+
}
|
|
811
|
+
const unexpectedExit = !state.stopRequested;
|
|
812
|
+
const crashed = unexpectedExit && code !== 0;
|
|
813
|
+
const now = this.currentTimeMs();
|
|
184
814
|
const startedAt = state.snapshot.startedAt ? Date.parse(state.snapshot.startedAt) : now;
|
|
185
815
|
const runDuration = Math.max(0, now - startedAt);
|
|
186
816
|
(0, runtime_1.emitNervesEvent)({
|
|
187
|
-
level:
|
|
817
|
+
level: unexpectedExit ? "warn" : "info",
|
|
188
818
|
component: "daemon",
|
|
189
819
|
event: "daemon.agent_exit",
|
|
190
820
|
message: "managed agent process exited",
|
|
191
|
-
meta: { agent: state.config.name, code, signal, crashed, runDurationMs: runDuration },
|
|
821
|
+
meta: { agent: state.config.name, code, signal, crashed, unexpectedExit, runDurationMs: runDuration },
|
|
192
822
|
});
|
|
193
|
-
if (
|
|
823
|
+
if (runDuration >= this.stabilityThresholdMs) {
|
|
824
|
+
state.snapshot.backoffMs = this.initialBackoffMs;
|
|
825
|
+
}
|
|
826
|
+
if (!unexpectedExit) {
|
|
194
827
|
state.snapshot.status = "stopped";
|
|
195
|
-
|
|
196
|
-
state.snapshot.backoffMs = this.initialBackoffMs;
|
|
197
|
-
}
|
|
828
|
+
this.notifySnapshotChange(state.snapshot);
|
|
198
829
|
return;
|
|
199
830
|
}
|
|
200
|
-
|
|
831
|
+
if (crashed) {
|
|
832
|
+
state.snapshot.lastCrashAt = new Date(now).toISOString();
|
|
833
|
+
}
|
|
834
|
+
if (state.config.autoStart === false) {
|
|
835
|
+
state.snapshot.status = "stopped";
|
|
836
|
+
state.snapshot.errorReason = "non-autostart worker exited unexpectedly; not respawning";
|
|
837
|
+
state.snapshot.fixHint = "Start it explicitly after checking the exit cause.";
|
|
838
|
+
this.notifySnapshotChange(state.snapshot);
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
// Fast-exit detection: repeated early exits, including clean exit-code-0
|
|
842
|
+
// terminations, indicate a startup/configuration or lifecycle fault rather
|
|
843
|
+
// than a stable long-lived worker. Bound them without claiming a crash.
|
|
844
|
+
const FAST_CRASH_THRESHOLD_MS = 5000;
|
|
845
|
+
const FAST_CRASH_MAX = 3;
|
|
846
|
+
if (runDuration < FAST_CRASH_THRESHOLD_MS) {
|
|
847
|
+
state.fastCrashCount = state.fastCrashCount + 1;
|
|
848
|
+
if (state.fastCrashCount >= FAST_CRASH_MAX) {
|
|
849
|
+
state.snapshot.status = "crashed";
|
|
850
|
+
// Capture the fast-exit diagnosis on the snapshot so it surfaces via
|
|
851
|
+
// the pulse with a prescriptive, cause-neutral repair path.
|
|
852
|
+
state.snapshot.errorReason = `agent exited unexpectedly ${FAST_CRASH_MAX} times within ${FAST_CRASH_THRESHOLD_MS}ms of startup — likely a startup/configuration or lifecycle issue.`;
|
|
853
|
+
state.snapshot.fixHint = `Inspect daemon logs and lifecycle/configuration, then run \`ouro up\` to restart.`;
|
|
854
|
+
(0, runtime_1.emitNervesEvent)({
|
|
855
|
+
level: "error",
|
|
856
|
+
component: "daemon",
|
|
857
|
+
event: "daemon.agent_config_failure",
|
|
858
|
+
message: `agent exited unexpectedly ${FAST_CRASH_MAX} times within ${FAST_CRASH_THRESHOLD_MS}ms of startup. Not retrying. Inspect lifecycle/configuration and run \`ouro up\` to restart.`,
|
|
859
|
+
meta: { agent: state.config.name, fastUnexpectedExitCount: state.fastCrashCount, avgRunDurationMs: runDuration },
|
|
860
|
+
});
|
|
861
|
+
this.notifySnapshotChange(state.snapshot);
|
|
862
|
+
return; // Don't schedule cooldown recovery — this needs human/agent intervention
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
else {
|
|
866
|
+
// Reset the fast unexpected-exit counter on a stable run.
|
|
867
|
+
state.fastCrashCount = 0;
|
|
868
|
+
}
|
|
201
869
|
state.crashTimestamps = state.crashTimestamps.filter((crashTs) => crashTs >= startOfHour(now));
|
|
202
870
|
state.crashTimestamps.push(now);
|
|
203
871
|
if (state.crashTimestamps.length > this.maxRestartsPerHour) {
|
|
204
872
|
state.snapshot.status = "crashed";
|
|
873
|
+
state.snapshot.errorReason = `agent exceeded unexpected-exit restart limit (${this.maxRestartsPerHour}/hr) — entering cooldown`;
|
|
874
|
+
state.snapshot.fixHint = "investigate why the agent keeps exiting unexpectedly; cooldown will retry shortly";
|
|
205
875
|
(0, runtime_1.emitNervesEvent)({
|
|
206
876
|
level: "error",
|
|
207
877
|
component: "daemon",
|
|
208
878
|
event: "daemon.agent_restart_exhausted",
|
|
209
|
-
message: "managed agent exceeded restart limit and is marked crashed",
|
|
879
|
+
message: "managed agent exceeded unexpected-exit restart limit and is marked crashed",
|
|
210
880
|
meta: { agent: state.config.name, maxRestartsPerHour: this.maxRestartsPerHour },
|
|
211
881
|
});
|
|
882
|
+
this.notifySnapshotChange(state.snapshot);
|
|
883
|
+
this.scheduleCooldownRecovery(state);
|
|
212
884
|
return;
|
|
213
885
|
}
|
|
214
886
|
state.snapshot.status = "starting";
|
|
@@ -219,6 +891,7 @@ class DaemonProcessManager {
|
|
|
219
891
|
state.restartTimer = this.setTimeoutFn(() => {
|
|
220
892
|
void this.startAgent(state.config.name);
|
|
221
893
|
}, waitMs);
|
|
894
|
+
this.notifySnapshotChange(state.snapshot);
|
|
222
895
|
}
|
|
223
896
|
clearRestartTimer(state) {
|
|
224
897
|
if (state.restartTimer === null)
|
|
@@ -226,6 +899,45 @@ class DaemonProcessManager {
|
|
|
226
899
|
this.clearTimeoutFn(state.restartTimer);
|
|
227
900
|
state.restartTimer = null;
|
|
228
901
|
}
|
|
902
|
+
scheduleCooldownRecovery(state) {
|
|
903
|
+
if (state.cooldownRetryCount >= this.maxCooldownRetries) {
|
|
904
|
+
(0, runtime_1.emitNervesEvent)({
|
|
905
|
+
level: "error",
|
|
906
|
+
component: "daemon",
|
|
907
|
+
event: "daemon.agent_permanent_failure",
|
|
908
|
+
message: "managed agent exhausted all cooldown retries — permanently stopped",
|
|
909
|
+
meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount, maxCooldownRetries: this.maxCooldownRetries },
|
|
910
|
+
});
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
this.clearCooldownTimer(state);
|
|
914
|
+
state.cooldownTimer = this.setTimeoutFn(() => {
|
|
915
|
+
state.cooldownRetryCount += 1;
|
|
916
|
+
state.crashTimestamps = [];
|
|
917
|
+
state.snapshot.backoffMs = this.initialBackoffMs;
|
|
918
|
+
state.snapshot.status = "starting";
|
|
919
|
+
state.snapshot.restartCount += 1;
|
|
920
|
+
(0, runtime_1.emitNervesEvent)({
|
|
921
|
+
component: "daemon",
|
|
922
|
+
event: "daemon.agent_cooldown_recovery",
|
|
923
|
+
message: "attempting cooldown recovery for managed agent",
|
|
924
|
+
meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount },
|
|
925
|
+
});
|
|
926
|
+
void this.startAgent(state.config.name);
|
|
927
|
+
}, this.cooldownRecoveryMs);
|
|
928
|
+
(0, runtime_1.emitNervesEvent)({
|
|
929
|
+
component: "daemon",
|
|
930
|
+
event: "daemon.agent_cooldown_scheduled",
|
|
931
|
+
message: `scheduled cooldown recovery in ${this.cooldownRecoveryMs}ms`,
|
|
932
|
+
meta: { agent: state.config.name, cooldownRecoveryMs: this.cooldownRecoveryMs, cooldownRetryCount: state.cooldownRetryCount },
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
clearCooldownTimer(state) {
|
|
936
|
+
if (state.cooldownTimer === null)
|
|
937
|
+
return;
|
|
938
|
+
this.clearTimeoutFn(state.cooldownTimer);
|
|
939
|
+
state.cooldownTimer = null;
|
|
940
|
+
}
|
|
229
941
|
requireAgent(agent) {
|
|
230
942
|
const state = this.agents.get(agent);
|
|
231
943
|
if (!state) {
|