@ouro.bot/cli 0.1.0-alpha.83 → 0.1.0-alpha.831
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 +5308 -16
- package/deploy/unraid/Dockerfile +54 -0
- package/deploy/unraid/README.txt +2983 -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-deployment-target.mjs +710 -0
- package/deploy/unraid/sanctuary-safe-rename.py +24 -0
- package/deploy/unraid/sanctuary-unit16-host-broker.mjs +2084 -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/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 +795 -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 +15 -0
- package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +25 -0
- package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +15 -0
- package/deploy/unraid/sanctuary.ouro/state/policy/steward.json +7 -0
- package/deploy/unraid/sanctuary.ouro/tool-profiles.json +135 -0
- package/deploy/unraid/sanctuary.xml +28 -0
- package/dist/a2a/card.js +57 -0
- package/dist/a2a/client.js +180 -0
- package/dist/a2a/config.js +50 -0
- package/dist/a2a/delegation-stores.js +56 -0
- package/dist/a2a/did-resolution.js +93 -0
- package/dist/a2a/identity.js +170 -0
- package/dist/a2a/inbound-share.js +107 -0
- package/dist/a2a/mission-result-wire.js +196 -0
- package/dist/a2a/onboarding.js +115 -0
- package/dist/a2a/pin-store.js +132 -0
- package/dist/a2a/seen-ledger.js +120 -0
- package/dist/a2a/server.js +604 -0
- package/dist/a2a/task-store.js +69 -0
- package/dist/a2a/transport.js +45 -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/await-alert.js +148 -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 +149 -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 +2169 -581
- 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 +8822 -0
- package/dist/heart/daemon/cli-help.js +716 -0
- package/dist/heart/daemon/cli-parse.js +2771 -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 +229 -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 +930 -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 +256 -0
- package/dist/heart/daemon/sanctuary-authority-installation.js +140 -0
- package/dist/heart/daemon/sanctuary-authority-ledger.js +241 -0
- package/dist/heart/daemon/sanctuary-authority-root-lifecycle.js +819 -0
- package/dist/heart/daemon/sanctuary-authority-vault-migration.js +79 -0
- package/dist/heart/daemon/sanctuary-bundle-migration.js +729 -0
- package/dist/heart/daemon/sanctuary-host-authority.js +1008 -0
- package/dist/heart/daemon/sanctuary-host-detached-supervisor.js +494 -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 +445 -0
- package/dist/heart/daemon/sanctuary-telegram-authority-gateway.js +585 -0
- package/dist/heart/daemon/sanctuary-telegram-authority-service.js +630 -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 +1430 -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 +129 -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/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/start-of-turn-packet.js +372 -0
- package/dist/heart/steward-policy.js +618 -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 +449 -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 +200 -0
- package/dist/heart/turn-context.js +482 -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 +309 -111
- 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 +1050 -189
- 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 +811 -18
- package/dist/repertoire/mcp-client.js +44 -3
- package/dist/repertoire/mcp-manager.js +427 -139
- package/dist/repertoire/mcp-tools.js +124 -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 +170 -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 +575 -0
- package/dist/repertoire/tools-base.js +77 -1138
- 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-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-mail.js +1828 -0
- package/dist/repertoire/tools-notes.js +485 -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 +150 -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 +119 -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 +531 -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 +494 -148
- 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 +115 -0
- package/dist/senses/acp-server.js +386 -0
- package/dist/senses/attention-queue.js +186 -0
- package/dist/senses/await-turn-message.js +58 -0
- package/dist/senses/bluebubbles/active-turns.js +216 -0
- package/dist/senses/bluebubbles/attachment-cache.js +53 -0
- package/dist/senses/bluebubbles/attachment-download.js +137 -0
- package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +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 +604 -320
- 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 +1517 -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 +100 -0
- package/dist/senses/sanctuary-health.js +408 -0
- package/dist/senses/sanctuary-install-state-contract.js +126 -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 +340 -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 +772 -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 +896 -0
- package/dist/senses/telegram-entry.js +83 -0
- package/dist/senses/telegram.js +2324 -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 +8138 -0
- package/package.json +72 -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 -228
- 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,1986 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.getBlueBubblesSemanticPaths = getBlueBubblesSemanticPaths;
|
|
40
|
+
exports.getBlueBubblesSemanticPathsAtRoot = getBlueBubblesSemanticPathsAtRoot;
|
|
41
|
+
exports.serializeBlueBubblesSemanticJson = serializeBlueBubblesSemanticJson;
|
|
42
|
+
exports.normalizeIngressIdentifier = normalizeIngressIdentifier;
|
|
43
|
+
exports.hashIngressActorIdentity = hashIngressActorIdentity;
|
|
44
|
+
exports.normalizeIngressParticipants = normalizeIngressParticipants;
|
|
45
|
+
exports.initializeBlueBubblesSemanticCutover = initializeBlueBubblesSemanticCutover;
|
|
46
|
+
exports.rotateBlueBubblesSemanticCutover = rotateBlueBubblesSemanticCutover;
|
|
47
|
+
exports.readBlueBubblesSemanticCutover = readBlueBubblesSemanticCutover;
|
|
48
|
+
exports.readBlueBubblesSemanticCutoverAtRoot = readBlueBubblesSemanticCutoverAtRoot;
|
|
49
|
+
exports.buildBlueBubblesSemanticIdentity = buildBlueBubblesSemanticIdentity;
|
|
50
|
+
exports.buildBlueBubblesSemanticCapture = buildBlueBubblesSemanticCapture;
|
|
51
|
+
exports.buildBlueBubblesReactionCoordinateRecord = buildBlueBubblesReactionCoordinateRecord;
|
|
52
|
+
exports.buildBlueBubblesSemanticClaimRecord = buildBlueBubblesSemanticClaimRecord;
|
|
53
|
+
exports.classifyBlueBubblesRecoveryRecord = classifyBlueBubblesRecoveryRecord;
|
|
54
|
+
exports.writeBlueBubblesSemanticCapture = writeBlueBubblesSemanticCapture;
|
|
55
|
+
exports.readBlueBubblesSemanticCapture = readBlueBubblesSemanticCapture;
|
|
56
|
+
exports.readBlueBubblesSemanticCaptureAtRoot = readBlueBubblesSemanticCaptureAtRoot;
|
|
57
|
+
exports.listPendingBlueBubblesSemanticCaptures = listPendingBlueBubblesSemanticCaptures;
|
|
58
|
+
exports.compareBlueBubblesSemanticCaptureOrder = compareBlueBubblesSemanticCaptureOrder;
|
|
59
|
+
exports.writeBlueBubblesSemanticHandled = writeBlueBubblesSemanticHandled;
|
|
60
|
+
exports.readBlueBubblesSemanticHandled = readBlueBubblesSemanticHandled;
|
|
61
|
+
exports.acquireBlueBubblesSemanticClaim = acquireBlueBubblesSemanticClaim;
|
|
62
|
+
exports.releaseBlueBubblesSemanticClaim = releaseBlueBubblesSemanticClaim;
|
|
63
|
+
exports.allocateBlueBubblesReactionCoordinate = allocateBlueBubblesReactionCoordinate;
|
|
64
|
+
const node_crypto_1 = require("node:crypto");
|
|
65
|
+
const node_child_process_1 = require("node:child_process");
|
|
66
|
+
const fs = __importStar(require("node:fs"));
|
|
67
|
+
const os = __importStar(require("node:os"));
|
|
68
|
+
const path = __importStar(require("node:path"));
|
|
69
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
70
|
+
const identity_1 = require("../../heart/identity");
|
|
71
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
72
|
+
const UUID_V4_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
73
|
+
const ISO_MILLISECONDS_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
|
|
74
|
+
const CUTOVER_KEYS = ["schemaVersion", "providerNamespace", "effectiveAt"];
|
|
75
|
+
const CANONICAL_VALUES = new Set([
|
|
76
|
+
"love",
|
|
77
|
+
"like",
|
|
78
|
+
"dislike",
|
|
79
|
+
"laugh",
|
|
80
|
+
"emphasize",
|
|
81
|
+
"question",
|
|
82
|
+
"custom",
|
|
83
|
+
"unknown",
|
|
84
|
+
]);
|
|
85
|
+
const CANONICAL_ACTIONS = new Set(["add", "remove"]);
|
|
86
|
+
const SEMANTIC_KEY_HASH_PATTERN = /^[0-9a-f]{64}$/;
|
|
87
|
+
const SEMANTIC_CLAIM_POLL_MS = 50;
|
|
88
|
+
const SEMANTIC_CLAIM_TIMEOUT_MS = 5_000;
|
|
89
|
+
const SEMANTIC_OWNERSHIP_SCHEMA_VERSION = 1;
|
|
90
|
+
const SEMANTIC_OWNERSHIP_SCHEMA_SQL = `CREATE TABLE owner_leases (
|
|
91
|
+
resource_key TEXT PRIMARY KEY,
|
|
92
|
+
lease_id TEXT NOT NULL,
|
|
93
|
+
owner_json TEXT NOT NULL
|
|
94
|
+
) STRICT, WITHOUT ROWID`;
|
|
95
|
+
const CAPTURE_KEYS = ["schemaVersion", "canonicalKey", "keyHash", "providerNamespace", "capturedAt", "event"];
|
|
96
|
+
const OBSERVATION_ORDER_KEYS = [
|
|
97
|
+
"schemaVersion",
|
|
98
|
+
"keyHash",
|
|
99
|
+
"captureHash",
|
|
100
|
+
"observedAt",
|
|
101
|
+
"observationEpoch",
|
|
102
|
+
"observationOrdinal",
|
|
103
|
+
];
|
|
104
|
+
const CAPTURE_EVENT_KEYS = [
|
|
105
|
+
"provider",
|
|
106
|
+
"kind",
|
|
107
|
+
"eventGuid",
|
|
108
|
+
"fromMe",
|
|
109
|
+
"actor",
|
|
110
|
+
"participants",
|
|
111
|
+
"sourceEventType",
|
|
112
|
+
"sessionKey",
|
|
113
|
+
"chatGuid",
|
|
114
|
+
"chatIdentifier",
|
|
115
|
+
"text",
|
|
116
|
+
"textSha256",
|
|
117
|
+
"targetGuid",
|
|
118
|
+
"targetAuthorship",
|
|
119
|
+
"canonicalAction",
|
|
120
|
+
"canonicalValue",
|
|
121
|
+
"rawTransportValue",
|
|
122
|
+
"effectiveAt",
|
|
123
|
+
"revision",
|
|
124
|
+
"contentSha256",
|
|
125
|
+
];
|
|
126
|
+
const OBSERVED_IDENTITY_KEYS = ["provider", "externalId", "displayName"];
|
|
127
|
+
const CLAIM_KEYS = ["schemaVersion", "canonicalKey", "keyHash", "owner"];
|
|
128
|
+
const CLAIM_OWNER_KEYS = ["operationId", "pid", "bootIdentity", "processStartedAt", "acquiredAt"];
|
|
129
|
+
const HANDLED_KEYS = ["schemaVersion", "canonicalKey", "keyHash", "handledAt", "outcome", "detailCode"];
|
|
130
|
+
const COORDINATE_KEYS = [
|
|
131
|
+
"schemaVersion",
|
|
132
|
+
"coordinateKey",
|
|
133
|
+
"coordinateHash",
|
|
134
|
+
"generation",
|
|
135
|
+
"lastAction",
|
|
136
|
+
"updatedAt",
|
|
137
|
+
];
|
|
138
|
+
const SEMANTIC_EVENT_KINDS = new Set(["message", "reaction", "edit", "unsend", "read", "delivery"]);
|
|
139
|
+
const SEMANTIC_HANDLED_OUTCOMES = new Set([
|
|
140
|
+
"ignored_self",
|
|
141
|
+
"capture_only_removal",
|
|
142
|
+
"capture_only_positive",
|
|
143
|
+
"capture_only_custom",
|
|
144
|
+
"capture_only_unknown",
|
|
145
|
+
"capture_only_target_not_agent",
|
|
146
|
+
"capture_only_untrusted_actor",
|
|
147
|
+
"restricted_feedback_settled",
|
|
148
|
+
"restricted_feedback_observed",
|
|
149
|
+
"restricted_feedback_failed",
|
|
150
|
+
"message_completed",
|
|
151
|
+
"message_observed",
|
|
152
|
+
"message_failed",
|
|
153
|
+
"edit_capture_only",
|
|
154
|
+
"unsend_capture_only",
|
|
155
|
+
"read_audit_only",
|
|
156
|
+
"delivery_audit_only",
|
|
157
|
+
]);
|
|
158
|
+
const lastOwnerAcquisitionMs = new Map();
|
|
159
|
+
const semanticClaimLeaseIds = new WeakMap();
|
|
160
|
+
function getBlueBubblesSemanticPaths(agentName) {
|
|
161
|
+
return getBlueBubblesSemanticPathsAtRoot((0, identity_1.getAgentRoot)(agentName));
|
|
162
|
+
}
|
|
163
|
+
function getBlueBubblesSemanticPathsAtRoot(agentRoot) {
|
|
164
|
+
const root = path.join(agentRoot, "state", "senses", "bluebubbles", "semantic-receipts");
|
|
165
|
+
return {
|
|
166
|
+
root,
|
|
167
|
+
cutover: path.join(root, "cutover.json"),
|
|
168
|
+
captures: path.join(root, "captures"),
|
|
169
|
+
observationOrders: path.join(root, "observation-orders"),
|
|
170
|
+
handled: path.join(root, "handled"),
|
|
171
|
+
claims: path.join(root, "claims"),
|
|
172
|
+
coordinates: path.join(root, "coordinates"),
|
|
173
|
+
quarantine: path.join(root, "quarantine"),
|
|
174
|
+
ownership: path.join(root, "ownership.sqlite"),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
function serializeBlueBubblesSemanticJson(value) {
|
|
178
|
+
return `${JSON.stringify(value, null, 2)}\n`;
|
|
179
|
+
}
|
|
180
|
+
function normalizeIngressIdentifier(value) {
|
|
181
|
+
return String(value).trim().toLowerCase();
|
|
182
|
+
}
|
|
183
|
+
function hashIngressActorIdentity(externalId) {
|
|
184
|
+
const normalized = normalizeIngressIdentifier(externalId);
|
|
185
|
+
return sha256Utf8(JSON.stringify(["imessage-handle", normalized]));
|
|
186
|
+
}
|
|
187
|
+
function normalizeIngressParticipants(participants) {
|
|
188
|
+
return participants
|
|
189
|
+
.map((participant) => ({
|
|
190
|
+
provider: "imessage-handle",
|
|
191
|
+
externalId: normalizeIngressIdentifier(participant.externalId),
|
|
192
|
+
displayName: normalizeDisplayName(participant.displayName),
|
|
193
|
+
}))
|
|
194
|
+
.filter((participant) => participant.externalId.length > 0)
|
|
195
|
+
.sort((left, right) => left.externalId.localeCompare(right.externalId));
|
|
196
|
+
}
|
|
197
|
+
function initializeBlueBubblesSemanticCutover(agentName, deps = {}) {
|
|
198
|
+
const paths = getBlueBubblesSemanticPaths(agentName);
|
|
199
|
+
const existing = readBlueBubblesSemanticCutover(agentName);
|
|
200
|
+
if (existing)
|
|
201
|
+
return existing;
|
|
202
|
+
if (fs.existsSync(paths.cutover))
|
|
203
|
+
throw new Error("semantic_cutover_invalid");
|
|
204
|
+
const marker = createCutover(deps);
|
|
205
|
+
fs.mkdirSync(paths.root, { recursive: true });
|
|
206
|
+
const tempPath = path.join(paths.root, `.cutover.json.${process.pid}.${marker.providerNamespace}.tmp`);
|
|
207
|
+
let tempFd = null;
|
|
208
|
+
let tempOwned = false;
|
|
209
|
+
let published = false;
|
|
210
|
+
let primaryError;
|
|
211
|
+
try {
|
|
212
|
+
tempFd = fs.openSync(tempPath, "wx", 0o600);
|
|
213
|
+
tempOwned = true;
|
|
214
|
+
fs.writeFileSync(tempFd, serializeBlueBubblesSemanticJson(marker), "utf8");
|
|
215
|
+
fs.fsyncSync(tempFd);
|
|
216
|
+
fs.closeSync(tempFd);
|
|
217
|
+
tempFd = null;
|
|
218
|
+
try {
|
|
219
|
+
fs.linkSync(tempPath, paths.cutover);
|
|
220
|
+
published = true;
|
|
221
|
+
fsyncDirectory(paths.root);
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
if (!isNodeError(error, "EEXIST"))
|
|
225
|
+
throw error;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
primaryError = error;
|
|
230
|
+
throw error;
|
|
231
|
+
}
|
|
232
|
+
finally {
|
|
233
|
+
let cleanupError;
|
|
234
|
+
if (tempFd !== null) {
|
|
235
|
+
try {
|
|
236
|
+
fs.closeSync(tempFd);
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
cleanupError = error;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (tempOwned) {
|
|
243
|
+
try {
|
|
244
|
+
fs.unlinkSync(tempPath);
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
if (!isNodeError(error, "ENOENT") && cleanupError === undefined)
|
|
248
|
+
cleanupError = error;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (primaryError === undefined && cleanupError !== undefined)
|
|
252
|
+
throw cleanupError;
|
|
253
|
+
}
|
|
254
|
+
if (!published) {
|
|
255
|
+
const winner = readBlueBubblesSemanticCutover(agentName);
|
|
256
|
+
if (!winner)
|
|
257
|
+
throw new Error("semantic_cutover_invalid");
|
|
258
|
+
return winner;
|
|
259
|
+
}
|
|
260
|
+
(0, runtime_1.emitNervesEvent)({
|
|
261
|
+
component: "senses",
|
|
262
|
+
event: "senses.bluebubbles_semantic_cutover_initialized",
|
|
263
|
+
message: "initialized bluebubbles semantic cutover",
|
|
264
|
+
meta: { agentName, schemaVersion: 1 },
|
|
265
|
+
});
|
|
266
|
+
return marker;
|
|
267
|
+
}
|
|
268
|
+
function rotateBlueBubblesSemanticCutover(agentName, reason, deps = {}) {
|
|
269
|
+
const paths = getBlueBubblesSemanticPaths(agentName);
|
|
270
|
+
const marker = createCutover(deps);
|
|
271
|
+
fs.mkdirSync(paths.root, { recursive: true });
|
|
272
|
+
const tempPath = path.join(paths.root, `.cutover.json.${process.pid}.${marker.providerNamespace}.tmp`);
|
|
273
|
+
let tempFd = null;
|
|
274
|
+
let tempOwned = false;
|
|
275
|
+
let primaryError;
|
|
276
|
+
try {
|
|
277
|
+
tempFd = fs.openSync(tempPath, "wx", 0o600);
|
|
278
|
+
tempOwned = true;
|
|
279
|
+
fs.writeFileSync(tempFd, serializeBlueBubblesSemanticJson(marker), "utf8");
|
|
280
|
+
fs.fsyncSync(tempFd);
|
|
281
|
+
fs.closeSync(tempFd);
|
|
282
|
+
tempFd = null;
|
|
283
|
+
fs.renameSync(tempPath, paths.cutover);
|
|
284
|
+
fsyncDirectory(paths.root);
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
primaryError = error;
|
|
288
|
+
throw error;
|
|
289
|
+
}
|
|
290
|
+
finally {
|
|
291
|
+
let cleanupError;
|
|
292
|
+
if (tempFd !== null) {
|
|
293
|
+
try {
|
|
294
|
+
fs.closeSync(tempFd);
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
cleanupError = error;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (tempOwned) {
|
|
301
|
+
try {
|
|
302
|
+
fs.unlinkSync(tempPath);
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
if (!isNodeError(error, "ENOENT") && cleanupError === undefined)
|
|
306
|
+
cleanupError = error;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (primaryError === undefined && cleanupError !== undefined)
|
|
310
|
+
throw cleanupError;
|
|
311
|
+
}
|
|
312
|
+
(0, runtime_1.emitNervesEvent)({
|
|
313
|
+
component: "senses",
|
|
314
|
+
event: "senses.bluebubbles_semantic_cutover_rotated",
|
|
315
|
+
message: "rotated bluebubbles semantic provider namespace",
|
|
316
|
+
meta: { agentName, reason, schemaVersion: 1 },
|
|
317
|
+
});
|
|
318
|
+
return marker;
|
|
319
|
+
}
|
|
320
|
+
function readBlueBubblesSemanticCutover(agentName) {
|
|
321
|
+
return readBlueBubblesSemanticCutoverAtRoot((0, identity_1.getAgentRoot)(agentName), {}, agentName);
|
|
322
|
+
}
|
|
323
|
+
function readBlueBubblesSemanticCutoverAtRoot(agentRoot, deps = {}, diagnosticIdentity = path.basename(agentRoot)) {
|
|
324
|
+
const storeFs = deps.fs ?? fs;
|
|
325
|
+
const filePath = getBlueBubblesSemanticPathsAtRoot(agentRoot).cutover;
|
|
326
|
+
if (!storeFs.existsSync(filePath))
|
|
327
|
+
return null;
|
|
328
|
+
try {
|
|
329
|
+
const parsed = JSON.parse(storeFs.readFileSync(filePath, "utf8"));
|
|
330
|
+
return isBlueBubblesSemanticCutover(parsed) ? parsed : null;
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
(0, runtime_1.emitNervesEvent)({
|
|
334
|
+
level: "error",
|
|
335
|
+
component: "senses",
|
|
336
|
+
event: "senses.bluebubbles_semantic_cutover_error",
|
|
337
|
+
message: "failed to read bluebubbles semantic cutover",
|
|
338
|
+
meta: {
|
|
339
|
+
agentName: diagnosticIdentity,
|
|
340
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
function buildBlueBubblesSemanticIdentity(input) {
|
|
347
|
+
const providerNamespace = normalizeProviderNamespace(input.providerNamespace);
|
|
348
|
+
if (!providerNamespace)
|
|
349
|
+
return null;
|
|
350
|
+
if (input.kind === "message") {
|
|
351
|
+
const eventGuid = requiredIdentifier(input.eventGuid);
|
|
352
|
+
if (!eventGuid)
|
|
353
|
+
return null;
|
|
354
|
+
return identityFromKey(["bb-sem-v1", providerNamespace, "message", eventGuid], true);
|
|
355
|
+
}
|
|
356
|
+
if (input.kind === "reaction") {
|
|
357
|
+
const eventGuid = requiredIdentifier(input.eventGuid);
|
|
358
|
+
const targetGuid = requiredIdentifier(input.targetGuid);
|
|
359
|
+
const actorExternalId = requiredIdentifier(input.actorExternalId);
|
|
360
|
+
const canonicalValue = normalizeCanonicalValue(input.canonicalValue);
|
|
361
|
+
const canonicalAction = normalizeCanonicalAction(input.canonicalAction);
|
|
362
|
+
if (!eventGuid || !targetGuid || !actorExternalId || !canonicalValue || !canonicalAction)
|
|
363
|
+
return null;
|
|
364
|
+
const actorIdentityHash = hashIngressActorIdentity(actorExternalId);
|
|
365
|
+
const coordinateTuple = [
|
|
366
|
+
"bb-sem-v1",
|
|
367
|
+
providerNamespace,
|
|
368
|
+
"reaction-coordinate",
|
|
369
|
+
eventGuid,
|
|
370
|
+
targetGuid,
|
|
371
|
+
actorIdentityHash,
|
|
372
|
+
canonicalValue,
|
|
373
|
+
];
|
|
374
|
+
const coordinateKey = JSON.stringify(coordinateTuple);
|
|
375
|
+
const coordinateHash = sha256Utf8(coordinateKey);
|
|
376
|
+
const discriminator = mutationDiscriminator(input)
|
|
377
|
+
?? generationDiscriminator(input.coordinateGeneration);
|
|
378
|
+
if (!discriminator)
|
|
379
|
+
return null;
|
|
380
|
+
return identityFromKey([
|
|
381
|
+
"bb-sem-v1",
|
|
382
|
+
providerNamespace,
|
|
383
|
+
"reaction",
|
|
384
|
+
eventGuid,
|
|
385
|
+
targetGuid,
|
|
386
|
+
actorIdentityHash,
|
|
387
|
+
canonicalValue,
|
|
388
|
+
canonicalAction,
|
|
389
|
+
discriminator,
|
|
390
|
+
], true, discriminator, coordinateKey, coordinateHash);
|
|
391
|
+
}
|
|
392
|
+
if (input.kind === "edit") {
|
|
393
|
+
const messageGuid = requiredIdentifier(input.eventGuid);
|
|
394
|
+
if (!messageGuid)
|
|
395
|
+
return null;
|
|
396
|
+
const discriminator = mutationDiscriminator(input)
|
|
397
|
+
?? (typeof input.text === "string" ? `content:${sha256Utf8(input.text)}` : null);
|
|
398
|
+
if (!discriminator)
|
|
399
|
+
return null;
|
|
400
|
+
return identityFromKey(["bb-sem-v1", providerNamespace, "edit", messageGuid, discriminator], true, discriminator);
|
|
401
|
+
}
|
|
402
|
+
if (input.kind === "unsend") {
|
|
403
|
+
const targetGuid = requiredIdentifier(input.targetGuid);
|
|
404
|
+
if (!targetGuid)
|
|
405
|
+
return null;
|
|
406
|
+
const discriminator = mutationDiscriminator(input) ?? "terminal";
|
|
407
|
+
return identityFromKey(["bb-sem-v1", providerNamespace, "unsend", targetGuid, discriminator], true, discriminator);
|
|
408
|
+
}
|
|
409
|
+
const eventGuid = requiredIdentifier(input.eventGuid);
|
|
410
|
+
if (!eventGuid)
|
|
411
|
+
return null;
|
|
412
|
+
const discriminator = effectiveTimestampDiscriminator(input.effectiveTimestamp) ?? "terminal";
|
|
413
|
+
return identityFromKey(["bb-sem-v1", providerNamespace, "audit", input.kind, eventGuid, discriminator], false, discriminator);
|
|
414
|
+
}
|
|
415
|
+
function buildBlueBubblesSemanticCapture(input) {
|
|
416
|
+
if (typeof input.event.fromMe !== "boolean")
|
|
417
|
+
return null;
|
|
418
|
+
const providerNamespace = normalizeProviderNamespace(input.cutover.providerNamespace);
|
|
419
|
+
const capturedAt = exactIsoMilliseconds(input.capturedAt);
|
|
420
|
+
const sender = input.event.sender;
|
|
421
|
+
const actorExternalId = sender.observed === true
|
|
422
|
+
? requiredIdentifier(sender.externalId)
|
|
423
|
+
: null;
|
|
424
|
+
if (!providerNamespace || !capturedAt || !actorExternalId)
|
|
425
|
+
return null;
|
|
426
|
+
const inputObservationEpoch = input.observationEpoch;
|
|
427
|
+
const inputObservationOrdinal = input.observationOrdinal;
|
|
428
|
+
const observationClock = inputObservationEpoch === undefined
|
|
429
|
+
&& inputObservationOrdinal === undefined
|
|
430
|
+
? null
|
|
431
|
+
: isBlueBubblesObservationEpoch(inputObservationEpoch)
|
|
432
|
+
&& typeof inputObservationOrdinal === "number"
|
|
433
|
+
&& Number.isSafeInteger(inputObservationOrdinal)
|
|
434
|
+
&& inputObservationOrdinal > 0
|
|
435
|
+
? {
|
|
436
|
+
observationEpoch: inputObservationEpoch,
|
|
437
|
+
observationOrdinal: inputObservationOrdinal,
|
|
438
|
+
}
|
|
439
|
+
: false;
|
|
440
|
+
if (observationClock === false)
|
|
441
|
+
return null;
|
|
442
|
+
const kind = input.event.kind === "message" ? "message" : input.event.mutationType;
|
|
443
|
+
const eventGuid = requiredIdentifier(input.event.messageGuid);
|
|
444
|
+
const targetGuid = input.event.kind === "mutation"
|
|
445
|
+
? requiredIdentifier(input.event.targetMessageGuid)
|
|
446
|
+
?? (input.event.mutationType === "unsend" ? eventGuid : null)
|
|
447
|
+
: null;
|
|
448
|
+
const reaction = input.event.kind === "mutation" ? input.event.reaction : undefined;
|
|
449
|
+
const editedText = input.event.kind === "mutation" ? input.event.editedText : undefined;
|
|
450
|
+
const identity = buildBlueBubblesSemanticIdentity({
|
|
451
|
+
providerNamespace,
|
|
452
|
+
kind,
|
|
453
|
+
eventGuid,
|
|
454
|
+
targetGuid,
|
|
455
|
+
actorExternalId,
|
|
456
|
+
canonicalValue: reaction?.canonicalValue,
|
|
457
|
+
canonicalAction: reaction?.action,
|
|
458
|
+
revision: input.event.kind === "mutation" ? input.event.revision : undefined,
|
|
459
|
+
effectiveTimestamp: input.event.kind === "mutation" ? input.event.effectiveTimestamp : undefined,
|
|
460
|
+
text: input.event.kind === "message" ? input.event.text : editedText,
|
|
461
|
+
coordinateGeneration: input.coordinateGeneration,
|
|
462
|
+
});
|
|
463
|
+
if (!identity)
|
|
464
|
+
return null;
|
|
465
|
+
const text = input.event.kind === "message"
|
|
466
|
+
? input.event.text
|
|
467
|
+
: kind === "edit" ? editedText ?? null : null;
|
|
468
|
+
const textSha256 = text === null ? null : sha256Utf8(text);
|
|
469
|
+
const effectiveAt = input.event.kind === "mutation"
|
|
470
|
+
? finiteTimestampIso(input.event.effectiveTimestamp)
|
|
471
|
+
: null;
|
|
472
|
+
const revision = input.event.kind === "mutation"
|
|
473
|
+
? normalizedRevision(input.event.revision)
|
|
474
|
+
: null;
|
|
475
|
+
const participants = normalizeIngressParticipants(input.event.chat.participantHandles.map((externalId) => ({
|
|
476
|
+
provider: "imessage-handle",
|
|
477
|
+
externalId,
|
|
478
|
+
displayName: null,
|
|
479
|
+
})));
|
|
480
|
+
const capture = {
|
|
481
|
+
schemaVersion: 1,
|
|
482
|
+
canonicalKey: identity.canonicalKey,
|
|
483
|
+
keyHash: identity.keyHash,
|
|
484
|
+
providerNamespace,
|
|
485
|
+
capturedAt,
|
|
486
|
+
event: {
|
|
487
|
+
provider: "bluebubbles",
|
|
488
|
+
kind,
|
|
489
|
+
eventGuid,
|
|
490
|
+
fromMe: input.event.fromMe,
|
|
491
|
+
actor: {
|
|
492
|
+
provider: "imessage-handle",
|
|
493
|
+
externalId: actorExternalId,
|
|
494
|
+
displayName: normalizeDisplayName(sender.displayName),
|
|
495
|
+
},
|
|
496
|
+
participants,
|
|
497
|
+
sourceEventType: input.event.eventType,
|
|
498
|
+
sessionKey: nullableString(input.event.chat.sessionKey),
|
|
499
|
+
chatGuid: nullableString(input.event.chat.chatGuid),
|
|
500
|
+
chatIdentifier: nullableString(input.event.chat.chatIdentifier),
|
|
501
|
+
text,
|
|
502
|
+
textSha256,
|
|
503
|
+
targetGuid,
|
|
504
|
+
targetAuthorship: input.targetAuthorship,
|
|
505
|
+
canonicalAction: reaction?.action ?? null,
|
|
506
|
+
canonicalValue: reaction?.canonicalValue ?? null,
|
|
507
|
+
rawTransportValue: reaction?.rawTransportValue ?? null,
|
|
508
|
+
effectiveAt,
|
|
509
|
+
revision,
|
|
510
|
+
contentSha256: kind === "edit" ? textSha256 : null,
|
|
511
|
+
},
|
|
512
|
+
};
|
|
513
|
+
if (observationClock) {
|
|
514
|
+
attachBlueBubblesSemanticObservationOrder(capture, {
|
|
515
|
+
schemaVersion: 1,
|
|
516
|
+
keyHash: capture.keyHash,
|
|
517
|
+
captureHash: blueBubblesSemanticCaptureHash(capture),
|
|
518
|
+
observedAt: capturedAt,
|
|
519
|
+
...observationClock,
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
return capture;
|
|
523
|
+
}
|
|
524
|
+
function buildBlueBubblesReactionCoordinateRecord(input) {
|
|
525
|
+
return {
|
|
526
|
+
schemaVersion: 1,
|
|
527
|
+
coordinateKey: input.coordinateKey,
|
|
528
|
+
coordinateHash: input.coordinateHash,
|
|
529
|
+
generation: input.generation,
|
|
530
|
+
lastAction: input.lastAction,
|
|
531
|
+
updatedAt: input.updatedAt,
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
function buildBlueBubblesSemanticClaimRecord(input) {
|
|
535
|
+
return {
|
|
536
|
+
schemaVersion: 1,
|
|
537
|
+
canonicalKey: input.canonicalKey,
|
|
538
|
+
keyHash: input.keyHash,
|
|
539
|
+
owner: {
|
|
540
|
+
operationId: input.operationId,
|
|
541
|
+
pid: input.pid,
|
|
542
|
+
bootIdentity: input.bootIdentity,
|
|
543
|
+
processStartedAt: input.processStartedAt,
|
|
544
|
+
acquiredAt: input.acquiredAt,
|
|
545
|
+
},
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
function classifyBlueBubblesRecoveryRecord(value, cutover) {
|
|
549
|
+
if (!isRecord(value) || value.schemaVersion !== 1) {
|
|
550
|
+
emitLegacyRecoveryBlocked(value);
|
|
551
|
+
return { disposition: "audit_only", reason: "legacy_or_actorless" };
|
|
552
|
+
}
|
|
553
|
+
if (!isRecord(value.event)) {
|
|
554
|
+
emitInvalidSemanticRecoveryRecord(value, false, "event_missing");
|
|
555
|
+
return { disposition: "audit_only", reason: "legacy_or_actorless" };
|
|
556
|
+
}
|
|
557
|
+
const actor = value.event.actor;
|
|
558
|
+
if (!isRecord(actor)
|
|
559
|
+
|| actor.provider !== "imessage-handle"
|
|
560
|
+
|| typeof actor.externalId !== "string"
|
|
561
|
+
|| actor.externalId.trim().length === 0) {
|
|
562
|
+
emitInvalidSemanticRecoveryRecord(value, false, "actor_missing");
|
|
563
|
+
return { disposition: "audit_only", reason: "legacy_or_actorless" };
|
|
564
|
+
}
|
|
565
|
+
if (value.providerNamespace !== cutover.providerNamespace
|
|
566
|
+
|| typeof value.capturedAt !== "string"
|
|
567
|
+
|| !exactIsoMilliseconds(value.capturedAt)
|
|
568
|
+
|| value.capturedAt < cutover.effectiveAt) {
|
|
569
|
+
return { disposition: "audit_only", reason: "before_cutover" };
|
|
570
|
+
}
|
|
571
|
+
if (value.event.kind === "read" || value.event.kind === "delivery") {
|
|
572
|
+
return { disposition: "audit_only", reason: "audit_event" };
|
|
573
|
+
}
|
|
574
|
+
if (typeof value.keyHash !== "string" || value.keyHash.length === 0) {
|
|
575
|
+
emitInvalidSemanticRecoveryRecord(value, true, "key_hash_missing");
|
|
576
|
+
return { disposition: "audit_only", reason: "legacy_or_actorless" };
|
|
577
|
+
}
|
|
578
|
+
return { disposition: "handleable", keyHash: value.keyHash };
|
|
579
|
+
}
|
|
580
|
+
function writeBlueBubblesSemanticCapture(agentName, capture, deps = {}) {
|
|
581
|
+
(0, runtime_1.emitNervesEvent)({
|
|
582
|
+
component: "senses",
|
|
583
|
+
event: "bluebubbles_semantic_capture_start",
|
|
584
|
+
message: "publishing bluebubbles semantic capture",
|
|
585
|
+
meta: { agentName, keyHash: capture.keyHash },
|
|
586
|
+
});
|
|
587
|
+
try {
|
|
588
|
+
const paths = getBlueBubblesSemanticPaths(agentName);
|
|
589
|
+
const finalPath = semanticRecordPath(paths.captures, capture.keyHash);
|
|
590
|
+
const validCapture = isBlueBubblesSemanticCapture(capture, capture.keyHash);
|
|
591
|
+
const existing = readExistingCaptureForWrite(agentName, capture.keyHash, deps);
|
|
592
|
+
if (existing) {
|
|
593
|
+
if (validCapture && capturesAreEquivalent(existing, capture)) {
|
|
594
|
+
persistBlueBubblesSemanticObservationOrder(agentName, capture, deps);
|
|
595
|
+
}
|
|
596
|
+
return finishCaptureComparison(agentName, existing, capture, deps);
|
|
597
|
+
}
|
|
598
|
+
if (!validCapture)
|
|
599
|
+
throw semanticStoreError("semantic_capture_invalid");
|
|
600
|
+
persistBlueBubblesSemanticObservationOrder(agentName, capture, deps);
|
|
601
|
+
const publication = publishImmutableSemanticRecord(paths.captures, finalPath, capture, deps);
|
|
602
|
+
if (publication === "exists") {
|
|
603
|
+
const winner = readExistingCaptureForWrite(agentName, capture.keyHash, deps);
|
|
604
|
+
if (!winner) {
|
|
605
|
+
return writeBlueBubblesSemanticCaptureAfterQuarantine(agentName, capture, deps);
|
|
606
|
+
}
|
|
607
|
+
return finishCaptureComparison(agentName, winner, capture, deps);
|
|
608
|
+
}
|
|
609
|
+
(0, runtime_1.emitNervesEvent)({
|
|
610
|
+
component: "senses",
|
|
611
|
+
event: "bluebubbles_semantic_capture_end",
|
|
612
|
+
message: "published bluebubbles semantic capture",
|
|
613
|
+
meta: { agentName, keyHash: capture.keyHash, result: "semantic_capture_published" },
|
|
614
|
+
});
|
|
615
|
+
return "semantic_capture_published";
|
|
616
|
+
}
|
|
617
|
+
catch (error) {
|
|
618
|
+
emitCaptureError(agentName, capture.keyHash, error);
|
|
619
|
+
throw semanticStoreError("semantic_capture_failed", error);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
function readBlueBubblesSemanticCapture(agentName, keyHash, deps = {}) {
|
|
623
|
+
const paths = getBlueBubblesSemanticPaths(agentName);
|
|
624
|
+
const finalPath = semanticRecordPath(paths.captures, keyHash);
|
|
625
|
+
const capture = readSemanticRecord(finalPath, "capture", keyHash, (value) => isBlueBubblesSemanticCapture(value, keyHash), deps);
|
|
626
|
+
return capture ? attachStoredBlueBubblesSemanticObservationOrder(paths, capture, deps) : null;
|
|
627
|
+
}
|
|
628
|
+
function readBlueBubblesSemanticCaptureAtRoot(agentRoot, keyHash, deps = {}) {
|
|
629
|
+
const paths = getBlueBubblesSemanticPathsAtRoot(agentRoot);
|
|
630
|
+
const finalPath = semanticRecordPath(paths.captures, keyHash);
|
|
631
|
+
const capture = readSemanticRecord(finalPath, "capture", keyHash, (value) => isBlueBubblesSemanticCapture(value, keyHash), deps);
|
|
632
|
+
return capture ? attachStoredBlueBubblesSemanticObservationOrder(paths, capture, deps) : null;
|
|
633
|
+
}
|
|
634
|
+
function listPendingBlueBubblesSemanticCaptures(agentName, deps = {}) {
|
|
635
|
+
const storeFs = semanticFs(deps);
|
|
636
|
+
const paths = getBlueBubblesSemanticPaths(agentName);
|
|
637
|
+
if (!storeFs.existsSync(paths.captures))
|
|
638
|
+
return [];
|
|
639
|
+
const captures = [];
|
|
640
|
+
const names = storeFs.readdirSync(paths.captures)
|
|
641
|
+
.filter((name) => /^[0-9a-f]{64}\.json$/.test(name))
|
|
642
|
+
.sort();
|
|
643
|
+
for (const name of names) {
|
|
644
|
+
const keyHash = name.slice(0, -".json".length);
|
|
645
|
+
const capture = readBlueBubblesSemanticCapture(agentName, keyHash, deps);
|
|
646
|
+
if (!capture)
|
|
647
|
+
continue;
|
|
648
|
+
if (readBlueBubblesSemanticHandled(agentName, keyHash, deps))
|
|
649
|
+
continue;
|
|
650
|
+
captures.push(capture);
|
|
651
|
+
}
|
|
652
|
+
return captures.sort((left, right) => (compareBlueBubblesSemanticCaptureOrder(left, right)));
|
|
653
|
+
}
|
|
654
|
+
function compareBlueBubblesSemanticCaptureOrder(left, right) {
|
|
655
|
+
if (left.observationEpoch !== undefined
|
|
656
|
+
&& left.observationEpoch === right.observationEpoch
|
|
657
|
+
&& left.observationOrdinal !== undefined
|
|
658
|
+
&& right.observationOrdinal !== undefined
|
|
659
|
+
&& left.observationOrdinal !== right.observationOrdinal) {
|
|
660
|
+
return left.observationOrdinal - right.observationOrdinal;
|
|
661
|
+
}
|
|
662
|
+
const capturedOrder = (left.observationObservedAt ?? left.capturedAt)
|
|
663
|
+
.localeCompare(right.observationObservedAt ?? right.capturedAt);
|
|
664
|
+
if (capturedOrder !== 0)
|
|
665
|
+
return capturedOrder;
|
|
666
|
+
if (left.observationEpoch === undefined && right.observationEpoch === undefined)
|
|
667
|
+
return 0;
|
|
668
|
+
const epochOrder = (left.observationEpoch ?? "").localeCompare(right.observationEpoch ?? "");
|
|
669
|
+
if (epochOrder !== 0)
|
|
670
|
+
return epochOrder;
|
|
671
|
+
return left.keyHash.localeCompare(right.keyHash);
|
|
672
|
+
}
|
|
673
|
+
function attachBlueBubblesSemanticObservationOrder(capture, order) {
|
|
674
|
+
Object.defineProperties(capture, {
|
|
675
|
+
observationObservedAt: {
|
|
676
|
+
configurable: true,
|
|
677
|
+
enumerable: false,
|
|
678
|
+
value: order.observedAt,
|
|
679
|
+
},
|
|
680
|
+
observationEpoch: {
|
|
681
|
+
configurable: true,
|
|
682
|
+
enumerable: false,
|
|
683
|
+
value: order.observationEpoch,
|
|
684
|
+
},
|
|
685
|
+
observationOrdinal: {
|
|
686
|
+
configurable: true,
|
|
687
|
+
enumerable: false,
|
|
688
|
+
value: order.observationOrdinal,
|
|
689
|
+
},
|
|
690
|
+
});
|
|
691
|
+
return capture;
|
|
692
|
+
}
|
|
693
|
+
function blueBubblesSemanticObservationOrderFromCapture(capture) {
|
|
694
|
+
if (!isBlueBubblesObservationEpoch(capture.observationEpoch)
|
|
695
|
+
|| typeof capture.observationOrdinal !== "number"
|
|
696
|
+
|| !Number.isSafeInteger(capture.observationOrdinal)
|
|
697
|
+
|| capture.observationOrdinal <= 0)
|
|
698
|
+
return null;
|
|
699
|
+
return {
|
|
700
|
+
schemaVersion: 1,
|
|
701
|
+
keyHash: capture.keyHash,
|
|
702
|
+
captureHash: blueBubblesSemanticCaptureHash(capture),
|
|
703
|
+
observedAt: capture.capturedAt,
|
|
704
|
+
observationEpoch: capture.observationEpoch,
|
|
705
|
+
observationOrdinal: capture.observationOrdinal,
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
function compareBlueBubblesSemanticObservationOrders(left, right) {
|
|
709
|
+
if (left.observationEpoch === right.observationEpoch
|
|
710
|
+
&& left.observationOrdinal !== right.observationOrdinal) {
|
|
711
|
+
return left.observationOrdinal - right.observationOrdinal;
|
|
712
|
+
}
|
|
713
|
+
const observedOrder = left.observedAt.localeCompare(right.observedAt);
|
|
714
|
+
if (observedOrder !== 0)
|
|
715
|
+
return observedOrder;
|
|
716
|
+
const epochOrder = left.observationEpoch.localeCompare(right.observationEpoch);
|
|
717
|
+
if (epochOrder !== 0)
|
|
718
|
+
return epochOrder;
|
|
719
|
+
return left.observationOrdinal - right.observationOrdinal;
|
|
720
|
+
}
|
|
721
|
+
function persistBlueBubblesSemanticObservationOrder(agentName, capture, deps) {
|
|
722
|
+
const candidate = blueBubblesSemanticObservationOrderFromCapture(capture);
|
|
723
|
+
if (!candidate)
|
|
724
|
+
return;
|
|
725
|
+
const paths = getBlueBubblesSemanticPaths(agentName);
|
|
726
|
+
const finalPath = semanticRecordPath(paths.observationOrders, candidate.captureHash);
|
|
727
|
+
const startedAt = semanticNow(deps).getTime();
|
|
728
|
+
while (true) {
|
|
729
|
+
const result = withExclusiveSemanticOwnership(paths.ownership, deps, () => {
|
|
730
|
+
const inspected = inspectSemanticRecord(finalPath, capture.keyHash, (value) => (isBlueBubblesSemanticObservationOrderRecord(value, capture.keyHash, candidate.captureHash)), deps);
|
|
731
|
+
if (inspected.status === "valid") {
|
|
732
|
+
if (compareBlueBubblesSemanticObservationOrders(inspected.value, candidate) <= 0) {
|
|
733
|
+
fsyncSemanticDirectory(paths.observationOrders, semanticFs(deps));
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
else if (inspected.status === "invalid") {
|
|
738
|
+
quarantineSemanticRecord(finalPath, "observation-order", capture.keyHash, deps, inspected.reason);
|
|
739
|
+
}
|
|
740
|
+
writeMutableSemanticRecord(paths.observationOrders, finalPath, candidate, deps);
|
|
741
|
+
});
|
|
742
|
+
if (result.status === "completed")
|
|
743
|
+
return;
|
|
744
|
+
if (semanticNow(deps).getTime() - startedAt >= SEMANTIC_CLAIM_TIMEOUT_MS) {
|
|
745
|
+
throw semanticStoreError("semantic_ownership_busy");
|
|
746
|
+
}
|
|
747
|
+
semanticSleepSync(SEMANTIC_CLAIM_POLL_MS, deps);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
function attachStoredBlueBubblesSemanticObservationOrder(paths, capture, deps) {
|
|
751
|
+
const captureHash = blueBubblesSemanticCaptureHash(capture);
|
|
752
|
+
const finalPath = semanticRecordPath(paths.observationOrders, captureHash);
|
|
753
|
+
const order = readSemanticRecord(finalPath, "observation-order", capture.keyHash, (value) => (isBlueBubblesSemanticObservationOrderRecord(value, capture.keyHash, captureHash)), deps);
|
|
754
|
+
return order ? attachBlueBubblesSemanticObservationOrder(capture, order) : capture;
|
|
755
|
+
}
|
|
756
|
+
function writeBlueBubblesSemanticHandled(agentName, record, deps = {}) {
|
|
757
|
+
try {
|
|
758
|
+
const paths = getBlueBubblesSemanticPaths(agentName);
|
|
759
|
+
const finalPath = semanticRecordPath(paths.handled, record.keyHash);
|
|
760
|
+
const existing = readExistingHandledForWrite(agentName, record.keyHash, deps);
|
|
761
|
+
if (existing)
|
|
762
|
+
return finishHandledComparison(agentName, existing, record, deps);
|
|
763
|
+
if (!isBlueBubblesSemanticHandledRecord(record, record.keyHash)) {
|
|
764
|
+
throw semanticStoreError("semantic_handled_invalid");
|
|
765
|
+
}
|
|
766
|
+
const publication = publishImmutableSemanticRecord(paths.handled, finalPath, record, deps);
|
|
767
|
+
if (publication === "exists") {
|
|
768
|
+
const winner = readExistingHandledForWrite(agentName, record.keyHash, deps);
|
|
769
|
+
if (!winner)
|
|
770
|
+
return writeBlueBubblesSemanticHandled(agentName, record, deps);
|
|
771
|
+
return finishHandledComparison(agentName, winner, record, deps);
|
|
772
|
+
}
|
|
773
|
+
return "semantic_handled_published";
|
|
774
|
+
}
|
|
775
|
+
catch (error) {
|
|
776
|
+
emitSemanticStoreError(agentName, record.keyHash, "semantic_handled_failed", error);
|
|
777
|
+
if (error instanceof Error && error.message === "semantic_handled_failed")
|
|
778
|
+
throw error;
|
|
779
|
+
throw semanticStoreError("semantic_handled_failed", error);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
function readBlueBubblesSemanticHandled(agentName, keyHash, deps = {}) {
|
|
783
|
+
const paths = getBlueBubblesSemanticPaths(agentName);
|
|
784
|
+
const finalPath = semanticRecordPath(paths.handled, keyHash);
|
|
785
|
+
return readSemanticRecord(finalPath, "handled", keyHash, (value) => isBlueBubblesSemanticHandledRecord(value, keyHash), deps);
|
|
786
|
+
}
|
|
787
|
+
async function acquireBlueBubblesSemanticClaim(agentName, identity, deps = {}) {
|
|
788
|
+
const keyHash = validatedKeyHash(identity.keyHash);
|
|
789
|
+
if (sha256Utf8(identity.canonicalKey) !== keyHash) {
|
|
790
|
+
throw semanticStoreError("semantic_claim_invalid");
|
|
791
|
+
}
|
|
792
|
+
const handled = readBlueBubblesSemanticHandled(agentName, identity.keyHash, deps);
|
|
793
|
+
if (handled)
|
|
794
|
+
return { status: "already_handled", record: handled };
|
|
795
|
+
const paths = getBlueBubblesSemanticPaths(agentName);
|
|
796
|
+
const ownerPath = path.join(paths.claims, `${validatedKeyHash(identity.keyHash)}.owner.json`);
|
|
797
|
+
let ownerResult;
|
|
798
|
+
try {
|
|
799
|
+
ownerResult = await acquireExclusiveSemanticOwner({
|
|
800
|
+
directoryPath: paths.claims,
|
|
801
|
+
ownerPath,
|
|
802
|
+
canonicalKey: identity.canonicalKey,
|
|
803
|
+
keyHash: identity.keyHash,
|
|
804
|
+
operationId: `semantic-handle:${identity.keyHash}`,
|
|
805
|
+
recordKind: "claim",
|
|
806
|
+
ownershipPath: paths.ownership,
|
|
807
|
+
resourceKey: `claim:${keyHash}`,
|
|
808
|
+
}, deps);
|
|
809
|
+
}
|
|
810
|
+
catch (error) {
|
|
811
|
+
if (error instanceof Error && error.message === "semantic_owner_liveness_failed") {
|
|
812
|
+
throw semanticStoreError("semantic_claim_liveness_failed", error);
|
|
813
|
+
}
|
|
814
|
+
throw error;
|
|
815
|
+
}
|
|
816
|
+
if (ownerResult.status === "timeout") {
|
|
817
|
+
return { status: "timeout", code: "semantic_claim_timeout" };
|
|
818
|
+
}
|
|
819
|
+
const lease = { status: "acquired", record: ownerResult.record };
|
|
820
|
+
semanticClaimLeaseIds.set(lease, ownerResult.leaseId);
|
|
821
|
+
const handledAfterClaim = readBlueBubblesSemanticHandled(agentName, identity.keyHash, deps);
|
|
822
|
+
if (handledAfterClaim) {
|
|
823
|
+
releaseBlueBubblesSemanticClaim(agentName, lease, deps);
|
|
824
|
+
return { status: "already_handled", record: handledAfterClaim };
|
|
825
|
+
}
|
|
826
|
+
(0, runtime_1.emitNervesEvent)({
|
|
827
|
+
component: "senses",
|
|
828
|
+
event: "bluebubbles_semantic_recovery_claimed",
|
|
829
|
+
message: "claimed captured bluebubbles semantic event",
|
|
830
|
+
meta: { schemaVersion: 1, actorPresent: true, keyHash: identity.keyHash },
|
|
831
|
+
});
|
|
832
|
+
return lease;
|
|
833
|
+
}
|
|
834
|
+
function releaseBlueBubblesSemanticClaim(agentName, lease, deps = {}) {
|
|
835
|
+
const paths = getBlueBubblesSemanticPaths(agentName);
|
|
836
|
+
const ownerPath = path.join(paths.claims, `${validatedKeyHash(lease.record.keyHash)}.owner.json`);
|
|
837
|
+
const leaseId = semanticClaimLeaseIds.get(lease);
|
|
838
|
+
if (!leaseId)
|
|
839
|
+
return false;
|
|
840
|
+
return releaseExclusiveSemanticOwner({
|
|
841
|
+
directoryPath: paths.claims,
|
|
842
|
+
ownerPath,
|
|
843
|
+
ownershipPath: paths.ownership,
|
|
844
|
+
resourceKey: `claim:${lease.record.keyHash}`,
|
|
845
|
+
record: lease.record,
|
|
846
|
+
leaseId,
|
|
847
|
+
}, deps);
|
|
848
|
+
}
|
|
849
|
+
async function allocateBlueBubblesReactionCoordinate(agentName, input, deps = {}) {
|
|
850
|
+
const coordinateHash = validatedKeyHash(input.coordinateHash);
|
|
851
|
+
if (sha256Utf8(input.coordinateKey) !== coordinateHash || !CANONICAL_ACTIONS.has(input.canonicalAction)) {
|
|
852
|
+
throw semanticStoreError("semantic_coordinate_invalid");
|
|
853
|
+
}
|
|
854
|
+
const paths = getBlueBubblesSemanticPaths(agentName);
|
|
855
|
+
const finalPath = path.join(paths.coordinates, `${coordinateHash}.json`);
|
|
856
|
+
const ownerPath = path.join(paths.coordinates, `${coordinateHash}.owner.lock`);
|
|
857
|
+
if (hasSemanticQuarantine(paths.quarantine, "coordinate", path.basename(finalPath), deps)) {
|
|
858
|
+
throw semanticStoreError("semantic_coordinate_invalid");
|
|
859
|
+
}
|
|
860
|
+
let ownerResult;
|
|
861
|
+
try {
|
|
862
|
+
ownerResult = await acquireExclusiveSemanticOwner({
|
|
863
|
+
directoryPath: paths.coordinates,
|
|
864
|
+
ownerPath,
|
|
865
|
+
canonicalKey: input.coordinateKey,
|
|
866
|
+
keyHash: coordinateHash,
|
|
867
|
+
operationId: `semantic-coordinate:${coordinateHash}`,
|
|
868
|
+
recordKind: "coordinate-owner",
|
|
869
|
+
ownershipPath: paths.ownership,
|
|
870
|
+
resourceKey: `coordinate:${coordinateHash}`,
|
|
871
|
+
}, deps);
|
|
872
|
+
}
|
|
873
|
+
catch (error) {
|
|
874
|
+
if (error instanceof Error && error.message === "semantic_owner_liveness_failed") {
|
|
875
|
+
throw semanticStoreError("semantic_coordinate_liveness_failed", error);
|
|
876
|
+
}
|
|
877
|
+
throw error;
|
|
878
|
+
}
|
|
879
|
+
if (ownerResult.status === "timeout") {
|
|
880
|
+
throw semanticStoreError("semantic_coordinate_lock_timeout");
|
|
881
|
+
}
|
|
882
|
+
try {
|
|
883
|
+
if (hasSemanticQuarantine(paths.quarantine, "coordinate", path.basename(finalPath), deps)) {
|
|
884
|
+
throw semanticStoreError("semantic_coordinate_invalid");
|
|
885
|
+
}
|
|
886
|
+
const existing = readSemanticRecord(finalPath, "coordinate", coordinateHash, (value) => isBlueBubblesReactionCoordinateRecord(value, input.coordinateKey, coordinateHash), deps);
|
|
887
|
+
if (!existing && hasSemanticQuarantine(paths.quarantine, "coordinate", path.basename(finalPath), deps)) {
|
|
888
|
+
throw semanticStoreError("semantic_coordinate_invalid");
|
|
889
|
+
}
|
|
890
|
+
if (existing && existing.lastAction === input.canonicalAction) {
|
|
891
|
+
fsyncSemanticDirectory(paths.coordinates, semanticFs(deps));
|
|
892
|
+
return existing;
|
|
893
|
+
}
|
|
894
|
+
const record = buildBlueBubblesReactionCoordinateRecord({
|
|
895
|
+
coordinateKey: input.coordinateKey,
|
|
896
|
+
coordinateHash,
|
|
897
|
+
generation: existing ? existing.generation + 1 : 0,
|
|
898
|
+
lastAction: input.canonicalAction,
|
|
899
|
+
updatedAt: semanticNow(deps).toISOString(),
|
|
900
|
+
});
|
|
901
|
+
writeMutableSemanticRecord(paths.coordinates, finalPath, record, deps);
|
|
902
|
+
return record;
|
|
903
|
+
}
|
|
904
|
+
finally {
|
|
905
|
+
releaseExclusiveSemanticOwner({
|
|
906
|
+
directoryPath: paths.coordinates,
|
|
907
|
+
ownerPath,
|
|
908
|
+
ownershipPath: paths.ownership,
|
|
909
|
+
resourceKey: `coordinate:${coordinateHash}`,
|
|
910
|
+
record: ownerResult.record,
|
|
911
|
+
leaseId: ownerResult.leaseId,
|
|
912
|
+
}, deps);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
function writeBlueBubblesSemanticCaptureAfterQuarantine(agentName, capture, deps) {
|
|
916
|
+
const paths = getBlueBubblesSemanticPaths(agentName);
|
|
917
|
+
const finalPath = semanticRecordPath(paths.captures, capture.keyHash);
|
|
918
|
+
const publication = publishImmutableSemanticRecord(paths.captures, finalPath, capture, deps);
|
|
919
|
+
if (publication === "exists") {
|
|
920
|
+
const winner = readExistingCaptureForWrite(agentName, capture.keyHash, deps);
|
|
921
|
+
if (!winner)
|
|
922
|
+
throw semanticStoreError("semantic_capture_failed");
|
|
923
|
+
return finishCaptureComparison(agentName, winner, capture, deps);
|
|
924
|
+
}
|
|
925
|
+
(0, runtime_1.emitNervesEvent)({
|
|
926
|
+
component: "senses",
|
|
927
|
+
event: "bluebubbles_semantic_capture_end",
|
|
928
|
+
message: "published bluebubbles semantic capture after quarantining invalid state",
|
|
929
|
+
meta: { agentName, keyHash: capture.keyHash, result: "semantic_capture_published" },
|
|
930
|
+
});
|
|
931
|
+
return "semantic_capture_published";
|
|
932
|
+
}
|
|
933
|
+
function readExistingCaptureForWrite(agentName, keyHash, deps) {
|
|
934
|
+
const paths = getBlueBubblesSemanticPaths(agentName);
|
|
935
|
+
const finalPath = semanticRecordPath(paths.captures, keyHash);
|
|
936
|
+
return semanticFs(deps).existsSync(finalPath)
|
|
937
|
+
? readBlueBubblesSemanticCapture(agentName, keyHash, deps)
|
|
938
|
+
: null;
|
|
939
|
+
}
|
|
940
|
+
function finishCaptureComparison(agentName, existing, candidate, deps) {
|
|
941
|
+
fsyncSemanticDirectory(getBlueBubblesSemanticPaths(agentName).captures, semanticFs(deps));
|
|
942
|
+
if (capturesAreEquivalent(existing, candidate)) {
|
|
943
|
+
(0, runtime_1.emitNervesEvent)({
|
|
944
|
+
component: "senses",
|
|
945
|
+
event: "bluebubbles_semantic_capture_end",
|
|
946
|
+
message: "recognized duplicate bluebubbles semantic capture",
|
|
947
|
+
meta: { agentName, keyHash: candidate.keyHash, result: "semantic_capture_duplicate" },
|
|
948
|
+
});
|
|
949
|
+
return "semantic_capture_duplicate";
|
|
950
|
+
}
|
|
951
|
+
const error = semanticStoreError("semantic_identity_collision");
|
|
952
|
+
emitCaptureError(agentName, candidate.keyHash, error);
|
|
953
|
+
return "semantic_identity_collision";
|
|
954
|
+
}
|
|
955
|
+
function readExistingHandledForWrite(agentName, keyHash, deps) {
|
|
956
|
+
const paths = getBlueBubblesSemanticPaths(agentName);
|
|
957
|
+
const finalPath = semanticRecordPath(paths.handled, keyHash);
|
|
958
|
+
return semanticFs(deps).existsSync(finalPath)
|
|
959
|
+
? readBlueBubblesSemanticHandled(agentName, keyHash, deps)
|
|
960
|
+
: null;
|
|
961
|
+
}
|
|
962
|
+
function finishHandledComparison(agentName, existing, candidate, deps) {
|
|
963
|
+
fsyncSemanticDirectory(getBlueBubblesSemanticPaths(agentName).handled, semanticFs(deps));
|
|
964
|
+
if (handledRecordsAreEquivalent(existing, candidate))
|
|
965
|
+
return "semantic_handled_duplicate";
|
|
966
|
+
emitSemanticStoreError(agentName, candidate.keyHash, "semantic_handled_collision", semanticStoreError("semantic_handled_collision"));
|
|
967
|
+
return "semantic_handled_collision";
|
|
968
|
+
}
|
|
969
|
+
function capturesAreEquivalent(left, right) {
|
|
970
|
+
return blueBubblesSemanticCaptureHash(left) === blueBubblesSemanticCaptureHash(right);
|
|
971
|
+
}
|
|
972
|
+
function blueBubblesSemanticCaptureHash(capture) {
|
|
973
|
+
const projection = {
|
|
974
|
+
schemaVersion: capture.schemaVersion,
|
|
975
|
+
canonicalKey: capture.canonicalKey,
|
|
976
|
+
keyHash: capture.keyHash,
|
|
977
|
+
providerNamespace: capture.providerNamespace,
|
|
978
|
+
event: {
|
|
979
|
+
provider: capture.event.provider,
|
|
980
|
+
kind: capture.event.kind,
|
|
981
|
+
eventGuid: capture.event.eventGuid,
|
|
982
|
+
fromMe: capture.event.fromMe,
|
|
983
|
+
actor: capture.event.actor,
|
|
984
|
+
participants: capture.event.participants,
|
|
985
|
+
text: capture.event.text,
|
|
986
|
+
textSha256: capture.event.textSha256,
|
|
987
|
+
targetGuid: capture.event.targetGuid,
|
|
988
|
+
targetAuthorship: capture.event.targetAuthorship,
|
|
989
|
+
canonicalAction: capture.event.canonicalAction,
|
|
990
|
+
canonicalValue: capture.event.canonicalValue,
|
|
991
|
+
effectiveAt: capture.event.effectiveAt,
|
|
992
|
+
revision: capture.event.revision,
|
|
993
|
+
contentSha256: capture.event.contentSha256,
|
|
994
|
+
},
|
|
995
|
+
};
|
|
996
|
+
return sha256Utf8(JSON.stringify(projection));
|
|
997
|
+
}
|
|
998
|
+
function handledRecordsAreEquivalent(left, right) {
|
|
999
|
+
const project = (record) => ({
|
|
1000
|
+
schemaVersion: record.schemaVersion,
|
|
1001
|
+
canonicalKey: record.canonicalKey,
|
|
1002
|
+
keyHash: record.keyHash,
|
|
1003
|
+
outcome: record.outcome,
|
|
1004
|
+
detailCode: record.detailCode,
|
|
1005
|
+
});
|
|
1006
|
+
return JSON.stringify(project(left)) === JSON.stringify(project(right));
|
|
1007
|
+
}
|
|
1008
|
+
function publishImmutableSemanticRecord(directoryPath, finalPath, value, deps) {
|
|
1009
|
+
const storeFs = semanticFs(deps);
|
|
1010
|
+
storeFs.mkdirSync(directoryPath, { recursive: true });
|
|
1011
|
+
const tempPath = semanticTempPath(finalPath, deps);
|
|
1012
|
+
let tempFd = null;
|
|
1013
|
+
let tempOwned = false;
|
|
1014
|
+
let outcome = null;
|
|
1015
|
+
let primaryError;
|
|
1016
|
+
try {
|
|
1017
|
+
tempFd = storeFs.openSync(tempPath, "wx", 0o600);
|
|
1018
|
+
tempOwned = true;
|
|
1019
|
+
storeFs.writeFileSync(tempFd, serializeBlueBubblesSemanticJson(value), "utf8");
|
|
1020
|
+
storeFs.fsyncSync(tempFd);
|
|
1021
|
+
storeFs.closeSync(tempFd);
|
|
1022
|
+
tempFd = null;
|
|
1023
|
+
try {
|
|
1024
|
+
storeFs.linkSync(tempPath, finalPath);
|
|
1025
|
+
fsyncSemanticDirectory(directoryPath, storeFs);
|
|
1026
|
+
outcome = "published";
|
|
1027
|
+
}
|
|
1028
|
+
catch (error) {
|
|
1029
|
+
if (!isNodeError(error, "EEXIST"))
|
|
1030
|
+
throw error;
|
|
1031
|
+
outcome = "exists";
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
catch (error) {
|
|
1035
|
+
primaryError = error;
|
|
1036
|
+
}
|
|
1037
|
+
let cleanupError;
|
|
1038
|
+
if (tempFd !== null) {
|
|
1039
|
+
try {
|
|
1040
|
+
storeFs.closeSync(tempFd);
|
|
1041
|
+
}
|
|
1042
|
+
catch (error) {
|
|
1043
|
+
cleanupError = error;
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
if (tempOwned) {
|
|
1047
|
+
try {
|
|
1048
|
+
storeFs.unlinkSync(tempPath);
|
|
1049
|
+
}
|
|
1050
|
+
catch (error) {
|
|
1051
|
+
if (!isNodeError(error, "ENOENT") && cleanupError === undefined)
|
|
1052
|
+
cleanupError = error;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
if (primaryError !== undefined)
|
|
1056
|
+
throw primaryError;
|
|
1057
|
+
if (cleanupError !== undefined)
|
|
1058
|
+
throw cleanupError;
|
|
1059
|
+
return outcome;
|
|
1060
|
+
}
|
|
1061
|
+
function writeMutableSemanticRecord(directoryPath, finalPath, value, deps) {
|
|
1062
|
+
const storeFs = semanticFs(deps);
|
|
1063
|
+
storeFs.mkdirSync(directoryPath, { recursive: true });
|
|
1064
|
+
const tempPath = semanticTempPath(finalPath, deps);
|
|
1065
|
+
let tempFd = null;
|
|
1066
|
+
let tempOwned = false;
|
|
1067
|
+
let primaryError;
|
|
1068
|
+
try {
|
|
1069
|
+
tempFd = storeFs.openSync(tempPath, "wx", 0o600);
|
|
1070
|
+
tempOwned = true;
|
|
1071
|
+
storeFs.writeFileSync(tempFd, serializeBlueBubblesSemanticJson(value), "utf8");
|
|
1072
|
+
storeFs.fsyncSync(tempFd);
|
|
1073
|
+
storeFs.closeSync(tempFd);
|
|
1074
|
+
tempFd = null;
|
|
1075
|
+
storeFs.renameSync(tempPath, finalPath);
|
|
1076
|
+
fsyncSemanticDirectory(directoryPath, storeFs);
|
|
1077
|
+
}
|
|
1078
|
+
catch (error) {
|
|
1079
|
+
primaryError = error;
|
|
1080
|
+
}
|
|
1081
|
+
let cleanupError;
|
|
1082
|
+
if (tempFd !== null) {
|
|
1083
|
+
try {
|
|
1084
|
+
storeFs.closeSync(tempFd);
|
|
1085
|
+
}
|
|
1086
|
+
catch (error) {
|
|
1087
|
+
cleanupError = error;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
if (tempOwned) {
|
|
1091
|
+
try {
|
|
1092
|
+
storeFs.unlinkSync(tempPath);
|
|
1093
|
+
}
|
|
1094
|
+
catch (error) {
|
|
1095
|
+
if (!isNodeError(error, "ENOENT") && cleanupError === undefined)
|
|
1096
|
+
cleanupError = error;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
if (primaryError !== undefined)
|
|
1100
|
+
throw primaryError;
|
|
1101
|
+
if (cleanupError !== undefined)
|
|
1102
|
+
throw cleanupError;
|
|
1103
|
+
}
|
|
1104
|
+
async function acquireExclusiveSemanticOwner(input, deps) {
|
|
1105
|
+
const keyHash = validatedKeyHash(input.keyHash);
|
|
1106
|
+
const pid = semanticPid(deps);
|
|
1107
|
+
const ownerRecord = buildBlueBubblesSemanticClaimRecord({
|
|
1108
|
+
canonicalKey: input.canonicalKey,
|
|
1109
|
+
keyHash,
|
|
1110
|
+
operationId: input.operationId,
|
|
1111
|
+
pid,
|
|
1112
|
+
bootIdentity: semanticBootIdentity(deps),
|
|
1113
|
+
processStartedAt: requiredProbeIdentity(semanticProcessStartedAt(pid, deps)),
|
|
1114
|
+
acquiredAt: nextOwnerAcquiredAt(input.ownerPath, deps),
|
|
1115
|
+
});
|
|
1116
|
+
const leaseId = semanticRandomUuid(deps);
|
|
1117
|
+
const startedAt = semanticNow(deps).getTime();
|
|
1118
|
+
while (true) {
|
|
1119
|
+
const attempt = withExclusiveSemanticOwnership(input.ownershipPath, deps, (database) => acquireSemanticOwnerInTransaction(database, input, ownerRecord, leaseId, deps));
|
|
1120
|
+
if (attempt.status === "completed" && attempt.value === "acquired") {
|
|
1121
|
+
return { status: "acquired", record: ownerRecord, leaseId };
|
|
1122
|
+
}
|
|
1123
|
+
if (semanticNow(deps).getTime() - startedAt >= SEMANTIC_CLAIM_TIMEOUT_MS) {
|
|
1124
|
+
return { status: "timeout" };
|
|
1125
|
+
}
|
|
1126
|
+
await semanticSleep(SEMANTIC_CLAIM_POLL_MS, deps);
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
function releaseExclusiveSemanticOwner(input, deps) {
|
|
1130
|
+
const startedAt = semanticNow(deps).getTime();
|
|
1131
|
+
while (true) {
|
|
1132
|
+
const result = withExclusiveSemanticOwnership(input.ownershipPath, deps, (database) => {
|
|
1133
|
+
const row = readSemanticOwnershipRow(database, input.resourceKey);
|
|
1134
|
+
if (!row || row.lease_id !== input.leaseId)
|
|
1135
|
+
return false;
|
|
1136
|
+
const rowOwner = parseSemanticOwnershipRow(row, {
|
|
1137
|
+
canonicalKey: input.record.canonicalKey,
|
|
1138
|
+
keyHash: input.record.keyHash,
|
|
1139
|
+
operationId: input.record.owner.operationId,
|
|
1140
|
+
});
|
|
1141
|
+
if (serializeBlueBubblesSemanticJson(rowOwner) !== serializeBlueBubblesSemanticJson(input.record)) {
|
|
1142
|
+
return false;
|
|
1143
|
+
}
|
|
1144
|
+
const evidence = inspectSemanticOwnerEvidence(input.ownerPath, {
|
|
1145
|
+
canonicalKey: input.record.canonicalKey,
|
|
1146
|
+
keyHash: input.record.keyHash,
|
|
1147
|
+
operationId: input.record.owner.operationId,
|
|
1148
|
+
}, deps);
|
|
1149
|
+
if (evidence.status === "invalid")
|
|
1150
|
+
return false;
|
|
1151
|
+
if (evidence.status === "valid" && evidence.bytes !== row.owner_json)
|
|
1152
|
+
return false;
|
|
1153
|
+
if (evidence.status === "valid" && !removeSemanticOwnerEvidenceIfUnchanged(input.directoryPath, input.ownerPath, row.owner_json, semanticFs(deps)))
|
|
1154
|
+
return false;
|
|
1155
|
+
deleteSemanticOwnershipRow(database, input.resourceKey, input.leaseId);
|
|
1156
|
+
return true;
|
|
1157
|
+
});
|
|
1158
|
+
if (result.status === "completed")
|
|
1159
|
+
return result.value;
|
|
1160
|
+
if (semanticNow(deps).getTime() - startedAt >= SEMANTIC_CLAIM_TIMEOUT_MS) {
|
|
1161
|
+
throw semanticStoreError("semantic_ownership_busy");
|
|
1162
|
+
}
|
|
1163
|
+
semanticSleepSync(SEMANTIC_CLAIM_POLL_MS, deps);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
function removeSemanticOwnerEvidenceIfUnchanged(directoryPath, ownerPath, expectedBytes, storeFs) {
|
|
1167
|
+
let before;
|
|
1168
|
+
try {
|
|
1169
|
+
before = storeFs.lstatSync(ownerPath);
|
|
1170
|
+
}
|
|
1171
|
+
catch (error) {
|
|
1172
|
+
if (isNodeError(error, "ENOENT"))
|
|
1173
|
+
return false;
|
|
1174
|
+
throw error;
|
|
1175
|
+
}
|
|
1176
|
+
let current;
|
|
1177
|
+
try {
|
|
1178
|
+
current = storeFs.readFileSync(ownerPath, "utf8");
|
|
1179
|
+
}
|
|
1180
|
+
catch (error) {
|
|
1181
|
+
if (isNodeError(error, "ENOENT"))
|
|
1182
|
+
return false;
|
|
1183
|
+
throw error;
|
|
1184
|
+
}
|
|
1185
|
+
const after = storeFs.lstatSync(ownerPath);
|
|
1186
|
+
if (!before.isFile()
|
|
1187
|
+
|| !after.isFile()
|
|
1188
|
+
|| before.dev !== after.dev
|
|
1189
|
+
|| before.ino !== after.ino
|
|
1190
|
+
|| current !== expectedBytes)
|
|
1191
|
+
return false;
|
|
1192
|
+
storeFs.unlinkSync(ownerPath);
|
|
1193
|
+
fsyncSemanticDirectory(directoryPath, storeFs);
|
|
1194
|
+
return true;
|
|
1195
|
+
}
|
|
1196
|
+
function withExclusiveSemanticOwnership(ownershipPath, deps, operation) {
|
|
1197
|
+
const storeFs = semanticFs(deps);
|
|
1198
|
+
storeFs.mkdirSync(path.dirname(ownershipPath), { recursive: true });
|
|
1199
|
+
let database = null;
|
|
1200
|
+
try {
|
|
1201
|
+
database = new better_sqlite3_1.default(ownershipPath, { timeout: 0 });
|
|
1202
|
+
database.pragma("busy_timeout = 0");
|
|
1203
|
+
database.pragma("journal_mode = DELETE");
|
|
1204
|
+
database.pragma("synchronous = FULL");
|
|
1205
|
+
database.exec("BEGIN EXCLUSIVE");
|
|
1206
|
+
initializeSemanticOwnershipSchema(database);
|
|
1207
|
+
fsyncSemanticDirectory(path.dirname(ownershipPath), storeFs);
|
|
1208
|
+
const value = operation(database);
|
|
1209
|
+
database.exec("COMMIT");
|
|
1210
|
+
return { status: "completed", value };
|
|
1211
|
+
}
|
|
1212
|
+
catch (error) {
|
|
1213
|
+
if (database?.inTransaction) {
|
|
1214
|
+
database.exec("ROLLBACK");
|
|
1215
|
+
}
|
|
1216
|
+
if (isSqliteBusy(error))
|
|
1217
|
+
return { status: "busy" };
|
|
1218
|
+
if (isSqliteError(error))
|
|
1219
|
+
throw semanticStoreError("semantic_ownership_invalid", error);
|
|
1220
|
+
throw error;
|
|
1221
|
+
}
|
|
1222
|
+
finally {
|
|
1223
|
+
database?.close();
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
function initializeSemanticOwnershipSchema(database) {
|
|
1227
|
+
const version = database.pragma("user_version", { simple: true });
|
|
1228
|
+
if (version === 0) {
|
|
1229
|
+
database.exec(SEMANTIC_OWNERSHIP_SCHEMA_SQL);
|
|
1230
|
+
database.pragma(`user_version = ${SEMANTIC_OWNERSHIP_SCHEMA_VERSION}`);
|
|
1231
|
+
}
|
|
1232
|
+
else if (version !== SEMANTIC_OWNERSHIP_SCHEMA_VERSION) {
|
|
1233
|
+
throw semanticStoreError("semantic_ownership_invalid");
|
|
1234
|
+
}
|
|
1235
|
+
const schema = database.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'owner_leases'").get();
|
|
1236
|
+
if (schema?.sql !== SEMANTIC_OWNERSHIP_SCHEMA_SQL) {
|
|
1237
|
+
throw semanticStoreError("semantic_ownership_invalid");
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
function acquireSemanticOwnerInTransaction(database, input, candidate, leaseId, deps) {
|
|
1241
|
+
const row = readSemanticOwnershipRow(database, input.resourceKey);
|
|
1242
|
+
if (row) {
|
|
1243
|
+
const existing = parseSemanticOwnershipRow(row, input);
|
|
1244
|
+
if (!semanticOwnerIsStale(existing, deps)) {
|
|
1245
|
+
reconcileSemanticOwnerEvidence(input, existing, deps);
|
|
1246
|
+
return "occupied";
|
|
1247
|
+
}
|
|
1248
|
+
quarantineSemanticOwnerEvidenceIfPresent(input, deps);
|
|
1249
|
+
deleteSemanticOwnershipRow(database, input.resourceKey, row.lease_id);
|
|
1250
|
+
}
|
|
1251
|
+
else {
|
|
1252
|
+
const evidence = inspectSemanticOwnerEvidence(input.ownerPath, input, deps);
|
|
1253
|
+
if (evidence.status === "valid" && !semanticOwnerIsStale(evidence.record, deps)) {
|
|
1254
|
+
insertSemanticOwnershipRow(database, input.resourceKey, semanticRandomUuid(deps), evidence.bytes);
|
|
1255
|
+
return "occupied";
|
|
1256
|
+
}
|
|
1257
|
+
if (evidence.status !== "missing") {
|
|
1258
|
+
quarantineSemanticRecord(input.ownerPath, input.recordKind, input.keyHash, deps, evidence.status === "invalid"
|
|
1259
|
+
? evidence.reason
|
|
1260
|
+
: semanticStoreError("semantic_owner_stale"));
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
const ownerJson = serializeBlueBubblesSemanticJson(candidate);
|
|
1264
|
+
insertSemanticOwnershipRow(database, input.resourceKey, leaseId, ownerJson);
|
|
1265
|
+
ensureSemanticOwnerEvidence(input, candidate, ownerJson, deps);
|
|
1266
|
+
return "acquired";
|
|
1267
|
+
}
|
|
1268
|
+
function readSemanticOwnershipRow(database, resourceKey) {
|
|
1269
|
+
return database.prepare("SELECT resource_key, lease_id, owner_json FROM owner_leases WHERE resource_key = ?").get(resourceKey) ?? null;
|
|
1270
|
+
}
|
|
1271
|
+
function insertSemanticOwnershipRow(database, resourceKey, leaseId, ownerJson) {
|
|
1272
|
+
database.prepare("INSERT INTO owner_leases (resource_key, lease_id, owner_json) VALUES (?, ?, ?)").run(resourceKey, leaseId, ownerJson);
|
|
1273
|
+
}
|
|
1274
|
+
function deleteSemanticOwnershipRow(database, resourceKey, leaseId) {
|
|
1275
|
+
database.prepare("DELETE FROM owner_leases WHERE resource_key = ? AND lease_id = ?").run(resourceKey, leaseId);
|
|
1276
|
+
}
|
|
1277
|
+
function parseSemanticOwnershipRow(row, expected) {
|
|
1278
|
+
if (!UUID_V4_PATTERN.test(row.lease_id)) {
|
|
1279
|
+
throw semanticStoreError("semantic_ownership_invalid");
|
|
1280
|
+
}
|
|
1281
|
+
let value;
|
|
1282
|
+
try {
|
|
1283
|
+
value = JSON.parse(row.owner_json);
|
|
1284
|
+
}
|
|
1285
|
+
catch (error) {
|
|
1286
|
+
throw semanticStoreError("semantic_ownership_invalid", error);
|
|
1287
|
+
}
|
|
1288
|
+
if (!isBlueBubblesSemanticClaimRecord(value, expected)
|
|
1289
|
+
|| row.owner_json !== serializeBlueBubblesSemanticJson(value)) {
|
|
1290
|
+
throw semanticStoreError("semantic_ownership_invalid");
|
|
1291
|
+
}
|
|
1292
|
+
return value;
|
|
1293
|
+
}
|
|
1294
|
+
function semanticOwnerIsStale(owner, deps) {
|
|
1295
|
+
try {
|
|
1296
|
+
if (owner.owner.bootIdentity !== semanticBootIdentity(deps))
|
|
1297
|
+
return true;
|
|
1298
|
+
if (!semanticIsProcessAlive(owner.owner.pid, deps))
|
|
1299
|
+
return true;
|
|
1300
|
+
const observedStart = semanticProcessStartedAt(owner.owner.pid, deps);
|
|
1301
|
+
if (observedStart === null)
|
|
1302
|
+
throw semanticStoreError("semantic_owner_liveness_failed");
|
|
1303
|
+
return observedStart !== owner.owner.processStartedAt;
|
|
1304
|
+
}
|
|
1305
|
+
catch (error) {
|
|
1306
|
+
if (error instanceof Error && error.message === "semantic_owner_liveness_failed")
|
|
1307
|
+
throw error;
|
|
1308
|
+
throw semanticStoreError("semantic_owner_liveness_failed", error);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
function inspectSemanticOwnerEvidence(ownerPath, input, deps) {
|
|
1312
|
+
const storeFs = semanticFs(deps);
|
|
1313
|
+
let stat;
|
|
1314
|
+
try {
|
|
1315
|
+
stat = storeFs.lstatSync(ownerPath);
|
|
1316
|
+
}
|
|
1317
|
+
catch (error) {
|
|
1318
|
+
if (isNodeError(error, "ENOENT"))
|
|
1319
|
+
return { status: "missing" };
|
|
1320
|
+
throw error;
|
|
1321
|
+
}
|
|
1322
|
+
if (!stat.isFile() || stat.isSymbolicLink()) {
|
|
1323
|
+
return { status: "invalid", reason: semanticStoreError("semantic_owner_evidence_invalid") };
|
|
1324
|
+
}
|
|
1325
|
+
let bytes;
|
|
1326
|
+
try {
|
|
1327
|
+
bytes = storeFs.readFileSync(ownerPath, "utf8");
|
|
1328
|
+
}
|
|
1329
|
+
catch (error) {
|
|
1330
|
+
return { status: "invalid", reason: error };
|
|
1331
|
+
}
|
|
1332
|
+
let value;
|
|
1333
|
+
try {
|
|
1334
|
+
value = JSON.parse(bytes);
|
|
1335
|
+
}
|
|
1336
|
+
catch (error) {
|
|
1337
|
+
return { status: "invalid", reason: error };
|
|
1338
|
+
}
|
|
1339
|
+
if (!isBlueBubblesSemanticClaimRecord(value, input)
|
|
1340
|
+
|| bytes !== serializeBlueBubblesSemanticJson(value)) {
|
|
1341
|
+
return { status: "invalid", reason: semanticStoreError("semantic_owner_evidence_invalid") };
|
|
1342
|
+
}
|
|
1343
|
+
return { status: "valid", record: value, bytes };
|
|
1344
|
+
}
|
|
1345
|
+
function reconcileSemanticOwnerEvidence(input, owner, deps) {
|
|
1346
|
+
const ownerJson = serializeBlueBubblesSemanticJson(owner);
|
|
1347
|
+
const evidence = inspectSemanticOwnerEvidence(input.ownerPath, input, deps);
|
|
1348
|
+
if (evidence.status === "valid" && evidence.bytes === ownerJson) {
|
|
1349
|
+
fsyncSemanticDirectory(input.directoryPath, semanticFs(deps));
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
if (evidence.status !== "missing") {
|
|
1353
|
+
quarantineSemanticRecord(input.ownerPath, input.recordKind, input.keyHash, deps, evidence.status === "invalid"
|
|
1354
|
+
? evidence.reason
|
|
1355
|
+
: semanticStoreError("semantic_owner_evidence_mismatch"));
|
|
1356
|
+
}
|
|
1357
|
+
ensureSemanticOwnerEvidence(input, owner, ownerJson, deps);
|
|
1358
|
+
}
|
|
1359
|
+
function quarantineSemanticOwnerEvidenceIfPresent(input, deps) {
|
|
1360
|
+
const evidence = inspectSemanticOwnerEvidence(input.ownerPath, input, deps);
|
|
1361
|
+
if (evidence.status === "missing")
|
|
1362
|
+
return;
|
|
1363
|
+
quarantineSemanticRecord(input.ownerPath, input.recordKind, input.keyHash, deps, evidence.status === "invalid" ? evidence.reason : semanticStoreError("semantic_owner_stale"));
|
|
1364
|
+
}
|
|
1365
|
+
function ensureSemanticOwnerEvidence(input, owner, ownerJson, deps) {
|
|
1366
|
+
const evidence = inspectSemanticOwnerEvidence(input.ownerPath, input, deps);
|
|
1367
|
+
if (evidence.status === "valid" && evidence.bytes === ownerJson) {
|
|
1368
|
+
fsyncSemanticDirectory(input.directoryPath, semanticFs(deps));
|
|
1369
|
+
return;
|
|
1370
|
+
}
|
|
1371
|
+
if (evidence.status !== "missing") {
|
|
1372
|
+
quarantineSemanticRecord(input.ownerPath, input.recordKind, input.keyHash, deps, evidence.status === "invalid"
|
|
1373
|
+
? evidence.reason
|
|
1374
|
+
: semanticStoreError("semantic_owner_evidence_mismatch"));
|
|
1375
|
+
}
|
|
1376
|
+
const publication = publishImmutableSemanticRecord(input.directoryPath, input.ownerPath, owner, deps);
|
|
1377
|
+
if (publication !== "published")
|
|
1378
|
+
throw semanticStoreError("semantic_owner_evidence_collision");
|
|
1379
|
+
}
|
|
1380
|
+
function isSqliteBusy(error) {
|
|
1381
|
+
if (!(error instanceof Error) || !("code" in error) || typeof error.code !== "string") {
|
|
1382
|
+
return false;
|
|
1383
|
+
}
|
|
1384
|
+
return error.code.startsWith("SQLITE_BUSY") || error.code.startsWith("SQLITE_LOCKED");
|
|
1385
|
+
}
|
|
1386
|
+
function isSqliteError(error) {
|
|
1387
|
+
return error instanceof Error
|
|
1388
|
+
&& "code" in error
|
|
1389
|
+
&& typeof error.code === "string"
|
|
1390
|
+
&& error.code.startsWith("SQLITE_");
|
|
1391
|
+
}
|
|
1392
|
+
function readSemanticRecord(finalPath, recordKind, keyHash, validate, deps) {
|
|
1393
|
+
const initial = inspectSemanticRecord(finalPath, keyHash, validate, deps);
|
|
1394
|
+
if (initial.status === "missing")
|
|
1395
|
+
return null;
|
|
1396
|
+
if (initial.status === "valid")
|
|
1397
|
+
return initial.value;
|
|
1398
|
+
const ownershipPath = path.join(path.dirname(path.dirname(finalPath)), "ownership.sqlite");
|
|
1399
|
+
const startedAt = semanticNow(deps).getTime();
|
|
1400
|
+
while (true) {
|
|
1401
|
+
const result = withExclusiveSemanticOwnership(ownershipPath, deps, () => {
|
|
1402
|
+
const current = inspectSemanticRecord(finalPath, keyHash, validate, deps);
|
|
1403
|
+
if (current.status === "missing")
|
|
1404
|
+
return null;
|
|
1405
|
+
if (current.status === "valid")
|
|
1406
|
+
return current.value;
|
|
1407
|
+
quarantineSemanticRecord(finalPath, recordKind, keyHash, deps, current.reason);
|
|
1408
|
+
return null;
|
|
1409
|
+
});
|
|
1410
|
+
if (result.status === "completed")
|
|
1411
|
+
return result.value;
|
|
1412
|
+
if (semanticNow(deps).getTime() - startedAt >= SEMANTIC_CLAIM_TIMEOUT_MS) {
|
|
1413
|
+
throw semanticStoreError("semantic_ownership_busy");
|
|
1414
|
+
}
|
|
1415
|
+
semanticSleepSync(SEMANTIC_CLAIM_POLL_MS, deps);
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
function inspectSemanticRecord(finalPath, keyHash, validate, deps) {
|
|
1419
|
+
const storeFs = semanticFs(deps);
|
|
1420
|
+
if (!storeFs.existsSync(finalPath))
|
|
1421
|
+
return { status: "missing" };
|
|
1422
|
+
let bytes;
|
|
1423
|
+
try {
|
|
1424
|
+
bytes = storeFs.readFileSync(finalPath, "utf8");
|
|
1425
|
+
}
|
|
1426
|
+
catch (error) {
|
|
1427
|
+
emitSemanticStoreError("unknown", keyHash, "semantic_record_read_failed", error);
|
|
1428
|
+
throw semanticStoreError("semantic_record_read_failed", error);
|
|
1429
|
+
}
|
|
1430
|
+
let value;
|
|
1431
|
+
try {
|
|
1432
|
+
value = JSON.parse(bytes);
|
|
1433
|
+
}
|
|
1434
|
+
catch (error) {
|
|
1435
|
+
return { status: "invalid", reason: error };
|
|
1436
|
+
}
|
|
1437
|
+
if (!validate(value)) {
|
|
1438
|
+
return {
|
|
1439
|
+
status: "invalid",
|
|
1440
|
+
reason: semanticStoreError("semantic_record_invalid"),
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
return { status: "valid", value };
|
|
1444
|
+
}
|
|
1445
|
+
function quarantineSemanticRecord(finalPath, recordKind, keyHash, deps, reason) {
|
|
1446
|
+
const storeFs = semanticFs(deps);
|
|
1447
|
+
const paths = semanticQuarantinePaths(finalPath, recordKind, deps);
|
|
1448
|
+
try {
|
|
1449
|
+
storeFs.mkdirSync(paths.directoryPath, { recursive: true });
|
|
1450
|
+
const sourceStat = storeFs.lstatSync(finalPath);
|
|
1451
|
+
if (sourceStat.isSymbolicLink()) {
|
|
1452
|
+
storeFs.symlinkSync(storeFs.readlinkSync(finalPath), paths.quarantinePath);
|
|
1453
|
+
}
|
|
1454
|
+
else {
|
|
1455
|
+
storeFs.linkSync(finalPath, paths.quarantinePath);
|
|
1456
|
+
}
|
|
1457
|
+
fsyncSemanticDirectory(paths.directoryPath, storeFs);
|
|
1458
|
+
storeFs.unlinkSync(finalPath);
|
|
1459
|
+
fsyncSemanticDirectory(path.dirname(finalPath), storeFs);
|
|
1460
|
+
}
|
|
1461
|
+
catch (error) {
|
|
1462
|
+
emitSemanticStoreError("unknown", keyHash, "semantic_quarantine_failed", error);
|
|
1463
|
+
throw semanticStoreError("semantic_quarantine_failed", error);
|
|
1464
|
+
}
|
|
1465
|
+
emitSemanticStoreError("unknown", keyHash, "semantic_record_quarantined", reason);
|
|
1466
|
+
}
|
|
1467
|
+
function semanticQuarantinePaths(finalPath, recordKind, deps) {
|
|
1468
|
+
const semanticRoot = path.dirname(path.dirname(finalPath));
|
|
1469
|
+
const directoryPath = path.join(semanticRoot, "quarantine", recordKind);
|
|
1470
|
+
const observedAtUnixMs = semanticNow(deps).getTime();
|
|
1471
|
+
const uuid = semanticRandomUuid(deps);
|
|
1472
|
+
return {
|
|
1473
|
+
directoryPath,
|
|
1474
|
+
quarantinePath: path.join(directoryPath, `${path.basename(finalPath)}.${observedAtUnixMs}.${uuid}.json`),
|
|
1475
|
+
};
|
|
1476
|
+
}
|
|
1477
|
+
function hasSemanticQuarantine(quarantineRoot, recordKind, originalBase, deps) {
|
|
1478
|
+
const storeFs = semanticFs(deps);
|
|
1479
|
+
const directoryPath = path.join(quarantineRoot, recordKind);
|
|
1480
|
+
if (!storeFs.existsSync(directoryPath))
|
|
1481
|
+
return false;
|
|
1482
|
+
return storeFs.readdirSync(directoryPath).some((name) => name.startsWith(`${originalBase}.`));
|
|
1483
|
+
}
|
|
1484
|
+
function isBlueBubblesSemanticCapture(value, expectedKeyHash) {
|
|
1485
|
+
if (!isRecord(value) || !hasExactKeys(value, CAPTURE_KEYS))
|
|
1486
|
+
return false;
|
|
1487
|
+
if (value.schemaVersion !== 1
|
|
1488
|
+
|| typeof value.canonicalKey !== "string"
|
|
1489
|
+
|| value.keyHash !== expectedKeyHash
|
|
1490
|
+
|| sha256Utf8(value.canonicalKey) !== value.keyHash
|
|
1491
|
+
|| normalizeProviderNamespace(value.providerNamespace) !== value.providerNamespace
|
|
1492
|
+
|| exactIsoMilliseconds(value.capturedAt) === null
|
|
1493
|
+
|| !isRecord(value.event)
|
|
1494
|
+
|| !hasExactKeys(value.event, CAPTURE_EVENT_KEYS))
|
|
1495
|
+
return false;
|
|
1496
|
+
const event = value.event;
|
|
1497
|
+
if (event.provider !== "bluebubbles"
|
|
1498
|
+
|| typeof event.kind !== "string"
|
|
1499
|
+
|| !SEMANTIC_EVENT_KINDS.has(event.kind)
|
|
1500
|
+
|| !isNullableString(event.eventGuid)
|
|
1501
|
+
|| typeof event.fromMe !== "boolean"
|
|
1502
|
+
|| !isObservedIngressIdentity(event.actor)
|
|
1503
|
+
|| !Array.isArray(event.participants)
|
|
1504
|
+
|| !event.participants.every(isObservedIngressIdentity)
|
|
1505
|
+
|| !participantsAreSorted(event.participants)
|
|
1506
|
+
|| typeof event.sourceEventType !== "string"
|
|
1507
|
+
|| !isNullableString(event.sessionKey)
|
|
1508
|
+
|| !isNullableString(event.chatGuid)
|
|
1509
|
+
|| !isNullableString(event.chatIdentifier)
|
|
1510
|
+
|| !isNullableString(event.text)
|
|
1511
|
+
|| !isNullableString(event.textSha256)
|
|
1512
|
+
|| !isNullableString(event.targetGuid)
|
|
1513
|
+
|| !isIngressTargetAuthorship(event.targetAuthorship)
|
|
1514
|
+
|| !isIngressCanonicalAction(event.canonicalAction)
|
|
1515
|
+
|| !isIngressCanonicalValue(event.canonicalValue)
|
|
1516
|
+
|| !isNullableString(event.rawTransportValue)
|
|
1517
|
+
|| !(event.effectiveAt === null || exactIsoMilliseconds(event.effectiveAt) !== null)
|
|
1518
|
+
|| !isNullableString(event.revision)
|
|
1519
|
+
|| !isNullableString(event.contentSha256))
|
|
1520
|
+
return false;
|
|
1521
|
+
if (event.text === null) {
|
|
1522
|
+
if (event.textSha256 !== null)
|
|
1523
|
+
return false;
|
|
1524
|
+
}
|
|
1525
|
+
else if (event.textSha256 !== sha256Utf8(event.text)) {
|
|
1526
|
+
return false;
|
|
1527
|
+
}
|
|
1528
|
+
return captureIdentityMatchesEvent(value);
|
|
1529
|
+
}
|
|
1530
|
+
function isBlueBubblesSemanticObservationOrderRecord(value, expectedKeyHash, expectedCaptureHash) {
|
|
1531
|
+
return isRecord(value)
|
|
1532
|
+
&& hasExactKeys(value, OBSERVATION_ORDER_KEYS)
|
|
1533
|
+
&& value.schemaVersion === 1
|
|
1534
|
+
&& value.keyHash === expectedKeyHash
|
|
1535
|
+
&& value.captureHash === expectedCaptureHash
|
|
1536
|
+
&& exactIsoMilliseconds(value.observedAt) !== null
|
|
1537
|
+
&& isBlueBubblesObservationEpoch(value.observationEpoch)
|
|
1538
|
+
&& typeof value.observationOrdinal === "number"
|
|
1539
|
+
&& Number.isSafeInteger(value.observationOrdinal)
|
|
1540
|
+
&& value.observationOrdinal > 0;
|
|
1541
|
+
}
|
|
1542
|
+
function isBlueBubblesObservationEpoch(value) {
|
|
1543
|
+
if (typeof value !== "string")
|
|
1544
|
+
return false;
|
|
1545
|
+
const parts = value.split("/");
|
|
1546
|
+
return parts.length === 2
|
|
1547
|
+
&& exactIsoMilliseconds(parts[0]) !== null
|
|
1548
|
+
&& UUID_V4_PATTERN.test(parts[1]);
|
|
1549
|
+
}
|
|
1550
|
+
function captureIdentityMatchesEvent(value) {
|
|
1551
|
+
const event = value.event;
|
|
1552
|
+
const coordinateGeneration = event.kind === "reaction"
|
|
1553
|
+
&& event.revision === null
|
|
1554
|
+
&& event.effectiveAt === null
|
|
1555
|
+
? reactionGenerationFromCanonicalKey(value.canonicalKey)
|
|
1556
|
+
: undefined;
|
|
1557
|
+
if (coordinateGeneration === null)
|
|
1558
|
+
return false;
|
|
1559
|
+
const identity = buildBlueBubblesSemanticIdentity({
|
|
1560
|
+
providerNamespace: value.providerNamespace,
|
|
1561
|
+
kind: event.kind,
|
|
1562
|
+
eventGuid: event.eventGuid,
|
|
1563
|
+
targetGuid: event.targetGuid,
|
|
1564
|
+
actorExternalId: event.actor.externalId,
|
|
1565
|
+
canonicalValue: event.canonicalValue ?? undefined,
|
|
1566
|
+
canonicalAction: event.canonicalAction ?? undefined,
|
|
1567
|
+
revision: event.revision ?? undefined,
|
|
1568
|
+
effectiveTimestamp: event.effectiveAt === null ? undefined : Date.parse(event.effectiveAt),
|
|
1569
|
+
text: event.text ?? undefined,
|
|
1570
|
+
coordinateGeneration,
|
|
1571
|
+
});
|
|
1572
|
+
return identity !== null
|
|
1573
|
+
&& identity.canonicalKey === value.canonicalKey
|
|
1574
|
+
&& identity.keyHash === value.keyHash;
|
|
1575
|
+
}
|
|
1576
|
+
function reactionGenerationFromCanonicalKey(canonicalKey) {
|
|
1577
|
+
let tuple;
|
|
1578
|
+
try {
|
|
1579
|
+
tuple = JSON.parse(canonicalKey);
|
|
1580
|
+
}
|
|
1581
|
+
catch {
|
|
1582
|
+
return null;
|
|
1583
|
+
}
|
|
1584
|
+
if (!Array.isArray(tuple) || tuple.length !== 9 || typeof tuple[8] !== "string")
|
|
1585
|
+
return null;
|
|
1586
|
+
const match = /^generation:(0|[1-9]\d*)$/.exec(tuple[8]);
|
|
1587
|
+
if (!match)
|
|
1588
|
+
return null;
|
|
1589
|
+
const generation = Number(match[1]);
|
|
1590
|
+
return Number.isInteger(generation) && generation >= 0 ? generation : null;
|
|
1591
|
+
}
|
|
1592
|
+
function isBlueBubblesSemanticHandledRecord(value, expectedKeyHash) {
|
|
1593
|
+
return isRecord(value)
|
|
1594
|
+
&& hasExactKeys(value, HANDLED_KEYS)
|
|
1595
|
+
&& value.schemaVersion === 1
|
|
1596
|
+
&& typeof value.canonicalKey === "string"
|
|
1597
|
+
&& value.keyHash === expectedKeyHash
|
|
1598
|
+
&& sha256Utf8(value.canonicalKey) === value.keyHash
|
|
1599
|
+
&& exactIsoMilliseconds(value.handledAt) !== null
|
|
1600
|
+
&& typeof value.outcome === "string"
|
|
1601
|
+
&& SEMANTIC_HANDLED_OUTCOMES.has(value.outcome)
|
|
1602
|
+
&& (value.detailCode === null || typeof value.detailCode === "string");
|
|
1603
|
+
}
|
|
1604
|
+
function isBlueBubblesSemanticClaimRecord(value, expected) {
|
|
1605
|
+
if (!isRecord(value) || !hasExactKeys(value, CLAIM_KEYS) || !isRecord(value.owner))
|
|
1606
|
+
return false;
|
|
1607
|
+
return hasExactKeys(value.owner, CLAIM_OWNER_KEYS)
|
|
1608
|
+
&& value.schemaVersion === 1
|
|
1609
|
+
&& value.canonicalKey === expected.canonicalKey
|
|
1610
|
+
&& value.keyHash === expected.keyHash
|
|
1611
|
+
&& sha256Utf8(value.canonicalKey) === value.keyHash
|
|
1612
|
+
&& value.owner.operationId === expected.operationId
|
|
1613
|
+
&& typeof value.owner.pid === "number"
|
|
1614
|
+
&& Number.isInteger(value.owner.pid)
|
|
1615
|
+
&& value.owner.pid > 0
|
|
1616
|
+
&& typeof value.owner.bootIdentity === "string"
|
|
1617
|
+
&& value.owner.bootIdentity.length > 0
|
|
1618
|
+
&& typeof value.owner.processStartedAt === "string"
|
|
1619
|
+
&& value.owner.processStartedAt.length > 0
|
|
1620
|
+
&& exactIsoMilliseconds(value.owner.acquiredAt) !== null;
|
|
1621
|
+
}
|
|
1622
|
+
function isBlueBubblesReactionCoordinateRecord(value, coordinateKey, coordinateHash) {
|
|
1623
|
+
return isRecord(value)
|
|
1624
|
+
&& hasExactKeys(value, COORDINATE_KEYS)
|
|
1625
|
+
&& value.schemaVersion === 1
|
|
1626
|
+
&& value.coordinateKey === coordinateKey
|
|
1627
|
+
&& value.coordinateHash === coordinateHash
|
|
1628
|
+
&& sha256Utf8(coordinateKey) === coordinateHash
|
|
1629
|
+
&& typeof value.generation === "number"
|
|
1630
|
+
&& Number.isInteger(value.generation)
|
|
1631
|
+
&& value.generation >= 0
|
|
1632
|
+
&& (value.lastAction === "add" || value.lastAction === "remove")
|
|
1633
|
+
&& exactIsoMilliseconds(value.updatedAt) !== null;
|
|
1634
|
+
}
|
|
1635
|
+
function isObservedIngressIdentity(value) {
|
|
1636
|
+
return isRecord(value)
|
|
1637
|
+
&& hasExactKeys(value, OBSERVED_IDENTITY_KEYS)
|
|
1638
|
+
&& value.provider === "imessage-handle"
|
|
1639
|
+
&& typeof value.externalId === "string"
|
|
1640
|
+
&& value.externalId.length > 0
|
|
1641
|
+
&& normalizeIngressIdentifier(value.externalId) === value.externalId
|
|
1642
|
+
&& (value.displayName === null || typeof value.displayName === "string");
|
|
1643
|
+
}
|
|
1644
|
+
function participantsAreSorted(participants) {
|
|
1645
|
+
return participants.every((participant, index) => (index === 0 || participants[index - 1].externalId.localeCompare(participant.externalId) <= 0));
|
|
1646
|
+
}
|
|
1647
|
+
function isIngressTargetAuthorship(value) {
|
|
1648
|
+
return value === null || value === "agent" || value === "non_agent_unknown";
|
|
1649
|
+
}
|
|
1650
|
+
function isIngressCanonicalAction(value) {
|
|
1651
|
+
return value === null || value === "add" || value === "remove";
|
|
1652
|
+
}
|
|
1653
|
+
function isIngressCanonicalValue(value) {
|
|
1654
|
+
return value === null || (typeof value === "string"
|
|
1655
|
+
&& CANONICAL_VALUES.has(value));
|
|
1656
|
+
}
|
|
1657
|
+
function isNullableString(value) {
|
|
1658
|
+
return value === null || typeof value === "string";
|
|
1659
|
+
}
|
|
1660
|
+
function hasExactKeys(value, keys) {
|
|
1661
|
+
const actual = Object.keys(value);
|
|
1662
|
+
return actual.length === keys.length && actual.every((key, index) => key === keys[index]);
|
|
1663
|
+
}
|
|
1664
|
+
function semanticRecordPath(directoryPath, keyHash) {
|
|
1665
|
+
return path.join(directoryPath, `${validatedKeyHash(keyHash)}.json`);
|
|
1666
|
+
}
|
|
1667
|
+
function validatedKeyHash(value) {
|
|
1668
|
+
if (!SEMANTIC_KEY_HASH_PATTERN.test(value))
|
|
1669
|
+
throw semanticStoreError("semantic_key_hash_invalid");
|
|
1670
|
+
return value;
|
|
1671
|
+
}
|
|
1672
|
+
function semanticTempPath(finalPath, deps) {
|
|
1673
|
+
return path.join(path.dirname(finalPath), `.${path.basename(finalPath)}.${semanticPid(deps)}.${semanticRandomUuid(deps)}.tmp`);
|
|
1674
|
+
}
|
|
1675
|
+
function semanticFs(deps) {
|
|
1676
|
+
return deps.fs ?? fs;
|
|
1677
|
+
}
|
|
1678
|
+
function semanticNow(deps) {
|
|
1679
|
+
return (deps.now ?? (() => new Date()))();
|
|
1680
|
+
}
|
|
1681
|
+
function semanticRandomUuid(deps) {
|
|
1682
|
+
const uuid = (deps.randomUUID ?? node_crypto_1.randomUUID)();
|
|
1683
|
+
if (!UUID_V4_PATTERN.test(uuid))
|
|
1684
|
+
throw semanticStoreError("semantic_uuid_invalid");
|
|
1685
|
+
return uuid;
|
|
1686
|
+
}
|
|
1687
|
+
function semanticPid(deps) {
|
|
1688
|
+
const pid = (deps.pid ?? (() => process.pid))();
|
|
1689
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
1690
|
+
throw semanticStoreError("semantic_pid_invalid");
|
|
1691
|
+
return pid;
|
|
1692
|
+
}
|
|
1693
|
+
function semanticBootIdentity(deps) {
|
|
1694
|
+
const identity = deps.bootIdentity ? deps.bootIdentity() : defaultBootIdentity(deps);
|
|
1695
|
+
return requiredProbeIdentity(identity);
|
|
1696
|
+
}
|
|
1697
|
+
function semanticProcessStartedAt(pid, deps) {
|
|
1698
|
+
return deps.processStartedAt
|
|
1699
|
+
? deps.processStartedAt(pid)
|
|
1700
|
+
: defaultProcessStartedAt(pid, deps);
|
|
1701
|
+
}
|
|
1702
|
+
function semanticIsProcessAlive(pid, deps) {
|
|
1703
|
+
return deps.isProcessAlive ? deps.isProcessAlive(pid) : defaultIsProcessAlive(pid, deps);
|
|
1704
|
+
}
|
|
1705
|
+
async function semanticSleep(milliseconds, deps) {
|
|
1706
|
+
if (deps.sleep) {
|
|
1707
|
+
await deps.sleep(milliseconds);
|
|
1708
|
+
return;
|
|
1709
|
+
}
|
|
1710
|
+
await new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
1711
|
+
}
|
|
1712
|
+
function semanticSleepSync(milliseconds, deps) {
|
|
1713
|
+
if (deps.sleepSync) {
|
|
1714
|
+
deps.sleepSync(milliseconds);
|
|
1715
|
+
return;
|
|
1716
|
+
}
|
|
1717
|
+
const waitCell = new Int32Array(new SharedArrayBuffer(4));
|
|
1718
|
+
Atomics.wait(waitCell, 0, 0, milliseconds);
|
|
1719
|
+
}
|
|
1720
|
+
function requiredProbeIdentity(value) {
|
|
1721
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
1722
|
+
throw semanticStoreError("semantic_owner_liveness_failed");
|
|
1723
|
+
}
|
|
1724
|
+
return value;
|
|
1725
|
+
}
|
|
1726
|
+
function defaultBootIdentity(deps) {
|
|
1727
|
+
const platform = deps.platform ?? process.platform;
|
|
1728
|
+
const storeFs = semanticFs(deps);
|
|
1729
|
+
const run = deps.execFileSync ?? node_child_process_1.execFileSync;
|
|
1730
|
+
if (platform === "linux") {
|
|
1731
|
+
const bootId = storeFs.readFileSync("/proc/sys/kernel/random/boot_id", "utf8").trim();
|
|
1732
|
+
return requiredProbeIdentity(`linux:${bootId}`);
|
|
1733
|
+
}
|
|
1734
|
+
if (platform === "darwin" || platform === "freebsd") {
|
|
1735
|
+
const output = run("/usr/sbin/sysctl", ["-n", "kern.boottime"], {
|
|
1736
|
+
encoding: "utf8",
|
|
1737
|
+
}).trim();
|
|
1738
|
+
return requiredProbeIdentity(`${platform}:${output}`);
|
|
1739
|
+
}
|
|
1740
|
+
if (platform === "win32") {
|
|
1741
|
+
const output = run("powershell.exe", [
|
|
1742
|
+
"-NoProfile",
|
|
1743
|
+
"-Command",
|
|
1744
|
+
"(Get-CimInstance Win32_OperatingSystem).LastBootUpTime.ToUniversalTime().Ticks",
|
|
1745
|
+
], { encoding: "utf8" }).trim();
|
|
1746
|
+
return requiredProbeIdentity(`win32:${output}`);
|
|
1747
|
+
}
|
|
1748
|
+
const uptime = deps.uptime ?? os.uptime;
|
|
1749
|
+
const bootEpochSeconds = Math.round((semanticNow(deps).getTime() - (uptime() * 1_000)) / 1_000);
|
|
1750
|
+
return requiredProbeIdentity(`${platform}:${bootEpochSeconds}`);
|
|
1751
|
+
}
|
|
1752
|
+
function defaultProcessStartedAt(pid, deps) {
|
|
1753
|
+
const platform = deps.platform ?? process.platform;
|
|
1754
|
+
const storeFs = semanticFs(deps);
|
|
1755
|
+
const run = deps.execFileSync ?? node_child_process_1.execFileSync;
|
|
1756
|
+
if (platform === "linux") {
|
|
1757
|
+
try {
|
|
1758
|
+
const stat = storeFs.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
1759
|
+
const closeParen = stat.lastIndexOf(")");
|
|
1760
|
+
if (closeParen < 0)
|
|
1761
|
+
return null;
|
|
1762
|
+
const fields = stat.slice(closeParen + 2).trim().split(/\s+/);
|
|
1763
|
+
return fields[19] ? `linux:${fields[19]}` : null;
|
|
1764
|
+
}
|
|
1765
|
+
catch (error) {
|
|
1766
|
+
if (isNodeError(error, "ENOENT") || isNodeError(error, "ESRCH"))
|
|
1767
|
+
return null;
|
|
1768
|
+
throw error;
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
if (platform === "win32") {
|
|
1772
|
+
try {
|
|
1773
|
+
const output = run("powershell.exe", ["-NoProfile", "-Command", `(Get-Process -Id ${pid}).StartTime.ToUniversalTime().Ticks`], { encoding: "utf8" }).trim();
|
|
1774
|
+
return output ? `win32:${output}` : null;
|
|
1775
|
+
}
|
|
1776
|
+
catch {
|
|
1777
|
+
return null;
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
try {
|
|
1781
|
+
const output = run("/bin/ps", ["-o", "lstart=", "-p", String(pid)], {
|
|
1782
|
+
encoding: "utf8",
|
|
1783
|
+
}).trim();
|
|
1784
|
+
return output ? `${platform}:${output}` : null;
|
|
1785
|
+
}
|
|
1786
|
+
catch {
|
|
1787
|
+
return null;
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
function defaultIsProcessAlive(pid, deps) {
|
|
1791
|
+
const kill = deps.kill ?? process.kill;
|
|
1792
|
+
try {
|
|
1793
|
+
kill(pid, 0);
|
|
1794
|
+
return true;
|
|
1795
|
+
}
|
|
1796
|
+
catch (error) {
|
|
1797
|
+
if (isNodeError(error, "ESRCH"))
|
|
1798
|
+
return false;
|
|
1799
|
+
if (isNodeError(error, "EPERM"))
|
|
1800
|
+
return true;
|
|
1801
|
+
throw error;
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
function nextOwnerAcquiredAt(ownerPath, deps) {
|
|
1805
|
+
const observed = semanticNow(deps).getTime();
|
|
1806
|
+
const previous = lastOwnerAcquisitionMs.get(ownerPath);
|
|
1807
|
+
const next = previous === undefined ? observed : Math.max(observed, previous + 1);
|
|
1808
|
+
lastOwnerAcquisitionMs.set(ownerPath, next);
|
|
1809
|
+
return new Date(next).toISOString();
|
|
1810
|
+
}
|
|
1811
|
+
function fsyncSemanticDirectory(directoryPath, storeFs) {
|
|
1812
|
+
const directoryFd = storeFs.openSync(directoryPath, "r");
|
|
1813
|
+
try {
|
|
1814
|
+
storeFs.fsyncSync(directoryFd);
|
|
1815
|
+
}
|
|
1816
|
+
finally {
|
|
1817
|
+
storeFs.closeSync(directoryFd);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
function semanticStoreError(code, cause) {
|
|
1821
|
+
return cause === undefined ? new Error(code) : new Error(code, { cause });
|
|
1822
|
+
}
|
|
1823
|
+
function emitCaptureError(agentName, keyHash, error) {
|
|
1824
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1825
|
+
level: "error",
|
|
1826
|
+
component: "senses",
|
|
1827
|
+
event: "bluebubbles_semantic_capture_error",
|
|
1828
|
+
message: "failed to publish bluebubbles semantic capture",
|
|
1829
|
+
meta: { agentName, keyHash, reason: errorReason(error) },
|
|
1830
|
+
});
|
|
1831
|
+
}
|
|
1832
|
+
function emitSemanticStoreError(agentName, keyHash, code, error) {
|
|
1833
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1834
|
+
level: "error",
|
|
1835
|
+
component: "senses",
|
|
1836
|
+
event: "bluebubbles_semantic_store_error",
|
|
1837
|
+
message: "bluebubbles semantic store operation failed",
|
|
1838
|
+
meta: { agentName, keyHash, code, reason: errorReason(error) },
|
|
1839
|
+
});
|
|
1840
|
+
}
|
|
1841
|
+
function errorReason(error) {
|
|
1842
|
+
return error instanceof Error ? error.message : String(error);
|
|
1843
|
+
}
|
|
1844
|
+
function createCutover(deps) {
|
|
1845
|
+
const providerNamespace = normalizeProviderNamespace((deps.randomUUID ?? node_crypto_1.randomUUID)());
|
|
1846
|
+
if (!providerNamespace)
|
|
1847
|
+
throw new Error("semantic_provider_namespace_invalid");
|
|
1848
|
+
const effectiveAt = (deps.now ?? (() => new Date()))().toISOString();
|
|
1849
|
+
if (!exactIsoMilliseconds(effectiveAt))
|
|
1850
|
+
throw new Error("semantic_cutover_timestamp_invalid");
|
|
1851
|
+
return { schemaVersion: 1, providerNamespace, effectiveAt };
|
|
1852
|
+
}
|
|
1853
|
+
function isBlueBubblesSemanticCutover(value) {
|
|
1854
|
+
if (!isRecord(value))
|
|
1855
|
+
return false;
|
|
1856
|
+
if (JSON.stringify(Object.keys(value)) !== JSON.stringify(CUTOVER_KEYS))
|
|
1857
|
+
return false;
|
|
1858
|
+
return value.schemaVersion === 1
|
|
1859
|
+
&& normalizeProviderNamespace(value.providerNamespace) === value.providerNamespace
|
|
1860
|
+
&& exactIsoMilliseconds(value.effectiveAt) !== null;
|
|
1861
|
+
}
|
|
1862
|
+
function identityFromKey(tuple, handleable, discriminator = null, coordinateKey = null, coordinateHash = null) {
|
|
1863
|
+
const canonicalKey = JSON.stringify(tuple);
|
|
1864
|
+
return {
|
|
1865
|
+
canonicalKey,
|
|
1866
|
+
keyHash: sha256Utf8(canonicalKey),
|
|
1867
|
+
handleable,
|
|
1868
|
+
discriminator,
|
|
1869
|
+
coordinateKey,
|
|
1870
|
+
coordinateHash,
|
|
1871
|
+
};
|
|
1872
|
+
}
|
|
1873
|
+
function mutationDiscriminator(input) {
|
|
1874
|
+
const revision = normalizedRevision(input.revision);
|
|
1875
|
+
if (revision)
|
|
1876
|
+
return `revision:${revision}`;
|
|
1877
|
+
return effectiveTimestampDiscriminator(input.effectiveTimestamp);
|
|
1878
|
+
}
|
|
1879
|
+
function effectiveTimestampDiscriminator(value) {
|
|
1880
|
+
const effectiveAt = finiteTimestampIso(value);
|
|
1881
|
+
return effectiveAt ? `effectiveAt:${effectiveAt}` : null;
|
|
1882
|
+
}
|
|
1883
|
+
function generationDiscriminator(value) {
|
|
1884
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0
|
|
1885
|
+
? `generation:${value}`
|
|
1886
|
+
: null;
|
|
1887
|
+
}
|
|
1888
|
+
function finiteTimestampIso(value) {
|
|
1889
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
1890
|
+
return null;
|
|
1891
|
+
const date = new Date(value);
|
|
1892
|
+
return Number.isFinite(date.getTime()) ? date.toISOString() : null;
|
|
1893
|
+
}
|
|
1894
|
+
function normalizedRevision(value) {
|
|
1895
|
+
if (typeof value !== "string")
|
|
1896
|
+
return null;
|
|
1897
|
+
return value.trim() || null;
|
|
1898
|
+
}
|
|
1899
|
+
function requiredIdentifier(value) {
|
|
1900
|
+
if (value === null || value === undefined)
|
|
1901
|
+
return null;
|
|
1902
|
+
const normalized = normalizeIngressIdentifier(value);
|
|
1903
|
+
return normalized || null;
|
|
1904
|
+
}
|
|
1905
|
+
function normalizeProviderNamespace(value) {
|
|
1906
|
+
const normalized = requiredIdentifier(value);
|
|
1907
|
+
return normalized && UUID_V4_PATTERN.test(normalized) ? normalized : null;
|
|
1908
|
+
}
|
|
1909
|
+
function normalizeCanonicalValue(value) {
|
|
1910
|
+
return typeof value === "string"
|
|
1911
|
+
&& CANONICAL_VALUES.has(value)
|
|
1912
|
+
? value
|
|
1913
|
+
: null;
|
|
1914
|
+
}
|
|
1915
|
+
function normalizeCanonicalAction(value) {
|
|
1916
|
+
return typeof value === "string"
|
|
1917
|
+
&& CANONICAL_ACTIONS.has(value)
|
|
1918
|
+
? value
|
|
1919
|
+
: null;
|
|
1920
|
+
}
|
|
1921
|
+
function normalizeDisplayName(value) {
|
|
1922
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
1923
|
+
}
|
|
1924
|
+
function nullableString(value) {
|
|
1925
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
1926
|
+
}
|
|
1927
|
+
function exactIsoMilliseconds(value) {
|
|
1928
|
+
if (typeof value !== "string" || !ISO_MILLISECONDS_PATTERN.test(value))
|
|
1929
|
+
return null;
|
|
1930
|
+
const milliseconds = Date.parse(value);
|
|
1931
|
+
return Number.isFinite(milliseconds) && new Date(milliseconds).toISOString() === value ? value : null;
|
|
1932
|
+
}
|
|
1933
|
+
function sha256Utf8(input) {
|
|
1934
|
+
return (0, node_crypto_1.createHash)("sha256").update(input, "utf8").digest("hex");
|
|
1935
|
+
}
|
|
1936
|
+
function fsyncDirectory(directoryPath) {
|
|
1937
|
+
const directoryFd = fs.openSync(directoryPath, "r");
|
|
1938
|
+
try {
|
|
1939
|
+
fs.fsyncSync(directoryFd);
|
|
1940
|
+
}
|
|
1941
|
+
finally {
|
|
1942
|
+
fs.closeSync(directoryFd);
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
function isNodeError(error, code) {
|
|
1946
|
+
return error instanceof Error && "code" in error && error.code === code;
|
|
1947
|
+
}
|
|
1948
|
+
function isRecord(value) {
|
|
1949
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1950
|
+
}
|
|
1951
|
+
function emitLegacyRecoveryBlocked(value) {
|
|
1952
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1953
|
+
level: "warn",
|
|
1954
|
+
component: "senses",
|
|
1955
|
+
event: "senses.bluebubbles_legacy_recovery_blocked",
|
|
1956
|
+
message: "blocked legacy or actorless bluebubbles recovery record",
|
|
1957
|
+
meta: {
|
|
1958
|
+
schemaVersion: 0,
|
|
1959
|
+
actorPresent: false,
|
|
1960
|
+
recordKind: recoveryRecordKind(value),
|
|
1961
|
+
reason: "legacy_or_actorless",
|
|
1962
|
+
},
|
|
1963
|
+
});
|
|
1964
|
+
}
|
|
1965
|
+
function emitInvalidSemanticRecoveryRecord(value, actorPresent, reason) {
|
|
1966
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1967
|
+
level: "warn",
|
|
1968
|
+
component: "senses",
|
|
1969
|
+
event: "senses.bluebubbles_semantic_recovery_invalid",
|
|
1970
|
+
message: "blocked invalid v1 bluebubbles recovery record",
|
|
1971
|
+
meta: {
|
|
1972
|
+
schemaVersion: 1,
|
|
1973
|
+
actorPresent,
|
|
1974
|
+
recordKind: recoveryRecordKind(value),
|
|
1975
|
+
reason,
|
|
1976
|
+
},
|
|
1977
|
+
});
|
|
1978
|
+
}
|
|
1979
|
+
function recoveryRecordKind(value) {
|
|
1980
|
+
const record = isRecord(value) ? value : null;
|
|
1981
|
+
return typeof record?.mutationType === "string"
|
|
1982
|
+
? record.mutationType
|
|
1983
|
+
: isRecord(record?.event) && typeof record.event.kind === "string"
|
|
1984
|
+
? record.event.kind
|
|
1985
|
+
: "inbound";
|
|
1986
|
+
}
|