@ouro.bot/cli 0.1.0-alpha.88 → 0.1.0-alpha.880
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 +154 -23
- package/RepairGuide.ouro/agent.json +5 -0
- package/RepairGuide.ouro/psyche/IDENTITY.md +19 -0
- package/RepairGuide.ouro/psyche/SOUL.md +55 -0
- package/RepairGuide.ouro/skills/diagnose-broken-remote.md +63 -0
- package/RepairGuide.ouro/skills/diagnose-stacked-typed-issues.md +35 -0
- package/RepairGuide.ouro/skills/diagnose-sync-blocked.md +54 -0
- package/RepairGuide.ouro/skills/diagnose-vault-expired.md +60 -0
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +4 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +2 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
- package/assets/bluebubbles-host +264 -0
- package/assets/sanctuary-host-launcher.sh +16 -0
- package/changelog.json +5601 -16
- package/deploy/unraid/Dockerfile +58 -0
- package/deploy/unraid/README.txt +2995 -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 +1197 -0
- package/deploy/unraid/sanctuary-deployment-target.mjs +710 -0
- package/deploy/unraid/sanctuary-replay-gate.mjs +1127 -0
- package/deploy/unraid/sanctuary-safe-rename.py +24 -0
- package/deploy/unraid/sanctuary-unit16-host-broker.mjs +2085 -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 +29 -0
- package/dist/a2a/card.js +57 -0
- package/dist/a2a/client.js +238 -0
- package/dist/a2a/config.js +50 -0
- package/dist/a2a/delegated-command.js +151 -0
- package/dist/a2a/delegation-stores.js +56 -0
- package/dist/a2a/did-resolution.js +93 -0
- package/dist/a2a/escalation-grants.js +122 -0
- package/dist/a2a/escalation-wiring.js +60 -0
- package/dist/a2a/identity.js +204 -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/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-harness.js +175 -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 +766 -0
- package/dist/a2a/task-store.js +69 -0
- package/dist/a2a/transport.js +45 -0
- package/dist/a2a/trusted-files.js +98 -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 +798 -40
- 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 +382 -0
- package/dist/heart/background-operations.js +281 -0
- package/dist/heart/bridges/manager.js +137 -17
- package/dist/heart/bridges/store.js +14 -2
- package/dist/heart/bundle-state.js +168 -0
- package/dist/heart/commitments.js +44 -18
- package/dist/heart/config-registry.js +331 -0
- package/dist/heart/config.js +174 -131
- package/dist/heart/context-loss-gauntlet.js +387 -0
- package/dist/heart/context-loss-sentinel.js +1157 -0
- package/dist/heart/core.js +2286 -580
- package/dist/heart/cross-chat-delivery.js +9 -22
- package/dist/heart/daemon/agent-config-check.js +451 -0
- package/dist/heart/daemon/agent-discovery.js +185 -3
- 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 +854 -0
- package/dist/heart/daemon/cli-desk.js +322 -0
- package/dist/heart/daemon/cli-exec.js +8951 -0
- package/dist/heart/daemon/cli-help.js +716 -0
- package/dist/heart/daemon/cli-parse.js +2941 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +777 -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 +232 -0
- package/dist/heart/daemon/daemon-bootstrap-startup.js +115 -0
- package/dist/heart/daemon/daemon-cli.js +29 -2349
- package/dist/heart/daemon/daemon-entry.js +932 -11
- 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 +88 -13
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +2084 -105
- 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 +135 -1
- package/dist/heart/daemon/http-health-probe.js +80 -0
- package/dist/heart/daemon/human-command-screens.js +234 -0
- package/dist/heart/daemon/human-readiness.js +114 -0
- package/dist/heart/daemon/inner-status.js +78 -0
- package/dist/heart/daemon/interactive-repair.js +394 -0
- package/dist/heart/daemon/launchd.js +37 -8
- package/dist/heart/daemon/log-tailer.js +79 -10
- package/dist/heart/daemon/logs-prune.js +132 -0
- package/dist/heart/daemon/mcp-canary.js +524 -0
- package/dist/heart/daemon/message-router.js +65 -5
- 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 +733 -61
- 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 +2 -0
- package/dist/heart/daemon/runtime-logging.js +47 -14
- package/dist/heart/daemon/runtime-metadata.js +2 -30
- package/dist/heart/daemon/runtime-mode.js +13 -2
- 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 +1310 -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 +699 -48
- package/dist/heart/daemon/session-id-resolver.js +131 -0
- package/dist/heart/daemon/skill-management-installer.js +1 -1
- package/dist/heart/daemon/socket-client.js +180 -12
- 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 +138 -54
- package/dist/heart/daemon/up-progress.js +366 -0
- package/dist/heart/daemon/vault-items.js +56 -0
- package/dist/heart/delegation.js +1 -4
- package/dist/heart/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-flow.js +55 -62
- 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 +53 -18
- package/dist/heart/identity.js +196 -59
- package/dist/heart/kept-notes.js +289 -0
- package/dist/heart/kicks.js +1 -1
- package/dist/heart/machine-identity.js +161 -0
- package/dist/heart/mail-import-discovery.js +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 +11 -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 +252 -0
- package/dist/heart/private-runtime/types.js +2 -0
- package/dist/heart/progress-story.js +1 -1
- 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 +173 -60
- package/dist/heart/providers/azure.js +23 -11
- package/dist/heart/providers/error-classification.js +127 -0
- package/dist/heart/providers/github-copilot.js +34 -38
- package/dist/heart/providers/minimax-vlm.js +189 -0
- package/dist/heart/providers/minimax.js +26 -8
- package/dist/heart/providers/openai-codex-token.js +349 -0
- package/dist/heart/providers/openai-codex.js +55 -40
- package/dist/heart/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 +22 -4
- package/dist/heart/session-activity.js +92 -24
- 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/source-grounding.js +194 -0
- package/dist/heart/start-of-turn-packet.js +372 -0
- package/dist/heart/steward-policy.js +656 -0
- package/dist/heart/streaming.js +604 -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 +11 -7
- 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 +487 -0
- package/dist/heart/turn-execution-lease.js +30 -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 +432 -0
- package/dist/heart/versioning/ouro-recovery-launcher.js +76 -0
- package/dist/heart/versioning/ouro-version-manager.js +409 -0
- package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
- package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
- package/dist/heart/versioning/update-hooks.js +154 -0
- package/dist/heart/versioning/version-intent.js +114 -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 +24 -2
- package/dist/mind/context.js +307 -139
- package/dist/mind/desk-section.js +362 -0
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +68 -77
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/{associative-recall.js → note-search.js} +47 -58
- package/dist/mind/obligation-steering.js +199 -3
- package/dist/mind/pending.js +36 -10
- package/dist/mind/prompt-budget.js +479 -0
- package/dist/mind/prompt-refresh.js +3 -2
- package/dist/mind/prompt.js +1048 -198
- 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/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 +55 -35
- package/dist/nerves/coverage/source-scanner.js +4 -4
- package/dist/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +293 -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 +15 -56
- package/dist/repertoire/ado-semantic.js +16 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +1045 -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 +174 -32
- package/dist/repertoire/coding/index.js +15 -3
- package/dist/repertoire/coding/manager.js +159 -10
- package/dist/repertoire/coding/spawner.js +58 -11
- package/dist/repertoire/coding/tools.js +221 -15
- 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 +178 -0
- package/dist/repertoire/desk/classifier.js +362 -0
- package/dist/repertoire/duffel-client.js +185 -0
- package/dist/repertoire/github-client.js +14 -55
- package/dist/repertoire/graph-client.js +11 -52
- package/dist/repertoire/guardrails.js +873 -19
- package/dist/repertoire/house-sweep.js +466 -0
- package/dist/repertoire/mcp-client.js +58 -5
- package/dist/repertoire/mcp-manager.js +427 -139
- 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/shell-sessions.js +134 -0
- package/dist/repertoire/skills.js +48 -4
- 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 +79 -1168
- package/dist/repertoire/tools-bluebubbles.js +2 -0
- package/dist/repertoire/tools-bridge.js +144 -0
- package/dist/repertoire/tools-bundle.js +993 -0
- package/dist/repertoire/tools-commerce.js +253 -0
- package/dist/repertoire/tools-config.js +186 -0
- package/dist/repertoire/tools-continuity.js +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 +138 -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-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 +12 -39
- 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.js +145 -0
- package/dist/repertoire/tools.js +509 -156
- 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 +628 -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 +131 -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 → bluebubbles/client.js} +355 -40
- 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 → bluebubbles/inbound-log.js} +21 -3
- package/dist/senses/bluebubbles/index.js +3841 -0
- package/dist/senses/bluebubbles/latest-turn.js +311 -0
- package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
- package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +159 -28
- package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +24 -13
- 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 → bluebubbles/runtime-state.js} +30 -2
- package/dist/senses/bluebubbles/semantic-receipts.js +1986 -0
- package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
- 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.js +601 -304
- 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/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 +1115 -204
- package/dist/senses/private-runtime-worker.js +859 -0
- package/dist/senses/private-runtime.js +1619 -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 +347 -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 +789 -0
- package/dist/senses/surface-tool.js +109 -0
- package/dist/senses/teams-entry.js +60 -8
- package/dist/senses/teams.js +519 -218
- 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 +2349 -0
- package/dist/senses/trust-gate.js +104 -9
- package/dist/senses/voice/audio-playback.js +237 -0
- package/dist/senses/voice/audio-routing.js +119 -0
- package/dist/senses/voice/elevenlabs.js +202 -0
- package/dist/senses/voice/floor-control.js +431 -0
- package/dist/senses/voice/floor-controller.js +115 -0
- package/dist/senses/voice/golden-path.js +116 -0
- package/dist/senses/voice/index.js +29 -0
- package/dist/senses/voice/meeting.js +113 -0
- package/dist/senses/voice/outbound.js +190 -0
- package/dist/senses/voice/phone.js +33 -0
- package/dist/senses/voice/playback.js +139 -0
- package/dist/senses/voice/realtime-eval.js +496 -0
- package/dist/senses/voice/realtime-trace.js +531 -0
- package/dist/senses/voice/transcript.js +70 -0
- package/dist/senses/voice/turn.js +191 -0
- package/dist/senses/voice/twilio-phone-runtime.js +807 -0
- package/dist/senses/voice/twilio-phone.js +5031 -0
- package/dist/senses/voice/types.js +2 -0
- package/dist/senses/voice/whisper.js +161 -0
- package/dist/senses/voice-entry.js +81 -0
- package/dist/senses/voice-realtime-eval-command.js +99 -0
- package/dist/senses/voice-realtime-eval-entry.js +21 -0
- package/dist/senses/voice-twilio-entry.js +87 -0
- package/dist/trips/core.js +138 -0
- package/dist/trips/store.js +265 -0
- package/dist/util/frontmatter.js +69 -0
- package/npm-shrinkwrap.json +8100 -0
- package/package.json +73 -13
- package/skills/agent-commerce.md +113 -0
- package/skills/browser-navigation.md +117 -0
- package/skills/commerce-setup-guide.md +116 -0
- package/skills/commerce-setup.md +84 -0
- package/skills/configure-dev-tools.md +99 -0
- package/skills/travel-planning.md +138 -0
- package/dist/heart/daemon/auth-flow.js +0 -430
- package/dist/heart/daemon/ouro-path-installer.js +0 -260
- package/dist/heart/daemon/ouro-version-manager.js +0 -171
- package/dist/heart/daemon/update-hooks.js +0 -138
- package/dist/heart/obligations.js +0 -191
- package/dist/heart/safe-workspace.js +0 -362
- package/dist/heart/session-recall.js +0 -116
- package/dist/mind/friends/channel.js +0 -84
- package/dist/mind/friends/group-context.js +0 -144
- package/dist/mind/friends/resolver.js +0 -84
- package/dist/mind/friends/store-file.js +0 -190
- package/dist/mind/friends/store.js +0 -4
- package/dist/mind/friends/tokens.js +0 -26
- package/dist/mind/friends/trust-explanation.js +0 -74
- package/dist/mind/friends/types.js +0 -29
- package/dist/repertoire/tasks/board.js +0 -134
- package/dist/repertoire/tasks/index.js +0 -224
- package/dist/repertoire/tasks/lifecycle.js +0 -80
- package/dist/repertoire/tasks/middleware.js +0 -65
- package/dist/repertoire/tasks/parser.js +0 -173
- package/dist/repertoire/tasks/scanner.js +0 -132
- package/dist/repertoire/tasks/transitions.js +0 -144
- package/dist/senses/bluebubbles-entry.js +0 -13
- package/dist/senses/bluebubbles.js +0 -1181
- package/dist/senses/debug-activity.js +0 -154
- package/dist/senses/inner-dialog-worker.js +0 -90
- package/dist/senses/inner-dialog.js +0 -525
- package/subagents/README.md +0 -7
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
- /package/dist/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +0 -0
- /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
- /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
- /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
- /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wrapMissionResultDataPart = wrapMissionResultDataPart;
|
|
4
|
+
exports.isMissionResultDataPart = isMissionResultDataPart;
|
|
5
|
+
exports.receiveInboundMissionResult = receiveInboundMissionResult;
|
|
6
|
+
const a2a_client_1 = require("@ouro.bot/friends/a2a-client");
|
|
7
|
+
const friends_1 = require("@ouro.bot/friends");
|
|
8
|
+
const runtime_1 = require("../nerves/runtime");
|
|
9
|
+
/**
|
|
10
|
+
* THE HARNESS-OWNED RESULT WIRE.
|
|
11
|
+
*
|
|
12
|
+
* A mission RESULT is NOT a `FriendsKind` (`FriendsKind` = profile_share |
|
|
13
|
+
* mission_share | coordination) — `prepareMissionResult`/`importMissionResult` are
|
|
14
|
+
* transport-agnostic + store-only, and the result does NOT ride `sendShare`/
|
|
15
|
+
* `receiveShare`. So the HARNESS owns the wire: the result envelope is sealed (reusing
|
|
16
|
+
* the sign-then-seal crypto) and wrapped in a DataPart carrying a distinct `ouroKind:
|
|
17
|
+
* "mission_result"` discriminator. The inbound bridge recognizes that tag and routes
|
|
18
|
+
* it to `importMissionResult` — NEVER to `receiveShare`, which would mis-route a
|
|
19
|
+
* non-`FriendsKind` envelope. This is "a DataPart variant + an inbound route branch"
|
|
20
|
+
* (the carrier is the friends `unwrapDataPart` shape + one extra tag field — no
|
|
21
|
+
* structural change beyond that).
|
|
22
|
+
*
|
|
23
|
+
* SECURITY — the wire AUTHENTICATES the sender exactly like the share path
|
|
24
|
+
* (`receiveShare` in friends' `adapter.ts`). `openSealedEnvelope` does AEAD
|
|
25
|
+
* decryption ONLY — it does NOT verify the signature (its docstring names the
|
|
26
|
+
* adapter's `DidVerifier` as "the single authentication gate"). The decrypted
|
|
27
|
+
* `envelope.fromAgentId` is therefore attacker-controlled plaintext: the recipient's
|
|
28
|
+
* X25519 pubkey is public (derived from the published DID), so any party can SEAL a
|
|
29
|
+
* forged result to us. Authentication is the ONLY barrier. Before trusting the
|
|
30
|
+
* claimed sender we therefore replicate `receiveShare`'s gate:
|
|
31
|
+
* 1. binding check — `opened.signerDid === opened.fromAgentId` (and non-empty),
|
|
32
|
+
* 2. resolve + pin the sender DID (TOFU pin + signed-rotation evaluation),
|
|
33
|
+
* 3. hand `importMissionResult` a REAL `DidVerifier` bound to the pinned key, so
|
|
34
|
+
* `verifier.verify(fromAgentId, proof)` runs the Ed25519 signature check against
|
|
35
|
+
* the pinned key — NOT the no-op `tofuVerifier` the importer defaults to.
|
|
36
|
+
* Replay is deduped on the seal nonce (durable `SeenLedger`), matching the share path.
|
|
37
|
+
*/
|
|
38
|
+
const OURO_RESULT_KIND = "mission_result";
|
|
39
|
+
/** Wrap a sealed result envelope into the harness-owned `mission_result` DataPart. */
|
|
40
|
+
function wrapMissionResultDataPart(input) {
|
|
41
|
+
return {
|
|
42
|
+
messageId: `result-${Math.random().toString(36).slice(2)}`,
|
|
43
|
+
role: "agent",
|
|
44
|
+
parts: [{
|
|
45
|
+
kind: "data",
|
|
46
|
+
data: {
|
|
47
|
+
v: input.sealedEnvelope.v,
|
|
48
|
+
sealed: input.sealedEnvelope.sealed,
|
|
49
|
+
recipientDid: input.recipientDid,
|
|
50
|
+
ouroKind: OURO_RESULT_KIND,
|
|
51
|
+
},
|
|
52
|
+
}],
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** Whether an inbound message carries the harness-owned `mission_result` DataPart. */
|
|
56
|
+
function isMissionResultDataPart(message) {
|
|
57
|
+
if (!Array.isArray(message.parts))
|
|
58
|
+
return false;
|
|
59
|
+
return message.parts.some((p) => p && p.kind === "data" && typeof p.data === "object" && p.data !== null
|
|
60
|
+
&& p.data.ouroKind === OURO_RESULT_KIND);
|
|
61
|
+
}
|
|
62
|
+
/** Extract the sealed envelope from a `mission_result` DataPart, or null. */
|
|
63
|
+
function unwrapMissionResultDataPart(message) {
|
|
64
|
+
if (!Array.isArray(message.parts) || message.parts.length !== 1)
|
|
65
|
+
return null;
|
|
66
|
+
const part = message.parts[0];
|
|
67
|
+
if (!part || part.kind !== "data" || !part.data)
|
|
68
|
+
return null;
|
|
69
|
+
const data = part.data;
|
|
70
|
+
if (data.ouroKind !== OURO_RESULT_KIND)
|
|
71
|
+
return null;
|
|
72
|
+
if (typeof data.v !== "number" || typeof data.recipientDid !== "string" || !data.sealed)
|
|
73
|
+
return null;
|
|
74
|
+
return { sealed: { v: data.v, sealed: data.sealed }, recipientDid: data.recipientDid };
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Route an inbound `mission_result` DataPart to `importMissionResult`, AUTHENTICATING
|
|
78
|
+
* the sender exactly like `receiveShare` does for every other inbound kind.
|
|
79
|
+
*
|
|
80
|
+
* Ordering (mirrors the share bridge's trust trap + authentication gate):
|
|
81
|
+
* 1. recognize + unwrap the harness `mission_result` carrier (else `not-a-result`).
|
|
82
|
+
* 2. replay dedup on the seal nonce (durable `SeenLedger`) — before any state change.
|
|
83
|
+
* 3. `openSealedEnvelope` → AEAD-decrypt only (unseal/recipient failures → reject).
|
|
84
|
+
* 4. binding check — reject unless `opened.signerDid === opened.fromAgentId` (and
|
|
85
|
+
* non-empty): the advisory `signerDid` (outer, unsigned) must match the claimed
|
|
86
|
+
* sender. A divergence is a forged/spoofed bundle (`sender_binding_mismatch`).
|
|
87
|
+
* 5. `resolveAndPin` the sender DID (TOFU pin / signed-rotation) → the pinned key
|
|
88
|
+
* (unresolvable/failed-binding/failed-rotation → `resolve_failed`).
|
|
89
|
+
* 6. build a `DidVerifier` bound to THIS envelope + the pinned key, mark the nonce
|
|
90
|
+
* seen, then call `importMissionResult({ envelope, fromAgentId: <signed DID>,
|
|
91
|
+
* trustOfSource }, { verifier })`. The verifier runs the REAL signature check
|
|
92
|
+
* against the pinned key — a result signed with a key that is not the pinned key
|
|
93
|
+
* for the claimed DID is rejected (`untrusted_source`). Every authorization gate
|
|
94
|
+
* (no_mission / no_delegation / assignee_mismatch, fail-closed on a legacy
|
|
95
|
+
* assignee-less delegation) is still enforced by the importer.
|
|
96
|
+
*/
|
|
97
|
+
async function receiveInboundMissionResult(message, deps) {
|
|
98
|
+
const payload = unwrapMissionResultDataPart(message);
|
|
99
|
+
if (!payload)
|
|
100
|
+
return { outcome: "not-a-result" };
|
|
101
|
+
// Replay dedup BEFORE any state change, keyed on the seal nonce (matches receiveShare).
|
|
102
|
+
if (deps.seen.isSeen(payload.sealed.sealed.n)) {
|
|
103
|
+
(0, runtime_1.emitNervesEvent)({
|
|
104
|
+
component: "channels",
|
|
105
|
+
event: "channel.a2a_result_rejected",
|
|
106
|
+
message: "rejected inbound mission_result (replayed seal nonce)",
|
|
107
|
+
meta: { reason: "replayed" },
|
|
108
|
+
});
|
|
109
|
+
return { outcome: "rejected", reason: "replayed" };
|
|
110
|
+
}
|
|
111
|
+
const recipientIdentity = { x25519Priv: deps.identity.x25519Priv, x25519Pub: deps.identity.x25519Pub };
|
|
112
|
+
const opened = (0, a2a_client_1.openSealedEnvelope)({
|
|
113
|
+
sodium: deps.sodium,
|
|
114
|
+
sealedEnvelope: payload.sealed,
|
|
115
|
+
recipientDid: deps.identity.did,
|
|
116
|
+
recipientIdentity,
|
|
117
|
+
});
|
|
118
|
+
if (!opened.ok) {
|
|
119
|
+
(0, runtime_1.emitNervesEvent)({
|
|
120
|
+
component: "channels",
|
|
121
|
+
event: "channel.a2a_result_rejected",
|
|
122
|
+
message: "rejected inbound mission_result (unseal/recipient)",
|
|
123
|
+
meta: { reason: opened.error },
|
|
124
|
+
});
|
|
125
|
+
return { outcome: "rejected", reason: opened.error };
|
|
126
|
+
}
|
|
127
|
+
// SECURITY (binding): the trustworthy sender identity is the SIGNED `fromAgentId`.
|
|
128
|
+
// `openSealedEnvelope` does NOT verify the signature, so `fromAgentId` is untrusted
|
|
129
|
+
// until the DidVerifier confirms the pinned key signed THIS envelope. The advisory
|
|
130
|
+
// `signerDid` (from the outer, unsigned sealed plaintext) must match the claimed
|
|
131
|
+
// sender; a divergence is a forged/spoofed bundle (e.g. an attacker signing with its
|
|
132
|
+
// OWN key while claiming a victim's DID). Reject before resolving or importing.
|
|
133
|
+
const senderDid = opened.fromAgentId;
|
|
134
|
+
if (senderDid.length === 0 || opened.signerDid !== senderDid) {
|
|
135
|
+
(0, runtime_1.emitNervesEvent)({
|
|
136
|
+
component: "channels",
|
|
137
|
+
event: "channel.a2a_result_rejected",
|
|
138
|
+
message: "rejected inbound mission_result (sender binding mismatch — forged sender)",
|
|
139
|
+
meta: { reason: "sender_binding_mismatch", claimedDid: senderDid, signerDid: opened.signerDid },
|
|
140
|
+
});
|
|
141
|
+
return { outcome: "rejected", reason: "sender_binding_mismatch" };
|
|
142
|
+
}
|
|
143
|
+
// Read trust from the friend store BY the verified DID — never defaulted, never from
|
|
144
|
+
// the envelope. Unknown (or empty) DID ⇒ stranger.
|
|
145
|
+
const record = await (0, friends_1.findFriendByDid)(deps.store, senderDid);
|
|
146
|
+
const trust = record?.trustLevel ?? "stranger";
|
|
147
|
+
// Resolve + pin the SENDER's DID (async — BEFORE the sync verifier/importer).
|
|
148
|
+
const resolved = await deps.didResolution.resolveAndPin({
|
|
149
|
+
fromAgentId: senderDid,
|
|
150
|
+
did: senderDid,
|
|
151
|
+
pinStore: deps.pinStore,
|
|
152
|
+
trustOfSource: trust,
|
|
153
|
+
});
|
|
154
|
+
if (!resolved) {
|
|
155
|
+
(0, runtime_1.emitNervesEvent)({
|
|
156
|
+
component: "channels",
|
|
157
|
+
event: "channel.a2a_result_rejected",
|
|
158
|
+
message: "rejected inbound mission_result (sender DID resolve/pin failed)",
|
|
159
|
+
meta: { reason: "resolve_failed", verifiedDid: senderDid, trust },
|
|
160
|
+
});
|
|
161
|
+
return { outcome: "rejected", reason: "resolve_failed" };
|
|
162
|
+
}
|
|
163
|
+
// Build the sync verifier bound to THIS envelope + the pinned sender key. This is the
|
|
164
|
+
// single authentication gate (the importer would otherwise default to the no-op TOFU
|
|
165
|
+
// verifier). `verify` confirms the agentId===did binding AND the Ed25519 signature
|
|
166
|
+
// over the envelope against the pinned key.
|
|
167
|
+
const verifier = new a2a_client_1.DidVerifier({
|
|
168
|
+
sodium: deps.sodium,
|
|
169
|
+
pinnedEd25519Pub: resolved.ed25519Pub,
|
|
170
|
+
pinnedDid: senderDid,
|
|
171
|
+
envelope: opened.envelope,
|
|
172
|
+
});
|
|
173
|
+
// Mark seen now (idempotent imports + the replay guard above keep this safe).
|
|
174
|
+
deps.seen.markSeen(payload.sealed.sealed.n);
|
|
175
|
+
const result = await (0, friends_1.importMissionResult)(deps.missionStore, {
|
|
176
|
+
envelope: opened.envelope,
|
|
177
|
+
fromAgentId: senderDid,
|
|
178
|
+
trustOfSource: trust,
|
|
179
|
+
}, { verifier });
|
|
180
|
+
if (result.ok) {
|
|
181
|
+
(0, runtime_1.emitNervesEvent)({
|
|
182
|
+
component: "channels",
|
|
183
|
+
event: "channel.a2a_result_imported",
|
|
184
|
+
message: "imported inbound mission_result (authenticated, quarantined, attributed)",
|
|
185
|
+
meta: { verifiedDid: senderDid, trust, status: result.status },
|
|
186
|
+
});
|
|
187
|
+
return { outcome: "imported", verifiedDid: senderDid };
|
|
188
|
+
}
|
|
189
|
+
(0, runtime_1.emitNervesEvent)({
|
|
190
|
+
component: "channels",
|
|
191
|
+
event: "channel.a2a_result_rejected",
|
|
192
|
+
message: "rejected inbound mission_result (import gate)",
|
|
193
|
+
meta: { reason: result.status, verifiedDid: senderDid, trust },
|
|
194
|
+
});
|
|
195
|
+
return { outcome: "rejected", reason: result.status };
|
|
196
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.onboardA2APeer = onboardA2APeer;
|
|
37
|
+
exports.onboardA2AClientByDid = onboardA2AClientByDid;
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
const identity_1 = require("../heart/identity");
|
|
40
|
+
const runtime_1 = require("../nerves/runtime");
|
|
41
|
+
const friends_1 = require("@ouro.bot/friends");
|
|
42
|
+
const a2a_client_1 = require("@ouro.bot/friends/a2a-client");
|
|
43
|
+
const client_1 = require("./client");
|
|
44
|
+
function storeFor(options) {
|
|
45
|
+
if (options.store)
|
|
46
|
+
return options.store;
|
|
47
|
+
/* v8 ignore next -- default bundle root fallback is owned by identity path tests; CLI and onboarding tests inject explicit roots @preserve */
|
|
48
|
+
return new friends_1.FileFriendStore(path.join(options.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)(), `${options.agentName}.ouro`, "friends"));
|
|
49
|
+
}
|
|
50
|
+
/** The normalized card URL (legacy URL-keyed identity for did-less cards). */
|
|
51
|
+
function urlAgentId(cardUrl) {
|
|
52
|
+
const parsed = new URL(cardUrl);
|
|
53
|
+
parsed.hash = "";
|
|
54
|
+
return parsed.toString();
|
|
55
|
+
}
|
|
56
|
+
async function onboardA2APeer(options) {
|
|
57
|
+
// HTTP/URL glue stays harness-side: fetch the agent card, resolve its JSON-RPC
|
|
58
|
+
// endpoint, and derive the stable agentId. The record-shaping (mint/update the
|
|
59
|
+
// agent-peer friend) is delegated to @ouro.bot/friends' upsertAgentPeer.
|
|
60
|
+
const card = await (0, client_1.fetchA2AAgentCard)(options.cardUrl, options.fetchImpl);
|
|
61
|
+
const store = storeFor(options);
|
|
62
|
+
/* v8 ignore next -- fetchA2AAgentCard validates a usable endpoint or legacy url before returning a card @preserve */
|
|
63
|
+
const endpointUrl = (0, client_1.endpointForCard)(card) ?? options.cardUrl;
|
|
64
|
+
const protocolVersion = card.supportedInterfaces?.find((entry) => entry.url === endpointUrl)?.protocolVersion
|
|
65
|
+
?? card.protocolVersion;
|
|
66
|
+
const name = options.name ?? card.name;
|
|
67
|
+
// THE FOOTGUN FIX: a DID-bearing card keys the record on its VERIFIED did:key
|
|
68
|
+
// (so an inbound sealed envelope — keyed on the signed DID — resolves to THIS
|
|
69
|
+
// record, not a stranger). A did-less (legacy) card keeps the URL-keyed identity
|
|
70
|
+
// unchanged (backward compatible). For did:key the binding is self-contained
|
|
71
|
+
// (`card.did === did`, `didDoc: null`); we ALSO require the served did:key to be a
|
|
72
|
+
// well-formed, parseable Ed25519 key (a malformed did:key could never key/verify
|
|
73
|
+
// an inbound envelope, so onboarding it would silently break the resolve later). A
|
|
74
|
+
// card whose DID is unparseable or fails the binding is REFUSED (no record
|
|
75
|
+
// written) — never TOFU-trust a mis-bound or malformed card.
|
|
76
|
+
const did = (0, client_1.cardDid)(card);
|
|
77
|
+
if (did) {
|
|
78
|
+
const parsed = (0, a2a_client_1.parseDidKey)(did);
|
|
79
|
+
const bound = parsed !== null && (0, a2a_client_1.verifyCardDidBinding)({ card: { did, url: options.cardUrl }, did, didDoc: null });
|
|
80
|
+
if (!bound) {
|
|
81
|
+
(0, runtime_1.emitNervesEvent)({
|
|
82
|
+
component: "friends",
|
|
83
|
+
event: "friends.a2a_card_binding_rejected",
|
|
84
|
+
message: "refused A2A onboarding: card↔DID binding failed",
|
|
85
|
+
meta: { agentName: options.agentName, cardUrl: options.cardUrl, did, parseable: parsed !== null },
|
|
86
|
+
});
|
|
87
|
+
throw new Error(`A2A card↔DID binding failed for ${did}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// DID-keyed when present (externalId === did, a2a.did === did), else URL-keyed.
|
|
91
|
+
const externalId = did ?? urlAgentId(options.cardUrl);
|
|
92
|
+
const record = await (0, friends_1.upsertAgentPeer)(store, {
|
|
93
|
+
name,
|
|
94
|
+
agentId: externalId,
|
|
95
|
+
...(options.trustLevel ? { trustLevel: options.trustLevel } : {}),
|
|
96
|
+
a2a: {
|
|
97
|
+
cardUrl: options.cardUrl,
|
|
98
|
+
endpointUrl,
|
|
99
|
+
agentId: externalId,
|
|
100
|
+
protocolVersion,
|
|
101
|
+
...(did ? { did } : {}),
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
(0, runtime_1.emitNervesEvent)({
|
|
105
|
+
component: "friends",
|
|
106
|
+
event: "friends.a2a_peer_onboarded",
|
|
107
|
+
message: "onboarded A2A peer into friend model",
|
|
108
|
+
meta: { agentName: options.agentName, friendId: record.id, peerName: record.name, trustLevel: record.trustLevel, ...(did ? { did } : {}) },
|
|
109
|
+
});
|
|
110
|
+
return record;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Onboard a client that serves no agent card (for example a coding harness with a
|
|
114
|
+
* file-backed identity) by its did:key alone. The record is keyed on the DID, so its
|
|
115
|
+
* signed, sealed messages resolve to this friend; the key must parse, or nothing is written.
|
|
116
|
+
*/
|
|
117
|
+
async function onboardA2AClientByDid(options) {
|
|
118
|
+
if ((0, a2a_client_1.parseDidKey)(options.did) === null)
|
|
119
|
+
throw new Error(`not a valid did:key: ${options.did}`);
|
|
120
|
+
const record = await (0, friends_1.upsertAgentPeer)(storeFor(options), {
|
|
121
|
+
name: options.name,
|
|
122
|
+
agentId: options.did,
|
|
123
|
+
...(options.trustLevel ? { trustLevel: options.trustLevel } : {}),
|
|
124
|
+
a2a: { did: options.did, agentId: options.did },
|
|
125
|
+
});
|
|
126
|
+
(0, runtime_1.emitNervesEvent)({
|
|
127
|
+
component: "friends",
|
|
128
|
+
event: "friends.a2a_client_onboarded",
|
|
129
|
+
message: "onboarded card-less A2A client into friend model",
|
|
130
|
+
meta: { agentName: options.agentName, friendId: record.id, peerName: record.name, trustLevel: record.trustLevel, did: options.did },
|
|
131
|
+
});
|
|
132
|
+
return record;
|
|
133
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.callOutboxMethod = callOutboxMethod;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const a2a_client_1 = require("@ouro.bot/friends/a2a-client");
|
|
6
|
+
const client_1 = require("./client");
|
|
7
|
+
const outbox_wire_1 = require("./outbox-wire");
|
|
8
|
+
const sealed_chat_1 = require("./sealed-chat");
|
|
9
|
+
const runtime_1 = require("../nerves/runtime");
|
|
10
|
+
/**
|
|
11
|
+
* Calls one outbox method on an A2A agent as a verified friend. The request is sealed to the agent's card DID and
|
|
12
|
+
* signed by `identity`, and the reply is opened only if it was sealed by that same card key, so neither direction
|
|
13
|
+
* travels in plaintext. Throws with the agent's refusal text when the agent answers with a JSON-RPC error.
|
|
14
|
+
*/
|
|
15
|
+
async function callOutboxMethod(input) {
|
|
16
|
+
const fetchImpl = input.fetchImpl ?? fetch;
|
|
17
|
+
const sodium = input.sodium ?? await (0, a2a_client_1.ready)();
|
|
18
|
+
const card = await (0, client_1.fetchA2AAgentCard)(input.cardUrl, fetchImpl);
|
|
19
|
+
const peerDid = (0, client_1.cardDid)(card);
|
|
20
|
+
if (!peerDid)
|
|
21
|
+
throw new Error(`A2A card ${input.cardUrl} serves no DID; outbox calls need one`);
|
|
22
|
+
const parsed = (0, a2a_client_1.parseDidKey)(peerDid);
|
|
23
|
+
if (!parsed || !(0, a2a_client_1.verifyCardDidBinding)({ card: { did: peerDid, url: input.cardUrl }, did: peerDid, didDoc: null })) {
|
|
24
|
+
throw new Error(`A2A card↔DID binding failed for ${peerDid}`);
|
|
25
|
+
}
|
|
26
|
+
const endpointUrl = (0, client_1.endpointForCard)(card);
|
|
27
|
+
const conversationId = (0, node_crypto_1.randomUUID)();
|
|
28
|
+
const sealed = (0, sealed_chat_1.sealChatMessage)({
|
|
29
|
+
sodium, from: input.identity, recipientDid: peerDid, recipientEd25519Pub: parsed.ed25519Pub,
|
|
30
|
+
text: (0, outbox_wire_1.encodeOutboxCommand)(input.method, input.params), conversationId,
|
|
31
|
+
});
|
|
32
|
+
const request = {
|
|
33
|
+
jsonrpc: "2.0", id: (0, node_crypto_1.randomUUID)(), method: input.method,
|
|
34
|
+
params: { message: { kind: "message", role: "ROLE_USER", messageId: (0, node_crypto_1.randomUUID)(), contextId: conversationId, parts: sealed.parts } },
|
|
35
|
+
};
|
|
36
|
+
(0, runtime_1.emitNervesEvent)({ component: "channels", event: "channel.a2a_outbox_call_start", message: "calling an A2A outbox method", meta: { endpointUrl, peerDid, method: input.method } });
|
|
37
|
+
const rpc = await (0, client_1.postJsonRpc)(endpointUrl, request, fetchImpl);
|
|
38
|
+
if ("error" in rpc)
|
|
39
|
+
throw new Error(`A2A error ${rpc.error.code}: ${rpc.error.message}`);
|
|
40
|
+
const reply = rpc.result;
|
|
41
|
+
if (!reply || !Array.isArray(reply.parts))
|
|
42
|
+
throw new Error("A2A outbox call returned no reply message");
|
|
43
|
+
const opened = await (0, sealed_chat_1.openChatMessage)({ sodium, self: input.identity, message: reply, senderDid: peerDid, senderEd25519Pub: parsed.ed25519Pub });
|
|
44
|
+
if (!opened.ok)
|
|
45
|
+
throw new Error(`A2A outbox reply rejected: ${opened.reason}`);
|
|
46
|
+
let result;
|
|
47
|
+
try {
|
|
48
|
+
result = JSON.parse(opened.text);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
throw new Error("A2A outbox reply was not JSON");
|
|
52
|
+
}
|
|
53
|
+
if (!result || typeof result !== "object" || Array.isArray(result))
|
|
54
|
+
throw new Error("A2A outbox reply was not a JSON object");
|
|
55
|
+
(0, runtime_1.emitNervesEvent)({ component: "channels", event: "channel.a2a_outbox_call_end", message: "received an A2A outbox reply", meta: { endpointUrl, peerDid, method: input.method } });
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.FileOutboxStore = exports.OUTBOX_LIST_MAX_CHARS = exports.OUTBOX_LIST_MAX_LIMIT = exports.OUTBOX_LIST_DEFAULT_LIMIT = exports.OUTBOX_MAX_BODY_CHARS = exports.OUTBOX_MAX_ENTRIES = void 0;
|
|
37
|
+
exports.isOutboxEntryId = isOutboxEntryId;
|
|
38
|
+
exports.outboxRoot = outboxRoot;
|
|
39
|
+
const node_crypto_1 = require("node:crypto");
|
|
40
|
+
const fs = __importStar(require("node:fs"));
|
|
41
|
+
const path = __importStar(require("node:path"));
|
|
42
|
+
const file_lock_1 = require("../heart/file-lock");
|
|
43
|
+
const runtime_1 = require("../nerves/runtime");
|
|
44
|
+
exports.OUTBOX_MAX_ENTRIES = 500;
|
|
45
|
+
exports.OUTBOX_MAX_BODY_CHARS = 6_000;
|
|
46
|
+
exports.OUTBOX_LIST_DEFAULT_LIMIT = 20;
|
|
47
|
+
exports.OUTBOX_LIST_MAX_LIMIT = 50;
|
|
48
|
+
/** One listing reply is a single sealed message of at most 16,000 characters, so a listing stops filling well before that. */
|
|
49
|
+
exports.OUTBOX_LIST_MAX_CHARS = 12_000;
|
|
50
|
+
const FRIEND_ID = /^[A-Za-z0-9][A-Za-z0-9._-]*$/u;
|
|
51
|
+
const ENTRY_ID = /^\d{13}-[0-9a-f]{6}$/u;
|
|
52
|
+
function isOutboxEntryId(value) {
|
|
53
|
+
return typeof value === "string" && ENTRY_ID.test(value);
|
|
54
|
+
}
|
|
55
|
+
function entryDir(agentRoot, friendId) {
|
|
56
|
+
if (!FRIEND_ID.test(friendId))
|
|
57
|
+
throw new Error("outbox friend id is invalid");
|
|
58
|
+
return path.join(agentRoot, "state", "outbox", friendId);
|
|
59
|
+
}
|
|
60
|
+
function outboxRoot(agentRoot) {
|
|
61
|
+
return path.join(agentRoot, "state", "outbox");
|
|
62
|
+
}
|
|
63
|
+
function readEntry(file) {
|
|
64
|
+
try {
|
|
65
|
+
const parsed = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
66
|
+
if (!isOutboxEntryId(parsed.id) || typeof parsed.kind !== "string" || typeof parsed.createdAt !== "string" || typeof parsed.body !== "string")
|
|
67
|
+
return null;
|
|
68
|
+
return { id: parsed.id, kind: parsed.kind, createdAt: parsed.createdAt, body: parsed.body, ...(parsed.meta ? { meta: parsed.meta } : {}) };
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function sortedIds(dir) {
|
|
75
|
+
let names;
|
|
76
|
+
try {
|
|
77
|
+
names = fs.readdirSync(dir);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
return names.filter((name) => name.endsWith(".json") && isOutboxEntryId(name.slice(0, -5))).map((name) => name.slice(0, -5)).sort();
|
|
83
|
+
}
|
|
84
|
+
class FileOutboxStore {
|
|
85
|
+
agentRoot;
|
|
86
|
+
constructor(agentRoot) {
|
|
87
|
+
this.agentRoot = agentRoot;
|
|
88
|
+
}
|
|
89
|
+
/** Adds one entry for `friendId`. The oldest entries beyond the cap are dropped so a peer that never reads cannot fill the disk. */
|
|
90
|
+
append(friendId, input, now = Date.now()) {
|
|
91
|
+
const dir = entryDir(this.agentRoot, friendId);
|
|
92
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
93
|
+
const id = `${String(now).padStart(13, "0")}-${(0, node_crypto_1.randomBytes)(3).toString("hex")}`;
|
|
94
|
+
const entry = {
|
|
95
|
+
id,
|
|
96
|
+
kind: input.kind,
|
|
97
|
+
createdAt: new Date(now).toISOString(),
|
|
98
|
+
body: input.body.slice(0, exports.OUTBOX_MAX_BODY_CHARS),
|
|
99
|
+
...(input.meta ? { meta: input.meta } : {}),
|
|
100
|
+
};
|
|
101
|
+
const file = path.join(dir, `${id}.json`);
|
|
102
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
103
|
+
fs.writeFileSync(tmp, `${JSON.stringify(entry, null, 2)}\n`, { mode: 0o600 });
|
|
104
|
+
fs.renameSync(tmp, file);
|
|
105
|
+
const ids = sortedIds(dir);
|
|
106
|
+
const evicted = ids.slice(0, Math.max(0, ids.length - exports.OUTBOX_MAX_ENTRIES));
|
|
107
|
+
for (const stale of evicted)
|
|
108
|
+
fs.rmSync(path.join(dir, `${stale}.json`), { force: true });
|
|
109
|
+
if (evicted.length > 0) {
|
|
110
|
+
(0, runtime_1.emitNervesEvent)({
|
|
111
|
+
level: "warn",
|
|
112
|
+
component: "senses",
|
|
113
|
+
event: "senses.a2a_outbox_evicted",
|
|
114
|
+
message: "a peer outbox is full, so its oldest unread entries were dropped",
|
|
115
|
+
meta: { friendId, evicted: evicted.length, oldestDropped: evicted[0], cap: exports.OUTBOX_MAX_ENTRIES },
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
(0, runtime_1.emitNervesEvent)({
|
|
119
|
+
component: "senses",
|
|
120
|
+
event: "senses.a2a_outbox_appended",
|
|
121
|
+
message: "queued a message in a peer outbox",
|
|
122
|
+
meta: { friendId, entryId: id, kind: input.kind },
|
|
123
|
+
});
|
|
124
|
+
return entry;
|
|
125
|
+
}
|
|
126
|
+
/** Like `append`, but a second call with the same `dedupeKey` while the first entry is still unacked returns that entry, so a retried delivery cannot double-post. */
|
|
127
|
+
async appendOnce(friendId, dedupeKey, input, now = Date.now()) {
|
|
128
|
+
const dir = entryDir(this.agentRoot, friendId);
|
|
129
|
+
// The check and the append happen under one cross-process lock, so two senses retrying the same delivery cannot both miss it.
|
|
130
|
+
return (0, file_lock_1.withFileLock)(path.join(this.agentRoot, "state", "outbox", ".locks"), `once-${friendId}`, () => {
|
|
131
|
+
for (const id of sortedIds(dir)) {
|
|
132
|
+
const entry = readEntry(path.join(dir, `${id}.json`));
|
|
133
|
+
if (entry?.meta?.dedupeKey === dedupeKey)
|
|
134
|
+
return entry;
|
|
135
|
+
}
|
|
136
|
+
return this.append(friendId, { ...input, meta: { ...input.meta, dedupeKey } }, now);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
/** Entries after `since` (exclusive), oldest first, within the count and size limits. */
|
|
140
|
+
list(friendId, options = {}) {
|
|
141
|
+
const dir = entryDir(this.agentRoot, friendId);
|
|
142
|
+
const limit = Math.min(Math.max(1, Math.floor(options.limit ?? exports.OUTBOX_LIST_DEFAULT_LIMIT)), exports.OUTBOX_LIST_MAX_LIMIT);
|
|
143
|
+
const since = options.since ?? "";
|
|
144
|
+
const ids = sortedIds(dir).filter((id) => id > since);
|
|
145
|
+
const entries = [];
|
|
146
|
+
let chars = 0;
|
|
147
|
+
let more = false;
|
|
148
|
+
for (const id of ids) {
|
|
149
|
+
const entry = readEntry(path.join(dir, `${id}.json`));
|
|
150
|
+
if (!entry)
|
|
151
|
+
continue;
|
|
152
|
+
const size = JSON.stringify(entry).length;
|
|
153
|
+
if (entries.length >= limit || (entries.length > 0 && chars + size > exports.OUTBOX_LIST_MAX_CHARS)) {
|
|
154
|
+
more = true;
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
entries.push(entry);
|
|
158
|
+
chars += size;
|
|
159
|
+
}
|
|
160
|
+
return { entries, nextCursor: entries.length > 0 ? entries[entries.length - 1].id : null, more };
|
|
161
|
+
}
|
|
162
|
+
/** Removes the named entries from this friend's outbox only; ids that are malformed or not there come back as unknown. */
|
|
163
|
+
ack(friendId, ids) {
|
|
164
|
+
const dir = entryDir(this.agentRoot, friendId);
|
|
165
|
+
const acked = [];
|
|
166
|
+
const unknown = [];
|
|
167
|
+
for (const id of ids) {
|
|
168
|
+
const file = isOutboxEntryId(id) ? path.join(dir, `${id}.json`) : null;
|
|
169
|
+
if (file && fs.existsSync(file)) {
|
|
170
|
+
fs.rmSync(file, { force: true });
|
|
171
|
+
acked.push(id);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
unknown.push(id);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (acked.length > 0) {
|
|
178
|
+
(0, runtime_1.emitNervesEvent)({
|
|
179
|
+
component: "senses",
|
|
180
|
+
event: "senses.a2a_outbox_acked",
|
|
181
|
+
message: "a peer cleared entries from its outbox",
|
|
182
|
+
meta: { friendId, count: acked.length },
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return { acked, unknown };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
exports.FileOutboxStore = FileOutboxStore;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OUTBOX_ERROR_INVALID = exports.OUTBOX_ERROR_REFUSED = exports.OUTBOX_METHODS = void 0;
|
|
4
|
+
exports.isOutboxMethod = isOutboxMethod;
|
|
5
|
+
exports.encodeOutboxCommand = encodeOutboxCommand;
|
|
6
|
+
exports.parseOutboxCommand = parseOutboxCommand;
|
|
7
|
+
exports.handleOutboxCommand = handleOutboxCommand;
|
|
8
|
+
const a2a_client_1 = require("@ouro.bot/friends/a2a-client");
|
|
9
|
+
const escalation_grants_1 = require("./escalation-grants");
|
|
10
|
+
const resolution_proof_1 = require("./resolution-proof");
|
|
11
|
+
const outbox_store_1 = require("./outbox-store");
|
|
12
|
+
const failure_reports_1 = require("../heart/failure-reports");
|
|
13
|
+
const runtime_1 = require("../nerves/runtime");
|
|
14
|
+
/**
|
|
15
|
+
* The outbox methods ride the same sealed, signed chat envelope as `a2a message`: the request is a `message` whose
|
|
16
|
+
* signed text is `{"ouro":"a2a-method","method":...,"params":...}`, posted under the JSON-RPC method of the same name.
|
|
17
|
+
* The server verifies the sender exactly as it does for a chat (unseal, pinned key, signature, replay guard, friend
|
|
18
|
+
* record), requires the signed method to equal the JSON-RPC method, and then decides access itself from the verified
|
|
19
|
+
* friend record. A sender can only ever name its own outbox: no parameter selects another peer's.
|
|
20
|
+
*
|
|
21
|
+
* outbox/list { since?: string, limit?: number } -> { entries, nextCursor, more } any active friend, own outbox
|
|
22
|
+
* outbox/ack { ids: string[] } -> { acked, unknown } any active friend, own outbox
|
|
23
|
+
* report/resolve { id, version, note, resolvedAt, proof } -> { id, status: "resolved" } escalation holders, own reports
|
|
24
|
+
*/
|
|
25
|
+
exports.OUTBOX_METHODS = ["outbox/list", "outbox/ack", "report/resolve"];
|
|
26
|
+
exports.OUTBOX_ERROR_REFUSED = -32003;
|
|
27
|
+
exports.OUTBOX_ERROR_INVALID = -32602;
|
|
28
|
+
const MAX_ACK_IDS = 100;
|
|
29
|
+
function isOutboxMethod(method) {
|
|
30
|
+
return typeof method === "string" && exports.OUTBOX_METHODS.includes(method);
|
|
31
|
+
}
|
|
32
|
+
function encodeOutboxCommand(method, params) {
|
|
33
|
+
return JSON.stringify({ ouro: "a2a-method", method, params });
|
|
34
|
+
}
|
|
35
|
+
function parseOutboxCommand(text) {
|
|
36
|
+
let parsed;
|
|
37
|
+
try {
|
|
38
|
+
parsed = JSON.parse(text);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
44
|
+
return null;
|
|
45
|
+
const command = parsed;
|
|
46
|
+
if (command.ouro !== "a2a-method" || !isOutboxMethod(command.method))
|
|
47
|
+
return null;
|
|
48
|
+
const params = command.params === undefined ? {} : command.params;
|
|
49
|
+
if (!params || typeof params !== "object" || Array.isArray(params))
|
|
50
|
+
return null;
|
|
51
|
+
return { method: command.method, params: params };
|
|
52
|
+
}
|
|
53
|
+
const refuse = (message) => ({ ok: false, code: exports.OUTBOX_ERROR_REFUSED, message });
|
|
54
|
+
const invalid = (message) => ({ ok: false, code: exports.OUTBOX_ERROR_INVALID, message });
|
|
55
|
+
/** Runs one verified outbox command for `friend`. `friend` must be the record found by the signed sender DID. */
|
|
56
|
+
async function handleOutboxCommand(input) {
|
|
57
|
+
const { agentRoot, friend, method, params } = input;
|
|
58
|
+
if (!friend || friend.admissionState !== "active") {
|
|
59
|
+
(0, runtime_1.emitNervesEvent)({ level: "warn", component: "senses", event: "senses.a2a_outbox_refused", message: "refused an outbox call from a peer that is not an active friend", meta: { method, friendId: friend?.id ?? null } });
|
|
60
|
+
return refuse("only an active friend can use its outbox");
|
|
61
|
+
}
|
|
62
|
+
const outbox = new outbox_store_1.FileOutboxStore(agentRoot);
|
|
63
|
+
if (method === "outbox/list") {
|
|
64
|
+
const { since, limit } = params;
|
|
65
|
+
if (since !== undefined && typeof since !== "string")
|
|
66
|
+
return invalid("since must be a string cursor");
|
|
67
|
+
if (limit !== undefined && (typeof limit !== "number" || !Number.isFinite(limit)))
|
|
68
|
+
return invalid("limit must be a number");
|
|
69
|
+
return { ok: true, result: { ...outbox.list(friend.id, { ...(since !== undefined ? { since } : {}), ...(limit !== undefined ? { limit: Math.min(limit, outbox_store_1.OUTBOX_LIST_MAX_LIMIT) } : {}) }) } };
|
|
70
|
+
}
|
|
71
|
+
if (method === "outbox/ack") {
|
|
72
|
+
const { ids } = params;
|
|
73
|
+
if (!Array.isArray(ids) || ids.length === 0 || ids.length > MAX_ACK_IDS || ids.some((id) => typeof id !== "string"))
|
|
74
|
+
return invalid(`ids must be 1 to ${MAX_ACK_IDS} strings`);
|
|
75
|
+
return { ok: true, result: { ...outbox.ack(friend.id, ids) } };
|
|
76
|
+
}
|
|
77
|
+
if (!(0, escalation_grants_1.holdsEscalation)(agentRoot, friend)) {
|
|
78
|
+
(0, runtime_1.emitNervesEvent)({ level: "warn", component: "senses", event: "senses.a2a_report_resolve_refused", message: "refused report/resolve from a peer without the escalation grant", meta: { friendId: friend.id } });
|
|
79
|
+
return refuse("report/resolve needs the escalation grant");
|
|
80
|
+
}
|
|
81
|
+
const { id, version, note, resolvedAt, proof } = params;
|
|
82
|
+
if (typeof id !== "string" || typeof version !== "string" || typeof note !== "string" || typeof resolvedAt !== "string")
|
|
83
|
+
return invalid("id, version, note and resolvedAt must be strings");
|
|
84
|
+
// The key is the DID the operator pinned in the root-owned grant, never the friend record; and the A2A-verified caller must be that same DID.
|
|
85
|
+
const holderDid = (0, escalation_grants_1.pinnedHolderDid)(agentRoot, friend.id);
|
|
86
|
+
if (!holderDid || input.verifiedDid !== holderDid) {
|
|
87
|
+
(0, runtime_1.emitNervesEvent)({ level: "warn", component: "senses", event: "senses.a2a_report_resolve_refused", message: "refused report/resolve from a caller whose verified DID is not the pinned holder DID", meta: { friendId: friend.id } });
|
|
88
|
+
return refuse("report not resolved: the caller is not the pinned escalation holder");
|
|
89
|
+
}
|
|
90
|
+
const checked = (0, resolution_proof_1.verifyResolution)({ sodium: await (0, a2a_client_1.ready)(), claim: { reportId: id, version, note, resolvedAt }, proof, holderDid });
|
|
91
|
+
if (!checked.ok)
|
|
92
|
+
return refuse(`report not resolved: the resolution is not signed by your key (${checked.reason})`);
|
|
93
|
+
const resolved = await (0, failure_reports_1.resolveFailureReport)(agentRoot, { id, version, note, byFriendId: friend.id, resolvedAt, proof }, input.now);
|
|
94
|
+
return resolved.ok ? { ok: true, result: { id: resolved.id, status: resolved.status } } : refuse(`report not resolved: ${resolved.reason}`);
|
|
95
|
+
}
|