@lucern/graph-primitives 1.0.28 → 1.0.30
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/dist/{beliefDecay-DZ6tkLYq.d.ts → beliefDecay-BmkEk5OJ.d.ts} +3 -3
- package/dist/beliefDecay.d.ts +1 -1
- package/dist/beliefDecay.js +448 -314
- package/dist/beliefDecay.js.map +1 -1
- package/dist/{beliefEvidenceLinks-CWOXxxJg.d.ts → beliefEvidenceLinks-BzfjON_6.d.ts} +13 -13
- package/dist/beliefEvidenceLinks.d.ts +1 -1
- package/dist/beliefEvidenceLinks.js +843 -624
- package/dist/beliefEvidenceLinks.js.map +1 -1
- package/dist/beliefEvidenceLinks.operational.d.ts +7 -5
- package/dist/beliefEvidenceLinks.operational.js +91 -18
- package/dist/beliefEvidenceLinks.operational.js.map +1 -1
- package/dist/beliefLifecycle.js.map +1 -1
- package/dist/confidencePropagationDispatch.d.ts +28 -27
- package/dist/confidencePropagationDispatch.js +157 -99
- package/dist/confidencePropagationDispatch.js.map +1 -1
- package/dist/{contradictions-51VLsESq.d.ts → contradictions-BATPuZTL.d.ts} +10 -10
- package/dist/contradictions.d.ts +1 -1
- package/dist/contradictions.js +398 -228
- package/dist/contradictions.js.map +1 -1
- package/dist/convex.d.ts +65 -30
- package/dist/convex.js +7 -3
- package/dist/convex.js.map +1 -1
- package/dist/debug.js.map +1 -1
- package/dist/edgeValidation.js +293 -85
- package/dist/edgeValidation.js.map +1 -1
- package/dist/edges/contains.d.ts +1 -1
- package/dist/edges/contains.js.map +1 -1
- package/dist/edges/contradicts.d.ts +1 -1
- package/dist/edges/contradicts.js.map +1 -1
- package/dist/edges/{dependsOn.d.ts → depends-on.d.ts} +1 -1
- package/dist/edges/{dependsOn.js → depends-on.js} +4 -4
- package/dist/edges/depends-on.js.map +1 -0
- package/dist/edges/{derivedFrom.d.ts → derived-from.d.ts} +1 -1
- package/dist/edges/{derivedFrom.js → derived-from.js} +3 -3
- package/dist/edges/derived-from.js.map +1 -0
- package/dist/edges/elaborates.d.ts +1 -1
- package/dist/edges/elaborates.js.map +1 -1
- package/dist/edges/index.d.ts +7 -3
- package/dist/edges/index.js +7 -4
- package/dist/edges/index.js.map +1 -1
- package/dist/edges/informs.d.ts +1 -1
- package/dist/edges/informs.js.map +1 -1
- package/dist/edges/{propagationTypes.d.ts → propagation-types.d.ts} +14 -14
- package/dist/edges/{propagationTypes.js → propagation-types.js} +3 -3
- package/dist/edges/propagation-types.js.map +1 -0
- package/dist/edges/refutes.d.ts +1 -1
- package/dist/edges/refutes.js.map +1 -1
- package/dist/edges/supports.d.ts +1 -1
- package/dist/edges/supports.js.map +1 -1
- package/dist/edges/tests.d.ts +1 -1
- package/dist/edges/tests.js.map +1 -1
- package/dist/edges/utils.d.ts +1 -1
- package/dist/edges/utils.js.map +1 -1
- package/dist/embeddingTrigger.d.ts +14 -6
- package/dist/embeddingTrigger.js +11 -14
- package/dist/embeddingTrigger.js.map +1 -1
- package/dist/{entityBridge-DMaKooYn.d.ts → entityBridge-BhVDM3pc.d.ts} +5 -5
- package/dist/entityBridge.d.ts +1 -1
- package/dist/entityBridge.js +602 -225
- package/dist/entityBridge.js.map +1 -1
- package/dist/entityCanonicalMatch.d.ts +14 -12
- package/dist/entityCanonicalMatch.js.map +1 -1
- package/dist/{entityLifecycle-CvgSK5FV.d.ts → entityLifecycle-BsfCz9pS.d.ts} +5 -9
- package/dist/entityLifecycle.d.ts +1 -1
- package/dist/entityLifecycle.js +857 -515
- package/dist/entityLifecycle.js.map +1 -1
- package/dist/{entityValidation-KLZ_Xl2D.d.ts → entityValidation-B1yNEHJx.d.ts} +7 -6
- package/dist/entityValidation.d.ts +3 -1
- package/dist/entityValidation.js +60 -8
- package/dist/entityValidation.js.map +1 -1
- package/dist/{epistemicAnswers-C5ib4z6_.d.ts → epistemicAnswers-f47YMu9U.d.ts} +6 -6
- package/dist/epistemicAnswers.d.ts +1 -1
- package/dist/epistemicAnswers.js +587 -545
- package/dist/epistemicAnswers.js.map +1 -1
- package/dist/epistemicBeliefs.admin.d.ts +8 -8
- package/dist/epistemicBeliefs.admin.js +366 -203
- package/dist/epistemicBeliefs.admin.js.map +1 -1
- package/dist/epistemicBeliefs.backfills.d.ts +8 -8
- package/dist/epistemicBeliefs.backfills.js +655 -308
- package/dist/epistemicBeliefs.backfills.js.map +1 -1
- package/dist/epistemicBeliefs.confidence.d.ts +19 -14
- package/dist/epistemicBeliefs.confidence.js +634 -423
- package/dist/epistemicBeliefs.confidence.js.map +1 -1
- package/dist/epistemicBeliefs.core.d.ts +6 -6
- package/dist/epistemicBeliefs.core.js +719 -411
- package/dist/epistemicBeliefs.core.js.map +1 -1
- package/dist/epistemicBeliefs.d.ts +11 -8
- package/dist/epistemicBeliefs.forkEvidence.d.ts +2 -0
- package/dist/epistemicBeliefs.forkEvidence.js +8 -28
- package/dist/epistemicBeliefs.forkEvidence.js.map +1 -1
- package/dist/epistemicBeliefs.helpers.d.ts +69 -74
- package/dist/epistemicBeliefs.helpers.js +359 -248
- package/dist/epistemicBeliefs.helpers.js.map +1 -1
- package/dist/epistemicBeliefs.internal.d.ts +5 -5
- package/dist/epistemicBeliefs.internal.js +1246 -1044
- package/dist/epistemicBeliefs.internal.js.map +1 -1
- package/dist/epistemicBeliefs.js +4922 -3608
- package/dist/epistemicBeliefs.js.map +1 -1
- package/dist/epistemicBeliefs.lifecycle.d.ts +5 -5
- package/dist/epistemicBeliefs.lifecycle.js +1137 -818
- package/dist/epistemicBeliefs.lifecycle.js.map +1 -1
- package/dist/epistemicBeliefs.links.d.ts +7 -7
- package/dist/epistemicBeliefs.links.js +408 -307
- package/dist/epistemicBeliefs.links.js.map +1 -1
- package/dist/epistemicBeliefs.queries.d.ts +4 -4
- package/dist/epistemicBeliefs.queries.js +175 -20
- package/dist/epistemicBeliefs.queries.js.map +1 -1
- package/dist/epistemicBeliefs.topicAnchor.d.ts +6 -4
- package/dist/epistemicBeliefs.topicAnchor.js +12 -5
- package/dist/epistemicBeliefs.topicAnchor.js.map +1 -1
- package/dist/epistemicContracts.d.ts +28 -3
- package/dist/epistemicContracts.evaluators.d.ts +2 -0
- package/dist/epistemicContracts.evaluators.js +1063 -613
- package/dist/epistemicContracts.evaluators.js.map +1 -1
- package/dist/epistemicContracts.handlers.d.ts +15 -32
- package/dist/epistemicContracts.handlers.js +2086 -1644
- package/dist/epistemicContracts.handlers.js.map +1 -1
- package/dist/epistemicContracts.js +1131 -672
- package/dist/epistemicContracts.js.map +1 -1
- package/dist/epistemicContracts.metrics.d.ts +2 -0
- package/dist/epistemicContracts.metrics.js +375 -158
- package/dist/epistemicContracts.metrics.js.map +1 -1
- package/dist/epistemicContracts.types.d.ts +87 -81
- package/dist/epistemicEdgeCreation.d.ts +2 -0
- package/dist/epistemicEdgeCreation.js +87 -16
- package/dist/epistemicEdgeCreation.js.map +1 -1
- package/dist/{epistemicEdges-BF-cn4i3.d.ts → epistemicEdges-BGBh0QSP.d.ts} +4 -7
- package/dist/epistemicEdges.d.ts +6 -5
- package/dist/epistemicEdges.handlers.d.ts +3 -3
- package/dist/epistemicEdges.handlers.js +129 -24
- package/dist/epistemicEdges.handlers.js.map +1 -1
- package/dist/epistemicEdges.helpers.d.ts +6 -4
- package/dist/epistemicEdges.helpers.js +37 -2
- package/dist/epistemicEdges.helpers.js.map +1 -1
- package/dist/epistemicEdges.js +1969 -1205
- package/dist/epistemicEdges.js.map +1 -1
- package/dist/epistemicEdges.mutations.d.ts +7 -7
- package/dist/epistemicEdges.mutations.js +960 -583
- package/dist/epistemicEdges.mutations.js.map +1 -1
- package/dist/epistemicEdges.queries.d.ts +16 -16
- package/dist/epistemicEdges.queries.js +639 -367
- package/dist/epistemicEdges.queries.js.map +1 -1
- package/dist/epistemicEdges.types.d.ts +10 -8
- package/dist/epistemicEvidence.d.ts +4 -1
- package/dist/epistemicEvidence.js +937 -536
- package/dist/epistemicEvidence.js.map +1 -1
- package/dist/epistemicEvidenceHelpers.d.ts +26 -10
- package/dist/epistemicEvidenceHelpers.js +239 -200
- package/dist/epistemicEvidenceHelpers.js.map +1 -1
- package/dist/epistemicEvidenceMutations.d.ts +8 -8
- package/dist/epistemicEvidenceMutations.js +844 -696
- package/dist/epistemicEvidenceMutations.js.map +1 -1
- package/dist/epistemicEvidenceQueries.d.ts +8 -8
- package/dist/epistemicEvidenceQueries.js +514 -238
- package/dist/epistemicEvidenceQueries.js.map +1 -1
- package/dist/epistemicHelpers.d.ts +4 -2
- package/dist/epistemicHelpers.js +308 -134
- package/dist/epistemicHelpers.js.map +1 -1
- package/dist/epistemicInsert.d.ts +16 -4
- package/dist/epistemicInsert.js +6 -3
- package/dist/epistemicInsert.js.map +1 -1
- package/dist/epistemicLayerRules.d.ts +10 -8
- package/dist/epistemicLayerRules.js +1 -5
- package/dist/epistemicLayerRules.js.map +1 -1
- package/dist/{epistemicLinking-CfE00tHJ.d.ts → epistemicLinking-CsCDv2cN.d.ts} +3 -3
- package/dist/epistemicLinking.d.ts +1 -1
- package/dist/epistemicLinking.js +177 -100
- package/dist/epistemicLinking.js.map +1 -1
- package/dist/epistemicNodeCreation.d.ts +2 -0
- package/dist/epistemicNodeCreation.js +203 -40
- package/dist/epistemicNodeCreation.js.map +1 -1
- package/dist/{epistemicNodes-BCQxpYx_.d.ts → epistemicNodes-CokAgBHg.d.ts} +3 -3
- package/dist/epistemicNodes.d.ts +3 -3
- package/dist/epistemicNodes.helpers.d.ts +24 -15
- package/dist/epistemicNodes.helpers.js.map +1 -1
- package/dist/epistemicNodes.internal.d.ts +6 -6
- package/dist/epistemicNodes.internal.js +389 -319
- package/dist/epistemicNodes.internal.js.map +1 -1
- package/dist/epistemicNodes.js +704 -508
- package/dist/epistemicNodes.js.map +1 -1
- package/dist/epistemicNodes.mutations.d.ts +6 -6
- package/dist/epistemicNodes.mutations.js +564 -467
- package/dist/epistemicNodes.mutations.js.map +1 -1
- package/dist/epistemicNodes.queries.d.ts +8 -8
- package/dist/epistemicNodes.queries.js +311 -314
- package/dist/epistemicNodes.queries.js.map +1 -1
- package/dist/epistemicNodes.validators.d.ts +2 -2
- package/dist/epistemicNodes.validators.js.map +1 -1
- package/dist/epistemicQuestions.conviction.d.ts +8 -8
- package/dist/epistemicQuestions.conviction.js +665 -484
- package/dist/epistemicQuestions.conviction.js.map +1 -1
- package/dist/epistemicQuestions.create.d.ts +4 -4
- package/dist/epistemicQuestions.create.js +640 -612
- package/dist/epistemicQuestions.create.js.map +1 -1
- package/dist/epistemicQuestions.d.ts +8 -5
- package/dist/epistemicQuestions.evidence.d.ts +2 -2
- package/dist/epistemicQuestions.evidence.js +475 -383
- package/dist/epistemicQuestions.evidence.js.map +1 -1
- package/dist/epistemicQuestions.helpers.d.ts +125 -24
- package/dist/epistemicQuestions.helpers.js +240 -209
- package/dist/epistemicQuestions.helpers.js.map +1 -1
- package/dist/epistemicQuestions.js +3474 -2823
- package/dist/epistemicQuestions.js.map +1 -1
- package/dist/epistemicQuestions.lifecycle.d.ts +2 -2
- package/dist/epistemicQuestions.lifecycle.js +607 -546
- package/dist/epistemicQuestions.lifecycle.js.map +1 -1
- package/dist/epistemicQuestions.queries.d.ts +12 -7
- package/dist/epistemicQuestions.queries.js +305 -244
- package/dist/epistemicQuestions.queries.js.map +1 -1
- package/dist/epistemicQuestions.sprint.d.ts +2 -2
- package/dist/epistemicQuestions.sprint.js +600 -394
- package/dist/epistemicQuestions.sprint.js.map +1 -1
- package/dist/epistemicQuestions.tail.d.ts +6 -6
- package/dist/epistemicQuestions.tail.js +572 -433
- package/dist/epistemicQuestions.tail.js.map +1 -1
- package/dist/{epistemicSources-dlKj58Jp.d.ts → epistemicSources-DQtaEkWs.d.ts} +4 -4
- package/dist/epistemicSources.d.ts +1 -1
- package/dist/epistemicSources.js +352 -312
- package/dist/epistemicSources.js.map +1 -1
- package/dist/evaluators/index.d.ts +8 -6
- package/dist/evaluators/index.js +399 -167
- package/dist/evaluators/index.js.map +1 -1
- package/dist/evaluators/lint-checker-evaluator.d.ts +16 -0
- package/dist/evaluators/{lintCheckerEvaluator.js → lint-checker-evaluator.js} +10 -5
- package/dist/evaluators/lint-checker-evaluator.js.map +1 -0
- package/dist/evaluators/{sentryCheckerEvaluator.d.ts → sentry-checker-evaluator.d.ts} +7 -2
- package/dist/evaluators/{sentryCheckerEvaluator.js → sentry-checker-evaluator.js} +3 -3
- package/dist/evaluators/sentry-checker-evaluator.js.map +1 -0
- package/dist/evaluators/shared.d.ts +2 -2
- package/dist/evaluators/shared.js +3 -1
- package/dist/evaluators/shared.js.map +1 -1
- package/dist/evaluators/{testRunnerEvaluator.d.ts → test-runner-evaluator.d.ts} +6 -1
- package/dist/evaluators/{testRunnerEvaluator.js → test-runner-evaluator.js} +6 -4
- package/dist/evaluators/test-runner-evaluator.js.map +1 -0
- package/dist/evaluators/tsc-checker-evaluator.d.ts +16 -0
- package/dist/evaluators/{tscCheckerEvaluator.js → tsc-checker-evaluator.js} +10 -5
- package/dist/evaluators/tsc-checker-evaluator.js.map +1 -0
- package/dist/graphTypes.js +6 -2
- package/dist/graphTypes.js.map +1 -1
- package/dist/helpers.d.ts +2 -0
- package/dist/helpers.js +313 -93
- package/dist/helpers.js.map +1 -1
- package/dist/{index-C-Kyd7hD.d.ts → index-DZxyC9Pb.d.ts} +7 -6
- package/dist/index.d.ts +87 -83
- package/dist/index.js +15677 -10594
- package/dist/index.js.map +1 -1
- package/dist/invariantEnforcement.d.ts +3 -3
- package/dist/invariantEnforcement.js.map +1 -1
- package/dist/logicalRoleInference.d.ts +2 -0
- package/dist/logicalRoleInference.js +1 -1
- package/dist/logicalRoleInference.js.map +1 -1
- package/dist/matcherFeedbackUtils.d.ts +2 -2
- package/dist/matcherFeedbackUtils.js.map +1 -1
- package/dist/{ontology-matching-C6rrz2VP.d.ts → ontology-matching-C-mYFrir.d.ts} +16 -16
- package/dist/ontology-matching.d.ts +1 -1
- package/dist/{ontologyApproval-CFYmqKmk.d.ts → ontologyApproval-BVt0feJi.d.ts} +10 -10
- package/dist/ontologyApproval.d.ts +1 -1
- package/dist/ontologyApproval.js +7 -1
- package/dist/ontologyApproval.js.map +1 -1
- package/dist/ontologyDefinitions.d.ts +14 -24
- package/dist/ontologyDefinitions.js +269 -34
- package/dist/ontologyDefinitions.js.map +1 -1
- package/dist/ontologyHelpers.d.ts +13 -13
- package/dist/ontologyHelpers.js.map +1 -1
- package/dist/{ontologyRegistry-B67rPJ16.d.ts → ontologyRegistry-CljS-ENv.d.ts} +2 -2
- package/dist/ontologyRegistry.d.ts +1 -1
- package/dist/ontologyRegistry.js +34 -6
- package/dist/ontologyRegistry.js.map +1 -1
- package/dist/{projectionReconciliation-jww2fBI0.d.ts → projectionReconciliation-DnrSgHSQ.d.ts} +4 -4
- package/dist/projectionReconciliation.d.ts +1 -1
- package/dist/projectionReconciliation.js +57 -10
- package/dist/projectionReconciliation.js.map +1 -1
- package/dist/{projectionStaleness-CmdbpjVK.d.ts → projectionStaleness-C8ImQ2zP.d.ts} +17 -17
- package/dist/projectionStaleness.d.ts +1 -1
- package/dist/projectionStaleness.js +8 -2
- package/dist/projectionStaleness.js.map +1 -1
- package/dist/proof-attestation.json +1 -1
- package/dist/{questionEvidenceLinks-DFlyPpAj.d.ts → questionEvidenceLinks-_nPRa-LY.d.ts} +10 -10
- package/dist/questionEvidenceLinks.d.ts +1 -1
- package/dist/questionEvidenceLinks.js +564 -347
- package/dist/questionEvidenceLinks.js.map +1 -1
- package/dist/{resolverTypes-CC8Ea2E2.d.ts → resolverTypes-BOXPxLET.d.ts} +8 -7
- package/dist/resolverTypes.d.ts +4 -2
- package/dist/{resolvers-Br1a6eLV.d.ts → resolvers-B1TIBmRO.d.ts} +3 -1
- package/dist/resolvers.d.ts +5 -3
- package/dist/resolvers.js +121 -77
- package/dist/resolvers.js.map +1 -1
- package/dist/scopeResolverCompat.d.ts +10 -7
- package/dist/scopeResolverCompat.js +106 -123
- package/dist/scopeResolverCompat.js.map +1 -1
- package/dist/{text-matching-DNg4M5Wd.d.ts → text-matching-DzFooju6.d.ts} +7 -7
- package/dist/text-matching.d.ts +1 -1
- package/dist/topicOntologyResolver.d.ts +22 -21
- package/dist/topicOntologyResolver.js +54 -32
- package/dist/topicOntologyResolver.js.map +1 -1
- package/dist/topicProjectOverlay.d.ts +30 -20
- package/dist/topicProjectOverlay.js +120 -76
- package/dist/topicProjectOverlay.js.map +1 -1
- package/dist/{topicScope-7zhyeGl7.d.ts → topicScope-DJVa0mLa.d.ts} +22 -7
- package/dist/topicScope.d.ts +3 -1
- package/dist/topicScope.js +104 -119
- package/dist/topicScope.js.map +1 -1
- package/dist/workflowBridge.d.ts +26 -15
- package/dist/workflowBridge.js +140 -144
- package/dist/workflowBridge.js.map +1 -1
- package/dist/workspaceIsolation.d.ts +14 -12
- package/dist/workspaceIsolation.js +108 -122
- package/dist/workspaceIsolation.js.map +1 -1
- package/package.json +4 -4
- package/dist/edges/dependsOn.js.map +0 -1
- package/dist/edges/derivedFrom.js.map +0 -1
- package/dist/edges/propagationTypes.js.map +0 -1
- package/dist/evaluators/lintCheckerEvaluator.d.ts +0 -11
- package/dist/evaluators/lintCheckerEvaluator.js.map +0 -1
- package/dist/evaluators/sentryCheckerEvaluator.js.map +0 -1
- package/dist/evaluators/testRunnerEvaluator.js.map +0 -1
- package/dist/evaluators/tscCheckerEvaluator.d.ts +0 -11
- package/dist/evaluators/tscCheckerEvaluator.js.map +0 -1
- package/dist/{epistemicQuestions-bwHd2FWE.d.ts → epistemicQuestions-Do1fhYm5.d.ts} +4 -4
package/dist/epistemicSources.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { checkScopeAccess, requireProjectAccess } from '@lucern/access-control/access';
|
|
1
|
+
import { checkScopeAccess, requireScopeWriteAccess } from '@lucern/access-control/access';
|
|
3
2
|
import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
|
|
4
|
-
import {
|
|
3
|
+
import { v } from 'convex/values';
|
|
4
|
+
import { unsafeConvexAnyApi } from '@lucern/contracts/convex/unsafeAnyApi';
|
|
5
|
+
import { componentsGeneric, mutationGeneric, queryGeneric } from 'convex/server';
|
|
6
|
+
import '@lucern/contracts';
|
|
5
7
|
import { generateGlobalId, assertUuidV7Identity } from '@lucern/contracts/ids';
|
|
6
8
|
import { isNodeType, getLayerForNodeType } from '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
|
|
7
|
-
import '@lucern/contracts';
|
|
8
9
|
|
|
9
10
|
// src/epistemicSources.ts
|
|
10
|
-
var
|
|
11
|
+
var unsafeApi = unsafeConvexAnyApi(
|
|
12
|
+
"graph-primitives top-level module bundle lacks a committed Convex _generated/api surface"
|
|
13
|
+
);
|
|
14
|
+
var api = unsafeApi;
|
|
11
15
|
componentsGeneric();
|
|
12
|
-
var internal =
|
|
16
|
+
var internal = unsafeApi;
|
|
13
17
|
var mutation = mutationGeneric;
|
|
14
18
|
var query = queryGeneric;
|
|
15
19
|
|
|
@@ -26,22 +30,19 @@ function debugGraphPrimitiveFallback(message, context) {
|
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
// src/embeddingTrigger.ts
|
|
33
|
+
var embeddingActionRef = "embeddingActions:generateEpistemicNodeEmbedding";
|
|
29
34
|
async function scheduleEmbeddingGeneration(args) {
|
|
30
35
|
try {
|
|
31
|
-
await args.ctx.scheduler.runAfter(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
hasAnswer: args.hasAnswer,
|
|
42
|
-
confidence: args.confidence
|
|
43
|
-
}
|
|
44
|
-
);
|
|
36
|
+
await args.ctx.scheduler.runAfter(0, embeddingActionRef, {
|
|
37
|
+
nodeId: args.nodeId,
|
|
38
|
+
projectId: args.projectId ? String(args.projectId) : void 0,
|
|
39
|
+
topicId: args.topicId ? String(args.topicId) : void 0,
|
|
40
|
+
createdBy: args.createdBy,
|
|
41
|
+
nodeType: args.nodeType,
|
|
42
|
+
text: args.text.slice(0, 2e4),
|
|
43
|
+
hasAnswer: args.hasAnswer,
|
|
44
|
+
confidence: args.confidence
|
|
45
|
+
});
|
|
45
46
|
} catch (error) {
|
|
46
47
|
debugGraphPrimitiveFallback(
|
|
47
48
|
"[embeddingTrigger] Failed to schedule embedding generation",
|
|
@@ -53,6 +54,10 @@ async function scheduleEmbeddingGeneration(args) {
|
|
|
53
54
|
);
|
|
54
55
|
}
|
|
55
56
|
}
|
|
57
|
+
function insertEpistemicNode(ctx, doc) {
|
|
58
|
+
assertUuidV7Identity("epistemicNodes", doc.globalId);
|
|
59
|
+
return ctx.db.insert("epistemicNodes", doc);
|
|
60
|
+
}
|
|
56
61
|
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
57
62
|
async function resolveTopicNodeScopeOrNull(ctx, ref) {
|
|
58
63
|
if (!ctx?.db || typeof ctx.db.query !== "function") {
|
|
@@ -87,13 +92,15 @@ function asMappedProjectId(topic) {
|
|
|
87
92
|
if (!topic) {
|
|
88
93
|
return;
|
|
89
94
|
}
|
|
90
|
-
const directLegacyProjectId = normalizeScopeValue(
|
|
95
|
+
const directLegacyProjectId = normalizeScopeValue(
|
|
96
|
+
topic[LEGACY_SCOPE_FIELD]
|
|
97
|
+
);
|
|
91
98
|
if (directLegacyProjectId) {
|
|
92
99
|
return directLegacyProjectId;
|
|
93
100
|
}
|
|
94
101
|
const metadata = topic.metadata || {};
|
|
95
102
|
const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
96
|
-
return candidate ? candidate : void 0;
|
|
103
|
+
return typeof candidate === "string" ? normalizeScopeValue(candidate) : void 0;
|
|
97
104
|
}
|
|
98
105
|
function normalizeScopeValue(value) {
|
|
99
106
|
if (typeof value !== "string") {
|
|
@@ -118,8 +125,9 @@ function pickPrimaryTopic(candidates) {
|
|
|
118
125
|
})[0];
|
|
119
126
|
}
|
|
120
127
|
async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
128
|
+
const query2 = ctx.db.query("topics");
|
|
121
129
|
try {
|
|
122
|
-
return await
|
|
130
|
+
return await query2.withIndex(
|
|
123
131
|
"by_graph_scope_project",
|
|
124
132
|
(q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
|
|
125
133
|
).collect();
|
|
@@ -131,7 +139,7 @@ async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
|
131
139
|
scopeId
|
|
132
140
|
}
|
|
133
141
|
);
|
|
134
|
-
const topics = await
|
|
142
|
+
const topics = await query2.collect();
|
|
135
143
|
return topics.filter((topic) => {
|
|
136
144
|
const normalizedGlobalId = normalizeScopeValue(topic.globalId);
|
|
137
145
|
const mappedProjectId = asMappedProjectId(topic);
|
|
@@ -187,137 +195,115 @@ async function resolveInheritedWorkspaceScope(ctx, topic) {
|
|
|
187
195
|
let current = topic;
|
|
188
196
|
for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
|
|
189
197
|
current = await ctx.db.get(current.parentTopicId);
|
|
190
|
-
if (!current)
|
|
198
|
+
if (!current) {
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
191
201
|
if (!tenantId) {
|
|
192
202
|
tenantId = normalizeScopeValue(current.tenantId);
|
|
193
203
|
}
|
|
194
204
|
if (!workspaceId) {
|
|
195
205
|
workspaceId = normalizeScopeValue(current.workspaceId);
|
|
196
206
|
}
|
|
197
|
-
if (tenantId && workspaceId)
|
|
207
|
+
if (tenantId && workspaceId) {
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
198
210
|
}
|
|
199
211
|
return { tenantId, workspaceId };
|
|
200
212
|
}
|
|
201
213
|
async function resolveTopicProjectScope(ctx, args) {
|
|
202
214
|
if (args.topicId) {
|
|
203
|
-
|
|
204
|
-
try {
|
|
205
|
-
topic = await ctx.db.get(
|
|
206
|
-
args.topicId
|
|
207
|
-
);
|
|
208
|
-
} catch (error) {
|
|
209
|
-
debugGraphPrimitiveFallback(
|
|
210
|
-
"[topicScope] Failed to load topic by direct id",
|
|
211
|
-
{
|
|
212
|
-
error,
|
|
213
|
-
topicId: args.topicId
|
|
214
|
-
}
|
|
215
|
-
);
|
|
216
|
-
}
|
|
217
|
-
if (!topic) {
|
|
218
|
-
topic = await tryResolveHostTopicById(ctx, String(args.topicId));
|
|
219
|
-
}
|
|
220
|
-
if (!topic) {
|
|
221
|
-
topic = pickPrimaryTopic(
|
|
222
|
-
await findTopicsByScopeAlias(ctx, String(args.topicId))
|
|
223
|
-
) ?? null;
|
|
224
|
-
}
|
|
225
|
-
if (!topic) {
|
|
226
|
-
const nodeScope = await resolveTopicNodeScopeOrNull(
|
|
227
|
-
ctx,
|
|
228
|
-
String(args.topicId)
|
|
229
|
-
);
|
|
230
|
-
if (nodeScope) {
|
|
231
|
-
return nodeScope;
|
|
232
|
-
}
|
|
233
|
-
throw new Error(`Topic not found: ${String(args.topicId)}`);
|
|
234
|
-
}
|
|
235
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
|
|
236
|
-
const mapped = asMappedProjectId(topic);
|
|
237
|
-
if (mapped) {
|
|
238
|
-
return {
|
|
239
|
-
topicId: topic._id,
|
|
240
|
-
projectId: mapped,
|
|
241
|
-
tenantId: inherited.tenantId,
|
|
242
|
-
workspaceId: inherited.workspaceId,
|
|
243
|
-
source: "topic"
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
return {
|
|
247
|
-
topicId: topic._id,
|
|
248
|
-
tenantId: inherited.tenantId,
|
|
249
|
-
workspaceId: inherited.workspaceId,
|
|
250
|
-
source: "topic"
|
|
251
|
-
};
|
|
215
|
+
return await resolveScopeFromTopicId(ctx, args.topicId);
|
|
252
216
|
}
|
|
253
217
|
if (args.projectId) {
|
|
254
|
-
|
|
255
|
-
try {
|
|
256
|
-
directTopic = await ctx.db.get(
|
|
257
|
-
args.projectId
|
|
258
|
-
);
|
|
259
|
-
} catch (error) {
|
|
260
|
-
debugGraphPrimitiveFallback(
|
|
261
|
-
"[topicScope] Failed to load direct project topic",
|
|
262
|
-
{
|
|
263
|
-
error,
|
|
264
|
-
projectId: args.projectId
|
|
265
|
-
}
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
if (directTopic) {
|
|
269
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
270
|
-
const mapped = asMappedProjectId(directTopic);
|
|
271
|
-
return {
|
|
272
|
-
topicId: directTopic._id,
|
|
273
|
-
projectId: mapped ?? args.projectId,
|
|
274
|
-
tenantId: inherited.tenantId,
|
|
275
|
-
workspaceId: inherited.workspaceId,
|
|
276
|
-
source: "topic_inferred"
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
directTopic = await tryResolveHostTopicByLegacyScope(ctx, args.projectId);
|
|
280
|
-
if (directTopic) {
|
|
281
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
282
|
-
const mapped = asMappedProjectId(directTopic);
|
|
283
|
-
return {
|
|
284
|
-
topicId: directTopic._id,
|
|
285
|
-
projectId: mapped ?? args.projectId,
|
|
286
|
-
tenantId: inherited.tenantId,
|
|
287
|
-
workspaceId: inherited.workspaceId,
|
|
288
|
-
source: "topic_inferred"
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
const topics = await findTopicsByScopeAlias(ctx, args.projectId);
|
|
292
|
-
const primary = pickPrimaryTopic(topics);
|
|
293
|
-
if (primary) {
|
|
294
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, primary);
|
|
295
|
-
return {
|
|
296
|
-
topicId: primary._id,
|
|
297
|
-
projectId: args.projectId,
|
|
298
|
-
tenantId: inherited.tenantId,
|
|
299
|
-
workspaceId: inherited.workspaceId,
|
|
300
|
-
source: "project_mapped_topic"
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
const nodeScope = await resolveTopicNodeScopeOrNull(
|
|
304
|
-
ctx,
|
|
305
|
-
String(args.projectId)
|
|
306
|
-
);
|
|
307
|
-
if (nodeScope) {
|
|
308
|
-
return {
|
|
309
|
-
...nodeScope,
|
|
310
|
-
projectId: nodeScope.projectId ?? String(args.projectId)
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
throw new Error(
|
|
314
|
-
`Legacy project scope ${String(args.projectId)} has no mapped topic.`
|
|
315
|
-
);
|
|
218
|
+
return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
|
|
316
219
|
}
|
|
317
220
|
throw new Error(
|
|
318
221
|
"Missing scope: provide topicId (preferred) or legacy projectId alias."
|
|
319
222
|
);
|
|
320
223
|
}
|
|
224
|
+
async function resolveScopeFromTopicId(ctx, topicId) {
|
|
225
|
+
const topic = await resolveTopicDocFromTopicId(ctx, topicId);
|
|
226
|
+
if (topic) {
|
|
227
|
+
return await buildTopicScope(ctx, topic, "topic");
|
|
228
|
+
}
|
|
229
|
+
const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
|
|
230
|
+
if (nodeScope) {
|
|
231
|
+
return nodeScope;
|
|
232
|
+
}
|
|
233
|
+
throw new Error(`Topic not found: ${String(topicId)}`);
|
|
234
|
+
}
|
|
235
|
+
async function resolveTopicDocFromTopicId(ctx, topicId) {
|
|
236
|
+
const direct = await tryReadTopicDoc(ctx, topicId, {
|
|
237
|
+
failureLog: "[topicScope] Failed to load topic by direct id",
|
|
238
|
+
idLogKey: "topicId"
|
|
239
|
+
});
|
|
240
|
+
if (direct) {
|
|
241
|
+
return direct;
|
|
242
|
+
}
|
|
243
|
+
const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
|
|
244
|
+
if (hostTopic) {
|
|
245
|
+
return hostTopic;
|
|
246
|
+
}
|
|
247
|
+
return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
|
|
248
|
+
}
|
|
249
|
+
async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
|
|
250
|
+
const directTopic = await resolveDirectLegacyProjectTopic(
|
|
251
|
+
ctx,
|
|
252
|
+
legacyProjectId
|
|
253
|
+
);
|
|
254
|
+
if (directTopic) {
|
|
255
|
+
return await buildTopicScope(ctx, directTopic, "topic_inferred", {
|
|
256
|
+
fallbackProjectId: legacyProjectId
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
const primary = pickPrimaryTopic(
|
|
260
|
+
await findTopicsByScopeAlias(ctx, legacyProjectId)
|
|
261
|
+
);
|
|
262
|
+
if (primary) {
|
|
263
|
+
return await buildTopicScope(ctx, primary, "project_mapped_topic", {
|
|
264
|
+
fallbackProjectId: legacyProjectId
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
|
|
268
|
+
if (nodeScope) {
|
|
269
|
+
return {
|
|
270
|
+
...nodeScope,
|
|
271
|
+
projectId: nodeScope.projectId ?? legacyProjectId
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
throw new Error(
|
|
275
|
+
`Legacy project scope ${legacyProjectId} has no mapped topic.`
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
|
|
279
|
+
const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
|
|
280
|
+
failureLog: "[topicScope] Failed to load direct project topic",
|
|
281
|
+
idLogKey: "projectId"
|
|
282
|
+
});
|
|
283
|
+
return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
|
|
284
|
+
}
|
|
285
|
+
async function tryReadTopicDoc(ctx, id, log) {
|
|
286
|
+
try {
|
|
287
|
+
return await ctx.db.get(id);
|
|
288
|
+
} catch (error) {
|
|
289
|
+
debugGraphPrimitiveFallback(log.failureLog, {
|
|
290
|
+
error,
|
|
291
|
+
[log.idLogKey]: id
|
|
292
|
+
});
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
async function buildTopicScope(ctx, topic, source, options = {}) {
|
|
297
|
+
const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
|
|
298
|
+
const mapped = asMappedProjectId(topic);
|
|
299
|
+
return {
|
|
300
|
+
topicId: topic._id,
|
|
301
|
+
...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
|
|
302
|
+
tenantId: inherited.tenantId,
|
|
303
|
+
workspaceId: inherited.workspaceId,
|
|
304
|
+
source
|
|
305
|
+
};
|
|
306
|
+
}
|
|
321
307
|
({
|
|
322
308
|
projectId: v.optional(v.string()),
|
|
323
309
|
topicId: v.optional(v.string())
|
|
@@ -359,12 +345,11 @@ function assertWorkspaceScopedEpistemicNodeScope(args) {
|
|
|
359
345
|
}
|
|
360
346
|
});
|
|
361
347
|
}
|
|
362
|
-
async function insertEpistemicNode(ctx, doc) {
|
|
363
|
-
assertUuidV7Identity("epistemicNodes", doc.globalId);
|
|
364
|
-
return ctx.db.insert("epistemicNodes", doc);
|
|
365
|
-
}
|
|
366
348
|
|
|
367
349
|
// src/epistemicSources.ts
|
|
350
|
+
var TRAILING_SLASHES_PATTERN = /\/+$/;
|
|
351
|
+
var HASH_MODULUS = 2 ** 32;
|
|
352
|
+
var HASH_SIGN_BOUNDARY = 2 ** 31;
|
|
368
353
|
function throwStructuredSourceError(args) {
|
|
369
354
|
const error = new Error(args.message);
|
|
370
355
|
error.status = args.status;
|
|
@@ -376,28 +361,39 @@ function throwStructuredSourceError(args) {
|
|
|
376
361
|
}
|
|
377
362
|
function normalizeString(value) {
|
|
378
363
|
if (typeof value !== "string") {
|
|
379
|
-
return
|
|
364
|
+
return;
|
|
380
365
|
}
|
|
381
366
|
const normalized = value.trim();
|
|
382
367
|
return normalized.length > 0 ? normalized : void 0;
|
|
383
368
|
}
|
|
369
|
+
function readConvexId(value) {
|
|
370
|
+
const normalized = normalizeString(value);
|
|
371
|
+
return normalized ? normalized : null;
|
|
372
|
+
}
|
|
384
373
|
function normalizeNumber(value) {
|
|
385
374
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
386
375
|
}
|
|
387
376
|
function asRecord(value) {
|
|
388
377
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
389
378
|
}
|
|
379
|
+
function isSourceNodeDoc(value) {
|
|
380
|
+
const record = asRecord(value);
|
|
381
|
+
return readConvexId(record._id) !== null && normalizeString(record.nodeType) === "source";
|
|
382
|
+
}
|
|
390
383
|
function normalizeMetadata(metadata) {
|
|
391
384
|
const record = asRecord(metadata);
|
|
392
385
|
const { url: _ignoredUrl, contentSha: _ignoredSha, ...rest } = record;
|
|
393
386
|
return rest;
|
|
394
387
|
}
|
|
388
|
+
function toSignedInt32(value) {
|
|
389
|
+
const normalized = (value % HASH_MODULUS + HASH_MODULUS) % HASH_MODULUS;
|
|
390
|
+
return normalized >= HASH_SIGN_BOUNDARY ? normalized - HASH_MODULUS : normalized;
|
|
391
|
+
}
|
|
395
392
|
function generateSourceContentHash(identity) {
|
|
396
393
|
const content = `source:${identity.trim().toLowerCase()}`;
|
|
397
394
|
let hash = 5381;
|
|
398
395
|
for (let index = 0; index < content.length; index += 1) {
|
|
399
|
-
hash = (hash
|
|
400
|
-
hash &= hash;
|
|
396
|
+
hash = toSignedInt32(hash * 33 + content.charCodeAt(index));
|
|
401
397
|
}
|
|
402
398
|
return Math.abs(hash).toString(16).padStart(8, "0");
|
|
403
399
|
}
|
|
@@ -428,7 +424,7 @@ function normalizeSourceUrl(url) {
|
|
|
428
424
|
const protocol = parsed.protocol.toLowerCase();
|
|
429
425
|
const hostname = parsed.hostname.toLowerCase();
|
|
430
426
|
const port = parsed.port ? `:${parsed.port}` : "";
|
|
431
|
-
const pathname = parsed.pathname === "/" ? "" : parsed.pathname.replace(
|
|
427
|
+
const pathname = parsed.pathname === "/" ? "" : parsed.pathname.replace(TRAILING_SLASHES_PATTERN, "");
|
|
432
428
|
const sortedEntries = [...parsed.searchParams.entries()].sort(
|
|
433
429
|
([aKey, aValue], [bKey, bValue]) => aKey === bKey ? aValue.localeCompare(bValue) : aKey.localeCompare(bKey)
|
|
434
430
|
);
|
|
@@ -440,13 +436,13 @@ function normalizeSourceUrl(url) {
|
|
|
440
436
|
return `${protocol}//${hostname}${port}${pathname}${search.length > 0 ? `?${search}` : ""}`;
|
|
441
437
|
}
|
|
442
438
|
async function findSourceByIdentity(ctx, args) {
|
|
443
|
-
const sourceNodes = await ctx.db.query("epistemicNodes").withIndex(
|
|
444
|
-
"by_nodeType",
|
|
445
|
-
(q) => q.eq("nodeType", "source")
|
|
446
|
-
).collect();
|
|
439
|
+
const sourceNodes = await ctx.db.query("epistemicNodes").withIndex("by_nodeType", (q) => q.eq("nodeType", "source")).collect();
|
|
447
440
|
let urlMatch = null;
|
|
448
441
|
let shaMatch = null;
|
|
449
442
|
for (const node of sourceNodes) {
|
|
443
|
+
if (!isSourceNodeDoc(node)) {
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
450
446
|
const metadata = asRecord(node.metadata);
|
|
451
447
|
if (args.normalizedUrl && normalizeString(metadata.url) === args.normalizedUrl && !urlMatch) {
|
|
452
448
|
urlMatch = node;
|
|
@@ -483,18 +479,198 @@ function buildSourceMetadata(args) {
|
|
|
483
479
|
if (args.capturedAt !== void 0) {
|
|
484
480
|
nextMetadata.capturedAt = args.capturedAt;
|
|
485
481
|
}
|
|
486
|
-
if (!Object.
|
|
482
|
+
if (!Object.hasOwn(existingMetadata, "url") && args.normalizedUrl) {
|
|
487
483
|
nextMetadata.url = args.normalizedUrl;
|
|
488
|
-
} else if (Object.
|
|
484
|
+
} else if (Object.hasOwn(existingMetadata, "url")) {
|
|
489
485
|
nextMetadata.url = existingMetadata.url;
|
|
490
486
|
}
|
|
491
|
-
if (!Object.
|
|
487
|
+
if (!Object.hasOwn(existingMetadata, "contentSha") && args.sha) {
|
|
492
488
|
nextMetadata.contentSha = args.sha;
|
|
493
|
-
} else if (Object.
|
|
489
|
+
} else if (Object.hasOwn(existingMetadata, "contentSha")) {
|
|
494
490
|
nextMetadata.contentSha = existingMetadata.contentSha;
|
|
495
491
|
}
|
|
496
492
|
return nextMetadata;
|
|
497
493
|
}
|
|
494
|
+
function readSourceIdentity(args) {
|
|
495
|
+
const sourceUrl = normalizeString(args.url);
|
|
496
|
+
const normalizedUrl = sourceUrl ? normalizeSourceUrl(sourceUrl) : void 0;
|
|
497
|
+
const sha = normalizeString(args.sha);
|
|
498
|
+
if (!(normalizedUrl || sha)) {
|
|
499
|
+
throwStructuredSourceError({
|
|
500
|
+
message: "Source identity requires a URL or content SHA.",
|
|
501
|
+
status: 400,
|
|
502
|
+
code: "INVALID_REQUEST",
|
|
503
|
+
invariantCode: "source.identity.required",
|
|
504
|
+
suggestion: "Provide url or sha."
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
return { normalizedUrl, sha };
|
|
508
|
+
}
|
|
509
|
+
async function syncSourceNodeToNeo4j(args) {
|
|
510
|
+
await args.ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
|
|
511
|
+
nodeId: args.nodeId,
|
|
512
|
+
operation: "upsert"
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
async function insertSourceAudit(ctx, audit) {
|
|
516
|
+
await ctx.db.insert("epistemicAudit", { ...audit });
|
|
517
|
+
}
|
|
518
|
+
async function updateExistingSource(args) {
|
|
519
|
+
if (args.existing.topicId && !await checkScopeAccess(
|
|
520
|
+
args.ctx,
|
|
521
|
+
String(args.existing.topicId),
|
|
522
|
+
args.userId
|
|
523
|
+
)) {
|
|
524
|
+
throwStructuredSourceError({
|
|
525
|
+
message: "Access denied to existing source scope.",
|
|
526
|
+
status: 403,
|
|
527
|
+
code: "FORBIDDEN",
|
|
528
|
+
invariantCode: "policy.scope_required",
|
|
529
|
+
suggestion: "Request access to the source topic scope and retry.",
|
|
530
|
+
details: { topicId: args.existing.topicId }
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
const existingMetadata = asRecord(args.existing.metadata);
|
|
534
|
+
const existingSha = normalizeString(existingMetadata.contentSha);
|
|
535
|
+
if (args.sha && existingSha && existingSha !== args.sha) {
|
|
536
|
+
throwStructuredSourceError({
|
|
537
|
+
message: "Same URL cannot be reused with a different content hash.",
|
|
538
|
+
status: 409,
|
|
539
|
+
code: "CONTENT_SHA_MISMATCH",
|
|
540
|
+
invariantCode: "source.content_hash_immutable",
|
|
541
|
+
suggestion: "Same URL, different content. Create a new source with a distinct URL or use fork semantics.",
|
|
542
|
+
details: {
|
|
543
|
+
sourceId: String(args.existing._id),
|
|
544
|
+
url: existingMetadata.url,
|
|
545
|
+
existingSha,
|
|
546
|
+
receivedSha: args.sha
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
const nextMetadata = buildSourceMetadata({
|
|
551
|
+
existingMetadata,
|
|
552
|
+
kind: args.kind,
|
|
553
|
+
capturedAt: normalizeNumber(args.capturedAt),
|
|
554
|
+
metadata: args.metadataPatch
|
|
555
|
+
});
|
|
556
|
+
const patch = {
|
|
557
|
+
metadata: nextMetadata,
|
|
558
|
+
updatedAt: args.now
|
|
559
|
+
};
|
|
560
|
+
const title = normalizeString(args.title);
|
|
561
|
+
if (title) {
|
|
562
|
+
patch.title = title;
|
|
563
|
+
patch.canonicalText = title;
|
|
564
|
+
}
|
|
565
|
+
await args.ctx.db.patch(args.existing._id, patch);
|
|
566
|
+
await syncSourceNodeToNeo4j({
|
|
567
|
+
ctx: args.ctx,
|
|
568
|
+
nodeId: args.existing._id
|
|
569
|
+
});
|
|
570
|
+
await insertSourceAudit(args.ctx, {
|
|
571
|
+
entityType: "source",
|
|
572
|
+
entityId: String(args.existing._id),
|
|
573
|
+
changeType: "source_metadata_updated",
|
|
574
|
+
changedAt: args.now,
|
|
575
|
+
changedBy: args.userId,
|
|
576
|
+
isAgent: false,
|
|
577
|
+
topicId: args.existing.topicId ? String(args.existing.topicId) : void 0,
|
|
578
|
+
previousState: {
|
|
579
|
+
title: args.existing.title,
|
|
580
|
+
metadata: existingMetadata
|
|
581
|
+
},
|
|
582
|
+
newState: {
|
|
583
|
+
title: title ?? args.existing.title,
|
|
584
|
+
metadata: nextMetadata
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
const updated = await args.ctx.db.get(args.existing._id);
|
|
588
|
+
return isSourceNodeDoc(updated) ? updated : args.existing;
|
|
589
|
+
}
|
|
590
|
+
async function createSource(args) {
|
|
591
|
+
if (!normalizeString(args.topicId)) {
|
|
592
|
+
throwStructuredSourceError({
|
|
593
|
+
message: "topicId is required when creating a new source.",
|
|
594
|
+
status: 400,
|
|
595
|
+
code: "INVALID_REQUEST",
|
|
596
|
+
invariantCode: "source.context_required",
|
|
597
|
+
suggestion: "Provide topicId when creating a new source node."
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
const scope = await resolveTopicProjectScope(args.ctx, {
|
|
601
|
+
topicId: args.topicId
|
|
602
|
+
});
|
|
603
|
+
assertWorkspaceScopedEpistemicNodeScope({
|
|
604
|
+
scope,
|
|
605
|
+
nodeType: "source",
|
|
606
|
+
mutationName: "epistemicSources.upsertSource"
|
|
607
|
+
});
|
|
608
|
+
if (scope.projectId) {
|
|
609
|
+
await requireScopeWriteAccess(args.ctx, scope.projectId, args.userId);
|
|
610
|
+
}
|
|
611
|
+
const globalId = generateGlobalId();
|
|
612
|
+
const title = normalizeString(args.title);
|
|
613
|
+
const nextMetadata = buildSourceMetadata({
|
|
614
|
+
normalizedUrl: args.normalizedUrl,
|
|
615
|
+
sha: args.sha,
|
|
616
|
+
kind: args.kind,
|
|
617
|
+
capturedAt: normalizeNumber(args.capturedAt),
|
|
618
|
+
metadata: args.metadataPatch
|
|
619
|
+
});
|
|
620
|
+
const nodeId = await insertEpistemicNode(args.ctx, {
|
|
621
|
+
globalId,
|
|
622
|
+
topicId: scope.topicId,
|
|
623
|
+
projectId: scope.projectId,
|
|
624
|
+
tenantId: scope.tenantId,
|
|
625
|
+
workspaceId: scope.workspaceId,
|
|
626
|
+
nodeType: "source",
|
|
627
|
+
epistemicLayer: "L1",
|
|
628
|
+
canonicalText: title ?? args.normalizedUrl ?? "source",
|
|
629
|
+
contentHash: args.sha ?? generateSourceContentHash(args.normalizedUrl ?? title ?? "source"),
|
|
630
|
+
...title ? { title } : {},
|
|
631
|
+
status: "active",
|
|
632
|
+
createdAt: args.now,
|
|
633
|
+
updatedAt: args.now,
|
|
634
|
+
createdBy: args.userId,
|
|
635
|
+
metadata: nextMetadata
|
|
636
|
+
});
|
|
637
|
+
await syncSourceNodeToNeo4j({
|
|
638
|
+
ctx: args.ctx,
|
|
639
|
+
nodeId
|
|
640
|
+
});
|
|
641
|
+
await scheduleEmbeddingGeneration({
|
|
642
|
+
ctx: args.ctx,
|
|
643
|
+
nodeId,
|
|
644
|
+
projectId: scope.projectId,
|
|
645
|
+
topicId: scope.topicId,
|
|
646
|
+
createdBy: args.userId,
|
|
647
|
+
nodeType: "source",
|
|
648
|
+
text: sourceEmbeddingText({
|
|
649
|
+
title,
|
|
650
|
+
url: args.normalizedUrl,
|
|
651
|
+
kind: args.kind,
|
|
652
|
+
metadata: nextMetadata
|
|
653
|
+
})
|
|
654
|
+
});
|
|
655
|
+
await insertSourceAudit(args.ctx, {
|
|
656
|
+
entityType: "source",
|
|
657
|
+
entityId: String(nodeId),
|
|
658
|
+
changeType: "source_created",
|
|
659
|
+
changedAt: args.now,
|
|
660
|
+
changedBy: args.userId,
|
|
661
|
+
isAgent: false,
|
|
662
|
+
topicId: scope.topicId ? String(scope.topicId) : void 0,
|
|
663
|
+
previousState: null,
|
|
664
|
+
newState: {
|
|
665
|
+
title,
|
|
666
|
+
url: args.normalizedUrl,
|
|
667
|
+
contentSha: args.sha,
|
|
668
|
+
kind: args.kind
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
const created = await args.ctx.db.get(nodeId);
|
|
672
|
+
return isSourceNodeDoc(created) ? created : null;
|
|
673
|
+
}
|
|
498
674
|
function sourceEmbeddingText(args) {
|
|
499
675
|
const lines = [
|
|
500
676
|
normalizeString(args.title),
|
|
@@ -517,169 +693,35 @@ var upsertSource = mutation({
|
|
|
517
693
|
},
|
|
518
694
|
returns: permissiveReturn,
|
|
519
695
|
handler: async (ctx, args) => {
|
|
520
|
-
const
|
|
521
|
-
const normalizedUrl = sourceUrl ? normalizeSourceUrl(sourceUrl) : void 0;
|
|
522
|
-
const sha = normalizeString(args.sha);
|
|
523
|
-
if (!normalizedUrl && !sha) {
|
|
524
|
-
throwStructuredSourceError({
|
|
525
|
-
message: "Source identity requires a URL or content SHA.",
|
|
526
|
-
status: 400,
|
|
527
|
-
code: "INVALID_REQUEST",
|
|
528
|
-
invariantCode: "source.identity.required",
|
|
529
|
-
suggestion: "Provide url or sha."
|
|
530
|
-
});
|
|
531
|
-
}
|
|
696
|
+
const { normalizedUrl, sha } = readSourceIdentity(args);
|
|
532
697
|
const now = Date.now();
|
|
533
698
|
const metadataPatch = normalizeMetadata(args.metadata);
|
|
534
699
|
const existing = await findSourceByIdentity(ctx, { normalizedUrl, sha });
|
|
535
700
|
if (existing) {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
status: 403,
|
|
540
|
-
code: "FORBIDDEN",
|
|
541
|
-
invariantCode: "policy.scope_required",
|
|
542
|
-
suggestion: "Request access to the source topic scope and retry.",
|
|
543
|
-
details: { topicId: existing.topicId }
|
|
544
|
-
});
|
|
545
|
-
}
|
|
546
|
-
const existingMetadata = asRecord(existing.metadata);
|
|
547
|
-
const existingSha = normalizeString(existingMetadata.contentSha);
|
|
548
|
-
if (sha && existingSha && existingSha !== sha) {
|
|
549
|
-
throwStructuredSourceError({
|
|
550
|
-
message: "Same URL cannot be reused with a different content hash.",
|
|
551
|
-
status: 409,
|
|
552
|
-
code: "CONTENT_SHA_MISMATCH",
|
|
553
|
-
invariantCode: "source.content_hash_immutable",
|
|
554
|
-
suggestion: "Same URL, different content. Create a new source with a distinct URL or use fork semantics.",
|
|
555
|
-
details: {
|
|
556
|
-
sourceId: String(existing._id),
|
|
557
|
-
url: existingMetadata.url,
|
|
558
|
-
existingSha,
|
|
559
|
-
receivedSha: sha
|
|
560
|
-
}
|
|
561
|
-
});
|
|
562
|
-
}
|
|
563
|
-
const nextMetadata2 = buildSourceMetadata({
|
|
564
|
-
existingMetadata,
|
|
701
|
+
return await updateExistingSource({
|
|
702
|
+
ctx,
|
|
703
|
+
existing,
|
|
565
704
|
kind: args.kind,
|
|
566
|
-
capturedAt:
|
|
567
|
-
|
|
705
|
+
capturedAt: args.capturedAt,
|
|
706
|
+
metadataPatch,
|
|
707
|
+
now,
|
|
708
|
+
sha,
|
|
709
|
+
title: args.title,
|
|
710
|
+
userId: args.userId
|
|
568
711
|
});
|
|
569
|
-
const patch = {
|
|
570
|
-
metadata: nextMetadata2,
|
|
571
|
-
updatedAt: now
|
|
572
|
-
};
|
|
573
|
-
const title2 = normalizeString(args.title);
|
|
574
|
-
if (title2) {
|
|
575
|
-
patch.title = title2;
|
|
576
|
-
patch.canonicalText = title2;
|
|
577
|
-
}
|
|
578
|
-
await ctx.db.patch(existing._id, patch);
|
|
579
|
-
await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
|
|
580
|
-
nodeId: existing._id,
|
|
581
|
-
operation: "upsert"
|
|
582
|
-
});
|
|
583
|
-
await ctx.db.insert("epistemicAudit", {
|
|
584
|
-
entityType: "source",
|
|
585
|
-
entityId: existing._id,
|
|
586
|
-
changeType: "source_metadata_updated",
|
|
587
|
-
changedAt: now,
|
|
588
|
-
changedBy: args.userId,
|
|
589
|
-
isAgent: false,
|
|
590
|
-
topicId: existing.topicId ? String(existing.topicId) : void 0,
|
|
591
|
-
previousState: {
|
|
592
|
-
title: existing.title,
|
|
593
|
-
metadata: existingMetadata
|
|
594
|
-
},
|
|
595
|
-
newState: {
|
|
596
|
-
title: title2 ?? existing.title,
|
|
597
|
-
metadata: nextMetadata2
|
|
598
|
-
}
|
|
599
|
-
});
|
|
600
|
-
return await ctx.db.get(existing._id) ?? existing;
|
|
601
712
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
message: "topicId is required when creating a new source.",
|
|
605
|
-
status: 400,
|
|
606
|
-
code: "INVALID_REQUEST",
|
|
607
|
-
invariantCode: "source.context_required",
|
|
608
|
-
suggestion: "Provide topicId when creating a new source node."
|
|
609
|
-
});
|
|
610
|
-
}
|
|
611
|
-
const scope = await resolveTopicProjectScope(ctx, {
|
|
612
|
-
topicId: args.topicId
|
|
613
|
-
});
|
|
614
|
-
assertWorkspaceScopedEpistemicNodeScope({
|
|
615
|
-
scope,
|
|
616
|
-
nodeType: "source",
|
|
617
|
-
mutationName: "epistemicSources.upsertSource"
|
|
618
|
-
});
|
|
619
|
-
if (scope.projectId) {
|
|
620
|
-
await requireProjectAccess(ctx, scope.projectId, args.userId);
|
|
621
|
-
}
|
|
622
|
-
const globalId = generateGlobalId();
|
|
623
|
-
const title = normalizeString(args.title);
|
|
624
|
-
const nextMetadata = buildSourceMetadata({
|
|
713
|
+
return await createSource({
|
|
714
|
+
ctx,
|
|
625
715
|
normalizedUrl,
|
|
626
716
|
sha,
|
|
627
717
|
kind: args.kind,
|
|
628
|
-
capturedAt:
|
|
629
|
-
|
|
718
|
+
capturedAt: args.capturedAt,
|
|
719
|
+
metadataPatch,
|
|
720
|
+
now,
|
|
721
|
+
title: args.title,
|
|
722
|
+
topicId: args.topicId,
|
|
723
|
+
userId: args.userId
|
|
630
724
|
});
|
|
631
|
-
const nodeId = await insertEpistemicNode(ctx, {
|
|
632
|
-
globalId,
|
|
633
|
-
topicId: scope.topicId,
|
|
634
|
-
projectId: scope.projectId,
|
|
635
|
-
tenantId: scope.tenantId,
|
|
636
|
-
workspaceId: scope.workspaceId,
|
|
637
|
-
nodeType: "source",
|
|
638
|
-
epistemicLayer: "L1",
|
|
639
|
-
canonicalText: title ?? normalizedUrl ?? "source",
|
|
640
|
-
contentHash: sha ?? generateSourceContentHash(normalizedUrl ?? title ?? "source"),
|
|
641
|
-
...title ? { title } : {},
|
|
642
|
-
status: "active",
|
|
643
|
-
createdAt: now,
|
|
644
|
-
updatedAt: now,
|
|
645
|
-
createdBy: args.userId,
|
|
646
|
-
metadata: nextMetadata
|
|
647
|
-
});
|
|
648
|
-
await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
|
|
649
|
-
nodeId,
|
|
650
|
-
operation: "upsert"
|
|
651
|
-
});
|
|
652
|
-
await scheduleEmbeddingGeneration({
|
|
653
|
-
ctx,
|
|
654
|
-
nodeId,
|
|
655
|
-
projectId: scope.projectId,
|
|
656
|
-
topicId: scope.topicId,
|
|
657
|
-
createdBy: args.userId,
|
|
658
|
-
nodeType: "source",
|
|
659
|
-
text: sourceEmbeddingText({
|
|
660
|
-
title,
|
|
661
|
-
url: normalizedUrl,
|
|
662
|
-
kind: args.kind,
|
|
663
|
-
metadata: nextMetadata
|
|
664
|
-
})
|
|
665
|
-
});
|
|
666
|
-
await ctx.db.insert("epistemicAudit", {
|
|
667
|
-
entityType: "source",
|
|
668
|
-
entityId: nodeId,
|
|
669
|
-
changeType: "source_created",
|
|
670
|
-
changedAt: now,
|
|
671
|
-
changedBy: args.userId,
|
|
672
|
-
isAgent: false,
|
|
673
|
-
topicId: scope.topicId ? String(scope.topicId) : void 0,
|
|
674
|
-
previousState: null,
|
|
675
|
-
newState: {
|
|
676
|
-
title,
|
|
677
|
-
url: normalizedUrl,
|
|
678
|
-
contentSha: sha,
|
|
679
|
-
kind: args.kind
|
|
680
|
-
}
|
|
681
|
-
});
|
|
682
|
-
return await ctx.db.get(nodeId);
|
|
683
725
|
}
|
|
684
726
|
});
|
|
685
727
|
var getById = query({
|
|
@@ -689,14 +731,12 @@ var getById = query({
|
|
|
689
731
|
},
|
|
690
732
|
returns: permissiveReturn,
|
|
691
733
|
handler: async (ctx, args) => {
|
|
692
|
-
const id = args.nodeId ?? args.sourceId;
|
|
734
|
+
const id = args.nodeId ?? readConvexId(args.sourceId);
|
|
693
735
|
if (!id) {
|
|
694
736
|
return null;
|
|
695
737
|
}
|
|
696
|
-
const node = await ctx.db.get(
|
|
697
|
-
|
|
698
|
-
);
|
|
699
|
-
if (!node || node.nodeType !== "source") {
|
|
738
|
+
const node = await ctx.db.get(id);
|
|
739
|
+
if (!isSourceNodeDoc(node)) {
|
|
700
740
|
return null;
|
|
701
741
|
}
|
|
702
742
|
return node;
|
|
@@ -710,7 +750,7 @@ var findByUrl = query({
|
|
|
710
750
|
handler: async (ctx, args) => {
|
|
711
751
|
const normalizedUrl = normalizeSourceUrl(args.url);
|
|
712
752
|
const node = await findSourceByIdentity(ctx, { normalizedUrl });
|
|
713
|
-
return node
|
|
753
|
+
return node?.nodeType === "source" ? node : null;
|
|
714
754
|
}
|
|
715
755
|
});
|
|
716
756
|
var findBySha = query({
|
|
@@ -724,7 +764,7 @@ var findBySha = query({
|
|
|
724
764
|
return null;
|
|
725
765
|
}
|
|
726
766
|
const node = await findSourceByIdentity(ctx, { sha });
|
|
727
|
-
return node
|
|
767
|
+
return node?.nodeType === "source" ? node : null;
|
|
728
768
|
}
|
|
729
769
|
});
|
|
730
770
|
|