@jungjaehoon/mama-core 2.4.1 → 4.0.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/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 +1968 -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/commitments.d.ts +188 -0
- package/dist/knowledge/commitments.js +370 -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/judgments.d.ts +118 -0
- package/dist/knowledge/judgments.js +808 -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 +155 -0
- package/dist/runtime/drivers/claude-cli-wrapper.js +392 -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 +424 -0
- package/dist/runtime/drivers/persistent-cli-process.js +1543 -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,808 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.JudgmentError = void 0;
|
|
7
|
+
exports.judgmentRecordId = judgmentRecordId;
|
|
8
|
+
exports.admittedScopeIds = admittedScopeIds;
|
|
9
|
+
exports.boundScopeIdsFor = boundScopeIdsFor;
|
|
10
|
+
exports.appendJudgment = appendJudgment;
|
|
11
|
+
exports.createJudgmentWriter = createJudgmentWriter;
|
|
12
|
+
exports.mapTwinEdgeRow = mapTwinEdgeRow;
|
|
13
|
+
exports.insertTwinEdge = insertTwinEdge;
|
|
14
|
+
exports.getTwinEdge = getTwinEdge;
|
|
15
|
+
exports.listTwinEdgesForRefs = listTwinEdgesForRefs;
|
|
16
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
17
|
+
const db_manager_js_1 = require("../db-manager.js");
|
|
18
|
+
const canonicalize_js_1 = require("../canonicalize.js");
|
|
19
|
+
const twin_edge_types_js_1 = require("./twin-edge-types.js");
|
|
20
|
+
const event_store_js_1 = require("../memory/event-store.js");
|
|
21
|
+
const record_identity_js_1 = require("../registry/record-identity.js");
|
|
22
|
+
class JudgmentError extends Error {
|
|
23
|
+
code;
|
|
24
|
+
constructor(code, message) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.code = code;
|
|
27
|
+
this.name = 'JudgmentError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.JudgmentError = JudgmentError;
|
|
31
|
+
function commandHash(command) {
|
|
32
|
+
return node_crypto_1.default.createHash('sha256').update((0, canonicalize_js_1.canonicalizeJSON)(command)).digest('hex');
|
|
33
|
+
}
|
|
34
|
+
function recordIdForCommand(command) {
|
|
35
|
+
return judgmentRecordId(command.commandId);
|
|
36
|
+
}
|
|
37
|
+
/** Deterministic record id a command will be bound to — lets adapters build
|
|
38
|
+
* projections (timeline events, identity bindings) before the write commits. */
|
|
39
|
+
function judgmentRecordId(commandId) {
|
|
40
|
+
return `judgment_${node_crypto_1.default.createHash('sha256').update(commandId).digest('hex').slice(0, 24)}`;
|
|
41
|
+
}
|
|
42
|
+
function commitmentIdForCommand(command) {
|
|
43
|
+
return `commitment_${node_crypto_1.default
|
|
44
|
+
.createHash('sha256')
|
|
45
|
+
.update(command.commandId)
|
|
46
|
+
.digest('hex')
|
|
47
|
+
.slice(0, 24)}`;
|
|
48
|
+
}
|
|
49
|
+
function requireText(value, field) {
|
|
50
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
51
|
+
throw new JudgmentError('INVALID_COMMAND', `${field} must be nonblank`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function scopeIdFor(scope) {
|
|
55
|
+
return `scope_${scope.kind}_${Buffer.from(scope.id).toString('base64url')}`;
|
|
56
|
+
}
|
|
57
|
+
function scopeKey(scope) {
|
|
58
|
+
return `${scope.kind}\0${scope.id}`;
|
|
59
|
+
}
|
|
60
|
+
/** Scopes the caller was admitted to; bounds what the command may reference.
|
|
61
|
+
* Exported for the sibling source-ingest command path; not part of the public API. */
|
|
62
|
+
function admittedScopeIds(access) {
|
|
63
|
+
const seen = new Set();
|
|
64
|
+
return access.scopes.map((scope) => {
|
|
65
|
+
requireText(scope.kind, 'scope kind');
|
|
66
|
+
requireText(scope.id, 'scope id');
|
|
67
|
+
const key = scopeKey(scope);
|
|
68
|
+
if (seen.has(key)) {
|
|
69
|
+
throw new JudgmentError('INVALID_SCOPE', 'Access scopes must be unique');
|
|
70
|
+
}
|
|
71
|
+
seen.add(key);
|
|
72
|
+
return scopeIdFor(scope);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Scopes bound to the new record. An explicit `scopes: []` declares an
|
|
77
|
+
* unscoped record (legacy parity); an omitted field inherits the access scope.
|
|
78
|
+
* Exported for the sibling source-ingest command path; not part of the public API.
|
|
79
|
+
*/
|
|
80
|
+
function boundScopeIdsFor(access, command) {
|
|
81
|
+
const scopes = command.scopes ?? access.scopes;
|
|
82
|
+
if (command.scopes === undefined && scopes.length === 0) {
|
|
83
|
+
throw new JudgmentError('INVALID_SCOPE', 'At least one judgment scope is required');
|
|
84
|
+
}
|
|
85
|
+
const admitted = new Set(access.scopes.map(scopeKey));
|
|
86
|
+
const seen = new Set();
|
|
87
|
+
return scopes.map((scope) => {
|
|
88
|
+
// A kind is nonblank text. Which kinds exist is the writer's statement; this
|
|
89
|
+
// compared against a list the core kept, so a consumer whose world had a kind
|
|
90
|
+
// of its own could not append a judgment at all.
|
|
91
|
+
requireText(scope.kind, 'scope kind');
|
|
92
|
+
requireText(scope.id, 'scope id');
|
|
93
|
+
const key = scopeKey(scope);
|
|
94
|
+
if (seen.has(key)) {
|
|
95
|
+
throw new JudgmentError('INVALID_SCOPE', 'Judgment scopes must be unique');
|
|
96
|
+
}
|
|
97
|
+
seen.add(key);
|
|
98
|
+
if (!admitted.has(key)) {
|
|
99
|
+
throw new JudgmentError('SCOPE_DENIED', 'Judgment scope is outside the admitted access');
|
|
100
|
+
}
|
|
101
|
+
return scopeIdFor(scope);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
function validateBounds(command) {
|
|
105
|
+
for (const [name, value] of [
|
|
106
|
+
['appliesFrom', command.appliesFrom],
|
|
107
|
+
['appliesUntil', command.appliesUntil],
|
|
108
|
+
]) {
|
|
109
|
+
if (value !== undefined && (!Number.isFinite(value) || value < 0)) {
|
|
110
|
+
throw new JudgmentError('INVALID_TIME', `${name} must be a finite nonnegative epoch`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (command.appliesFrom !== undefined &&
|
|
114
|
+
command.appliesUntil !== undefined &&
|
|
115
|
+
command.appliesFrom > command.appliesUntil) {
|
|
116
|
+
throw new JudgmentError('INVALID_TIME', 'appliesFrom cannot be after appliesUntil');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const AMEND_FIELDS = [
|
|
120
|
+
'outcome',
|
|
121
|
+
'failureReason',
|
|
122
|
+
'limitation',
|
|
123
|
+
'status',
|
|
124
|
+
'confidence',
|
|
125
|
+
'durationDays',
|
|
126
|
+
'supersedes',
|
|
127
|
+
'supersededBy',
|
|
128
|
+
];
|
|
129
|
+
/** Public-save parity fields carried by the command are validated up front so a
|
|
130
|
+
* malformed command fails before any write or embedder call. */
|
|
131
|
+
function validateCommandFields(command) {
|
|
132
|
+
if (command.confidence !== undefined &&
|
|
133
|
+
(!Number.isFinite(command.confidence) || command.confidence < 0 || command.confidence > 1)) {
|
|
134
|
+
throw new JudgmentError('INVALID_COMMAND', 'confidence must be a number between 0 and 1');
|
|
135
|
+
}
|
|
136
|
+
if (command.eventDate !== undefined && command.eventDate !== null) {
|
|
137
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(command.eventDate) ||
|
|
138
|
+
Number.isNaN(new Date(command.eventDate).getTime())) {
|
|
139
|
+
throw new JudgmentError('INVALID_TIME', `eventDate must be ISO 8601 YYYY-MM-DD (got: ${command.eventDate})`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (command.eventDatetime !== undefined && command.eventDatetime !== null) {
|
|
143
|
+
if (!Number.isFinite(command.eventDatetime) || command.eventDatetime <= 0) {
|
|
144
|
+
throw new JudgmentError('INVALID_TIME', `eventDatetime must be a positive millisecond timestamp (got: ${command.eventDatetime})`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (command.recordedAt !== undefined &&
|
|
148
|
+
(!Number.isFinite(command.recordedAt) || command.recordedAt < 0)) {
|
|
149
|
+
throw new JudgmentError('INVALID_TIME', 'recordedAt must be a finite nonnegative epoch');
|
|
150
|
+
}
|
|
151
|
+
if (command.sourceRefs !== undefined &&
|
|
152
|
+
(!Array.isArray(command.sourceRefs) ||
|
|
153
|
+
command.sourceRefs.some((ref) => typeof ref !== 'string' || ref.length === 0))) {
|
|
154
|
+
throw new JudgmentError('INVALID_COMMAND', 'sourceRefs must be an array of nonblank strings');
|
|
155
|
+
}
|
|
156
|
+
for (const amendment of command.amends ?? []) {
|
|
157
|
+
if (amendment.target?.kind !== 'memory') {
|
|
158
|
+
throw new JudgmentError('INVALID_COMMAND', 'amends targets must be memory references');
|
|
159
|
+
}
|
|
160
|
+
requireText(amendment.target.id, 'amends target id');
|
|
161
|
+
if (!AMEND_FIELDS.some((field) => field in amendment)) {
|
|
162
|
+
throw new JudgmentError('INVALID_COMMAND', 'amends requires at least one projection field to set');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
for (const edge of command.projections?.decisionEdges ?? []) {
|
|
166
|
+
if (edge.fromId !== undefined) {
|
|
167
|
+
requireText(edge.fromId, 'projection edge source');
|
|
168
|
+
}
|
|
169
|
+
requireText(edge.targetId, 'projection edge target');
|
|
170
|
+
requireText(edge.relationship, 'projection edge relationship');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function referenceExists(adapter, reference, admittedScopeIds) {
|
|
174
|
+
if (reference.kind === 'memory') {
|
|
175
|
+
const row = adapter
|
|
176
|
+
.prepare(`SELECT (SELECT COUNT(*) FROM memory_scope_bindings b WHERE b.memory_id = d.id) AS bindings
|
|
177
|
+
FROM decisions d WHERE d.id = ?`)
|
|
178
|
+
.get(reference.id);
|
|
179
|
+
if (!row)
|
|
180
|
+
return false;
|
|
181
|
+
// A record with no scope bindings has no partition boundary to violate.
|
|
182
|
+
if (row.bindings === 0)
|
|
183
|
+
return true;
|
|
184
|
+
if (admittedScopeIds.length === 0)
|
|
185
|
+
return false;
|
|
186
|
+
const placeholders = admittedScopeIds.map(() => '?').join(', ');
|
|
187
|
+
return (adapter
|
|
188
|
+
.prepare(`SELECT 1 FROM decisions d
|
|
189
|
+
JOIN memory_scope_bindings b ON b.memory_id = d.id
|
|
190
|
+
WHERE d.id = ? AND b.scope_id IN (${placeholders}) LIMIT 1`)
|
|
191
|
+
.get(reference.id, ...admittedScopeIds) !== undefined);
|
|
192
|
+
}
|
|
193
|
+
if (reference.kind === 'registry') {
|
|
194
|
+
const node = adapter.prepare('SELECT 1 FROM registry_nodes WHERE id = ?').get(reference.id) !== undefined;
|
|
195
|
+
if (!node)
|
|
196
|
+
return false;
|
|
197
|
+
const bindings = adapter
|
|
198
|
+
.prepare('SELECT scope_kind, scope_id FROM registry_scope_bindings WHERE node_id = ?')
|
|
199
|
+
.all(reference.id);
|
|
200
|
+
return (bindings.length === 0 ||
|
|
201
|
+
bindings.some((binding) => admittedScopeIds.includes(`scope_${binding.scope_kind}_${Buffer.from(binding.scope_id).toString('base64url')}`)));
|
|
202
|
+
}
|
|
203
|
+
if (reference.kind === 'observation') {
|
|
204
|
+
return (adapter
|
|
205
|
+
.prepare('SELECT 1 FROM observation_versions WHERE observation_id = ?')
|
|
206
|
+
.get(reference.id) !== undefined);
|
|
207
|
+
}
|
|
208
|
+
if (reference.kind === 'edge') {
|
|
209
|
+
return (adapter.prepare('SELECT 1 FROM twin_edges WHERE edge_id = ?').get(reference.id) !== undefined);
|
|
210
|
+
}
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
function validateLinks(adapter, links, admittedScopeIds) {
|
|
214
|
+
const keys = new Set();
|
|
215
|
+
for (const link of links) {
|
|
216
|
+
requireText(link.relation, 'link relation');
|
|
217
|
+
requireText(link.target.id, 'link target id');
|
|
218
|
+
const attrs = link.attrs ?? {};
|
|
219
|
+
const key = (0, canonicalize_js_1.canonicalizeJSON)({
|
|
220
|
+
relation: link.relation,
|
|
221
|
+
target: link.target,
|
|
222
|
+
role: attrs.role ?? null,
|
|
223
|
+
slot: attrs.slot ?? null,
|
|
224
|
+
});
|
|
225
|
+
if (keys.has(key)) {
|
|
226
|
+
throw new JudgmentError('DUPLICATE_LINK', 'A judgment cannot repeat the same relation target slot');
|
|
227
|
+
}
|
|
228
|
+
keys.add(key);
|
|
229
|
+
if (!referenceExists(adapter, link.target, admittedScopeIds)) {
|
|
230
|
+
// Echo only the caller's own input: an unavailable id reads the same whether wrong or outside scope.
|
|
231
|
+
throw new JudgmentError('REFERENCE_NOT_FOUND', `A judgment reference is unavailable: ${link.target.kind} ${link.target.id}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function validateAmends(adapter, amends, admittedScopeIds) {
|
|
236
|
+
for (const amendment of amends) {
|
|
237
|
+
if (!referenceExists(adapter, amendment.target, admittedScopeIds)) {
|
|
238
|
+
throw new JudgmentError('REFERENCE_NOT_FOUND', 'An amendment target is unavailable');
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function toColumnText(value) {
|
|
243
|
+
if (value === undefined || value === null)
|
|
244
|
+
return null;
|
|
245
|
+
return Array.isArray(value) ? JSON.stringify(value) : value;
|
|
246
|
+
}
|
|
247
|
+
const AMEND_COLUMN_MAP = {
|
|
248
|
+
outcome: 'outcome',
|
|
249
|
+
failureReason: 'failure_reason',
|
|
250
|
+
limitation: 'limitation',
|
|
251
|
+
status: 'status',
|
|
252
|
+
confidence: 'confidence',
|
|
253
|
+
durationDays: 'duration_days',
|
|
254
|
+
supersedes: 'supersedes',
|
|
255
|
+
supersededBy: 'superseded_by',
|
|
256
|
+
};
|
|
257
|
+
function applyAmendment(adapter, amendment, now) {
|
|
258
|
+
const sets = ['updated_at = ?'];
|
|
259
|
+
const params = [now];
|
|
260
|
+
for (const field of AMEND_FIELDS) {
|
|
261
|
+
if (field in amendment) {
|
|
262
|
+
sets.push(`${AMEND_COLUMN_MAP[field]} = ?`);
|
|
263
|
+
params.push(amendment[field] ?? null);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
adapter
|
|
267
|
+
.prepare(`UPDATE decisions SET ${sets.join(', ')} WHERE id = ?`)
|
|
268
|
+
.run(...params, amendment.target.id);
|
|
269
|
+
}
|
|
270
|
+
function applyProjections(adapter, command, recordId, effectiveScopes, now) {
|
|
271
|
+
const projections = command.projections;
|
|
272
|
+
if (!projections)
|
|
273
|
+
return;
|
|
274
|
+
if (projections.recordIdentity) {
|
|
275
|
+
(0, record_identity_js_1.writeRecordIdentity)(adapter, {
|
|
276
|
+
recordId,
|
|
277
|
+
itemId: projections.recordIdentity.itemId,
|
|
278
|
+
actors: projections.recordIdentity.actors ?? [],
|
|
279
|
+
scopes: effectiveScopes,
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
for (const targetId of projections.supersedeTargets ?? []) {
|
|
283
|
+
adapter
|
|
284
|
+
.prepare("UPDATE decisions SET superseded_by = ?, status = 'superseded', updated_at = ? WHERE id = ?")
|
|
285
|
+
.run(recordId, now, targetId);
|
|
286
|
+
}
|
|
287
|
+
const edgeInsert = adapter.prepare(`INSERT INTO decision_edges
|
|
288
|
+
(from_id, to_id, relationship, reason, weight, created_at, created_by, approved_by_user)
|
|
289
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
290
|
+
for (const edge of projections.decisionEdges ?? []) {
|
|
291
|
+
edgeInsert.run(edge.fromId ?? recordId, edge.targetId, edge.relationship, edge.reason ?? null, edge.weight ?? 1, now, edge.createdBy ?? 'user', edge.approvedByUser ?? 1);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function parseReceipt(value) {
|
|
295
|
+
let parsed;
|
|
296
|
+
try {
|
|
297
|
+
parsed = JSON.parse(value);
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
throw new Error('judgment_commands.receipt_json is malformed', { cause: error });
|
|
301
|
+
}
|
|
302
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
303
|
+
throw new Error('judgment_commands.receipt_json must contain an object');
|
|
304
|
+
}
|
|
305
|
+
return parsed;
|
|
306
|
+
}
|
|
307
|
+
function assertReplay(adapter, command, access, hash) {
|
|
308
|
+
const binding = adapter
|
|
309
|
+
.prepare('SELECT principal_id, action, payload_hash, receipt_key FROM command_bindings WHERE command_id = ?')
|
|
310
|
+
.get(command.commandId);
|
|
311
|
+
if (!binding)
|
|
312
|
+
return null;
|
|
313
|
+
if (binding.principal_id !== access.principalId ||
|
|
314
|
+
binding.action !== 'judgment.append' ||
|
|
315
|
+
binding.payload_hash !== hash) {
|
|
316
|
+
throw new JudgmentError('COMMAND_CONFLICT', 'The command id is already bound to another request');
|
|
317
|
+
}
|
|
318
|
+
const row = adapter
|
|
319
|
+
.prepare('SELECT receipt_json FROM judgment_commands WHERE command_id = ?')
|
|
320
|
+
.get(command.commandId);
|
|
321
|
+
if (!row) {
|
|
322
|
+
throw new Error('Command binding has no judgment receipt');
|
|
323
|
+
}
|
|
324
|
+
return parseReceipt(row.receipt_json);
|
|
325
|
+
}
|
|
326
|
+
function edgeId(commandId, index, relation, target) {
|
|
327
|
+
return `edge_${node_crypto_1.default
|
|
328
|
+
.createHash('sha256')
|
|
329
|
+
.update((0, canonicalize_js_1.canonicalizeJSON)({ commandId, index, relation, target }))
|
|
330
|
+
.digest('hex')
|
|
331
|
+
.slice(0, 24)}`;
|
|
332
|
+
}
|
|
333
|
+
function insertLink(adapter, command, recordId, link, index, access, now) {
|
|
334
|
+
const id = edgeId(command.commandId, index, link.relation, link.target);
|
|
335
|
+
const attrs = link.attrs ?? {};
|
|
336
|
+
const contentHash = node_crypto_1.default
|
|
337
|
+
.createHash('sha256')
|
|
338
|
+
.update((0, canonicalize_js_1.canonicalizeJSON)({ id, recordId, link }))
|
|
339
|
+
.digest();
|
|
340
|
+
insertTwinEdge(adapter, {
|
|
341
|
+
edge_id: id,
|
|
342
|
+
edge_type: link.relation,
|
|
343
|
+
subject_ref: { kind: 'memory', id: recordId },
|
|
344
|
+
object_ref: link.target,
|
|
345
|
+
relation_attrs: attrs,
|
|
346
|
+
confidence: 1.0,
|
|
347
|
+
source: access.edgeSource ?? 'agent',
|
|
348
|
+
agent_id: Object.hasOwn(command, 'agentId') ? (command.agentId ?? undefined) : access.agentId,
|
|
349
|
+
model_run_id: command.modelRunId ?? undefined,
|
|
350
|
+
reason_text: typeof link.attrs?.reason === 'string' ? link.attrs.reason : (command.reasoning ?? undefined),
|
|
351
|
+
evidence_refs: command.replaces?.length ? command.replaces : undefined,
|
|
352
|
+
content_hash: contentHash,
|
|
353
|
+
created_at: now,
|
|
354
|
+
});
|
|
355
|
+
return id;
|
|
356
|
+
}
|
|
357
|
+
function workPatch(value) {
|
|
358
|
+
return value?.set ?? {};
|
|
359
|
+
}
|
|
360
|
+
async function appendJudgmentOnAdapter(adapter, command, access, embedder) {
|
|
361
|
+
requireText(command.commandId, 'commandId');
|
|
362
|
+
requireText(command.topic, 'topic');
|
|
363
|
+
requireText(command.summary, 'summary');
|
|
364
|
+
requireText(access.principalId, 'principalId');
|
|
365
|
+
requireText(access.agentId, 'agentId');
|
|
366
|
+
if (command.recordKind !== 'judgment' && command.recordKind !== 'commitment') {
|
|
367
|
+
throw new JudgmentError('INVALID_COMMAND', 'recordKind is invalid');
|
|
368
|
+
}
|
|
369
|
+
if ((command.recordKind === 'commitment') !== (command.work !== undefined)) {
|
|
370
|
+
throw new JudgmentError('INVALID_COMMAND', 'Commitment records require a work assignment');
|
|
371
|
+
}
|
|
372
|
+
validateBounds(command);
|
|
373
|
+
validateCommandFields(command);
|
|
374
|
+
const admittedScopeIdList = admittedScopeIds(access);
|
|
375
|
+
const boundScopeIdList = boundScopeIdsFor(access, command);
|
|
376
|
+
const effectiveScopes = command.scopes ?? [...access.scopes];
|
|
377
|
+
const effectiveCommand = command.scopes ? command : { ...command, scopes: [...access.scopes] };
|
|
378
|
+
const hash = commandHash(effectiveCommand);
|
|
379
|
+
const replay = assertReplay(adapter, command, access, hash);
|
|
380
|
+
if (replay) {
|
|
381
|
+
validateLinks(adapter, command.links ?? [], admittedScopeIdList);
|
|
382
|
+
for (const replacement of command.replaces ?? []) {
|
|
383
|
+
if (!referenceExists(adapter, { kind: 'memory', id: replacement.id }, admittedScopeIdList)) {
|
|
384
|
+
throw new JudgmentError('REFERENCE_NOT_FOUND', 'A replacement target is unavailable');
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
validateAmends(adapter, command.amends ?? [], admittedScopeIdList);
|
|
388
|
+
return replay;
|
|
389
|
+
}
|
|
390
|
+
validateLinks(adapter, command.links ?? [], admittedScopeIdList);
|
|
391
|
+
validateAmends(adapter, command.amends ?? [], admittedScopeIdList);
|
|
392
|
+
const recordId = recordIdForCommand(command);
|
|
393
|
+
const now = Date.now();
|
|
394
|
+
const domainNow = command.recordedAt ?? now;
|
|
395
|
+
const embedding = embedder
|
|
396
|
+
? await embedder.embed(`${command.topic}\n${command.summary}`, 'passage')
|
|
397
|
+
: null;
|
|
398
|
+
const edgeIds = [];
|
|
399
|
+
let workReceipt;
|
|
400
|
+
let replayedReceipt = null;
|
|
401
|
+
const transaction = adapter.transactionImmediate
|
|
402
|
+
? adapter.transactionImmediate.bind(adapter)
|
|
403
|
+
: adapter.transaction.bind(adapter);
|
|
404
|
+
transaction(() => {
|
|
405
|
+
const bindingResult = adapter
|
|
406
|
+
.prepare(`INSERT OR IGNORE INTO command_bindings
|
|
407
|
+
(command_id, principal_id, action, payload_hash, receipt_kind, receipt_key, created_at)
|
|
408
|
+
VALUES (?, ?, 'judgment.append', ?, 'judgment', ?, ?)`)
|
|
409
|
+
.run(command.commandId, access.principalId, hash, recordId, now);
|
|
410
|
+
if (bindingResult.changes === 0) {
|
|
411
|
+
replayedReceipt = assertReplay(adapter, command, access, hash);
|
|
412
|
+
if (!replayedReceipt) {
|
|
413
|
+
throw new Error('Command binding was not readable after a conflict-free insert');
|
|
414
|
+
}
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
const decisionRowId = (0, db_manager_js_1.insertPreparedDecision)(adapter, {
|
|
418
|
+
id: recordId,
|
|
419
|
+
topic: command.topic,
|
|
420
|
+
decision: command.summary,
|
|
421
|
+
reasoning: command.reasoning ?? null,
|
|
422
|
+
outcome: command.outcome ?? null,
|
|
423
|
+
failure_reason: command.failureReason ?? null,
|
|
424
|
+
limitation: command.limitation ?? null,
|
|
425
|
+
user_involvement: command.record?.userInvolvement ?? null,
|
|
426
|
+
session_id: command.record?.sessionId ?? null,
|
|
427
|
+
supersedes: command.replaces?.[0]?.id ?? command.record?.supersedes ?? null,
|
|
428
|
+
refined_from: command.record?.refinedFrom ?? null,
|
|
429
|
+
confidence: command.confidence ?? 0.5,
|
|
430
|
+
created_at: domainNow,
|
|
431
|
+
updated_at: now,
|
|
432
|
+
needs_validation: command.record?.needsValidation ?? 0,
|
|
433
|
+
trust_context: command.record?.trustContext ?? null,
|
|
434
|
+
evidence: toColumnText(command.evidence),
|
|
435
|
+
alternatives: toColumnText(command.alternatives),
|
|
436
|
+
risks: command.risks ?? null,
|
|
437
|
+
event_date: command.eventDate ?? null,
|
|
438
|
+
event_datetime: command.eventDatetime ?? null,
|
|
439
|
+
agent_id: Object.hasOwn(command, 'agentId') ? (command.agentId ?? null) : access.agentId,
|
|
440
|
+
model_run_id: command.modelRunId ?? null,
|
|
441
|
+
envelope_hash: command.envelopeHash ?? null,
|
|
442
|
+
gateway_call_id: command.gatewayCallId ?? null,
|
|
443
|
+
source_refs_json: command.sourceRefs ? (0, canonicalize_js_1.canonicalizeJSON)(command.sourceRefs) : null,
|
|
444
|
+
provenance_json: command.provenance ? (0, canonicalize_js_1.canonicalizeJSON)(command.provenance) : null,
|
|
445
|
+
}, embedding);
|
|
446
|
+
if (!Number.isSafeInteger(decisionRowId) || decisionRowId <= 0) {
|
|
447
|
+
throw new Error('Judgment decision row was not inserted');
|
|
448
|
+
}
|
|
449
|
+
adapter
|
|
450
|
+
.prepare(`UPDATE decisions
|
|
451
|
+
SET record_kind = ?, payload_json = ?, applies_from = ?, applies_until = ?,
|
|
452
|
+
kind = COALESCE(?, kind), status = COALESCE(?, status),
|
|
453
|
+
summary = COALESCE(?, summary), is_static = COALESCE(?, is_static)
|
|
454
|
+
WHERE id = ?`)
|
|
455
|
+
.run(command.recordKind, (0, canonicalize_js_1.canonicalizeJSON)(command.payload ?? {}), command.appliesFrom ?? null, command.appliesUntil ?? null, command.record?.kind ?? null, command.record?.status ?? null,
|
|
456
|
+
// Never leave summary NULL: legacy readers (evolution candidates,
|
|
457
|
+
// recall) assume it is text. The command summary is the authored
|
|
458
|
+
// summary when the record does not carry a distinct one.
|
|
459
|
+
command.record?.summary ?? command.summary, command.record?.isStatic ?? null, recordId);
|
|
460
|
+
for (const [index, scopeId] of boundScopeIdList.entries()) {
|
|
461
|
+
const scope = effectiveScopes[index];
|
|
462
|
+
(0, db_manager_js_1.ensureMemoryScope)(adapter, scope.kind, scope.id);
|
|
463
|
+
adapter
|
|
464
|
+
.prepare('INSERT INTO memory_scope_bindings (memory_id, scope_id, is_primary) VALUES (?, ?, ?)')
|
|
465
|
+
.run(recordId, scopeId, index === 0 ? 1 : 0);
|
|
466
|
+
}
|
|
467
|
+
for (const [index, link] of (command.links ?? []).entries()) {
|
|
468
|
+
edgeIds.push(insertLink(adapter, command, recordId, link, index, access, now));
|
|
469
|
+
}
|
|
470
|
+
for (const replacement of command.replaces ?? []) {
|
|
471
|
+
if (!referenceExists(adapter, { kind: 'memory', id: replacement.id }, admittedScopeIdList)) {
|
|
472
|
+
throw new JudgmentError('REFERENCE_NOT_FOUND', 'A replacement target is unavailable');
|
|
473
|
+
}
|
|
474
|
+
adapter
|
|
475
|
+
.prepare("UPDATE decisions SET superseded_by = ?, status = 'superseded', updated_at = ? WHERE id = ?")
|
|
476
|
+
.run(recordId, domainNow, replacement.id);
|
|
477
|
+
edgeIds.push(insertLink(adapter, { ...command, links: [] }, recordId, {
|
|
478
|
+
relation: 'supersedes',
|
|
479
|
+
target: { kind: 'memory', id: replacement.id },
|
|
480
|
+
attrs: { reason: replacement.reason },
|
|
481
|
+
}, edgeIds.length, access, now));
|
|
482
|
+
}
|
|
483
|
+
for (const amendment of command.amends ?? []) {
|
|
484
|
+
if (!referenceExists(adapter, amendment.target, admittedScopeIdList)) {
|
|
485
|
+
throw new JudgmentError('REFERENCE_NOT_FOUND', 'An amendment target is unavailable');
|
|
486
|
+
}
|
|
487
|
+
applyAmendment(adapter, amendment, domainNow);
|
|
488
|
+
}
|
|
489
|
+
applyProjections(adapter, command, recordId, effectiveScopes, domainNow);
|
|
490
|
+
// A commitment's own clock is domain time, like the record's: `updated_at`
|
|
491
|
+
// is what the board sorts and filters on, so a caller that states when a
|
|
492
|
+
// revision happened must see that time on the row, not the wall clock the
|
|
493
|
+
// write happened to land on.
|
|
494
|
+
if (command.recordKind === 'commitment' && command.work) {
|
|
495
|
+
const work = command.work;
|
|
496
|
+
if (work.operation === 'create') {
|
|
497
|
+
const commitmentId = commitmentIdForCommand(command);
|
|
498
|
+
const imported = work.imported;
|
|
499
|
+
if (imported !== undefined) {
|
|
500
|
+
if (!Number.isSafeInteger(imported.rowId) || imported.rowId <= 0) {
|
|
501
|
+
throw new JudgmentError('INVALID_COMMAND', `imported.rowId must be a positive integer (got: ${imported.rowId})`);
|
|
502
|
+
}
|
|
503
|
+
if (imported.revision !== undefined &&
|
|
504
|
+
(!Number.isSafeInteger(imported.revision) || imported.revision <= 0)) {
|
|
505
|
+
throw new JudgmentError('INVALID_COMMAND', `imported.revision must be a positive integer (got: ${imported.revision})`);
|
|
506
|
+
}
|
|
507
|
+
if (imported.createdAt !== undefined &&
|
|
508
|
+
(!Number.isFinite(imported.createdAt) || imported.createdAt < 0)) {
|
|
509
|
+
throw new JudgmentError('INVALID_COMMAND', `imported.createdAt must be a finite nonnegative epoch (got: ${imported.createdAt})`);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
const revision = imported?.revision ?? 1;
|
|
513
|
+
const createdAt = imported?.createdAt ?? domainNow;
|
|
514
|
+
if (imported !== undefined) {
|
|
515
|
+
// An imported row keeps the task id it already had; sqlite_sequence
|
|
516
|
+
// moves with the explicit insert, so work first stated here still
|
|
517
|
+
// takes the next number after it.
|
|
518
|
+
adapter
|
|
519
|
+
.prepare(`INSERT INTO commitments
|
|
520
|
+
(row_id, commitment_id, current_revision, head_record_id, withdrawn, created_at, updated_at, agent_id, model_run_id)
|
|
521
|
+
VALUES (?, ?, ?, ?, 0, ?, ?, ?, ?)`)
|
|
522
|
+
.run(imported.rowId, commitmentId, revision, recordId, createdAt, domainNow, access.agentId, command.modelRunId ?? null);
|
|
523
|
+
}
|
|
524
|
+
else {
|
|
525
|
+
adapter
|
|
526
|
+
.prepare(`INSERT INTO commitments
|
|
527
|
+
(commitment_id, current_revision, head_record_id, withdrawn, created_at, updated_at, agent_id, model_run_id)
|
|
528
|
+
VALUES (?, ?, ?, 0, ?, ?, ?, ?)`)
|
|
529
|
+
.run(commitmentId, revision, recordId, createdAt, domainNow, access.agentId, command.modelRunId ?? null);
|
|
530
|
+
}
|
|
531
|
+
adapter
|
|
532
|
+
.prepare(`INSERT INTO commitment_assignments
|
|
533
|
+
(commitment_id, revision, record_id, operation, set_json, clear_json, applies_from, applies_until, created_at, agent_id, model_run_id)
|
|
534
|
+
VALUES (?, ?, ?, 'create', ?, ?, ?, ?, ?, ?, ?)`)
|
|
535
|
+
.run(commitmentId, revision, recordId, (0, canonicalize_js_1.canonicalizeJSON)(workPatch(work)), (0, canonicalize_js_1.canonicalizeJSON)(work.clear ?? []), command.eventDatetime ?? null, command.appliesUntil ?? null, domainNow, access.agentId, command.modelRunId ?? null);
|
|
536
|
+
workReceipt = { commitmentId, revision };
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
const current = adapter
|
|
540
|
+
.prepare('SELECT current_revision, withdrawn FROM commitments WHERE commitment_id = ?')
|
|
541
|
+
.get(work.commitmentId);
|
|
542
|
+
if (!current) {
|
|
543
|
+
throw new JudgmentError('REFERENCE_NOT_FOUND', `Commitment is unavailable: ${work.commitmentId}`);
|
|
544
|
+
}
|
|
545
|
+
// Only revise may omit the revision (it appends to the head); withdraw always states it.
|
|
546
|
+
if ((work.expectedRevision !== undefined || work.operation === 'withdraw') &&
|
|
547
|
+
current.current_revision !== work.expectedRevision) {
|
|
548
|
+
throw new JudgmentError('STALE_REVISION', 'Commitment revision is stale');
|
|
549
|
+
}
|
|
550
|
+
if (work.operation === 'revise' && current.withdrawn === 1) {
|
|
551
|
+
throw new JudgmentError('COMMITMENT_WITHDRAWN', 'Withdrawn commitments cannot be revised');
|
|
552
|
+
}
|
|
553
|
+
const revision = current.current_revision + 1;
|
|
554
|
+
adapter
|
|
555
|
+
.prepare(`INSERT INTO commitment_assignments
|
|
556
|
+
(commitment_id, revision, record_id, operation, set_json, clear_json, applies_from, applies_until, created_at, agent_id, model_run_id)
|
|
557
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
558
|
+
.run(work.commitmentId, revision, recordId, work.operation, (0, canonicalize_js_1.canonicalizeJSON)(workPatch(work)), (0, canonicalize_js_1.canonicalizeJSON)(work.clear ?? []), command.eventDatetime ?? null, command.appliesUntil ?? null, domainNow, access.agentId, command.modelRunId ?? null);
|
|
559
|
+
adapter
|
|
560
|
+
.prepare('UPDATE commitments SET current_revision = ?, head_record_id = ?, withdrawn = ?, updated_at = ?, agent_id = ?, model_run_id = ? WHERE commitment_id = ?')
|
|
561
|
+
.run(revision, recordId, work.operation === 'withdraw' ? 1 : 0, domainNow, access.agentId, command.modelRunId ?? null, work.commitmentId);
|
|
562
|
+
workReceipt = { commitmentId: work.commitmentId, revision };
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
(0, event_store_js_1.insertMemoryEventInTransaction)(adapter, {
|
|
566
|
+
event_type: command.event?.eventType ?? 'save',
|
|
567
|
+
actor: command.event?.actor ?? `actor:${access.principalId}`,
|
|
568
|
+
source_turn_id: command.event?.sourceTurnId,
|
|
569
|
+
memory_id: recordId,
|
|
570
|
+
topic: command.topic,
|
|
571
|
+
scope_refs: effectiveScopes,
|
|
572
|
+
evidence_refs: command.event?.evidenceRefs,
|
|
573
|
+
reason: command.event?.reason ?? 'agent judgment command',
|
|
574
|
+
created_at: domainNow,
|
|
575
|
+
});
|
|
576
|
+
const receipt = {
|
|
577
|
+
status: 'committed',
|
|
578
|
+
recordId,
|
|
579
|
+
commandId: command.commandId,
|
|
580
|
+
edgeIds,
|
|
581
|
+
watermark: now,
|
|
582
|
+
...(workReceipt ? { work: workReceipt } : {}),
|
|
583
|
+
diagnostics: [],
|
|
584
|
+
};
|
|
585
|
+
adapter
|
|
586
|
+
.prepare(`INSERT INTO judgment_commands (command_id, record_id, committed_watermark, receipt_json, created_at)
|
|
587
|
+
VALUES (?, ?, ?, ?, ?)`)
|
|
588
|
+
.run(command.commandId, recordId, receipt.watermark, (0, canonicalize_js_1.canonicalizeJSON)(receipt), now);
|
|
589
|
+
});
|
|
590
|
+
if (replayedReceipt) {
|
|
591
|
+
return replayedReceipt;
|
|
592
|
+
}
|
|
593
|
+
// Sync the adapter's status cache for every row whose projection columns were
|
|
594
|
+
// touched: the new record, superseded targets, and amended outcome rows.
|
|
595
|
+
refreshDecisionStatusCaches(adapter, [
|
|
596
|
+
recordId,
|
|
597
|
+
...(command.replaces ?? []).map((replacement) => replacement.id),
|
|
598
|
+
...(command.amends ?? []).map((amendment) => amendment.target.id),
|
|
599
|
+
]);
|
|
600
|
+
return {
|
|
601
|
+
status: 'committed',
|
|
602
|
+
recordId,
|
|
603
|
+
commandId: command.commandId,
|
|
604
|
+
edgeIds,
|
|
605
|
+
watermark: now,
|
|
606
|
+
...(workReceipt ? { work: workReceipt } : {}),
|
|
607
|
+
diagnostics: [],
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
function refreshDecisionStatusCaches(adapter, memoryIds) {
|
|
611
|
+
if (!adapter.refreshDecisionStatusCache)
|
|
612
|
+
return;
|
|
613
|
+
const stmt = adapter.prepare('SELECT rowid FROM decisions WHERE id = ?');
|
|
614
|
+
for (const memoryId of new Set(memoryIds)) {
|
|
615
|
+
const row = stmt.get(memoryId);
|
|
616
|
+
if (row) {
|
|
617
|
+
adapter.refreshDecisionStatusCache(row.rowid);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
async function appendJudgment(command, access, options) {
|
|
622
|
+
// The command boundary no longer reaches the process-global store: the
|
|
623
|
+
// caller names the database this judgment commits to.
|
|
624
|
+
if (!options?.adapter) {
|
|
625
|
+
throw new JudgmentError('INVALID_COMMAND', 'appendJudgment requires an explicit adapter; the process-global store is not a write path');
|
|
626
|
+
}
|
|
627
|
+
return appendJudgmentOnAdapter(options.adapter, command, access, options.embedder);
|
|
628
|
+
}
|
|
629
|
+
function createJudgmentWriter(options) {
|
|
630
|
+
return {
|
|
631
|
+
appendJudgment: (command, access) => appendJudgmentOnAdapter(options.adapter, command, access, options.embedder),
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
const EDGE_TYPE_SET = new Set(twin_edge_types_js_1.TWIN_EDGE_TYPES);
|
|
635
|
+
const REF_KIND_SET = new Set(twin_edge_types_js_1.TWIN_REF_KINDS);
|
|
636
|
+
const SOURCE_SET = new Set(twin_edge_types_js_1.TWIN_EDGE_SOURCES);
|
|
637
|
+
function nullableString(value) {
|
|
638
|
+
return typeof value === 'string' && value.length > 0 ? value : null;
|
|
639
|
+
}
|
|
640
|
+
function requireNonEmptyString(value, field) {
|
|
641
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
642
|
+
throw new Error(`twin_edges.${field} must be a non-empty string`);
|
|
643
|
+
}
|
|
644
|
+
return value;
|
|
645
|
+
}
|
|
646
|
+
function normalizeEdgeType(value) {
|
|
647
|
+
if (typeof value === 'string' && EDGE_TYPE_SET.has(value)) {
|
|
648
|
+
return value;
|
|
649
|
+
}
|
|
650
|
+
throw new Error(`Unsupported twin edge type: ${String(value)}`);
|
|
651
|
+
}
|
|
652
|
+
function normalizeSource(value) {
|
|
653
|
+
if (typeof value === 'string' && SOURCE_SET.has(value)) {
|
|
654
|
+
return value;
|
|
655
|
+
}
|
|
656
|
+
throw new Error(`Unsupported twin edge source: ${String(value)}`);
|
|
657
|
+
}
|
|
658
|
+
function normalizeRef(ref, field) {
|
|
659
|
+
if (!ref || typeof ref !== 'object') {
|
|
660
|
+
throw new Error(`${field} must be a TwinRef`);
|
|
661
|
+
}
|
|
662
|
+
if (!REF_KIND_SET.has(ref.kind)) {
|
|
663
|
+
throw new Error(`${field}.kind is unsupported: ${String(ref.kind)}`);
|
|
664
|
+
}
|
|
665
|
+
return {
|
|
666
|
+
kind: ref.kind,
|
|
667
|
+
id: requireNonEmptyString(ref.id, `${field}.id`),
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
function parseJsonField(value, field, edgeId) {
|
|
671
|
+
if (value === null || value === undefined) {
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
const text = String(value);
|
|
675
|
+
if (text.length === 0) {
|
|
676
|
+
return null;
|
|
677
|
+
}
|
|
678
|
+
try {
|
|
679
|
+
return JSON.parse(text);
|
|
680
|
+
}
|
|
681
|
+
catch (error) {
|
|
682
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
683
|
+
throw new Error(`Invalid twin_edges.${field} for ${edgeId}: ${message}`);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
function toBuffer(value) {
|
|
687
|
+
if (Buffer.isBuffer(value)) {
|
|
688
|
+
return value;
|
|
689
|
+
}
|
|
690
|
+
if (value instanceof Uint8Array) {
|
|
691
|
+
return Buffer.from(value);
|
|
692
|
+
}
|
|
693
|
+
throw new Error('twin_edges.content_hash must be a 32-byte Buffer');
|
|
694
|
+
}
|
|
695
|
+
function mapTwinEdgeRow(row) {
|
|
696
|
+
const edgeId = String(row.edge_id);
|
|
697
|
+
return {
|
|
698
|
+
edge_id: edgeId,
|
|
699
|
+
edge_type: normalizeEdgeType(row.edge_type),
|
|
700
|
+
subject_ref: {
|
|
701
|
+
kind: String(row.subject_kind),
|
|
702
|
+
id: String(row.subject_id),
|
|
703
|
+
},
|
|
704
|
+
object_ref: {
|
|
705
|
+
kind: String(row.object_kind),
|
|
706
|
+
id: String(row.object_id),
|
|
707
|
+
},
|
|
708
|
+
relation_attrs_json: nullableString(row.relation_attrs_json),
|
|
709
|
+
relation_attrs: parseJsonField(row.relation_attrs_json, 'relation_attrs_json', edgeId),
|
|
710
|
+
confidence: Number(row.confidence),
|
|
711
|
+
source: normalizeSource(row.source),
|
|
712
|
+
agent_id: nullableString(row.agent_id),
|
|
713
|
+
model_run_id: nullableString(row.model_run_id),
|
|
714
|
+
envelope_hash: nullableString(row.envelope_hash),
|
|
715
|
+
human_actor_id: nullableString(row.human_actor_id),
|
|
716
|
+
human_actor_role: nullableString(row.human_actor_role),
|
|
717
|
+
authority_scope_json: nullableString(row.authority_scope_json),
|
|
718
|
+
authority_scope: parseJsonField(row.authority_scope_json, 'authority_scope_json', edgeId),
|
|
719
|
+
reason_classification: nullableString(row.reason_classification),
|
|
720
|
+
reason_text: nullableString(row.reason_text),
|
|
721
|
+
evidence_refs_json: nullableString(row.evidence_refs_json),
|
|
722
|
+
evidence_refs: parseJsonField(row.evidence_refs_json, 'evidence_refs_json', edgeId),
|
|
723
|
+
request_idempotency_key: nullableString(row.request_idempotency_key),
|
|
724
|
+
edge_idempotency_key: nullableString(row.edge_idempotency_key),
|
|
725
|
+
content_hash: toBuffer(row.content_hash),
|
|
726
|
+
created_at: Number(row.created_at),
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
function jsonColumn(value) {
|
|
730
|
+
if (value === null || value === undefined) {
|
|
731
|
+
return null;
|
|
732
|
+
}
|
|
733
|
+
return typeof value === 'string' ? value : (0, canonicalize_js_1.canonicalizeJSON)(value);
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* The single write boundary for twin_edges. Both former write sites (the
|
|
737
|
+
* alias_of edge inside the alias write and the judgment-command link insert)
|
|
738
|
+
* now resolve their columns into a TwinEdgeInsert and call here, inside the
|
|
739
|
+
* caller's transaction. The row is read back so callers get the stored record.
|
|
740
|
+
*/
|
|
741
|
+
function insertTwinEdge(adapter, input) {
|
|
742
|
+
adapter
|
|
743
|
+
.prepare(`
|
|
744
|
+
INSERT INTO twin_edges (
|
|
745
|
+
edge_id, edge_type, subject_kind, subject_id, object_kind, object_id,
|
|
746
|
+
relation_attrs_json, confidence, source, agent_id, model_run_id, envelope_hash,
|
|
747
|
+
human_actor_id, human_actor_role, authority_scope_json, reason_classification,
|
|
748
|
+
reason_text, evidence_refs_json, request_idempotency_key, edge_idempotency_key,
|
|
749
|
+
content_hash, created_at
|
|
750
|
+
)
|
|
751
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
752
|
+
`)
|
|
753
|
+
.run(input.edge_id, input.edge_type, input.subject_ref.kind, input.subject_ref.id, input.object_ref.kind, input.object_ref.id, jsonColumn(input.relation_attrs), input.confidence ?? 1, input.source, input.agent_id ?? null, input.model_run_id ?? null, input.envelope_hash ?? null, input.human_actor_id ?? null, input.human_actor_role ?? null, jsonColumn(input.authority_scope_json), input.reason_classification ?? null, input.reason_text ?? null, jsonColumn(input.evidence_refs), input.request_idempotency_key ?? null, input.edge_idempotency_key ?? null, input.content_hash, input.created_at);
|
|
754
|
+
const record = getTwinEdge(adapter, input.edge_id);
|
|
755
|
+
if (!record) {
|
|
756
|
+
throw new Error(`Twin edge was not written: ${input.edge_id}`);
|
|
757
|
+
}
|
|
758
|
+
return record;
|
|
759
|
+
}
|
|
760
|
+
function getTwinEdge(adapter, edgeId) {
|
|
761
|
+
const row = adapter.prepare('SELECT * FROM twin_edges WHERE edge_id = ?').get(edgeId);
|
|
762
|
+
return row ? mapTwinEdgeRow(row) : null;
|
|
763
|
+
}
|
|
764
|
+
function listTwinEdgesForRefs(adapter, refs, page) {
|
|
765
|
+
const normalizedRefs = refs.map((ref, index) => normalizeRef(ref, `refs[${index}]`));
|
|
766
|
+
if (normalizedRefs.length === 0) {
|
|
767
|
+
return [];
|
|
768
|
+
}
|
|
769
|
+
const clauses = [];
|
|
770
|
+
const params = [];
|
|
771
|
+
for (const ref of normalizedRefs) {
|
|
772
|
+
clauses.push('(subject_kind = ? AND subject_id = ?)');
|
|
773
|
+
params.push(ref.kind, ref.id);
|
|
774
|
+
clauses.push('(object_kind = ? AND object_id = ?)');
|
|
775
|
+
params.push(ref.kind, ref.id);
|
|
776
|
+
}
|
|
777
|
+
const where = [`(${clauses.join(' OR ')})`];
|
|
778
|
+
if (page?.edgeTypes?.length) {
|
|
779
|
+
where.push(`edge_type IN (${page.edgeTypes.map(() => '?').join(', ')})`);
|
|
780
|
+
params.push(...page.edgeTypes);
|
|
781
|
+
}
|
|
782
|
+
if (typeof page?.startMs === 'number') {
|
|
783
|
+
where.push('created_at >= ?');
|
|
784
|
+
params.push(page.startMs);
|
|
785
|
+
}
|
|
786
|
+
if (typeof page?.asOfMs === 'number') {
|
|
787
|
+
where.push('created_at <= ?');
|
|
788
|
+
params.push(page.asOfMs);
|
|
789
|
+
}
|
|
790
|
+
if (page?.after) {
|
|
791
|
+
where.push('(created_at < ? OR (created_at = ? AND edge_id > ?))');
|
|
792
|
+
params.push(page.after.createdAt, page.after.createdAt, page.after.edgeId);
|
|
793
|
+
}
|
|
794
|
+
if (page && (!Number.isSafeInteger(page.limit) || page.limit < 1)) {
|
|
795
|
+
throw new Error('Twin edge scan limit must be a positive safe integer');
|
|
796
|
+
}
|
|
797
|
+
const rows = adapter
|
|
798
|
+
.prepare(`
|
|
799
|
+
SELECT *
|
|
800
|
+
FROM twin_edges
|
|
801
|
+
WHERE ${where.join(' AND ')}
|
|
802
|
+
ORDER BY created_at ${page ? 'DESC' : 'ASC'}, edge_id ASC
|
|
803
|
+
${page ? 'LIMIT ?' : ''}
|
|
804
|
+
`)
|
|
805
|
+
.all(...params, ...(page ? [page.limit] : []));
|
|
806
|
+
return rows.map(mapTwinEdgeRow);
|
|
807
|
+
}
|
|
808
|
+
//# sourceMappingURL=judgments.js.map
|