@ouro.bot/cli 0.1.0-alpha.55 → 0.1.0-alpha.550
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 +133 -19
- 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-bootstrap-drift.md +54 -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 +3555 -0
- package/dist/arc/attention-types.js +8 -0
- package/dist/arc/cares.js +140 -0
- package/dist/arc/episodes.js +117 -0
- package/dist/arc/intentions.js +133 -0
- package/dist/arc/json-store.js +117 -0
- package/dist/arc/obligations.js +237 -0
- package/dist/arc/packets.js +193 -0
- package/dist/arc/presence.js +185 -0
- package/dist/arc/task-lifecycle.js +65 -0
- package/dist/heart/active-work.js +837 -26
- package/dist/heart/agent-entry.js +58 -3
- package/dist/heart/attachments/image-normalize.js +194 -0
- package/dist/heart/attachments/materialize.js +97 -0
- package/dist/heart/attachments/originals.js +88 -0
- package/dist/heart/attachments/render.js +29 -0
- package/dist/heart/attachments/sources/adapter.js +2 -0
- package/dist/heart/attachments/sources/bluebubbles.js +156 -0
- package/dist/heart/attachments/sources/cli-local-file.js +78 -0
- package/dist/heart/attachments/sources/index.js +16 -0
- package/dist/heart/attachments/store.js +103 -0
- package/dist/heart/attachments/types.js +93 -0
- package/dist/heart/auth/auth-flow.js +479 -0
- package/dist/heart/background-operations.js +281 -0
- package/dist/heart/bundle-state.js +168 -0
- package/dist/heart/commitments.js +111 -0
- package/dist/heart/config-registry.js +304 -0
- package/dist/heart/config.js +114 -118
- package/dist/heart/core.js +925 -246
- package/dist/heart/cross-chat-delivery.js +3 -18
- package/dist/heart/daemon/agent-config-check.js +512 -0
- package/dist/heart/daemon/agent-discovery.js +102 -3
- package/dist/heart/daemon/agent-service.js +522 -0
- package/dist/heart/daemon/agentic-repair.js +554 -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 +665 -0
- package/dist/heart/daemon/cli-exec.js +7565 -0
- package/dist/heart/daemon/cli-help.js +498 -0
- package/dist/heart/daemon/cli-parse.js +1590 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +775 -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 -1672
- package/dist/heart/daemon/daemon-entry.js +417 -2
- package/dist/heart/daemon/daemon-health.js +183 -0
- package/dist/heart/daemon/daemon-rollup.js +58 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +87 -13
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +758 -71
- package/dist/heart/daemon/dns-workflow.js +394 -0
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +844 -0
- package/dist/heart/daemon/drift-detection.js +146 -0
- package/dist/heart/daemon/health-monitor.js +122 -1
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
- package/dist/heart/daemon/http-health-probe.js +80 -0
- package/dist/heart/daemon/human-command-screens.js +234 -0
- package/dist/heart/daemon/human-readiness.js +114 -0
- package/dist/heart/daemon/inner-status.js +102 -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 +82 -12
- 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 +2 -2
- package/dist/heart/daemon/os-cron-deps.js +135 -0
- package/dist/heart/daemon/os-cron.js +14 -12
- package/dist/heart/daemon/ouro-bot-entry.js +4 -2
- package/dist/heart/daemon/ouro-entry.js +3 -1
- package/dist/heart/daemon/process-manager.js +375 -33
- package/dist/heart/daemon/provider-discovery.js +137 -0
- package/dist/heart/daemon/provider-ping-progress.js +83 -0
- package/dist/heart/daemon/pulse.js +475 -0
- package/dist/heart/daemon/readiness-repair.js +365 -0
- package/dist/heart/daemon/run-hooks.js +2 -0
- package/dist/heart/daemon/runtime-logging.js +67 -16
- package/dist/heart/daemon/runtime-metadata.js +3 -31
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sense-manager.js +353 -38
- package/dist/heart/daemon/session-id-resolver.js +131 -0
- package/dist/heart/daemon/skill-management-installer.js +94 -0
- package/dist/heart/daemon/socket-client.js +158 -11
- package/dist/heart/daemon/stale-bundle-prune.js +96 -0
- package/dist/heart/daemon/startup-tui.js +330 -0
- package/dist/heart/daemon/task-scheduler.js +3 -25
- package/dist/heart/daemon/terminal-ui.js +499 -0
- package/dist/heart/daemon/thoughts.js +162 -17
- package/dist/heart/daemon/up-progress.js +366 -0
- package/dist/heart/daemon/vault-items.js +56 -0
- package/dist/heart/delegation.js +1 -1
- package/dist/heart/habits/habit-migration.js +189 -0
- package/dist/heart/habits/habit-parser.js +140 -0
- package/dist/heart/habits/habit-runtime-state.js +100 -0
- package/dist/heart/habits/habit-scheduler.js +372 -0
- package/dist/heart/{daemon → hatch}/hatch-flow.js +52 -117
- package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
- package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
- package/dist/heart/{daemon → hatch}/specialist-tools.js +35 -12
- package/dist/heart/identity.js +200 -51
- package/dist/heart/kept-notes.js +357 -0
- package/dist/heart/kicks.js +1 -1
- package/dist/heart/machine-identity.js +161 -0
- package/dist/heart/mail-import-discovery.js +353 -0
- package/dist/heart/mailbox/mailbox-http-hooks.js +66 -0
- package/dist/heart/mailbox/mailbox-http-response.js +7 -0
- package/dist/heart/mailbox/mailbox-http-routes.js +246 -0
- package/dist/heart/mailbox/mailbox-http-static.js +103 -0
- package/dist/heart/mailbox/mailbox-http-transport.js +116 -0
- package/dist/heart/mailbox/mailbox-http.js +99 -0
- package/dist/heart/mailbox/mailbox-read.js +31 -0
- package/dist/heart/mailbox/mailbox-types.js +27 -0
- package/dist/heart/mailbox/mailbox-view.js +195 -0
- package/dist/heart/mailbox/readers/agent-machine.js +382 -0
- package/dist/heart/mailbox/readers/continuity-readers.js +338 -0
- package/dist/heart/mailbox/readers/mail.js +362 -0
- package/dist/heart/mailbox/readers/runtime-readers.js +651 -0
- package/dist/heart/mailbox/readers/sessions.js +232 -0
- package/dist/heart/mailbox/readers/shared.js +111 -0
- package/dist/heart/mcp/mcp-server.js +683 -0
- package/dist/heart/migrate-config.js +100 -0
- package/dist/heart/model-capabilities.js +19 -0
- package/dist/heart/platform.js +81 -0
- package/dist/heart/provider-attempt.js +134 -0
- package/dist/heart/provider-binding-resolver.js +255 -0
- package/dist/heart/provider-credentials.js +425 -0
- package/dist/heart/provider-failover.js +301 -0
- package/dist/heart/provider-models.js +81 -0
- package/dist/heart/provider-ping.js +262 -0
- package/dist/heart/provider-state.js +216 -0
- package/dist/heart/provider-visibility.js +188 -0
- package/dist/heart/providers/anthropic-token.js +131 -0
- package/dist/heart/providers/anthropic.js +139 -52
- package/dist/heart/providers/azure.js +97 -13
- package/dist/heart/providers/error-classification.js +127 -0
- package/dist/heart/providers/github-copilot.js +145 -0
- package/dist/heart/providers/minimax-vlm.js +189 -0
- package/dist/heart/providers/minimax.js +26 -8
- package/dist/heart/providers/openai-codex.js +55 -40
- package/dist/heart/runtime-capability-check.js +170 -0
- package/dist/heart/runtime-credentials.js +367 -0
- package/dist/heart/runtime-cwd.js +87 -0
- package/dist/heart/sense-truth.js +11 -4
- package/dist/heart/session-activity.js +43 -22
- package/dist/heart/session-events.js +1149 -0
- package/dist/heart/session-playback-cli-main.js +5 -0
- package/dist/heart/session-playback-cli.js +36 -0
- package/dist/heart/session-playback.js +231 -0
- package/dist/heart/session-stats-cli-main.js +5 -0
- package/dist/heart/session-stats.js +182 -0
- package/dist/heart/session-transcript.js +243 -0
- package/dist/heart/start-of-turn-packet.js +345 -0
- package/dist/heart/streaming.js +44 -27
- package/dist/heart/sync-classification.js +176 -0
- package/dist/heart/sync.js +449 -0
- package/dist/heart/target-resolution.js +9 -5
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/timeouts.js +101 -0
- package/dist/heart/tool-activity-callbacks.js +59 -0
- package/dist/heart/tool-description.js +139 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-context.js +381 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
- package/dist/heart/versioning/ouro-path-installer.js +426 -0
- package/dist/heart/versioning/ouro-version-manager.js +295 -0
- package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
- package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
- package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
- package/dist/mailbox-ui/assets/index-BPr5vNuM.css +1 -0
- package/dist/mailbox-ui/assets/index-Cm51CY9W.js +61 -0
- package/dist/mailbox-ui/index.html +15 -0
- package/dist/mailroom/attention.js +167 -0
- package/dist/mailroom/autonomy.js +209 -0
- package/dist/mailroom/blob-store.js +674 -0
- package/dist/mailroom/body-cache.js +61 -0
- package/dist/mailroom/core.js +720 -0
- package/dist/mailroom/entry.js +160 -0
- package/dist/mailroom/file-store.js +430 -0
- package/dist/mailroom/mbox-import.js +383 -0
- package/dist/mailroom/outbound.js +380 -0
- package/dist/mailroom/policy.js +263 -0
- package/dist/mailroom/reader.js +233 -0
- package/dist/mailroom/search-cache.js +256 -0
- package/dist/mailroom/search-relevance.js +319 -0
- package/dist/mailroom/smtp-ingress.js +176 -0
- package/dist/mailroom/source-state.js +176 -0
- package/dist/mailroom/thread.js +109 -0
- package/dist/mailroom/travel-extract.js +89 -0
- package/dist/mind/bundle-manifest.js +7 -1
- package/dist/mind/context.js +165 -101
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +62 -75
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/friends/channel.js +30 -0
- package/dist/mind/friends/resolver.js +54 -2
- package/dist/mind/friends/store-file.js +39 -3
- package/dist/mind/friends/types.js +2 -2
- package/dist/mind/journal-index.js +161 -0
- package/dist/mind/note-search.js +268 -0
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/pending.js +4 -0
- package/dist/mind/prompt-refresh.js +3 -2
- package/dist/mind/prompt.js +995 -123
- package/dist/mind/provenance-trust.js +26 -0
- package/dist/mind/scrutiny.js +173 -0
- package/dist/nerves/cli-logging.js +7 -1
- package/dist/nerves/coverage/audit-rules.js +15 -6
- package/dist/nerves/coverage/audit.js +28 -2
- package/dist/nerves/coverage/cli.js +1 -1
- package/dist/nerves/coverage/contract.js +5 -5
- package/dist/nerves/coverage/file-completeness.js +139 -5
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +224 -4
- package/dist/nerves/observation.js +20 -0
- package/dist/nerves/redact.js +79 -0
- package/dist/nerves/review/cli-main.js +5 -0
- package/dist/nerves/review/cli.js +156 -0
- package/dist/nerves/review/core.js +152 -0
- package/dist/nerves/runtime.js +5 -1
- package/dist/repertoire/ado-client.js +15 -56
- package/dist/repertoire/ado-semantic.js +11 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +816 -0
- package/dist/repertoire/bundle-templates.js +72 -0
- package/dist/repertoire/bw-installer.js +180 -0
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/context-pack.js +330 -0
- package/dist/repertoire/coding/feedback.js +197 -30
- package/dist/repertoire/coding/manager.js +158 -9
- package/dist/repertoire/coding/spawner.js +55 -9
- package/dist/repertoire/coding/tools.js +170 -7
- package/dist/repertoire/commerce-errors.js +109 -0
- package/dist/repertoire/commerce-self-test.js +156 -0
- package/dist/repertoire/credential-access.js +111 -0
- package/dist/repertoire/duffel-client.js +185 -0
- package/dist/repertoire/github-client.js +14 -55
- package/dist/repertoire/graph-client.js +11 -52
- package/dist/repertoire/guardrails.js +396 -0
- package/dist/repertoire/mcp-client.js +295 -0
- package/dist/repertoire/mcp-manager.js +362 -0
- package/dist/repertoire/mcp-tools.js +63 -0
- package/dist/repertoire/shell-sessions.js +133 -0
- package/dist/repertoire/skills.js +15 -24
- package/dist/repertoire/stripe-client.js +131 -0
- package/dist/repertoire/tasks/board.js +31 -5
- package/dist/repertoire/tasks/fix.js +182 -0
- package/dist/repertoire/tasks/index.js +16 -4
- package/dist/repertoire/tasks/lifecycle.js +2 -2
- package/dist/repertoire/tasks/parser.js +3 -2
- package/dist/repertoire/tasks/scanner.js +194 -37
- package/dist/repertoire/tasks/transitions.js +16 -78
- package/dist/repertoire/tool-results.js +29 -0
- package/dist/repertoire/tools-attachments.js +317 -0
- package/dist/repertoire/tools-base.js +47 -1075
- package/dist/repertoire/tools-bluebubbles.js +1 -0
- package/dist/repertoire/tools-bridge.js +142 -0
- package/dist/repertoire/tools-bundle.js +984 -0
- package/dist/repertoire/tools-config.js +185 -0
- package/dist/repertoire/tools-continuity.js +248 -0
- package/dist/repertoire/tools-credential.js +381 -0
- package/dist/repertoire/tools-files.js +342 -0
- package/dist/repertoire/tools-flight.js +224 -0
- package/dist/repertoire/tools-flow.js +119 -0
- package/dist/repertoire/tools-github.js +1 -7
- package/dist/repertoire/tools-mail.js +1857 -0
- package/dist/repertoire/tools-notes.js +421 -0
- package/dist/repertoire/tools-session.js +750 -0
- package/dist/repertoire/tools-shell.js +120 -0
- package/dist/repertoire/tools-stripe.js +180 -0
- package/dist/repertoire/tools-surface.js +243 -0
- package/dist/repertoire/tools-teams.js +9 -39
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-trip.js +604 -0
- package/dist/repertoire/tools-user-profile.js +144 -0
- package/dist/repertoire/tools-vault.js +40 -0
- package/dist/repertoire/tools.js +108 -100
- 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 +561 -0
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/attention-queue.js +116 -0
- package/dist/senses/bluebubbles/active-turns.js +216 -0
- package/dist/senses/bluebubbles/attachment-cache.js +53 -0
- package/dist/senses/bluebubbles/attachment-download.js +137 -0
- package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +219 -18
- package/dist/senses/bluebubbles/entry.js +77 -0
- package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +20 -3
- package/dist/senses/bluebubbles/index.js +2305 -0
- package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -70
- package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +33 -12
- package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +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/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 +187 -0
- package/dist/senses/cli.js +520 -209
- package/dist/senses/commands.js +66 -3
- package/dist/senses/habit-turn-message.js +108 -0
- package/dist/senses/inner-dialog-worker.js +175 -21
- package/dist/senses/inner-dialog.js +330 -27
- package/dist/senses/mail-entry.js +66 -0
- package/dist/senses/mail.js +379 -0
- package/dist/senses/pipeline.js +569 -182
- package/dist/senses/proactive-content-guard.js +51 -0
- package/dist/senses/shared-turn.js +248 -0
- package/dist/senses/surface-tool.js +68 -0
- package/dist/senses/teams-entry.js +60 -8
- package/dist/senses/teams.js +387 -98
- package/dist/senses/trust-gate.js +100 -5
- package/dist/trips/core.js +138 -0
- package/dist/trips/store.js +146 -0
- package/package.json +38 -7
- package/skills/agent-commerce.md +106 -0
- package/skills/browser-navigation.md +117 -0
- package/skills/commerce-setup-guide.md +116 -0
- package/skills/commerce-setup.md +84 -0
- package/skills/configure-dev-tools.md +101 -0
- package/skills/travel-planning.md +138 -0
- package/dist/heart/daemon/ouro-path-installer.js +0 -178
- package/dist/heart/daemon/subagent-installer.js +0 -166
- package/dist/heart/session-recall.js +0 -116
- package/dist/mind/associative-recall.js +0 -209
- package/dist/senses/bluebubbles-entry.js +0 -13
- package/dist/senses/bluebubbles.js +0 -1177
- package/dist/senses/debug-activity.js +0 -148
- package/subagents/README.md +0 -86
- package/subagents/work-doer.md +0 -237
- package/subagents/work-merger.md +0 -618
- package/subagents/work-planner.md +0 -390
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
- /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
- /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
- /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
- /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.McpManager = void 0;
|
|
4
|
+
exports.getSharedMcpManager = getSharedMcpManager;
|
|
5
|
+
exports.shutdownSharedMcpManager = shutdownSharedMcpManager;
|
|
6
|
+
exports.resetSharedMcpManager = resetSharedMcpManager;
|
|
7
|
+
const mcp_client_1 = require("./mcp-client");
|
|
8
|
+
const identity_1 = require("../heart/identity");
|
|
9
|
+
const runtime_1 = require("../nerves/runtime");
|
|
10
|
+
const credential_access_1 = require("./credential-access");
|
|
11
|
+
const MAX_RESTART_RETRIES = 5;
|
|
12
|
+
const RESTART_DELAY_MS = 1000;
|
|
13
|
+
class McpManager {
|
|
14
|
+
servers = new Map();
|
|
15
|
+
shuttingDown = false;
|
|
16
|
+
async start(servers) {
|
|
17
|
+
(0, runtime_1.emitNervesEvent)({
|
|
18
|
+
event: "mcp.manager_start",
|
|
19
|
+
component: "repertoire",
|
|
20
|
+
message: "starting MCP manager",
|
|
21
|
+
meta: { serverCount: Object.keys(servers).length },
|
|
22
|
+
});
|
|
23
|
+
const entries = Object.entries(servers);
|
|
24
|
+
for (const [name, config] of entries) {
|
|
25
|
+
await this.connectServer(name, config);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
listAllTools() {
|
|
29
|
+
const result = [];
|
|
30
|
+
for (const [name, entry] of this.servers) {
|
|
31
|
+
result.push({ server: name, tools: entry.cachedTools });
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
async callTool(server, tool, args) {
|
|
36
|
+
let entry = this.servers.get(server);
|
|
37
|
+
if (!entry) {
|
|
38
|
+
throw new Error(`Unknown server: ${server}`);
|
|
39
|
+
}
|
|
40
|
+
if (!entry.client.isConnected()) {
|
|
41
|
+
await this.recoverStaleTransport(server, "pre-call disconnected");
|
|
42
|
+
entry = this.servers.get(server);
|
|
43
|
+
if (!entry?.client.isConnected()) {
|
|
44
|
+
throw new Error(`Server "${server}" is disconnected`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
return await entry.client.callTool(tool, args);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (!(0, mcp_client_1.isMcpTransportError)(error)) {
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
55
|
+
await this.recoverStaleTransport(server, reason);
|
|
56
|
+
const recovered = this.servers.get(server);
|
|
57
|
+
if (!recovered?.client.isConnected()) {
|
|
58
|
+
throw new Error(`Server "${server}" is disconnected after recovery: ${reason}`);
|
|
59
|
+
}
|
|
60
|
+
return recovered.client.callTool(tool, args);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
async runCanaries() {
|
|
64
|
+
const results = [];
|
|
65
|
+
for (const [server, entry] of [...this.servers]) {
|
|
66
|
+
try {
|
|
67
|
+
if (!entry.client.isConnected()) {
|
|
68
|
+
await this.recoverStaleTransport(server, "canary disconnected");
|
|
69
|
+
}
|
|
70
|
+
const current = this.servers.get(server);
|
|
71
|
+
if (!current?.client.isConnected()) {
|
|
72
|
+
results.push({ server, ok: false, detail: "disconnected after recovery attempt" });
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const tools = await current.client.refreshTools();
|
|
76
|
+
current.cachedTools = tools;
|
|
77
|
+
current.consecutiveFailures = 0;
|
|
78
|
+
results.push({ server, ok: true, detail: `${tools.length} tools listed` });
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
82
|
+
if ((0, mcp_client_1.isMcpTransportError)(error)) {
|
|
83
|
+
await this.recoverStaleTransport(server, reason);
|
|
84
|
+
}
|
|
85
|
+
results.push({ server, ok: false, detail: reason });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return results;
|
|
89
|
+
}
|
|
90
|
+
/* v8 ignore start — reconcile: dynamic MCP server management, tested via integration @preserve */
|
|
91
|
+
/** Re-read agent config and connect new servers / disconnect removed ones. */
|
|
92
|
+
async reconcile() {
|
|
93
|
+
try {
|
|
94
|
+
const config = (0, identity_1.loadAgentConfig)();
|
|
95
|
+
const servers = config.mcpServers ?? {};
|
|
96
|
+
const currentNames = new Set(this.servers.keys());
|
|
97
|
+
const desiredNames = new Set(Object.keys(servers));
|
|
98
|
+
// Connect new servers
|
|
99
|
+
for (const [name, cfg] of Object.entries(servers)) {
|
|
100
|
+
if (!currentNames.has(name)) {
|
|
101
|
+
(0, runtime_1.emitNervesEvent)({
|
|
102
|
+
event: "mcp.server_added",
|
|
103
|
+
component: "repertoire",
|
|
104
|
+
message: `connecting new MCP server: ${name}`,
|
|
105
|
+
meta: { server: name, command: cfg.command },
|
|
106
|
+
});
|
|
107
|
+
await this.connectServer(name, cfg);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Disconnect removed servers
|
|
111
|
+
for (const name of currentNames) {
|
|
112
|
+
if (!desiredNames.has(name)) {
|
|
113
|
+
(0, runtime_1.emitNervesEvent)({
|
|
114
|
+
event: "mcp.server_removed",
|
|
115
|
+
component: "repertoire",
|
|
116
|
+
message: `disconnecting removed MCP server: ${name}`,
|
|
117
|
+
meta: { server: name },
|
|
118
|
+
});
|
|
119
|
+
const entry = this.servers.get(name);
|
|
120
|
+
if (entry)
|
|
121
|
+
entry.client.shutdown();
|
|
122
|
+
this.servers.delete(name);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
(0, runtime_1.emitNervesEvent)({
|
|
128
|
+
level: "warn",
|
|
129
|
+
event: "mcp.reconcile_error",
|
|
130
|
+
component: "repertoire",
|
|
131
|
+
message: "failed to reconcile MCP servers",
|
|
132
|
+
meta: { reason: error instanceof Error ? error.message : String(error) },
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/* v8 ignore stop */
|
|
137
|
+
shutdown() {
|
|
138
|
+
this.shuttingDown = true;
|
|
139
|
+
// `_end` (not `_stop`) to pair with `mcp.manager_start` under the
|
|
140
|
+
// nerves audit start/end pairing rule.
|
|
141
|
+
(0, runtime_1.emitNervesEvent)({
|
|
142
|
+
event: "mcp.manager_end",
|
|
143
|
+
component: "repertoire",
|
|
144
|
+
message: "shutting down MCP manager",
|
|
145
|
+
meta: { serverCount: this.servers.size },
|
|
146
|
+
});
|
|
147
|
+
for (const [, entry] of this.servers) {
|
|
148
|
+
entry.client.shutdown();
|
|
149
|
+
}
|
|
150
|
+
this.servers.clear();
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Resolve `vault:DOMAIN/FIELD` references in server env config.
|
|
154
|
+
* Returns resolved env or throws with a descriptive error.
|
|
155
|
+
*/
|
|
156
|
+
async resolveVaultEnv(_serverName, env) {
|
|
157
|
+
const resolved = { ...env };
|
|
158
|
+
const store = (0, credential_access_1.getCredentialStore)();
|
|
159
|
+
for (const [key, value] of Object.entries(resolved)) {
|
|
160
|
+
const match = value.match(/^vault:([^/]+)\/(.+)$/);
|
|
161
|
+
if (!match)
|
|
162
|
+
continue;
|
|
163
|
+
const [, domain, field] = match;
|
|
164
|
+
try {
|
|
165
|
+
resolved[key] = await store.getRawSecret(domain, field);
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
/* v8 ignore next -- reason @preserve */
|
|
169
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
170
|
+
// Classify the error for actionable messaging
|
|
171
|
+
let classification = "vault unreachable";
|
|
172
|
+
if (reason.includes("no credential found")) {
|
|
173
|
+
classification = "item not found";
|
|
174
|
+
}
|
|
175
|
+
else if (reason.includes("field") && reason.includes("not found")) {
|
|
176
|
+
classification = "field empty";
|
|
177
|
+
}
|
|
178
|
+
throw new Error(`vault:${domain}/${field} could not be resolved: ${classification}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return resolved;
|
|
182
|
+
}
|
|
183
|
+
async connectServer(name, config) {
|
|
184
|
+
// Resolve vault: references in env before spawning
|
|
185
|
+
let resolvedConfig = config;
|
|
186
|
+
if (config.env) {
|
|
187
|
+
try {
|
|
188
|
+
const resolvedEnv = await this.resolveVaultEnv(name, config.env);
|
|
189
|
+
resolvedConfig = { ...config, env: resolvedEnv };
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
/* v8 ignore next -- reason @preserve */
|
|
193
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
194
|
+
(0, runtime_1.emitNervesEvent)({
|
|
195
|
+
level: "error",
|
|
196
|
+
event: "mcp.vault_resolve_error",
|
|
197
|
+
component: "repertoire",
|
|
198
|
+
message: `skipping MCP server "${name}": ${reason}`,
|
|
199
|
+
meta: { server: name, reason },
|
|
200
|
+
});
|
|
201
|
+
return; // Skip this server, continue to next
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const client = new mcp_client_1.McpClient(resolvedConfig);
|
|
205
|
+
const entry = {
|
|
206
|
+
name,
|
|
207
|
+
config,
|
|
208
|
+
client,
|
|
209
|
+
cachedTools: [],
|
|
210
|
+
consecutiveFailures: 0,
|
|
211
|
+
};
|
|
212
|
+
this.servers.set(name, entry);
|
|
213
|
+
client.onClose(() => {
|
|
214
|
+
if (this.shuttingDown)
|
|
215
|
+
return;
|
|
216
|
+
this.handleServerCrash(name);
|
|
217
|
+
});
|
|
218
|
+
try {
|
|
219
|
+
await client.connect();
|
|
220
|
+
const tools = await client.listTools();
|
|
221
|
+
entry.cachedTools = tools;
|
|
222
|
+
entry.consecutiveFailures = 0;
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
226
|
+
(0, runtime_1.emitNervesEvent)({
|
|
227
|
+
level: "error",
|
|
228
|
+
event: "mcp.connect_error",
|
|
229
|
+
component: "repertoire",
|
|
230
|
+
message: `failed to connect MCP server "${name}" (command: ${config.command}). Check that the command exists and is properly configured. Reason: ${reason}`,
|
|
231
|
+
meta: {
|
|
232
|
+
server: name,
|
|
233
|
+
command: config.command,
|
|
234
|
+
args: config.args,
|
|
235
|
+
reason,
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
handleServerCrash(name) {
|
|
241
|
+
const entry = this.servers.get(name);
|
|
242
|
+
/* v8 ignore next -- defensive: entry removed between close event and handler @preserve */
|
|
243
|
+
if (!entry)
|
|
244
|
+
return;
|
|
245
|
+
entry.consecutiveFailures++;
|
|
246
|
+
if (entry.consecutiveFailures > MAX_RESTART_RETRIES) {
|
|
247
|
+
(0, runtime_1.emitNervesEvent)({
|
|
248
|
+
level: "error",
|
|
249
|
+
event: "mcp.connect_error",
|
|
250
|
+
component: "repertoire",
|
|
251
|
+
message: `MCP server "${name}" exceeded max restart retries (${MAX_RESTART_RETRIES}). Giving up — check that "${entry.config.command}" exists and is properly configured in agent.json mcpServers.`,
|
|
252
|
+
meta: { server: name, command: entry.config.command, failures: entry.consecutiveFailures },
|
|
253
|
+
});
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
(0, runtime_1.emitNervesEvent)({
|
|
257
|
+
level: "warn",
|
|
258
|
+
event: "mcp.server_restart",
|
|
259
|
+
component: "repertoire",
|
|
260
|
+
message: `restarting crashed MCP server: ${name}`,
|
|
261
|
+
meta: { server: name, attempt: entry.consecutiveFailures },
|
|
262
|
+
});
|
|
263
|
+
/* v8 ignore start -- timer callback: covered by mcp-manager.test.ts via fake timers but v8 can't trace @preserve */
|
|
264
|
+
setTimeout(() => {
|
|
265
|
+
if (this.shuttingDown)
|
|
266
|
+
return;
|
|
267
|
+
this.restartServer(name).catch(() => {
|
|
268
|
+
// Error handling is inside restartServer
|
|
269
|
+
});
|
|
270
|
+
}, RESTART_DELAY_MS);
|
|
271
|
+
/* v8 ignore stop */
|
|
272
|
+
}
|
|
273
|
+
/* v8 ignore start -- called from timer callback: covered by mcp-manager.test.ts via fake timers but v8 can't trace @preserve */
|
|
274
|
+
async restartServer(name) {
|
|
275
|
+
const entry = this.servers.get(name);
|
|
276
|
+
if (!entry)
|
|
277
|
+
return;
|
|
278
|
+
// Remove old entry and reconnect
|
|
279
|
+
this.servers.delete(name);
|
|
280
|
+
entry.client.shutdown();
|
|
281
|
+
await this.connectServer(name, entry.config);
|
|
282
|
+
// Preserve failure count
|
|
283
|
+
const newEntry = this.servers.get(name);
|
|
284
|
+
if (newEntry) {
|
|
285
|
+
newEntry.consecutiveFailures = entry.consecutiveFailures;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/* v8 ignore stop */
|
|
289
|
+
async recoverStaleTransport(name, reason) {
|
|
290
|
+
(0, runtime_1.emitNervesEvent)({
|
|
291
|
+
level: "warn",
|
|
292
|
+
event: "mcp.transport_recovery",
|
|
293
|
+
component: "repertoire",
|
|
294
|
+
message: `recovering stale MCP transport: ${name}`,
|
|
295
|
+
meta: { server: name, reason },
|
|
296
|
+
});
|
|
297
|
+
await this.restartServer(name);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
exports.McpManager = McpManager;
|
|
301
|
+
let _sharedManager = null;
|
|
302
|
+
let _sharedManagerPromise = null;
|
|
303
|
+
/**
|
|
304
|
+
* Get or create a shared McpManager instance from the agent's config.
|
|
305
|
+
* Returns null if no mcpServers are configured.
|
|
306
|
+
* Safe to call from multiple senses — will only create one instance.
|
|
307
|
+
*/
|
|
308
|
+
async function getSharedMcpManager() {
|
|
309
|
+
// If manager exists, reconcile to pick up config changes (new/removed servers)
|
|
310
|
+
/* v8 ignore start — reconcile on existing manager @preserve */
|
|
311
|
+
if (_sharedManager) {
|
|
312
|
+
await _sharedManager.reconcile();
|
|
313
|
+
return _sharedManager;
|
|
314
|
+
}
|
|
315
|
+
/* v8 ignore stop */
|
|
316
|
+
/* v8 ignore next -- race guard: deduplicates concurrent initialization calls @preserve */
|
|
317
|
+
if (_sharedManagerPromise)
|
|
318
|
+
return _sharedManagerPromise;
|
|
319
|
+
// Always re-check config — agent may have added servers since last call
|
|
320
|
+
_sharedManagerPromise = (async () => {
|
|
321
|
+
try {
|
|
322
|
+
const config = (0, identity_1.loadAgentConfig)();
|
|
323
|
+
const servers = config.mcpServers;
|
|
324
|
+
if (!servers || Object.keys(servers).length === 0)
|
|
325
|
+
return null;
|
|
326
|
+
const manager = new McpManager();
|
|
327
|
+
await manager.start(servers);
|
|
328
|
+
_sharedManager = manager;
|
|
329
|
+
return manager;
|
|
330
|
+
}
|
|
331
|
+
catch (error) {
|
|
332
|
+
(0, runtime_1.emitNervesEvent)({
|
|
333
|
+
level: "error",
|
|
334
|
+
event: "mcp.manager_start",
|
|
335
|
+
component: "repertoire",
|
|
336
|
+
message: "failed to initialize shared MCP manager",
|
|
337
|
+
/* v8 ignore next -- both branches tested: Error in wiring test, non-Error is defensive @preserve */
|
|
338
|
+
meta: { reason: error instanceof Error ? error.message : String(error) },
|
|
339
|
+
});
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
finally {
|
|
343
|
+
_sharedManagerPromise = null;
|
|
344
|
+
}
|
|
345
|
+
})();
|
|
346
|
+
return _sharedManagerPromise;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Shut down the shared MCP manager and clear the singleton.
|
|
350
|
+
* Called during daemon/agent shutdown.
|
|
351
|
+
*/
|
|
352
|
+
function shutdownSharedMcpManager() {
|
|
353
|
+
if (_sharedManager) {
|
|
354
|
+
_sharedManager.shutdown();
|
|
355
|
+
_sharedManager = null;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/** Reset for testing only */
|
|
359
|
+
function resetSharedMcpManager() {
|
|
360
|
+
_sharedManager = null;
|
|
361
|
+
_sharedManagerPromise = null;
|
|
362
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* First-class MCP tool integration — converts MCP server tools into ToolDefinitions
|
|
4
|
+
* so the model can call them directly without shell indirection.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.mcpToolsAsDefinitions = mcpToolsAsDefinitions;
|
|
8
|
+
const runtime_1 = require("../nerves/runtime");
|
|
9
|
+
/**
|
|
10
|
+
* Convert all tools from an McpManager into ToolDefinition objects.
|
|
11
|
+
* Each tool gets named `{server}_{tool}` (e.g., `browser_navigate`).
|
|
12
|
+
* The handler calls `mcpManager.callTool()` and returns concatenated text content.
|
|
13
|
+
*/
|
|
14
|
+
function mcpToolsAsDefinitions(mcpManager) {
|
|
15
|
+
if (!mcpManager)
|
|
16
|
+
return [];
|
|
17
|
+
return mcpManager.listAllTools().flatMap((entry) => entry.tools.map((tool) => ({
|
|
18
|
+
tool: {
|
|
19
|
+
type: "function",
|
|
20
|
+
function: {
|
|
21
|
+
name: tool.name.startsWith(`${entry.server}_`) || tool.name === entry.server
|
|
22
|
+
? tool.name
|
|
23
|
+
: `${entry.server}_${tool.name}`,
|
|
24
|
+
description: tool.description || `MCP tool: ${tool.name} (server: ${entry.server})`,
|
|
25
|
+
parameters: tool.inputSchema ?? { type: "object", properties: {} },
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
handler: async (args) => {
|
|
29
|
+
(0, runtime_1.emitNervesEvent)({
|
|
30
|
+
event: "mcp.tool_start",
|
|
31
|
+
component: "repertoire",
|
|
32
|
+
message: `calling MCP tool ${entry.server}/${tool.name}`,
|
|
33
|
+
meta: { server: entry.server, tool: tool.name },
|
|
34
|
+
});
|
|
35
|
+
try {
|
|
36
|
+
const result = await mcpManager.callTool(entry.server, tool.name, args);
|
|
37
|
+
const text = result.content
|
|
38
|
+
.filter((c) => c.type === "text" && c.text)
|
|
39
|
+
.map((c) => c.text)
|
|
40
|
+
.join("");
|
|
41
|
+
(0, runtime_1.emitNervesEvent)({
|
|
42
|
+
event: "mcp.tool_end",
|
|
43
|
+
component: "repertoire",
|
|
44
|
+
message: `MCP tool ${entry.server}/${tool.name} completed`,
|
|
45
|
+
meta: { server: entry.server, tool: tool.name },
|
|
46
|
+
});
|
|
47
|
+
return text;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
51
|
+
(0, runtime_1.emitNervesEvent)({
|
|
52
|
+
level: "error",
|
|
53
|
+
event: "mcp.tool_error",
|
|
54
|
+
component: "repertoire",
|
|
55
|
+
message: `MCP tool ${entry.server}/${tool.name} failed: ${reason}`,
|
|
56
|
+
meta: { server: entry.server, tool: tool.name, reason },
|
|
57
|
+
});
|
|
58
|
+
return `[mcp error] ${entry.server}/${tool.name}: ${reason}`;
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
mcpServer: entry.server,
|
|
62
|
+
})));
|
|
63
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.spawnBackgroundShell = spawnBackgroundShell;
|
|
4
|
+
exports.getShellSession = getShellSession;
|
|
5
|
+
exports.listShellSessions = listShellSessions;
|
|
6
|
+
exports.tailShellSession = tailShellSession;
|
|
7
|
+
exports.resetShellSessions = resetShellSessions;
|
|
8
|
+
exports.detectDestructivePatterns = detectDestructivePatterns;
|
|
9
|
+
const child_process_1 = require("child_process");
|
|
10
|
+
const crypto_1 = require("crypto");
|
|
11
|
+
const runtime_1 = require("../nerves/runtime");
|
|
12
|
+
const sessions = new Map();
|
|
13
|
+
const MAX_OUTPUT_LINES = 200;
|
|
14
|
+
function spawnBackgroundShell(command) {
|
|
15
|
+
const id = (0, crypto_1.randomUUID)();
|
|
16
|
+
const proc = (0, child_process_1.spawn)("sh", ["-c", command], {
|
|
17
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
18
|
+
});
|
|
19
|
+
const session = {
|
|
20
|
+
process: proc,
|
|
21
|
+
info: {
|
|
22
|
+
id,
|
|
23
|
+
command,
|
|
24
|
+
status: "running",
|
|
25
|
+
exitCode: null,
|
|
26
|
+
pid: proc.pid,
|
|
27
|
+
startedAt: Date.now(),
|
|
28
|
+
output: [],
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
const appendOutput = (data) => {
|
|
32
|
+
const lines = data.toString().split("\n");
|
|
33
|
+
for (const line of lines) {
|
|
34
|
+
if (line.length > 0 || session.info.output.length > 0) {
|
|
35
|
+
session.info.output.push(line);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Keep only the last MAX_OUTPUT_LINES
|
|
39
|
+
if (session.info.output.length > MAX_OUTPUT_LINES) {
|
|
40
|
+
session.info.output = session.info.output.slice(-MAX_OUTPUT_LINES);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
proc.stdout?.on("data", appendOutput);
|
|
44
|
+
proc.stderr?.on("data", appendOutput);
|
|
45
|
+
proc.on("close", (code) => {
|
|
46
|
+
session.info.status = "exited";
|
|
47
|
+
session.info.exitCode = code;
|
|
48
|
+
(0, runtime_1.emitNervesEvent)({
|
|
49
|
+
component: "repertoire",
|
|
50
|
+
event: "repertoire.shell.process_exit",
|
|
51
|
+
message: "background shell process exited",
|
|
52
|
+
meta: { id, command, exitCode: code, pid: proc.pid },
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
sessions.set(id, session);
|
|
56
|
+
(0, runtime_1.emitNervesEvent)({
|
|
57
|
+
component: "repertoire",
|
|
58
|
+
event: "repertoire.shell.spawn",
|
|
59
|
+
message: "spawned background shell session",
|
|
60
|
+
meta: { id, command, pid: proc.pid },
|
|
61
|
+
});
|
|
62
|
+
return { ...session.info };
|
|
63
|
+
}
|
|
64
|
+
function getShellSession(id) {
|
|
65
|
+
const session = sessions.get(id);
|
|
66
|
+
if (!session)
|
|
67
|
+
return undefined;
|
|
68
|
+
(0, runtime_1.emitNervesEvent)({
|
|
69
|
+
component: "repertoire",
|
|
70
|
+
event: "repertoire.shell.status_check",
|
|
71
|
+
message: "checked shell session status",
|
|
72
|
+
meta: { id, status: session.info.status, exitCode: session.info.exitCode },
|
|
73
|
+
});
|
|
74
|
+
return { ...session.info };
|
|
75
|
+
}
|
|
76
|
+
function listShellSessions() {
|
|
77
|
+
return Array.from(sessions.values()).map((s) => ({
|
|
78
|
+
id: s.info.id,
|
|
79
|
+
command: s.info.command,
|
|
80
|
+
status: s.info.status,
|
|
81
|
+
exitCode: s.info.exitCode,
|
|
82
|
+
pid: s.info.pid,
|
|
83
|
+
startedAt: s.info.startedAt,
|
|
84
|
+
output: [], // Don't include full output in listing
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
function tailShellSession(id, lines = 50) {
|
|
88
|
+
const session = sessions.get(id);
|
|
89
|
+
if (!session)
|
|
90
|
+
return undefined;
|
|
91
|
+
const tail = session.info.output.slice(-lines);
|
|
92
|
+
(0, runtime_1.emitNervesEvent)({
|
|
93
|
+
component: "repertoire",
|
|
94
|
+
event: "repertoire.shell.tail",
|
|
95
|
+
message: "tailed shell session output",
|
|
96
|
+
meta: { id, requestedLines: lines, returnedLines: tail.length },
|
|
97
|
+
});
|
|
98
|
+
return tail.join("\n");
|
|
99
|
+
}
|
|
100
|
+
/** Reset all sessions (for testing) */
|
|
101
|
+
function resetShellSessions() {
|
|
102
|
+
for (const session of sessions.values()) {
|
|
103
|
+
if (session.info.status === "running") {
|
|
104
|
+
session.process.kill();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
sessions.clear();
|
|
108
|
+
}
|
|
109
|
+
const DESTRUCTIVE_PATTERNS = [
|
|
110
|
+
{ name: "rm -rf root/home", regex: /rm\s+-rf\s+[/~]/ },
|
|
111
|
+
{ name: "git push --force", regex: /git\s+push\s+--force/ },
|
|
112
|
+
{ name: "git reset --hard", regex: /git\s+reset\s+--hard/ },
|
|
113
|
+
{ name: "git clean -f", regex: /git\s+clean\s+-f/ },
|
|
114
|
+
{ name: "git branch -D", regex: /git\s+branch\s+-D/ },
|
|
115
|
+
{ name: "fork bomb", regex: /:\(\)\s*\{/ },
|
|
116
|
+
{ name: "write to raw device", regex: />\s*\/dev\/sd/ },
|
|
117
|
+
{ name: "git checkout .", regex: /git\s+checkout\s+\./ },
|
|
118
|
+
{ name: "git stash drop", regex: /git\s+stash\s+drop/ },
|
|
119
|
+
];
|
|
120
|
+
/**
|
|
121
|
+
* Detect destructive patterns in a shell command.
|
|
122
|
+
* Returns list of matched pattern names. Empty array = safe.
|
|
123
|
+
* This is a friction layer, not a hard block.
|
|
124
|
+
*/
|
|
125
|
+
function detectDestructivePatterns(command) {
|
|
126
|
+
const matched = [];
|
|
127
|
+
for (const pattern of DESTRUCTIVE_PATTERNS) {
|
|
128
|
+
if (pattern.regex.test(command)) {
|
|
129
|
+
matched.push(pattern.name);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return matched;
|
|
133
|
+
}
|
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.getSkillsDir = getSkillsDir;
|
|
37
|
+
exports.getHarnessSkillsDir = getHarnessSkillsDir;
|
|
37
38
|
exports.listSkills = listSkills;
|
|
38
39
|
exports.loadSkill = loadSkill;
|
|
39
40
|
exports.getLoadedSkills = getLoadedSkills;
|
|
@@ -50,9 +51,9 @@ function getSkillsDir() {
|
|
|
50
51
|
function getProtocolMirrorDir() {
|
|
51
52
|
return path.join(getSkillsDir(), "protocols");
|
|
52
53
|
}
|
|
53
|
-
//
|
|
54
|
-
function
|
|
55
|
-
return path.join((0, identity_1.getRepoRoot)(), "
|
|
54
|
+
// Harness-level skills live in {repoRoot}/skills/ directory.
|
|
55
|
+
function getHarnessSkillsDir() {
|
|
56
|
+
return path.join((0, identity_1.getRepoRoot)(), "skills");
|
|
56
57
|
}
|
|
57
58
|
function listMarkdownBasenames(dir) {
|
|
58
59
|
if (!fs.existsSync(dir))
|
|
@@ -63,7 +64,7 @@ function listMarkdownBasenames(dir) {
|
|
|
63
64
|
.map((f) => path.basename(f, ".md"))
|
|
64
65
|
.sort();
|
|
65
66
|
}
|
|
66
|
-
//
|
|
67
|
+
// process-local store for loaded skills
|
|
67
68
|
const loadedSkills = [];
|
|
68
69
|
function listSkills() {
|
|
69
70
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -74,8 +75,10 @@ function listSkills() {
|
|
|
74
75
|
});
|
|
75
76
|
const baseSkills = listMarkdownBasenames(getSkillsDir());
|
|
76
77
|
const protocolMirrors = listMarkdownBasenames(getProtocolMirrorDir());
|
|
77
|
-
const
|
|
78
|
-
|
|
78
|
+
const harnessSkills = listMarkdownBasenames(getHarnessSkillsDir());
|
|
79
|
+
// Agent skills (base + protocol) come first; harness skills are fallback.
|
|
80
|
+
// Set deduplicates by name — agent overrides harness.
|
|
81
|
+
const skills = [...new Set([...baseSkills, ...protocolMirrors, ...harnessSkills])].sort();
|
|
79
82
|
(0, runtime_1.emitNervesEvent)({
|
|
80
83
|
event: "repertoire.load_end",
|
|
81
84
|
component: "repertoire",
|
|
@@ -93,7 +96,7 @@ function loadSkill(skillName) {
|
|
|
93
96
|
});
|
|
94
97
|
const directSkillPath = path.join(getSkillsDir(), `${skillName}.md`);
|
|
95
98
|
const protocolMirrorPath = path.join(getProtocolMirrorDir(), `${skillName}.md`);
|
|
96
|
-
const
|
|
99
|
+
const harnessSkillPath = path.join(getHarnessSkillsDir(), `${skillName}.md`);
|
|
97
100
|
let resolvedPath = null;
|
|
98
101
|
// 1) Direct agent skill.
|
|
99
102
|
if (fs.existsSync(directSkillPath)) {
|
|
@@ -103,21 +106,9 @@ function loadSkill(skillName) {
|
|
|
103
106
|
else if (fs.existsSync(protocolMirrorPath)) {
|
|
104
107
|
resolvedPath = protocolMirrorPath;
|
|
105
108
|
}
|
|
106
|
-
// 3)
|
|
107
|
-
else if (fs.existsSync(
|
|
108
|
-
|
|
109
|
-
level: "warn",
|
|
110
|
-
event: "repertoire.error",
|
|
111
|
-
component: "repertoire",
|
|
112
|
-
message: "protocol mirror missing; using canonical fallback",
|
|
113
|
-
meta: {
|
|
114
|
-
operation: "loadSkill",
|
|
115
|
-
skill: skillName,
|
|
116
|
-
mirrorPath: protocolMirrorPath,
|
|
117
|
-
canonicalPath: canonicalProtocolPath,
|
|
118
|
-
},
|
|
119
|
-
});
|
|
120
|
-
resolvedPath = canonicalProtocolPath;
|
|
109
|
+
// 3) Harness-level skill (ships with npm package).
|
|
110
|
+
else if (fs.existsSync(harnessSkillPath)) {
|
|
111
|
+
resolvedPath = harnessSkillPath;
|
|
121
112
|
}
|
|
122
113
|
if (!resolvedPath) {
|
|
123
114
|
(0, runtime_1.emitNervesEvent)({
|
|
@@ -128,13 +119,13 @@ function loadSkill(skillName) {
|
|
|
128
119
|
meta: {
|
|
129
120
|
operation: "loadSkill",
|
|
130
121
|
skill: skillName,
|
|
131
|
-
checkedPaths: [directSkillPath, protocolMirrorPath,
|
|
122
|
+
checkedPaths: [directSkillPath, protocolMirrorPath, harnessSkillPath],
|
|
132
123
|
},
|
|
133
124
|
});
|
|
134
125
|
throw new Error(`skill '${skillName}' not found in:\n` +
|
|
135
126
|
`- ${directSkillPath}\n` +
|
|
136
127
|
`- ${protocolMirrorPath}\n` +
|
|
137
|
-
`- ${
|
|
128
|
+
`- ${harnessSkillPath}`);
|
|
138
129
|
}
|
|
139
130
|
const content = fs.readFileSync(resolvedPath, "utf-8");
|
|
140
131
|
if (!loadedSkills.includes(skillName)) {
|