@jungjaehoon/mama-core 2.4.1 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -239
- package/db/migrations/030-case-first-memory-substrate.sql +13 -98
- package/db/migrations/034-add-connector-event-scope-columns.sql +9 -13
- package/db/migrations/039-add-connector-event-operator-ingest-seq.sql +9 -95
- package/db/migrations/062-refresh-connector-event-sequences.sql +9 -113
- package/db/migrations/063-refresh-legacy-connector-event-updates.sql +9 -32
- package/db/migrations/067-add-connector-event-source-entity-id.sql +9 -13
- package/db/migrations/069-create-registry-nodes.sql +52 -0
- package/db/migrations/070-record-identity.sql +35 -0
- package/db/migrations/071-service-operation-origins.sql +187 -0
- package/db/migrations/072-observation-versions.sql +29 -0
- package/db/migrations/073-registry-corrections.sql +45 -0
- package/db/migrations/074-work-graph-ref-kinds.sql +39 -0
- package/db/migrations/075-agent-judgments.sql +58 -0
- package/db/migrations/076-scoped-checkpoint-bindings.sql +15 -0
- package/db/migrations/077-legacy-record-kind.sql +6 -0
- package/db/migrations/078-source-ingest-commands.sql +18 -0
- package/db/migrations/079-twin-edge-relations.sql +43 -0
- package/db/migrations/080-restore-duration-days.sql +7 -0
- package/db/migrations/081-mailbox.sql +61 -0
- package/db/migrations/082-commitments-row-id.sql +12 -0
- package/db/migrations/083-observation-is-the-evidence.sql +49 -0
- package/db/migrations/084-scope-kind-is-not-the-cores-to-list.sql +34 -0
- package/db/migrations/085-a-migration-has-a-source.sql +37 -0
- package/db/migrations/086-the-mailbox-is-generic.sql +110 -0
- package/db/migrations/087-mailbox-payload.sql +5 -0
- package/db/migrations/088-native-input-journal.sql +14 -0
- package/db/migrations/089-native-turn-receipt-lookup.sql +3 -0
- package/db/migrations/090-model-run-native-inputs.sql +29 -0
- package/db/migrations/091-native-turn-results.sql +11 -0
- package/db/migrations/092-restore-decision-fts.sql +34 -0
- package/db/migrations/093-native-turn-primary-kind.sql +14 -0
- package/db/migrations/094-commitment-run-provenance.sql +9 -0
- package/db/migrations/095-model-run-native-input-identity.sql +39 -0
- package/db/migrations/096-drop-connector-event-index-fts.sql +9 -0
- package/db/migrations/097-native-input-json-validity.sql +37 -0
- package/db/migrations/098-workflow-memory-kind.sql +105 -0
- package/db/migrations/099-commitment-revision-graph.sql +2 -0
- package/dist/action-contracts.d.ts +212 -0
- package/dist/action-contracts.js +10 -0
- package/dist/api/catalog.d.ts +118 -0
- package/dist/api/catalog.js +1756 -0
- package/dist/api/dispatch.d.ts +76 -0
- package/dist/api/dispatch.js +372 -0
- package/dist/canonicalize.d.ts +2 -2
- package/dist/canonicalize.js +3 -3
- package/dist/client/client.d.ts +33 -0
- package/dist/client/client.js +123 -0
- package/dist/client/ipc.d.ts +81 -0
- package/dist/client/ipc.js +314 -0
- package/dist/db-adapter/base-adapter.d.ts +6 -66
- package/dist/db-adapter/base-adapter.js +6 -6
- package/dist/db-adapter/index.d.ts +5 -5
- package/dist/db-adapter/index.js +2 -5
- package/dist/db-adapter/node-sqlite-adapter.d.ts +85 -15
- package/dist/db-adapter/node-sqlite-adapter.js +1985 -570
- package/dist/db-manager.d.ts +53 -97
- package/dist/db-manager.js +121 -603
- package/dist/embedding/embedder.d.ts +132 -0
- package/dist/embedding/embedder.js +299 -0
- package/dist/identity/principal-repository.d.ts +2 -2
- package/dist/index.d.ts +43 -58
- package/dist/index.js +97 -145
- package/dist/{context-compile/channel-grant.d.ts → knowledge/access.d.ts} +12 -17
- package/dist/knowledge/access.js +663 -0
- package/dist/knowledge/case-errors.d.ts +31 -0
- package/dist/knowledge/case-errors.js +40 -0
- package/dist/{cases/search-rollup.d.ts → knowledge/case-search-rollup.d.ts} +5 -4
- package/dist/{cases/search-rollup.js → knowledge/case-search-rollup.js} +6 -8
- package/dist/{cases/store.d.ts → knowledge/case-store.d.ts} +3 -6
- package/dist/{cases/store.js → knowledge/case-store.js} +7 -42
- package/dist/{cases/timeline-range.d.ts → knowledge/case-timeline-range.d.ts} +3 -3
- package/dist/{cases/timeline-range.js → knowledge/case-timeline-range.js} +47 -25
- package/dist/{cases/types.d.ts → knowledge/case-types.d.ts} +8 -2
- package/dist/{cases/types.js → knowledge/case-types.js} +1 -1
- package/dist/knowledge/commitment-revision-migration.d.ts +4 -0
- package/dist/knowledge/commitment-revision-migration.js +50 -0
- package/dist/knowledge/commitments.d.ts +192 -0
- package/dist/knowledge/commitments.js +386 -0
- package/dist/knowledge/decision-edges.d.ts +70 -0
- package/dist/knowledge/decision-edges.js +123 -0
- package/dist/{agent-graph/types.d.ts → knowledge/graph-query.d.ts} +80 -70
- package/dist/knowledge/graph-query.js +1585 -0
- package/dist/knowledge/identity.d.ts +16 -0
- package/dist/knowledge/identity.js +22 -0
- package/dist/knowledge/index.d.ts +47 -0
- package/dist/knowledge/index.js +90 -0
- package/dist/knowledge/judgment-edge.d.ts +6 -0
- package/dist/knowledge/judgment-edge.js +22 -0
- package/dist/knowledge/judgments.d.ts +118 -0
- package/dist/knowledge/judgments.js +802 -0
- package/dist/knowledge/observations.d.ts +160 -0
- package/dist/knowledge/observations.js +449 -0
- package/dist/knowledge/question-type.d.ts +34 -0
- package/dist/knowledge/question-type.js +70 -0
- package/dist/{search → knowledge}/ranker-features.js +1 -1
- package/dist/{search → knowledge}/search-quality.d.ts +0 -1
- package/dist/knowledge/search.d.ts +38 -0
- package/dist/knowledge/search.js +91 -0
- package/dist/knowledge/source-ingest.d.ts +65 -0
- package/dist/knowledge/source-ingest.js +174 -0
- package/dist/{edges/types.d.ts → knowledge/twin-edge-types.d.ts} +31 -4
- package/dist/{edges/types.js → knowledge/twin-edge-types.js} +13 -2
- package/dist/knowledge/work-dates.d.ts +49 -0
- package/dist/knowledge/work-dates.js +120 -0
- package/dist/mama-api.d.ts +114 -778
- package/dist/mama-api.js +313 -2461
- package/dist/memory/api.d.ts +264 -24
- package/dist/memory/api.js +1633 -843
- package/dist/memory/bootstrap-builder.d.ts +2 -1
- package/dist/memory/bootstrap-builder.js +5 -5
- package/dist/memory/channel-summary-state-store.d.ts +3 -2
- package/dist/memory/channel-summary-state-store.js +53 -51
- package/dist/memory/channel-summary-store.d.ts +5 -3
- package/dist/memory/channel-summary-store.js +3 -8
- package/dist/memory/dashboard-read.d.ts +46 -0
- package/dist/memory/dashboard-read.js +94 -0
- package/dist/memory/event-store.d.ts +3 -3
- package/dist/memory/event-store.js +3 -10
- package/dist/memory/finding-store.d.ts +3 -2
- package/dist/memory/finding-store.js +2 -7
- package/dist/memory/graph-read.d.ts +52 -0
- package/dist/memory/graph-read.js +130 -0
- package/dist/memory/judgment-types.d.ts +394 -0
- package/dist/memory/judgment-types.js +16 -0
- package/dist/memory/provenance-audit.d.ts +5 -2
- package/dist/memory/provenance-audit.js +8 -14
- package/dist/memory/provenance-live.d.ts +141 -0
- package/dist/memory/provenance-live.js +409 -0
- package/dist/memory/provenance-query.d.ts +5 -4
- package/dist/memory/provenance-query.js +21 -29
- package/dist/memory/provenance-resolver.d.ts +191 -0
- package/dist/memory/provenance-resolver.js +155 -0
- package/dist/memory/provenance.d.ts +1 -11
- package/dist/memory/provenance.js +2 -33
- package/dist/memory/recall-sanitize.d.ts +55 -0
- package/dist/memory/recall-sanitize.js +144 -0
- package/dist/memory/secret-filter.d.ts +32 -0
- package/dist/memory/secret-filter.js +126 -0
- package/dist/memory/truth-store.d.ts +2 -1
- package/dist/memory/truth-store.js +1 -4
- package/dist/memory/types.d.ts +74 -24
- package/dist/memory/types.js +75 -5
- package/dist/memory/write-adapters.d.ts +98 -0
- package/dist/memory/write-adapters.js +242 -0
- package/dist/operations/owner-action-effects.d.ts +53 -0
- package/dist/operations/owner-action-effects.js +96 -0
- package/dist/provenance/source-ref.d.ts +1 -1
- package/dist/provenance/source-ref.js +0 -2
- package/dist/registry/corrections.d.ts +31 -0
- package/dist/registry/corrections.js +332 -0
- package/dist/registry/record-identity.d.ts +49 -0
- package/dist/registry/record-identity.js +133 -0
- package/dist/registry/store.d.ts +101 -0
- package/dist/registry/store.js +395 -0
- package/dist/registry/types.d.ts +28 -0
- package/dist/registry/types.js +5 -0
- package/dist/relevance-scorer.js +2 -2
- package/dist/runtime/agent-event-bus.d.ts +50 -0
- package/dist/runtime/agent-event-bus.js +101 -0
- package/dist/runtime/drivers/claude-cli-wrapper.d.ts +152 -0
- package/dist/runtime/drivers/claude-cli-wrapper.js +375 -0
- package/dist/runtime/drivers/claude-effort.d.ts +4 -0
- package/dist/runtime/drivers/claude-effort.js +16 -0
- package/dist/runtime/drivers/cli-arg-redaction.d.ts +2 -0
- package/dist/runtime/drivers/cli-arg-redaction.js +18 -0
- package/dist/runtime/drivers/cli-secret-redaction.d.ts +17 -0
- package/dist/runtime/drivers/cli-secret-redaction.js +126 -0
- package/dist/runtime/drivers/codex-app-server-process.d.ts +277 -0
- package/dist/runtime/drivers/codex-app-server-process.js +2342 -0
- package/dist/runtime/drivers/codex-auxiliary-tools.d.ts +17 -0
- package/dist/runtime/drivers/codex-auxiliary-tools.js +267 -0
- package/dist/runtime/drivers/codex-home.d.ts +20 -0
- package/dist/runtime/drivers/codex-home.js +674 -0
- package/dist/runtime/drivers/codex-thread-registry.d.ts +63 -0
- package/dist/runtime/drivers/codex-thread-registry.js +433 -0
- package/dist/runtime/drivers/persistent-cli-adapter.d.ts +160 -0
- package/dist/runtime/drivers/persistent-cli-adapter.js +396 -0
- package/dist/runtime/drivers/persistent-cli-process.d.ts +425 -0
- package/dist/runtime/drivers/persistent-cli-process.js +1529 -0
- package/dist/runtime/drivers/types.d.ts +456 -0
- package/dist/runtime/drivers/types.js +110 -0
- package/dist/runtime/mailbox.d.ts +223 -0
- package/dist/runtime/mailbox.js +582 -0
- package/dist/runtime/model-run-store.d.ts +28 -0
- package/dist/{model-runs/store.js → runtime/model-run-store.js} +81 -41
- package/dist/{model-runs/types.d.ts → runtime/model-run-types.d.ts} +33 -4
- package/dist/{model-runs/types.js → runtime/model-run-types.js} +1 -1
- package/dist/runtime/native-effect-observer.d.ts +22 -0
- package/dist/runtime/native-effect-observer.js +103 -0
- package/dist/runtime/native-input-journal.d.ts +46 -0
- package/dist/runtime/native-input-journal.js +308 -0
- package/dist/runtime/native-prompt.d.ts +118 -0
- package/dist/runtime/native-prompt.js +496 -0
- package/dist/runtime/native-session.d.ts +10 -0
- package/dist/{agent-graph/index.js → runtime/native-session.js} +9 -6
- package/dist/runtime/native-tool-trace-observer.d.ts +5 -0
- package/dist/runtime/native-tool-trace-observer.js +65 -0
- package/dist/runtime/native-turn.d.ts +272 -0
- package/dist/runtime/native-turn.js +1041 -0
- package/dist/runtime/operations.d.ts +236 -0
- package/dist/runtime/operations.js +565 -0
- package/dist/runtime/post-tool-handler.d.ts +74 -0
- package/dist/runtime/post-tool-handler.js +137 -0
- package/dist/runtime/prompt-layers.d.ts +126 -0
- package/dist/runtime/prompt-layers.js +239 -0
- package/dist/runtime/runtime-process.d.ts +102 -0
- package/dist/runtime/runtime-process.js +206 -0
- package/dist/runtime/runtime.d.ts +278 -0
- package/dist/runtime/runtime.js +563 -0
- package/dist/runtime/session-pool.d.ts +176 -0
- package/dist/runtime/session-pool.js +369 -0
- package/dist/runtime/stimulus-payload.d.ts +5 -0
- package/dist/runtime/stimulus-payload.js +50 -0
- package/dist/runtime/subagent-bridge.d.ts +24 -0
- package/dist/{agent-graph/types.js → runtime/subagent-bridge.js} +1 -1
- package/dist/runtime/text-completion.d.ts +22 -0
- package/dist/{agent-situation/types.js → runtime/text-completion.js} +1 -1
- package/dist/runtime/token-estimator.d.ts +25 -0
- package/dist/runtime/token-estimator.js +75 -0
- package/dist/runtime/tool-trace-store.d.ts +22 -0
- package/dist/{model-runs → runtime}/tool-trace-store.js +46 -27
- package/dist/runtime/trace-summary.d.ts +5 -0
- package/dist/runtime/trace-summary.js +74 -0
- package/dist/runtime/turn-text.d.ts +23 -0
- package/dist/runtime/turn-text.js +41 -0
- package/dist/storage/database.d.ts +67 -0
- package/dist/storage/database.js +158 -0
- package/package.json +20 -34
- package/dist/agent-graph/alias-write.d.ts +0 -3
- package/dist/agent-graph/alias-write.js +0 -234
- package/dist/agent-graph/entity-resolve.d.ts +0 -3
- package/dist/agent-graph/entity-resolve.js +0 -271
- package/dist/agent-graph/errors.d.ts +0 -4
- package/dist/agent-graph/errors.js +0 -11
- package/dist/agent-graph/graph-query.d.ts +0 -5
- package/dist/agent-graph/graph-query.js +0 -360
- package/dist/agent-graph/index.d.ts +0 -6
- package/dist/agent-situation/builder.d.ts +0 -8
- package/dist/agent-situation/builder.js +0 -239
- package/dist/agent-situation/cache-key.d.ts +0 -4
- package/dist/agent-situation/cache-key.js +0 -64
- package/dist/agent-situation/index.d.ts +0 -7
- package/dist/agent-situation/index.js +0 -28
- package/dist/agent-situation/packet-store.d.ts +0 -27
- package/dist/agent-situation/packet-store.js +0 -228
- package/dist/agent-situation/ranking-policy.d.ts +0 -18
- package/dist/agent-situation/ranking-policy.js +0 -75
- package/dist/agent-situation/source-readers.d.ts +0 -52
- package/dist/agent-situation/source-readers.js +0 -300
- package/dist/agent-situation/types.d.ts +0 -195
- package/dist/cases/wiki-page-index.d.ts +0 -37
- package/dist/cases/wiki-page-index.js +0 -201
- package/dist/config-loader.d.ts +0 -68
- package/dist/config-loader.js +0 -239
- package/dist/connectors/event-index.d.ts +0 -24
- package/dist/connectors/event-index.js +0 -245
- package/dist/connectors/raw-query.d.ts +0 -40
- package/dist/connectors/raw-query.js +0 -412
- package/dist/connectors/types.d.ts +0 -117
- package/dist/connectors/types.js +0 -3
- package/dist/context-compile/boundary-defaults.d.ts +0 -12
- package/dist/context-compile/boundary-defaults.js +0 -105
- package/dist/context-compile/channel-grant.js +0 -66
- package/dist/context-compile/compiler-policy.d.ts +0 -39
- package/dist/context-compile/compiler-policy.js +0 -147
- package/dist/context-compile/compiler.d.ts +0 -18
- package/dist/context-compile/compiler.js +0 -404
- package/dist/context-compile/index.d.ts +0 -10
- package/dist/context-compile/index.js +0 -26
- package/dist/context-compile/packet-store.d.ts +0 -8
- package/dist/context-compile/packet-store.js +0 -239
- package/dist/context-compile/ref.d.ts +0 -7
- package/dist/context-compile/ref.js +0 -102
- package/dist/context-compile/source-readers.d.ts +0 -70
- package/dist/context-compile/source-readers.js +0 -840
- package/dist/context-compile/types.d.ts +0 -155
- package/dist/context-compile/types.js +0 -11
- package/dist/context-compile/visibility.d.ts +0 -21
- package/dist/context-compile/visibility.js +0 -224
- package/dist/decision-tracker.d.ts +0 -206
- package/dist/decision-tracker.js +0 -481
- package/dist/edges/ref-validation.d.ts +0 -7
- package/dist/edges/ref-validation.js +0 -321
- package/dist/edges/store.d.ts +0 -8
- package/dist/edges/store.js +0 -129
- package/dist/embedding-client.d.ts +0 -28
- package/dist/embedding-client.js +0 -53
- package/dist/embedding-server/index.d.ts +0 -67
- package/dist/embedding-server/index.js +0 -405
- package/dist/embedding-server/mobile/auth.d.ts +0 -44
- package/dist/embedding-server/mobile/auth.js +0 -150
- package/dist/embedding-server/mobile/daemon.d.ts +0 -129
- package/dist/embedding-server/mobile/daemon.js +0 -313
- package/dist/embedding-server/mobile/output-parser.d.ts +0 -115
- package/dist/embedding-server/mobile/output-parser.js +0 -241
- package/dist/embedding-server/mobile/session-api.d.ts +0 -57
- package/dist/embedding-server/mobile/session-api.js +0 -261
- package/dist/embedding-server/mobile/session-manager.d.ts +0 -138
- package/dist/embedding-server/mobile/session-manager.js +0 -378
- package/dist/embedding-server/mobile/websocket-handler.d.ts +0 -137
- package/dist/embedding-server/mobile/websocket-handler.js +0 -486
- package/dist/embeddings.d.ts +0 -68
- package/dist/embeddings.js +0 -244
- package/dist/entities/audit-metrics.d.ts +0 -68
- package/dist/entities/audit-metrics.js +0 -124
- package/dist/entities/candidate-generator.d.ts +0 -8
- package/dist/entities/candidate-generator.js +0 -24
- package/dist/entities/entity-linked-decision-counts.d.ts +0 -2
- package/dist/entities/entity-linked-decision-counts.js +0 -29
- package/dist/entities/entity-list.d.ts +0 -22
- package/dist/entities/entity-list.js +0 -3
- package/dist/entities/entity-orphan-list.d.ts +0 -17
- package/dist/entities/entity-orphan-list.js +0 -3
- package/dist/entities/entity-search.d.ts +0 -20
- package/dist/entities/entity-search.js +0 -3
- package/dist/entities/errors.d.ts +0 -55
- package/dist/entities/errors.js +0 -100
- package/dist/entities/lineage-store.d.ts +0 -28
- package/dist/entities/lineage-store.js +0 -204
- package/dist/entities/normalization.d.ts +0 -14
- package/dist/entities/normalization.js +0 -63
- package/dist/entities/policy-store.d.ts +0 -8
- package/dist/entities/policy-store.js +0 -75
- package/dist/entities/policy-types.d.ts +0 -72
- package/dist/entities/policy-types.js +0 -59
- package/dist/entities/projection.d.ts +0 -8
- package/dist/entities/projection.js +0 -82
- package/dist/entities/provenance-query.d.ts +0 -32
- package/dist/entities/provenance-query.js +0 -3
- package/dist/entities/read-identity.d.ts +0 -31
- package/dist/entities/read-identity.js +0 -264
- package/dist/entities/recall-bridge.d.ts +0 -5
- package/dist/entities/recall-bridge.js +0 -171
- package/dist/entities/resolution-engine.d.ts +0 -8
- package/dist/entities/resolution-engine.js +0 -3
- package/dist/entities/rollback-preview.d.ts +0 -35
- package/dist/entities/rollback-preview.js +0 -3
- package/dist/entities/source-locator.d.ts +0 -3
- package/dist/entities/source-locator.js +0 -32
- package/dist/entities/store.d.ts +0 -69
- package/dist/entities/store.js +0 -544
- package/dist/entities/types.d.ts +0 -166
- package/dist/entities/types.js +0 -30
- package/dist/memory/extraction-prompt.d.ts +0 -4
- package/dist/memory/extraction-prompt.js +0 -89
- package/dist/memory/scope-backfill.d.ts +0 -14
- package/dist/memory/scope-backfill.js +0 -3
- package/dist/memory/scope-store.d.ts +0 -4
- package/dist/memory/scope-store.js +0 -9
- package/dist/memory-store.d.ts +0 -87
- package/dist/memory-store.js +0 -78
- package/dist/model-runs/store.d.ts +0 -13
- package/dist/model-runs/tool-trace-store.d.ts +0 -8
- package/dist/notification-manager.d.ts +0 -7
- package/dist/notification-manager.js +0 -12
- package/dist/outcome-tracker.d.ts +0 -151
- package/dist/outcome-tracker.js +0 -271
- package/dist/query-intent.d.ts +0 -23
- package/dist/query-intent.js +0 -114
- package/dist/runtime/trusted-provenance.d.ts +0 -3
- package/dist/runtime/trusted-provenance.js +0 -6
- package/dist/search/question-type.d.ts +0 -5
- package/dist/search/question-type.js +0 -41
- package/dist/test-utils.d.ts +0 -80
- package/dist/test-utils.js +0 -218
- package/dist/tier-validator.d.ts +0 -43
- package/dist/tier-validator.js +0 -159
- package/dist/time-formatter.d.ts +0 -25
- package/dist/time-formatter.js +0 -93
- /package/dist/{search → knowledge}/feedback-store.d.ts +0 -0
- /package/dist/{search → knowledge}/feedback-store.js +0 -0
- /package/dist/{search → knowledge}/ranker-features.d.ts +0 -0
- /package/dist/{search → knowledge}/ranker-rescore.d.ts +0 -0
- /package/dist/{search → knowledge}/ranker-rescore.js +0 -0
- /package/dist/{search → knowledge}/ranker-trainer.d.ts +0 -0
- /package/dist/{search → knowledge}/ranker-trainer.js +0 -0
- /package/dist/{search → knowledge}/search-quality.js +0 -0
|
@@ -0,0 +1,663 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TwinRefNotVisibleError = void 0;
|
|
4
|
+
exports.visibleTwinRefKeys = visibleTwinRefKeys;
|
|
5
|
+
exports.visibleTwinRefKeysRecursive = visibleTwinRefKeysRecursive;
|
|
6
|
+
exports.assertTwinRefsVisible = assertTwinRefsVisible;
|
|
7
|
+
exports.listVisibleTwinEdgesForRefs = listVisibleTwinEdgesForRefs;
|
|
8
|
+
exports.isChannelGranted = isChannelGranted;
|
|
9
|
+
exports.channelGrantClause = channelGrantClause;
|
|
10
|
+
const observations_js_1 = require("../knowledge/observations.js");
|
|
11
|
+
const judgments_js_1 = require("./judgments.js");
|
|
12
|
+
const TABLE_COLUMN_PRAGMAS = {
|
|
13
|
+
decisions: 'PRAGMA table_info(decisions)',
|
|
14
|
+
case_truth: 'PRAGMA table_info(case_truth)',
|
|
15
|
+
};
|
|
16
|
+
const tableColumnCache = new WeakMap();
|
|
17
|
+
// Keep memory-truth quarantine semantics excluded for legacy/backcompat rows even though the
|
|
18
|
+
// decisions.status terminal states: 'superseded' is history a caller holding
|
|
19
|
+
// scope may read under includeReplaced; the rest are holds excluded always.
|
|
20
|
+
const RETIRED_MEMORY_STATUSES = new Set(['quarantined', 'contradicted', 'stale']);
|
|
21
|
+
class TwinRefNotVisibleError extends Error {
|
|
22
|
+
constructor(ref) {
|
|
23
|
+
super(`Twin ref is not visible to requested visibility: ${ref.kind}:${ref.id}`);
|
|
24
|
+
this.name = 'TwinRefNotVisibleError';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.TwinRefNotVisibleError = TwinRefNotVisibleError;
|
|
28
|
+
function scopeKey(scope) {
|
|
29
|
+
return `${scope.kind}\0${scope.id}`;
|
|
30
|
+
}
|
|
31
|
+
function hasScopes(scopes) {
|
|
32
|
+
return Array.isArray(scopes) && scopes.length > 0;
|
|
33
|
+
}
|
|
34
|
+
function hasProjectRefs(projectRefs) {
|
|
35
|
+
return Array.isArray(projectRefs) && projectRefs.length > 0;
|
|
36
|
+
}
|
|
37
|
+
function asOfMs(visibility) {
|
|
38
|
+
return typeof visibility.asOfMs === 'number' ? visibility.asOfMs : null;
|
|
39
|
+
}
|
|
40
|
+
function startMs(visibility) {
|
|
41
|
+
return typeof visibility.startMs === 'number' ? visibility.startMs : null;
|
|
42
|
+
}
|
|
43
|
+
function isWithinVisibilityTime(value, visibility) {
|
|
44
|
+
const start = startMs(visibility);
|
|
45
|
+
const asOf = asOfMs(visibility);
|
|
46
|
+
if (start === null && asOf === null) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
return (typeof value === 'number' &&
|
|
50
|
+
(start === null || value >= start) &&
|
|
51
|
+
(asOf === null || value <= asOf));
|
|
52
|
+
}
|
|
53
|
+
function parseTimestamp(value) {
|
|
54
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
55
|
+
return Math.floor(value);
|
|
56
|
+
}
|
|
57
|
+
if (typeof value === 'string') {
|
|
58
|
+
const trimmed = value.trim();
|
|
59
|
+
if (trimmed.length === 0) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const numeric = Number(trimmed);
|
|
63
|
+
if (Number.isFinite(numeric)) {
|
|
64
|
+
return Math.floor(numeric);
|
|
65
|
+
}
|
|
66
|
+
const parsed = Date.parse(trimmed);
|
|
67
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
function tableColumns(adapter, table) {
|
|
72
|
+
const pragma = TABLE_COLUMN_PRAGMAS[table];
|
|
73
|
+
if (!pragma) {
|
|
74
|
+
throw new Error(`Unsupported table for column introspection: ${table}`);
|
|
75
|
+
}
|
|
76
|
+
let adapterCache = tableColumnCache.get(adapter);
|
|
77
|
+
if (!adapterCache) {
|
|
78
|
+
adapterCache = new Map();
|
|
79
|
+
tableColumnCache.set(adapter, adapterCache);
|
|
80
|
+
}
|
|
81
|
+
const cached = adapterCache.get(table);
|
|
82
|
+
if (cached) {
|
|
83
|
+
return cached;
|
|
84
|
+
}
|
|
85
|
+
const rows = adapter.prepare(pragma).all();
|
|
86
|
+
const columns = new Set(rows.map((row) => row.name));
|
|
87
|
+
adapterCache.set(table, columns);
|
|
88
|
+
return columns;
|
|
89
|
+
}
|
|
90
|
+
function isMemoryVisible(adapter, id, visibility) {
|
|
91
|
+
const columns = tableColumns(adapter, 'decisions');
|
|
92
|
+
const statusSelect = columns.has('status') ? 'status' : 'NULL AS status';
|
|
93
|
+
const supersededBySelect = columns.has('superseded_by')
|
|
94
|
+
? 'superseded_by'
|
|
95
|
+
: 'NULL AS superseded_by';
|
|
96
|
+
const row = adapter
|
|
97
|
+
.prepare(`
|
|
98
|
+
SELECT created_at, event_datetime, ${statusSelect}, ${supersededBySelect}
|
|
99
|
+
FROM decisions
|
|
100
|
+
WHERE id = ?
|
|
101
|
+
LIMIT 1
|
|
102
|
+
`)
|
|
103
|
+
.get(id);
|
|
104
|
+
if (!row) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
const status = typeof row.status === 'string' ? row.status : 'active';
|
|
108
|
+
const replaced = status === 'superseded' ||
|
|
109
|
+
(typeof row.superseded_by === 'string' && row.superseded_by.length > 0);
|
|
110
|
+
if (RETIRED_MEMORY_STATUSES.has(status) || (replaced && !visibility.includeReplaced)) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
if (!isWithinVisibilityTime(row.event_datetime ?? row.created_at, visibility)) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
const scopes = visibility.scopes;
|
|
117
|
+
if (!hasScopes(scopes)) {
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
const scopeClauses = scopes.map(() => '(ms.kind = ? AND ms.external_id = ?)').join(' OR ');
|
|
121
|
+
const params = scopes.flatMap((scope) => [scope.kind, scope.id]);
|
|
122
|
+
const bindingRow = adapter
|
|
123
|
+
.prepare(`
|
|
124
|
+
SELECT 1 AS ok
|
|
125
|
+
FROM memory_scope_bindings msb
|
|
126
|
+
JOIN memory_scopes ms ON ms.id = msb.scope_id
|
|
127
|
+
WHERE msb.memory_id = ?
|
|
128
|
+
AND (${scopeClauses})
|
|
129
|
+
LIMIT 1
|
|
130
|
+
`)
|
|
131
|
+
.get(id, ...params);
|
|
132
|
+
if (bindingRow?.ok) {
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
if (columns.has('memory_scope_kind') && columns.has('memory_scope_id')) {
|
|
136
|
+
const row = adapter
|
|
137
|
+
.prepare(`
|
|
138
|
+
SELECT 1 AS ok
|
|
139
|
+
FROM decisions
|
|
140
|
+
WHERE id = ?
|
|
141
|
+
AND (${scopes.map(() => '(memory_scope_kind = ? AND memory_scope_id = ?)').join(' OR ')})
|
|
142
|
+
LIMIT 1
|
|
143
|
+
`)
|
|
144
|
+
.get(id, ...params);
|
|
145
|
+
return Boolean(row?.ok);
|
|
146
|
+
}
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
function parseCaseScopeRefs(scopeRefs) {
|
|
150
|
+
if (typeof scopeRefs !== 'string' || scopeRefs.length === 0) {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
const parsed = JSON.parse(scopeRefs);
|
|
155
|
+
if (!Array.isArray(parsed)) {
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
158
|
+
return parsed.filter((item) => item !== null &&
|
|
159
|
+
typeof item === 'object' &&
|
|
160
|
+
typeof item.kind === 'string' &&
|
|
161
|
+
typeof item.id === 'string');
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function isCaseVisible(adapter, id, visibility) {
|
|
168
|
+
const row = adapter.prepare('SELECT * FROM case_truth WHERE case_id = ? LIMIT 1').get(id);
|
|
169
|
+
if (!row) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
const timestamp = parseTimestamp(row.last_activity_at) ?? parseTimestamp(row.updated_at);
|
|
173
|
+
if (!isWithinVisibilityTime(timestamp, visibility)) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
const scopes = visibility.scopes;
|
|
177
|
+
if (!hasScopes(scopes)) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
const requested = new Set(scopes.map(scopeKey));
|
|
181
|
+
const scopeRefs = parseCaseScopeRefs(row.scope_refs);
|
|
182
|
+
if (scopeRefs.some((scope) => requested.has(scopeKey(scope)))) {
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
const columns = tableColumns(adapter, 'case_truth');
|
|
186
|
+
if (columns.has('memory_scope_kind') && columns.has('memory_scope_id')) {
|
|
187
|
+
return scopes.some((scope) => row.memory_scope_kind === scope.kind && row.memory_scope_id === scope.id);
|
|
188
|
+
}
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
/** Current rows use immutable observation capture time; valid legacy rows use source time. */
|
|
192
|
+
function isRawWithinTime(row, visibility) {
|
|
193
|
+
const maxSourceMs = visibility.maxSourceMs ?? null;
|
|
194
|
+
if (maxSourceMs !== null) {
|
|
195
|
+
if (!Number.isSafeInteger(maxSourceMs) || maxSourceMs < 0) {
|
|
196
|
+
throw new Error('Raw source ceiling must be a nonnegative epoch-millisecond integer');
|
|
197
|
+
}
|
|
198
|
+
const sourceAt = parseTimestamp(row.source_at);
|
|
199
|
+
if (sourceAt === null || sourceAt > maxSourceMs)
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
const rawTs = row.observation_observed_at ?? row.event_datetime ?? row.source_timestamp_ms;
|
|
203
|
+
if (rawTs === null || rawTs === undefined || (typeof rawTs === 'string' && rawTs.trim() === '')) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
const eventTsMs = parseTimestamp(rawTs);
|
|
207
|
+
return eventTsMs !== null && eventTsMs >= 0 && isWithinVisibilityTime(eventTsMs, visibility);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* A `raw` ref resolves against observations, the same as an `observation` ref.
|
|
211
|
+
*
|
|
212
|
+
* There is one evidence space in the core. `raw` was the spelling from when the
|
|
213
|
+
* connector event index WAS the substrate -- the schema still shows it, allowing
|
|
214
|
+
* `raw` as an edge object and never as a subject, while `observation` is allowed
|
|
215
|
+
* on both sides. A ref that does not name an observation does not name evidence
|
|
216
|
+
* this core can vouch for, and is not visible.
|
|
217
|
+
*/
|
|
218
|
+
function isRawVisible(adapter, id, visibility) {
|
|
219
|
+
const row = adapter
|
|
220
|
+
.prepare(`SELECT source, channel, project_id, tenant_id, memory_scope_kind, memory_scope_id, source_at,
|
|
221
|
+
observed_at AS observation_observed_at
|
|
222
|
+
FROM observation_versions
|
|
223
|
+
WHERE observation_id = ?
|
|
224
|
+
LIMIT 1`)
|
|
225
|
+
.get(id);
|
|
226
|
+
return row ? isRawRowVisible(row, visibility) : false;
|
|
227
|
+
}
|
|
228
|
+
/** One observation row, judged against the caller's window. */
|
|
229
|
+
function isRawRowVisible(row, visibility) {
|
|
230
|
+
if (Array.isArray(visibility.connectors) && !visibility.connectors.includes(String(row.source))) {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
if (visibility.connectorWideRead?.includes(String(row.source))) {
|
|
234
|
+
return isRawWithinTime(row, visibility);
|
|
235
|
+
}
|
|
236
|
+
// The grant decides, and it decides the same way here as in the reader - one rule, taken
|
|
237
|
+
// from one place. The scope/project/tenant clauses below are the pre-grant rule and are
|
|
238
|
+
// skipped when a grant is present, exactly as the reader skips them, because applying
|
|
239
|
+
// both would make a cited ref satisfy a stricter test than a read one.
|
|
240
|
+
if (visibility.channels) {
|
|
241
|
+
return (isChannelGranted(String(row.source), typeof row.channel === 'string' ? row.channel : null, visibility.channels) && isRawWithinTime(row, visibility));
|
|
242
|
+
}
|
|
243
|
+
if (hasProjectRefs(visibility.projectRefs) &&
|
|
244
|
+
!visibility.projectRefs.some((project) => project.id === row.project_id)) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
if (typeof visibility.tenantId === 'string' &&
|
|
248
|
+
visibility.tenantId.length > 0 &&
|
|
249
|
+
row.tenant_id !== visibility.tenantId) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
if (!isRawWithinTime(row, visibility)) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
if (!hasScopes(visibility.scopes)) {
|
|
256
|
+
return true;
|
|
257
|
+
}
|
|
258
|
+
return visibility.scopes.some((scope) => row.memory_scope_kind === scope.kind && row.memory_scope_id === scope.id);
|
|
259
|
+
}
|
|
260
|
+
function refVisibilityKey(ref) {
|
|
261
|
+
return `${ref.kind}\0${ref.id}`;
|
|
262
|
+
}
|
|
263
|
+
function placeholders(count) {
|
|
264
|
+
if (count < 1) {
|
|
265
|
+
throw new Error('Visibility batch requires at least one identifier');
|
|
266
|
+
}
|
|
267
|
+
return Array.from({ length: count }, () => '?').join(', ');
|
|
268
|
+
}
|
|
269
|
+
function visibleTwinRefKeys(adapter, refs, visibility) {
|
|
270
|
+
const unique = [...new Map(refs.map((ref) => [refVisibilityKey(ref), ref])).values()];
|
|
271
|
+
const visible = new Set();
|
|
272
|
+
const ids = (kind) => unique.filter((ref) => ref.kind === kind).map((ref) => ref.id);
|
|
273
|
+
const rawIds = ids('raw');
|
|
274
|
+
if (rawIds.length > 0) {
|
|
275
|
+
const rows = adapter
|
|
276
|
+
.prepare(`SELECT observation_id, source, channel, project_id, tenant_id,
|
|
277
|
+
memory_scope_kind, memory_scope_id, source_at,
|
|
278
|
+
observed_at AS observation_observed_at
|
|
279
|
+
FROM observation_versions
|
|
280
|
+
WHERE observation_id IN (${placeholders(rawIds.length)})`)
|
|
281
|
+
.all(...rawIds);
|
|
282
|
+
for (const row of rows) {
|
|
283
|
+
if (isRawRowVisible(row, visibility)) {
|
|
284
|
+
visible.add(refVisibilityKey({ kind: 'raw', id: String(row.observation_id) }));
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const observationIds = ids('observation');
|
|
289
|
+
if (observationIds.length > 0) {
|
|
290
|
+
const rows = adapter
|
|
291
|
+
.prepare(`SELECT observation_id, source, scope_json, observed_at, source_at
|
|
292
|
+
FROM observation_versions
|
|
293
|
+
WHERE observation_id IN (${placeholders(observationIds.length)})`)
|
|
294
|
+
.all(...observationIds);
|
|
295
|
+
for (const row of rows) {
|
|
296
|
+
if (isWithinVisibilityTime(row.observed_at, visibility) &&
|
|
297
|
+
(0, observations_js_1.isObservationVisibilityRowVisible)(row, {
|
|
298
|
+
principalId: visibility.principalId,
|
|
299
|
+
agentId: visibility.agentId,
|
|
300
|
+
scopes: visibility.scopes,
|
|
301
|
+
connectors: visibility.connectors,
|
|
302
|
+
connectorWideRead: visibility.connectorWideRead,
|
|
303
|
+
channels: visibility.channels,
|
|
304
|
+
maxSourceMs: visibility.maxSourceMs,
|
|
305
|
+
})) {
|
|
306
|
+
visible.add(refVisibilityKey({ kind: 'observation', id: row.observation_id }));
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
const caseIds = ids('case');
|
|
311
|
+
if (caseIds.length > 0) {
|
|
312
|
+
const rows = adapter
|
|
313
|
+
.prepare(`SELECT * FROM case_truth WHERE case_id IN (${placeholders(caseIds.length)})`)
|
|
314
|
+
.all(...caseIds);
|
|
315
|
+
for (const row of rows) {
|
|
316
|
+
const timestamp = parseTimestamp(row.last_activity_at) ?? parseTimestamp(row.updated_at);
|
|
317
|
+
const scopeVisible = !hasScopes(visibility.scopes) ||
|
|
318
|
+
parseCaseScopeRefs(row.scope_refs).some((scope) => new Set(visibility.scopes?.map(scopeKey) ?? []).has(scopeKey(scope))) ||
|
|
319
|
+
visibility.scopes.some((scope) => row.memory_scope_kind === scope.kind && row.memory_scope_id === scope.id);
|
|
320
|
+
if (typeof row.case_id === 'string' &&
|
|
321
|
+
isWithinVisibilityTime(timestamp, visibility) &&
|
|
322
|
+
scopeVisible) {
|
|
323
|
+
visible.add(refVisibilityKey({ kind: 'case', id: row.case_id }));
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
const memoryIds = ids('memory');
|
|
328
|
+
if (memoryIds.length > 0) {
|
|
329
|
+
const columns = tableColumns(adapter, 'decisions');
|
|
330
|
+
const rows = adapter
|
|
331
|
+
.prepare(`SELECT * FROM decisions WHERE id IN (${placeholders(memoryIds.length)})`)
|
|
332
|
+
.all(...memoryIds);
|
|
333
|
+
const admittedBindings = new Set();
|
|
334
|
+
if (hasScopes(visibility.scopes)) {
|
|
335
|
+
const scopeClauses = visibility.scopes
|
|
336
|
+
.map(() => '(ms.kind = ? AND ms.external_id = ?)')
|
|
337
|
+
.join(' OR ');
|
|
338
|
+
const bindings = adapter
|
|
339
|
+
.prepare(`SELECT msb.memory_id FROM memory_scope_bindings msb
|
|
340
|
+
JOIN memory_scopes ms ON ms.id = msb.scope_id
|
|
341
|
+
WHERE msb.memory_id IN (${placeholders(memoryIds.length)})
|
|
342
|
+
AND (${scopeClauses})`)
|
|
343
|
+
.all(...memoryIds, ...visibility.scopes.flatMap((scope) => [scope.kind, scope.id]));
|
|
344
|
+
bindings.forEach((row) => admittedBindings.add(row.memory_id));
|
|
345
|
+
}
|
|
346
|
+
for (const row of rows) {
|
|
347
|
+
const id = String(row.id);
|
|
348
|
+
const status = typeof row.status === 'string' ? row.status : 'active';
|
|
349
|
+
const replaced = status === 'superseded' ||
|
|
350
|
+
(typeof row.superseded_by === 'string' && row.superseded_by.length > 0);
|
|
351
|
+
const retired = RETIRED_MEMORY_STATUSES.has(status) || (replaced && !visibility.includeReplaced);
|
|
352
|
+
const scopeVisible = !hasScopes(visibility.scopes) ||
|
|
353
|
+
admittedBindings.has(id) ||
|
|
354
|
+
(columns.has('memory_scope_kind') &&
|
|
355
|
+
columns.has('memory_scope_id') &&
|
|
356
|
+
visibility.scopes.some((scope) => row.memory_scope_kind === scope.kind && row.memory_scope_id === scope.id));
|
|
357
|
+
if (!retired &&
|
|
358
|
+
isWithinVisibilityTime(parseTimestamp(row.event_datetime) ?? parseTimestamp(row.created_at), visibility) &&
|
|
359
|
+
scopeVisible) {
|
|
360
|
+
visible.add(refVisibilityKey({ kind: 'memory', id }));
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
const registryIds = ids('registry');
|
|
365
|
+
if (registryIds.length > 0) {
|
|
366
|
+
// `created_at` is read, not just `id`: a registry ref must have existed at the requested
|
|
367
|
+
// `as_of`, the same rule memory, raw and observation refs obey. The entity branch this
|
|
368
|
+
// replaced applied it (`isWithinVisibilityTime(row.created_at, visibility)`) and the
|
|
369
|
+
// replacement dropped it, so a packet could cite a node created after the boundary.
|
|
370
|
+
//
|
|
371
|
+
// A merged-away node is NOT excluded. Merging redirects identity, it does not retire the
|
|
372
|
+
// node, and the graph projection's contract is to keep the ORIGINAL ref citable while
|
|
373
|
+
// resolving the current one beside it (design section 4.1, original/corrected references).
|
|
374
|
+
// Filtering on `merged_into IS NULL` here broke exactly that - caught by
|
|
375
|
+
// agent-graph.test.ts.
|
|
376
|
+
const rows = adapter
|
|
377
|
+
.prepare(`SELECT id, created_at FROM registry_nodes
|
|
378
|
+
WHERE id IN (${placeholders(registryIds.length)})`)
|
|
379
|
+
.all(...registryIds);
|
|
380
|
+
const admitted = new Set();
|
|
381
|
+
if (hasScopes(visibility.scopes)) {
|
|
382
|
+
const scopeClauses = visibility.scopes
|
|
383
|
+
.map(() => '(scope_kind = ? AND scope_id = ?)')
|
|
384
|
+
.join(' OR ');
|
|
385
|
+
const bindings = adapter
|
|
386
|
+
.prepare(`SELECT DISTINCT node_id FROM registry_scope_bindings
|
|
387
|
+
WHERE node_id IN (${placeholders(registryIds.length)}) AND (${scopeClauses})`)
|
|
388
|
+
.all(...registryIds, ...visibility.scopes.flatMap((scope) => [scope.kind, scope.id]));
|
|
389
|
+
bindings.forEach((row) => admitted.add(row.node_id));
|
|
390
|
+
}
|
|
391
|
+
for (const row of rows) {
|
|
392
|
+
if (isWithinVisibilityTime(row.created_at, visibility) &&
|
|
393
|
+
(!hasScopes(visibility.scopes) || admitted.has(row.id))) {
|
|
394
|
+
visible.add(refVisibilityKey({ kind: 'registry', id: row.id }));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
for (const ref of unique) {
|
|
399
|
+
if (ref.kind === 'report' && !hasScopes(visibility.scopes)) {
|
|
400
|
+
visible.add(refVisibilityKey(ref));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return visible;
|
|
404
|
+
}
|
|
405
|
+
function preloadRecursiveEdgeVisibility(adapter, refs, visibility) {
|
|
406
|
+
const edgeCache = new Map();
|
|
407
|
+
const rootEdgeIds = [...new Set(refs.filter((ref) => ref.kind === 'edge').map((ref) => ref.id))];
|
|
408
|
+
const nestedRefs = refs.filter((ref) => ref.kind !== 'edge');
|
|
409
|
+
if (rootEdgeIds.length > 0) {
|
|
410
|
+
const rows = adapter
|
|
411
|
+
.prepare(`WITH RECURSIVE edge_tree(edge_id) AS (
|
|
412
|
+
SELECT CAST(value AS TEXT)
|
|
413
|
+
FROM json_each(?)
|
|
414
|
+
UNION
|
|
415
|
+
SELECT CAST(child.value AS TEXT)
|
|
416
|
+
FROM edge_tree tree
|
|
417
|
+
JOIN twin_edges parent ON parent.edge_id = tree.edge_id
|
|
418
|
+
JOIN json_each(json_array(
|
|
419
|
+
CASE WHEN parent.subject_kind = 'edge' THEN parent.subject_id END,
|
|
420
|
+
CASE WHEN parent.object_kind = 'edge' THEN parent.object_id END
|
|
421
|
+
)) child
|
|
422
|
+
WHERE child.value IS NOT NULL
|
|
423
|
+
)
|
|
424
|
+
SELECT edge.*
|
|
425
|
+
FROM edge_tree
|
|
426
|
+
JOIN twin_edges edge ON edge.edge_id = edge_tree.edge_id`)
|
|
427
|
+
.all(JSON.stringify(rootEdgeIds));
|
|
428
|
+
for (const row of rows) {
|
|
429
|
+
const edge = (0, judgments_js_1.mapTwinEdgeRow)(row);
|
|
430
|
+
edgeCache.set(edge.edge_id, edge);
|
|
431
|
+
if (edge.subject_ref.kind !== 'edge') {
|
|
432
|
+
nestedRefs.push(edge.subject_ref);
|
|
433
|
+
}
|
|
434
|
+
if (edge.object_ref.kind !== 'edge') {
|
|
435
|
+
nestedRefs.push(edge.object_ref);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
for (const edgeId of rootEdgeIds) {
|
|
439
|
+
if (!edgeCache.has(edgeId)) {
|
|
440
|
+
edgeCache.set(edgeId, null);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return {
|
|
445
|
+
edgeCache,
|
|
446
|
+
edgeVisibility: new Map(),
|
|
447
|
+
precomputed: visibleTwinRefKeys(adapter, nestedRefs, visibility),
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
function visibleTwinRefKeysRecursive(adapter, refs, visibility) {
|
|
451
|
+
const { edgeCache, edgeVisibility, precomputed } = preloadRecursiveEdgeVisibility(adapter, refs, visibility);
|
|
452
|
+
const visible = new Set();
|
|
453
|
+
for (const ref of refs) {
|
|
454
|
+
if (isTwinRefVisible(adapter, ref, visibility, new Set(), edgeCache, edgeVisibility, precomputed)) {
|
|
455
|
+
visible.add(refVisibilityKey(ref));
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return visible;
|
|
459
|
+
}
|
|
460
|
+
function isTwinRefVisible(adapter, ref, visibility, edgesInProgress, edgeCache, edgeVisibility, precomputed) {
|
|
461
|
+
if (precomputed && ref.kind !== 'edge') {
|
|
462
|
+
return precomputed.has(refVisibilityKey(ref));
|
|
463
|
+
}
|
|
464
|
+
if (ref.kind === 'report') {
|
|
465
|
+
return !hasScopes(visibility.scopes);
|
|
466
|
+
}
|
|
467
|
+
if (ref.kind === 'memory') {
|
|
468
|
+
return isMemoryVisible(adapter, ref.id, visibility);
|
|
469
|
+
}
|
|
470
|
+
if (ref.kind === 'case') {
|
|
471
|
+
return isCaseVisible(adapter, ref.id, visibility);
|
|
472
|
+
}
|
|
473
|
+
if (ref.kind === 'raw') {
|
|
474
|
+
return isRawVisible(adapter, ref.id, visibility);
|
|
475
|
+
}
|
|
476
|
+
if (ref.kind === 'registry') {
|
|
477
|
+
const node = adapter
|
|
478
|
+
.prepare('SELECT created_at FROM registry_nodes WHERE id = ?')
|
|
479
|
+
.get(ref.id);
|
|
480
|
+
if (!node || !isWithinVisibilityTime(node.created_at, visibility)) {
|
|
481
|
+
return false;
|
|
482
|
+
}
|
|
483
|
+
if (!hasScopes(visibility.scopes)) {
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
486
|
+
return visibility.scopes.some((scope) => Boolean(adapter
|
|
487
|
+
.prepare('SELECT 1 FROM registry_scope_bindings WHERE node_id = ? AND scope_kind = ? AND scope_id = ?')
|
|
488
|
+
.get(ref.id, scope.kind, scope.id)));
|
|
489
|
+
}
|
|
490
|
+
if (ref.kind === 'observation') {
|
|
491
|
+
const row = adapter
|
|
492
|
+
.prepare('SELECT scope_json, observed_at, source_at FROM observation_versions WHERE observation_id = ?')
|
|
493
|
+
.get(ref.id);
|
|
494
|
+
if (!row || !isWithinVisibilityTime(row.observed_at, visibility)) {
|
|
495
|
+
return false;
|
|
496
|
+
}
|
|
497
|
+
return (0, observations_js_1.isObservationVersionVisible)(adapter, ref.id, {
|
|
498
|
+
principalId: visibility.principalId,
|
|
499
|
+
agentId: visibility.agentId,
|
|
500
|
+
scopes: visibility.scopes,
|
|
501
|
+
connectors: visibility.connectors,
|
|
502
|
+
connectorWideRead: visibility.connectorWideRead,
|
|
503
|
+
channels: visibility.channels,
|
|
504
|
+
maxSourceMs: visibility.maxSourceMs,
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
if (ref.kind !== 'edge') {
|
|
508
|
+
throw new Error(`UNSUPPORTED_REFERENCE_KIND: ${ref.kind}`);
|
|
509
|
+
}
|
|
510
|
+
// An edge met again while it is being decided lies on a cycle through itself: never visible.
|
|
511
|
+
// Every edge on such a cycle is invisible from any root, so a finished answer is memoized.
|
|
512
|
+
if (edgesInProgress.has(ref.id)) {
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
const decided = edgeVisibility.get(ref.id);
|
|
516
|
+
if (decided !== undefined) {
|
|
517
|
+
return decided;
|
|
518
|
+
}
|
|
519
|
+
let edge = edgeCache.get(ref.id);
|
|
520
|
+
if (edge === undefined) {
|
|
521
|
+
edge = (0, judgments_js_1.getTwinEdge)(adapter, ref.id);
|
|
522
|
+
edgeCache.set(ref.id, edge);
|
|
523
|
+
}
|
|
524
|
+
if (!edge || !isWithinVisibilityTime(edge.created_at, visibility)) {
|
|
525
|
+
edgeVisibility.set(ref.id, false);
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
edgesInProgress.add(ref.id);
|
|
529
|
+
const visible = isTwinRefVisible(adapter, edge.subject_ref, visibility, edgesInProgress, edgeCache, edgeVisibility, precomputed) &&
|
|
530
|
+
isTwinRefVisible(adapter, edge.object_ref, visibility, edgesInProgress, edgeCache, edgeVisibility, precomputed);
|
|
531
|
+
edgesInProgress.delete(ref.id);
|
|
532
|
+
edgeVisibility.set(ref.id, visible);
|
|
533
|
+
return visible;
|
|
534
|
+
}
|
|
535
|
+
function assertTwinRefsVisible(adapter, refs, visibility = {}) {
|
|
536
|
+
const supportedKinds = new Set([
|
|
537
|
+
'memory',
|
|
538
|
+
'case',
|
|
539
|
+
'report',
|
|
540
|
+
'edge',
|
|
541
|
+
'raw',
|
|
542
|
+
'registry',
|
|
543
|
+
'observation',
|
|
544
|
+
]);
|
|
545
|
+
for (const ref of refs) {
|
|
546
|
+
if (!supportedKinds.has(ref.kind)) {
|
|
547
|
+
throw new Error(`UNSUPPORTED_REFERENCE_KIND: ${ref.kind}`);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
const { edgeCache, edgeVisibility, precomputed } = preloadRecursiveEdgeVisibility(adapter, refs, visibility);
|
|
551
|
+
for (const ref of refs) {
|
|
552
|
+
if (!isTwinRefVisible(adapter, ref, visibility, new Set(), edgeCache, edgeVisibility, precomputed)) {
|
|
553
|
+
throw new TwinRefNotVisibleError(ref);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
function listVisibleTwinEdgesForRefs(adapter, refs, options = {}) {
|
|
558
|
+
const edgeTypes = normalizeEdgeTypes(options.edgeTypes);
|
|
559
|
+
const limit = typeof options.limit === 'number' && Number.isFinite(options.limit)
|
|
560
|
+
? Math.max(0, Math.floor(options.limit))
|
|
561
|
+
: null;
|
|
562
|
+
if (limit === 0) {
|
|
563
|
+
return [];
|
|
564
|
+
}
|
|
565
|
+
if (limit !== null) {
|
|
566
|
+
const pageSize = Math.min(2000, Math.max(128, limit * 2));
|
|
567
|
+
const admitted = [];
|
|
568
|
+
let after;
|
|
569
|
+
while (admitted.length < limit) {
|
|
570
|
+
const candidates = (0, judgments_js_1.listTwinEdgesForRefs)(adapter, refs, {
|
|
571
|
+
newest: true,
|
|
572
|
+
limit: pageSize,
|
|
573
|
+
...(after ? { after } : {}),
|
|
574
|
+
...(edgeTypes.size > 0 ? { edgeTypes: [...edgeTypes] } : {}),
|
|
575
|
+
startMs: options.startMs,
|
|
576
|
+
asOfMs: options.asOfMs,
|
|
577
|
+
});
|
|
578
|
+
if (candidates.length === 0) {
|
|
579
|
+
break;
|
|
580
|
+
}
|
|
581
|
+
const { edgeCache, edgeVisibility, precomputed } = preloadRecursiveEdgeVisibility(adapter, candidates.flatMap((edge) => [edge.subject_ref, edge.object_ref]), options);
|
|
582
|
+
for (const edge of candidates) {
|
|
583
|
+
if (isTwinRefVisible(adapter, edge.subject_ref, options, new Set(), edgeCache, edgeVisibility, precomputed) &&
|
|
584
|
+
isTwinRefVisible(adapter, edge.object_ref, options, new Set(), edgeCache, edgeVisibility, precomputed)) {
|
|
585
|
+
admitted.push(edge);
|
|
586
|
+
if (admitted.length === limit) {
|
|
587
|
+
return admitted;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
if (candidates.length < pageSize) {
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
const last = candidates[candidates.length - 1];
|
|
595
|
+
after = { createdAt: last.created_at, edgeId: last.edge_id };
|
|
596
|
+
}
|
|
597
|
+
return admitted;
|
|
598
|
+
}
|
|
599
|
+
const candidateEdges = (0, judgments_js_1.listTwinEdgesForRefs)(adapter, refs);
|
|
600
|
+
const { edgeCache, edgeVisibility, precomputed } = preloadRecursiveEdgeVisibility(adapter, candidateEdges.flatMap((edge) => [edge.subject_ref, edge.object_ref]), options);
|
|
601
|
+
const edges = candidateEdges.filter((edge) => (edgeTypes.size === 0 || edgeTypes.has(edge.edge_type)) &&
|
|
602
|
+
(typeof options.startMs !== 'number' || edge.created_at >= options.startMs) &&
|
|
603
|
+
(typeof options.asOfMs !== 'number' || edge.created_at <= options.asOfMs) &&
|
|
604
|
+
isTwinRefVisible(adapter, edge.subject_ref, options, new Set(), edgeCache, edgeVisibility, precomputed) &&
|
|
605
|
+
isTwinRefVisible(adapter, edge.object_ref, options, new Set(), edgeCache, edgeVisibility, precomputed));
|
|
606
|
+
const sortedEdges = [...edges].sort((left, right) => {
|
|
607
|
+
const createdDiff = right.created_at - left.created_at;
|
|
608
|
+
return createdDiff !== 0 ? createdDiff : left.edge_id.localeCompare(right.edge_id);
|
|
609
|
+
});
|
|
610
|
+
return sortedEdges;
|
|
611
|
+
}
|
|
612
|
+
function normalizeEdgeTypes(edgeTypes) {
|
|
613
|
+
if (!Array.isArray(edgeTypes) || edgeTypes.length === 0) {
|
|
614
|
+
return new Set();
|
|
615
|
+
}
|
|
616
|
+
return new Set(edgeTypes);
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* The rule: an event is visible when its connector is granted and its channel is one of
|
|
620
|
+
* the channels granted for that connector.
|
|
621
|
+
*
|
|
622
|
+
* A connector absent from the grant is denied. A connector present with an empty list is
|
|
623
|
+
* also denied - "granted the connector but no channel of it" is not a useful state, and
|
|
624
|
+
* reading it as "all channels" is how a grant becomes a wildcard.
|
|
625
|
+
*/
|
|
626
|
+
function isChannelGranted(connector, channel, grant) {
|
|
627
|
+
if (typeof channel !== 'string' || channel.length === 0)
|
|
628
|
+
return false;
|
|
629
|
+
const channels = grant[connector];
|
|
630
|
+
return Array.isArray(channels) && channels.includes(channel);
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* The same rule as a SQL predicate.
|
|
634
|
+
*
|
|
635
|
+
* Returns null when nothing is readable, which callers must treat as "select nothing"
|
|
636
|
+
* rather than "no filter" - an empty WHERE is how a visibility rule turns into a full scan
|
|
637
|
+
* at the exact moment it should have refused.
|
|
638
|
+
*
|
|
639
|
+
* `requestedConnectors` narrows further: a connector the caller did not ask for is not read
|
|
640
|
+
* even though it is granted. The grant is a ceiling, never an instruction.
|
|
641
|
+
*/
|
|
642
|
+
function channelGrantClause(grant, requestedConnectors, columns) {
|
|
643
|
+
const pairs = [];
|
|
644
|
+
const params = [];
|
|
645
|
+
for (const [connector, channels] of Object.entries(grant)) {
|
|
646
|
+
if (Array.isArray(requestedConnectors) && !requestedConnectors.includes(connector))
|
|
647
|
+
continue;
|
|
648
|
+
// Array.isArray, not `?? []`: the boolean form denies a malformed grant quietly, so
|
|
649
|
+
// this one must not throw on it. Two forms of a rule that disagree on bad input have
|
|
650
|
+
// already started to be two rules.
|
|
651
|
+
if (!Array.isArray(channels))
|
|
652
|
+
continue;
|
|
653
|
+
const unique = [
|
|
654
|
+
...new Set(channels.filter((channel) => typeof channel === 'string' && channel.length > 0)),
|
|
655
|
+
];
|
|
656
|
+
if (unique.length === 0)
|
|
657
|
+
continue;
|
|
658
|
+
pairs.push(`(${columns.connector} = ? AND ${columns.channel} IN (${unique.map(() => '?').join(', ')}))`);
|
|
659
|
+
params.push(connector, ...unique);
|
|
660
|
+
}
|
|
661
|
+
return pairs.length === 0 ? null : { sql: `(${pairs.join(' OR ')})`, params };
|
|
662
|
+
}
|
|
663
|
+
//# sourceMappingURL=access.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Case errors.
|
|
3
|
+
*
|
|
4
|
+
* `CaseMergeChainCycleError` used to live in `entities/errors.ts` and extend
|
|
5
|
+
* that module's `EntityError`. Nothing about a cycle in `case_truth` belongs to
|
|
6
|
+
* the entity substrate; it sat there because the two subsystems were written
|
|
7
|
+
* together. It carries its own shape now.
|
|
8
|
+
*
|
|
9
|
+
* @module cases/errors
|
|
10
|
+
*/
|
|
11
|
+
export interface CaseErrorEnvelope {
|
|
12
|
+
error: {
|
|
13
|
+
code: string;
|
|
14
|
+
message: string;
|
|
15
|
+
hint: string;
|
|
16
|
+
doc_url: string;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export declare class CaseMergeChainCycleError extends Error {
|
|
20
|
+
readonly code = "case.merge_chain_cycle";
|
|
21
|
+
readonly doc_section = "#case-merge-chain-cycle";
|
|
22
|
+
readonly context: Record<string, unknown>;
|
|
23
|
+
readonly hint: string;
|
|
24
|
+
constructor(context: {
|
|
25
|
+
case_id: string;
|
|
26
|
+
chain: string[];
|
|
27
|
+
detected_at_depth: number;
|
|
28
|
+
});
|
|
29
|
+
toErrorEnvelope(): CaseErrorEnvelope;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=case-errors.d.ts.map
|