@lucern/graph-primitives 1.0.29 → 1.0.31
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 +395 -225
- 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 +854 -480
- 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 +365 -166
- package/dist/epistemicBeliefs.admin.js.map +1 -1
- package/dist/epistemicBeliefs.backfills.d.ts +8 -8
- package/dist/epistemicBeliefs.backfills.js +655 -289
- package/dist/epistemicBeliefs.backfills.js.map +1 -1
- package/dist/epistemicBeliefs.confidence.d.ts +19 -15
- package/dist/epistemicBeliefs.confidence.js +633 -386
- package/dist/epistemicBeliefs.confidence.js.map +1 -1
- package/dist/epistemicBeliefs.core.d.ts +6 -6
- package/dist/epistemicBeliefs.core.js +717 -371
- package/dist/epistemicBeliefs.core.js.map +1 -1
- package/dist/epistemicBeliefs.d.ts +11 -9
- package/dist/epistemicBeliefs.forkEvidence.d.ts +2 -0
- package/dist/epistemicBeliefs.forkEvidence.js +8 -8
- package/dist/epistemicBeliefs.forkEvidence.js.map +1 -1
- package/dist/epistemicBeliefs.helpers.d.ts +68 -49
- package/dist/epistemicBeliefs.helpers.js +358 -211
- package/dist/epistemicBeliefs.helpers.js.map +1 -1
- package/dist/epistemicBeliefs.internal.d.ts +5 -5
- package/dist/epistemicBeliefs.internal.js +1248 -1026
- package/dist/epistemicBeliefs.internal.js.map +1 -1
- package/dist/epistemicBeliefs.js +4942 -3590
- package/dist/epistemicBeliefs.js.map +1 -1
- package/dist/epistemicBeliefs.lifecycle.d.ts +5 -5
- package/dist/epistemicBeliefs.lifecycle.js +1138 -781
- package/dist/epistemicBeliefs.lifecycle.js.map +1 -1
- package/dist/epistemicBeliefs.links.d.ts +7 -7
- package/dist/epistemicBeliefs.links.js +404 -267
- 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 +1062 -576
- package/dist/epistemicContracts.evaluators.js.map +1 -1
- package/dist/epistemicContracts.handlers.d.ts +15 -32
- package/dist/epistemicContracts.handlers.js +1829 -1351
- package/dist/epistemicContracts.handlers.js.map +1 -1
- package/dist/epistemicContracts.js +1131 -636
- 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 +1966 -1202
- package/dist/epistemicEdges.js.map +1 -1
- package/dist/epistemicEdges.mutations.d.ts +7 -7
- package/dist/epistemicEdges.mutations.js +956 -579
- 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 +933 -532
- 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 +840 -692
- 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 +700 -504
- package/dist/epistemicNodes.js.map +1 -1
- package/dist/epistemicNodes.mutations.d.ts +6 -6
- package/dist/epistemicNodes.mutations.js +560 -463
- 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 +351 -311
- 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 +86 -83
- package/dist/index.js +16914 -11760
- 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/epistemicAnswers.js
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
|
-
import { v } from 'convex/values';
|
|
2
|
-
import { componentsGeneric, anyApi, mutationGeneric, internalMutationGeneric, queryGeneric } from 'convex/server';
|
|
3
1
|
import { checkScopeAccess } from '@lucern/access-control/access';
|
|
4
|
-
import { generateGlobalId, assertUuidV7Identity } from '@lucern/contracts/ids';
|
|
5
2
|
import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
|
|
3
|
+
import { v } from 'convex/values';
|
|
4
|
+
import { unsafeConvexAnyApi } from '@lucern/contracts/convex/unsafeAnyApi';
|
|
5
|
+
import { componentsGeneric, mutationGeneric, internalMutationGeneric, queryGeneric } from 'convex/server';
|
|
6
6
|
import '@lucern/contracts';
|
|
7
|
+
import { generateGlobalId, assertUuidV7Identity } from '@lucern/contracts/ids';
|
|
7
8
|
|
|
8
9
|
// src/epistemicAnswers.ts
|
|
9
|
-
var
|
|
10
|
+
var unsafeApi = unsafeConvexAnyApi(
|
|
11
|
+
"graph-primitives top-level module bundle lacks a committed Convex _generated/api surface"
|
|
12
|
+
);
|
|
13
|
+
var api = unsafeApi;
|
|
10
14
|
componentsGeneric();
|
|
11
|
-
var internal =
|
|
15
|
+
var internal = unsafeApi;
|
|
12
16
|
var internalMutation = internalMutationGeneric;
|
|
13
17
|
var mutation = mutationGeneric;
|
|
14
18
|
var query = queryGeneric;
|
|
19
|
+
function insertEpistemicNode(ctx, doc) {
|
|
20
|
+
assertUuidV7Identity("epistemicNodes", doc.globalId);
|
|
21
|
+
return ctx.db.insert("epistemicNodes", doc);
|
|
22
|
+
}
|
|
15
23
|
|
|
16
24
|
// src/debug.ts
|
|
17
25
|
function isGraphPrimitiveDebugEnabled() {
|
|
@@ -24,8 +32,6 @@ function debugGraphPrimitiveFallback(message, context) {
|
|
|
24
32
|
}
|
|
25
33
|
console.debug(message, context ?? {});
|
|
26
34
|
}
|
|
27
|
-
|
|
28
|
-
// src/topicScope.ts
|
|
29
35
|
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
30
36
|
async function resolveTopicNodeScopeOrNull(ctx, ref) {
|
|
31
37
|
if (!ctx?.db || typeof ctx.db.query !== "function") {
|
|
@@ -60,13 +66,15 @@ function asMappedProjectId(topic) {
|
|
|
60
66
|
if (!topic) {
|
|
61
67
|
return;
|
|
62
68
|
}
|
|
63
|
-
const directLegacyProjectId = normalizeScopeValue(
|
|
69
|
+
const directLegacyProjectId = normalizeScopeValue(
|
|
70
|
+
topic[LEGACY_SCOPE_FIELD]
|
|
71
|
+
);
|
|
64
72
|
if (directLegacyProjectId) {
|
|
65
73
|
return directLegacyProjectId;
|
|
66
74
|
}
|
|
67
75
|
const metadata = topic.metadata || {};
|
|
68
76
|
const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
69
|
-
return candidate ? candidate : void 0;
|
|
77
|
+
return typeof candidate === "string" ? normalizeScopeValue(candidate) : void 0;
|
|
70
78
|
}
|
|
71
79
|
function normalizeScopeValue(value) {
|
|
72
80
|
if (typeof value !== "string") {
|
|
@@ -91,8 +99,9 @@ function pickPrimaryTopic(candidates) {
|
|
|
91
99
|
})[0];
|
|
92
100
|
}
|
|
93
101
|
async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
102
|
+
const query2 = ctx.db.query("topics");
|
|
94
103
|
try {
|
|
95
|
-
return await
|
|
104
|
+
return await query2.withIndex(
|
|
96
105
|
"by_graph_scope_project",
|
|
97
106
|
(q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
|
|
98
107
|
).collect();
|
|
@@ -104,7 +113,7 @@ async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
|
104
113
|
scopeId
|
|
105
114
|
}
|
|
106
115
|
);
|
|
107
|
-
const topics = await
|
|
116
|
+
const topics = await query2.collect();
|
|
108
117
|
return topics.filter((topic) => {
|
|
109
118
|
const normalizedGlobalId = normalizeScopeValue(topic.globalId);
|
|
110
119
|
const mappedProjectId = asMappedProjectId(topic);
|
|
@@ -160,137 +169,115 @@ async function resolveInheritedWorkspaceScope(ctx, topic) {
|
|
|
160
169
|
let current = topic;
|
|
161
170
|
for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
|
|
162
171
|
current = await ctx.db.get(current.parentTopicId);
|
|
163
|
-
if (!current)
|
|
172
|
+
if (!current) {
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
164
175
|
if (!tenantId) {
|
|
165
176
|
tenantId = normalizeScopeValue(current.tenantId);
|
|
166
177
|
}
|
|
167
178
|
if (!workspaceId) {
|
|
168
179
|
workspaceId = normalizeScopeValue(current.workspaceId);
|
|
169
180
|
}
|
|
170
|
-
if (tenantId && workspaceId)
|
|
181
|
+
if (tenantId && workspaceId) {
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
171
184
|
}
|
|
172
185
|
return { tenantId, workspaceId };
|
|
173
186
|
}
|
|
174
187
|
async function resolveTopicProjectScope(ctx, args) {
|
|
175
188
|
if (args.topicId) {
|
|
176
|
-
|
|
177
|
-
try {
|
|
178
|
-
topic = await ctx.db.get(
|
|
179
|
-
args.topicId
|
|
180
|
-
);
|
|
181
|
-
} catch (error) {
|
|
182
|
-
debugGraphPrimitiveFallback(
|
|
183
|
-
"[topicScope] Failed to load topic by direct id",
|
|
184
|
-
{
|
|
185
|
-
error,
|
|
186
|
-
topicId: args.topicId
|
|
187
|
-
}
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
if (!topic) {
|
|
191
|
-
topic = await tryResolveHostTopicById(ctx, String(args.topicId));
|
|
192
|
-
}
|
|
193
|
-
if (!topic) {
|
|
194
|
-
topic = pickPrimaryTopic(
|
|
195
|
-
await findTopicsByScopeAlias(ctx, String(args.topicId))
|
|
196
|
-
) ?? null;
|
|
197
|
-
}
|
|
198
|
-
if (!topic) {
|
|
199
|
-
const nodeScope = await resolveTopicNodeScopeOrNull(
|
|
200
|
-
ctx,
|
|
201
|
-
String(args.topicId)
|
|
202
|
-
);
|
|
203
|
-
if (nodeScope) {
|
|
204
|
-
return nodeScope;
|
|
205
|
-
}
|
|
206
|
-
throw new Error(`Topic not found: ${String(args.topicId)}`);
|
|
207
|
-
}
|
|
208
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
|
|
209
|
-
const mapped = asMappedProjectId(topic);
|
|
210
|
-
if (mapped) {
|
|
211
|
-
return {
|
|
212
|
-
topicId: topic._id,
|
|
213
|
-
projectId: mapped,
|
|
214
|
-
tenantId: inherited.tenantId,
|
|
215
|
-
workspaceId: inherited.workspaceId,
|
|
216
|
-
source: "topic"
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
return {
|
|
220
|
-
topicId: topic._id,
|
|
221
|
-
tenantId: inherited.tenantId,
|
|
222
|
-
workspaceId: inherited.workspaceId,
|
|
223
|
-
source: "topic"
|
|
224
|
-
};
|
|
189
|
+
return await resolveScopeFromTopicId(ctx, args.topicId);
|
|
225
190
|
}
|
|
226
191
|
if (args.projectId) {
|
|
227
|
-
|
|
228
|
-
try {
|
|
229
|
-
directTopic = await ctx.db.get(
|
|
230
|
-
args.projectId
|
|
231
|
-
);
|
|
232
|
-
} catch (error) {
|
|
233
|
-
debugGraphPrimitiveFallback(
|
|
234
|
-
"[topicScope] Failed to load direct project topic",
|
|
235
|
-
{
|
|
236
|
-
error,
|
|
237
|
-
projectId: args.projectId
|
|
238
|
-
}
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
if (directTopic) {
|
|
242
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
243
|
-
const mapped = asMappedProjectId(directTopic);
|
|
244
|
-
return {
|
|
245
|
-
topicId: directTopic._id,
|
|
246
|
-
projectId: mapped ?? args.projectId,
|
|
247
|
-
tenantId: inherited.tenantId,
|
|
248
|
-
workspaceId: inherited.workspaceId,
|
|
249
|
-
source: "topic_inferred"
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
directTopic = await tryResolveHostTopicByLegacyScope(ctx, args.projectId);
|
|
253
|
-
if (directTopic) {
|
|
254
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
255
|
-
const mapped = asMappedProjectId(directTopic);
|
|
256
|
-
return {
|
|
257
|
-
topicId: directTopic._id,
|
|
258
|
-
projectId: mapped ?? args.projectId,
|
|
259
|
-
tenantId: inherited.tenantId,
|
|
260
|
-
workspaceId: inherited.workspaceId,
|
|
261
|
-
source: "topic_inferred"
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
const topics = await findTopicsByScopeAlias(ctx, args.projectId);
|
|
265
|
-
const primary = pickPrimaryTopic(topics);
|
|
266
|
-
if (primary) {
|
|
267
|
-
const inherited = await resolveInheritedWorkspaceScope(ctx, primary);
|
|
268
|
-
return {
|
|
269
|
-
topicId: primary._id,
|
|
270
|
-
projectId: args.projectId,
|
|
271
|
-
tenantId: inherited.tenantId,
|
|
272
|
-
workspaceId: inherited.workspaceId,
|
|
273
|
-
source: "project_mapped_topic"
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
const nodeScope = await resolveTopicNodeScopeOrNull(
|
|
277
|
-
ctx,
|
|
278
|
-
String(args.projectId)
|
|
279
|
-
);
|
|
280
|
-
if (nodeScope) {
|
|
281
|
-
return {
|
|
282
|
-
...nodeScope,
|
|
283
|
-
projectId: nodeScope.projectId ?? String(args.projectId)
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
throw new Error(
|
|
287
|
-
`Legacy project scope ${String(args.projectId)} has no mapped topic.`
|
|
288
|
-
);
|
|
192
|
+
return await resolveScopeFromLegacyProjectId(ctx, args.projectId);
|
|
289
193
|
}
|
|
290
194
|
throw new Error(
|
|
291
195
|
"Missing scope: provide topicId (preferred) or legacy projectId alias."
|
|
292
196
|
);
|
|
293
197
|
}
|
|
198
|
+
async function resolveScopeFromTopicId(ctx, topicId) {
|
|
199
|
+
const topic = await resolveTopicDocFromTopicId(ctx, topicId);
|
|
200
|
+
if (topic) {
|
|
201
|
+
return await buildTopicScope(ctx, topic, "topic");
|
|
202
|
+
}
|
|
203
|
+
const nodeScope = await resolveTopicNodeScopeOrNull(ctx, String(topicId));
|
|
204
|
+
if (nodeScope) {
|
|
205
|
+
return nodeScope;
|
|
206
|
+
}
|
|
207
|
+
throw new Error(`Topic not found: ${String(topicId)}`);
|
|
208
|
+
}
|
|
209
|
+
async function resolveTopicDocFromTopicId(ctx, topicId) {
|
|
210
|
+
const direct = await tryReadTopicDoc(ctx, topicId, {
|
|
211
|
+
failureLog: "[topicScope] Failed to load topic by direct id",
|
|
212
|
+
idLogKey: "topicId"
|
|
213
|
+
});
|
|
214
|
+
if (direct) {
|
|
215
|
+
return direct;
|
|
216
|
+
}
|
|
217
|
+
const hostTopic = await tryResolveHostTopicById(ctx, String(topicId));
|
|
218
|
+
if (hostTopic) {
|
|
219
|
+
return hostTopic;
|
|
220
|
+
}
|
|
221
|
+
return pickPrimaryTopic(await findTopicsByScopeAlias(ctx, String(topicId))) ?? null;
|
|
222
|
+
}
|
|
223
|
+
async function resolveScopeFromLegacyProjectId(ctx, legacyProjectId) {
|
|
224
|
+
const directTopic = await resolveDirectLegacyProjectTopic(
|
|
225
|
+
ctx,
|
|
226
|
+
legacyProjectId
|
|
227
|
+
);
|
|
228
|
+
if (directTopic) {
|
|
229
|
+
return await buildTopicScope(ctx, directTopic, "topic_inferred", {
|
|
230
|
+
fallbackProjectId: legacyProjectId
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
const primary = pickPrimaryTopic(
|
|
234
|
+
await findTopicsByScopeAlias(ctx, legacyProjectId)
|
|
235
|
+
);
|
|
236
|
+
if (primary) {
|
|
237
|
+
return await buildTopicScope(ctx, primary, "project_mapped_topic", {
|
|
238
|
+
fallbackProjectId: legacyProjectId
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
const nodeScope = await resolveTopicNodeScopeOrNull(ctx, legacyProjectId);
|
|
242
|
+
if (nodeScope) {
|
|
243
|
+
return {
|
|
244
|
+
...nodeScope,
|
|
245
|
+
projectId: nodeScope.projectId ?? legacyProjectId
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
throw new Error(
|
|
249
|
+
`Legacy project scope ${legacyProjectId} has no mapped topic.`
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
async function resolveDirectLegacyProjectTopic(ctx, legacyProjectId) {
|
|
253
|
+
const directTopic = await tryReadTopicDoc(ctx, legacyProjectId, {
|
|
254
|
+
failureLog: "[topicScope] Failed to load direct project topic",
|
|
255
|
+
idLogKey: "projectId"
|
|
256
|
+
});
|
|
257
|
+
return directTopic ?? tryResolveHostTopicByLegacyScope(ctx, legacyProjectId);
|
|
258
|
+
}
|
|
259
|
+
async function tryReadTopicDoc(ctx, id, log) {
|
|
260
|
+
try {
|
|
261
|
+
return await ctx.db.get(id);
|
|
262
|
+
} catch (error) {
|
|
263
|
+
debugGraphPrimitiveFallback(log.failureLog, {
|
|
264
|
+
error,
|
|
265
|
+
[log.idLogKey]: id
|
|
266
|
+
});
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
async function buildTopicScope(ctx, topic, source, options = {}) {
|
|
271
|
+
const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
|
|
272
|
+
const mapped = asMappedProjectId(topic);
|
|
273
|
+
return {
|
|
274
|
+
topicId: topic._id,
|
|
275
|
+
...mapped || options.fallbackProjectId ? { projectId: mapped ?? options.fallbackProjectId } : {},
|
|
276
|
+
tenantId: inherited.tenantId,
|
|
277
|
+
workspaceId: inherited.workspaceId,
|
|
278
|
+
source
|
|
279
|
+
};
|
|
280
|
+
}
|
|
294
281
|
var optionalScopeArgs = {
|
|
295
282
|
projectId: v.optional(v.string()),
|
|
296
283
|
topicId: v.optional(v.string())
|
|
@@ -308,7 +295,7 @@ function normalizeScopeValue2(value) {
|
|
|
308
295
|
async function resolveScope(ctx, args) {
|
|
309
296
|
const topicId = normalizeScopeValue2(args.topicId);
|
|
310
297
|
const projectId = normalizeScopeValue2(args.projectId);
|
|
311
|
-
if (!topicId
|
|
298
|
+
if (!(topicId || projectId)) {
|
|
312
299
|
return null;
|
|
313
300
|
}
|
|
314
301
|
try {
|
|
@@ -328,12 +315,84 @@ async function resolveScope(ctx, args) {
|
|
|
328
315
|
return null;
|
|
329
316
|
}
|
|
330
317
|
}
|
|
331
|
-
async function insertEpistemicNode(ctx, doc) {
|
|
332
|
-
assertUuidV7Identity("epistemicNodes", doc.globalId);
|
|
333
|
-
return ctx.db.insert("epistemicNodes", doc);
|
|
334
|
-
}
|
|
335
318
|
|
|
336
319
|
// src/epistemicAnswers.ts
|
|
320
|
+
function isRecord(value) {
|
|
321
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
322
|
+
}
|
|
323
|
+
function readConvexId(value) {
|
|
324
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
325
|
+
}
|
|
326
|
+
function readOptionalNumber(value) {
|
|
327
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
328
|
+
}
|
|
329
|
+
function readOptionalString(value) {
|
|
330
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
331
|
+
}
|
|
332
|
+
function readRecord(value) {
|
|
333
|
+
return isRecord(value) ? value : void 0;
|
|
334
|
+
}
|
|
335
|
+
function readAnswerNodeRow(value) {
|
|
336
|
+
if (!isRecord(value)) {
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
const id = readConvexId(value._id);
|
|
340
|
+
const globalId = readOptionalString(value.globalId);
|
|
341
|
+
if (!(id && globalId)) {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
const row = { _id: id, globalId };
|
|
345
|
+
const canonicalText = readOptionalString(value.canonicalText);
|
|
346
|
+
const createdAt = readOptionalNumber(value.createdAt);
|
|
347
|
+
const createdBy = readOptionalString(value.createdBy);
|
|
348
|
+
const metadata = readRecord(value.metadata);
|
|
349
|
+
const nodeType = readOptionalString(value.nodeType);
|
|
350
|
+
const projectId = readOptionalString(value.projectId);
|
|
351
|
+
const status = readOptionalString(value.status);
|
|
352
|
+
const supersededBy = readConvexId(value.supersededBy);
|
|
353
|
+
const topicId = readConvexId(value.topicId);
|
|
354
|
+
if (canonicalText !== void 0) {
|
|
355
|
+
row.canonicalText = canonicalText;
|
|
356
|
+
}
|
|
357
|
+
if (createdAt !== void 0) {
|
|
358
|
+
row.createdAt = createdAt;
|
|
359
|
+
}
|
|
360
|
+
if (createdBy !== void 0) {
|
|
361
|
+
row.createdBy = createdBy;
|
|
362
|
+
}
|
|
363
|
+
if (metadata !== void 0) {
|
|
364
|
+
row.metadata = metadata;
|
|
365
|
+
}
|
|
366
|
+
if (nodeType !== void 0) {
|
|
367
|
+
row.nodeType = nodeType;
|
|
368
|
+
}
|
|
369
|
+
if (projectId !== void 0) {
|
|
370
|
+
row.projectId = projectId;
|
|
371
|
+
}
|
|
372
|
+
if (status !== void 0) {
|
|
373
|
+
row.status = status;
|
|
374
|
+
}
|
|
375
|
+
if (supersededBy !== void 0) {
|
|
376
|
+
row.supersededBy = supersededBy;
|
|
377
|
+
}
|
|
378
|
+
if (topicId !== void 0) {
|
|
379
|
+
row.topicId = topicId;
|
|
380
|
+
}
|
|
381
|
+
return row;
|
|
382
|
+
}
|
|
383
|
+
function readAnswerEdgeRow(value) {
|
|
384
|
+
if (!isRecord(value)) {
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
const fromNodeId = readConvexId(value.fromNodeId);
|
|
388
|
+
return fromNodeId ? { fromNodeId } : null;
|
|
389
|
+
}
|
|
390
|
+
function readRowList(values, reader) {
|
|
391
|
+
return values.flatMap((value) => {
|
|
392
|
+
const row = reader(value);
|
|
393
|
+
return row ? [row] : [];
|
|
394
|
+
});
|
|
395
|
+
}
|
|
337
396
|
function generateContentHash(text) {
|
|
338
397
|
const content = `answer:${text.trim().toLowerCase().replace(/\s+/g, " ").slice(0, 500)}`;
|
|
339
398
|
let hash = 5381;
|
|
@@ -343,6 +402,283 @@ function generateContentHash(text) {
|
|
|
343
402
|
}
|
|
344
403
|
return Math.abs(hash).toString(16).padStart(8, "0");
|
|
345
404
|
}
|
|
405
|
+
async function getActiveAnswersForQuestion(ctx, questionNode, topicId, questionNodeId) {
|
|
406
|
+
const answers = readRowList(
|
|
407
|
+
await ctx.db.query("epistemicNodes").withIndex(
|
|
408
|
+
"by_topic_type",
|
|
409
|
+
(q) => q.eq("topicId", questionNode.topicId ?? topicId).eq("nodeType", "answer")
|
|
410
|
+
).collect(),
|
|
411
|
+
readAnswerNodeRow
|
|
412
|
+
);
|
|
413
|
+
return answers.filter((answerNode) => {
|
|
414
|
+
const answerMeta = answerNode.metadata ?? {};
|
|
415
|
+
return answerMeta.questionNodeId === questionNodeId && answerMeta.isLatest === true && answerNode.status === "active";
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
function getNextAnswerVersion(activeAnswers) {
|
|
419
|
+
if (activeAnswers.length === 0) {
|
|
420
|
+
return 1;
|
|
421
|
+
}
|
|
422
|
+
return Math.max(
|
|
423
|
+
...activeAnswers.map((answerNode) => {
|
|
424
|
+
const answerMeta = answerNode.metadata ?? {};
|
|
425
|
+
return readOptionalNumber(answerMeta.versionNumber) ?? 0;
|
|
426
|
+
})
|
|
427
|
+
) + 1;
|
|
428
|
+
}
|
|
429
|
+
async function supersedeActiveAnswers(ctx, activeAnswers, now, clearSupersededByBeforeInsert) {
|
|
430
|
+
for (const activeAnswer of activeAnswers) {
|
|
431
|
+
const metadata = {
|
|
432
|
+
...activeAnswer.metadata ?? {},
|
|
433
|
+
isLatest: false
|
|
434
|
+
};
|
|
435
|
+
await ctx.db.patch(activeAnswer._id, {
|
|
436
|
+
status: "superseded",
|
|
437
|
+
updatedAt: now,
|
|
438
|
+
metadata,
|
|
439
|
+
...clearSupersededByBeforeInsert ? {
|
|
440
|
+
supersededBy: void 0
|
|
441
|
+
} : {}
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
async function collectEvidenceGlobalIds(ctx, evidenceNodeIds) {
|
|
446
|
+
const evidenceGlobalIds = [];
|
|
447
|
+
if (!evidenceNodeIds) {
|
|
448
|
+
return evidenceGlobalIds;
|
|
449
|
+
}
|
|
450
|
+
for (const evidenceNodeId of evidenceNodeIds) {
|
|
451
|
+
const evidenceNode = readAnswerNodeRow(await ctx.db.get(evidenceNodeId));
|
|
452
|
+
if (evidenceNode) {
|
|
453
|
+
evidenceGlobalIds.push(evidenceNode.globalId);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return evidenceGlobalIds;
|
|
457
|
+
}
|
|
458
|
+
function resolveAnswerSource(answerSource) {
|
|
459
|
+
return answerSource === "human" ? "human" : "ai_generated";
|
|
460
|
+
}
|
|
461
|
+
function insertAnswerNodeRecord(ctx, args) {
|
|
462
|
+
return insertEpistemicNode(ctx, {
|
|
463
|
+
globalId: args.globalId,
|
|
464
|
+
projectId: args.topicId,
|
|
465
|
+
topicId: args.questionNode.topicId,
|
|
466
|
+
nodeType: "answer",
|
|
467
|
+
epistemicLayer: "L2",
|
|
468
|
+
canonicalText: args.answerText,
|
|
469
|
+
contentHash: args.contentHash,
|
|
470
|
+
status: "active",
|
|
471
|
+
sourceType: resolveAnswerSource(args.args.answerSource),
|
|
472
|
+
createdBy: args.args.userId,
|
|
473
|
+
createdAt: args.now,
|
|
474
|
+
updatedAt: args.now,
|
|
475
|
+
metadata: {
|
|
476
|
+
questionGlobalId: args.questionNode.globalId,
|
|
477
|
+
questionNodeId: String(args.questionNode._id),
|
|
478
|
+
versionNumber: args.versionNumber,
|
|
479
|
+
isLatest: true,
|
|
480
|
+
evidenceGlobalIds: args.evidenceGlobalIds,
|
|
481
|
+
evidenceCount: args.evidenceGlobalIds.length,
|
|
482
|
+
answeredBy: args.args.userId,
|
|
483
|
+
answerSource: args.args.answerSource || "human",
|
|
484
|
+
sprintId: args.args.sprintId,
|
|
485
|
+
...args.args.backfilledFromQuestionId ? {
|
|
486
|
+
backfilledFromQuestionId: args.args.backfilledFromQuestionId
|
|
487
|
+
} : {}
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
async function setAnswerSupersession(ctx, activeAnswers, newNodeId) {
|
|
492
|
+
for (const activeAnswer of activeAnswers) {
|
|
493
|
+
await ctx.db.patch(activeAnswer._id, {
|
|
494
|
+
supersededBy: newNodeId
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
async function scheduleAnswerSyncJobs(ctx, args) {
|
|
499
|
+
await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
|
|
500
|
+
nodeId: args.nodeId,
|
|
501
|
+
operation: "upsert"
|
|
502
|
+
});
|
|
503
|
+
await ctx.scheduler.runAfter(
|
|
504
|
+
0,
|
|
505
|
+
api.embeddingActions.generateEpistemicNodeEmbedding,
|
|
506
|
+
{
|
|
507
|
+
nodeId: args.nodeId,
|
|
508
|
+
projectId: args.topicId,
|
|
509
|
+
topicId: args.questionNode.topicId ? String(args.questionNode.topicId) : void 0,
|
|
510
|
+
createdBy: args.userId,
|
|
511
|
+
nodeType: "answer",
|
|
512
|
+
text: args.answerText.slice(0, 2e4)
|
|
513
|
+
}
|
|
514
|
+
);
|
|
515
|
+
await ctx.scheduler.runAfter(
|
|
516
|
+
0,
|
|
517
|
+
internal.sprints.embedProjectThemeCollectiveInternal,
|
|
518
|
+
{
|
|
519
|
+
projectId: args.topicId,
|
|
520
|
+
userId: args.userId
|
|
521
|
+
}
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
async function scheduleAnswerGraphEdges(ctx, args) {
|
|
525
|
+
await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
|
|
526
|
+
globalId: generateGlobalId(),
|
|
527
|
+
fromGlobalId: args.answerGlobalId,
|
|
528
|
+
toGlobalId: args.questionNode.globalId,
|
|
529
|
+
edgeType: "responds_to",
|
|
530
|
+
weight: 1,
|
|
531
|
+
createdBy: args.userId,
|
|
532
|
+
topicId: args.edgeTopicId,
|
|
533
|
+
fromNodeType: "answer",
|
|
534
|
+
toNodeType: "question",
|
|
535
|
+
fromLayer: "L2",
|
|
536
|
+
toLayer: "L3"
|
|
537
|
+
});
|
|
538
|
+
for (const activeAnswer of args.activeAnswers) {
|
|
539
|
+
await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
|
|
540
|
+
globalId: generateGlobalId(),
|
|
541
|
+
fromGlobalId: args.answerGlobalId,
|
|
542
|
+
toGlobalId: activeAnswer.globalId,
|
|
543
|
+
edgeType: "supersedes",
|
|
544
|
+
createdBy: args.userId,
|
|
545
|
+
topicId: args.edgeTopicId,
|
|
546
|
+
fromNodeType: "answer",
|
|
547
|
+
toNodeType: "answer",
|
|
548
|
+
fromLayer: "L2",
|
|
549
|
+
toLayer: "L2"
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
if (args.evidenceNodeIds) {
|
|
553
|
+
for (const evidenceNodeId of args.evidenceNodeIds) {
|
|
554
|
+
const evidenceNode = readAnswerNodeRow(await ctx.db.get(evidenceNodeId));
|
|
555
|
+
if (evidenceNode) {
|
|
556
|
+
await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
|
|
557
|
+
globalId: generateGlobalId(),
|
|
558
|
+
fromGlobalId: args.answerGlobalId,
|
|
559
|
+
toGlobalId: evidenceNode.globalId,
|
|
560
|
+
edgeType: "derived_from",
|
|
561
|
+
createdBy: args.userId,
|
|
562
|
+
topicId: args.edgeTopicId,
|
|
563
|
+
fromNodeType: "answer",
|
|
564
|
+
toNodeType: "evidence",
|
|
565
|
+
fromLayer: "L2",
|
|
566
|
+
toLayer: "L2"
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
const evidenceEdges = readRowList(
|
|
572
|
+
await ctx.db.query("epistemicEdges").withIndex(
|
|
573
|
+
"by_to_type",
|
|
574
|
+
(q) => q.eq("toNodeId", args.questionNodeId).eq("edgeType", "derived_from")
|
|
575
|
+
).collect(),
|
|
576
|
+
readAnswerEdgeRow
|
|
577
|
+
);
|
|
578
|
+
for (const evidenceEdge of evidenceEdges) {
|
|
579
|
+
const evidenceNode = readAnswerNodeRow(
|
|
580
|
+
await ctx.db.get(evidenceEdge.fromNodeId)
|
|
581
|
+
);
|
|
582
|
+
if (!evidenceNode) {
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
|
|
586
|
+
globalId: generateGlobalId(),
|
|
587
|
+
fromGlobalId: evidenceNode.globalId,
|
|
588
|
+
toGlobalId: args.answerGlobalId,
|
|
589
|
+
edgeType: "derived_from",
|
|
590
|
+
createdBy: args.userId,
|
|
591
|
+
topicId: args.edgeTopicId,
|
|
592
|
+
fromNodeType: "evidence",
|
|
593
|
+
toNodeType: "answer",
|
|
594
|
+
fromLayer: "L2",
|
|
595
|
+
toLayer: "L2"
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
async function createAnswerMutationResult(ctx, args) {
|
|
600
|
+
const now = Date.now();
|
|
601
|
+
const answerText = args.answerText;
|
|
602
|
+
const globalId = generateGlobalId();
|
|
603
|
+
const contentHash = generateContentHash(answerText);
|
|
604
|
+
const activeAnswers = await getActiveAnswersForQuestion(
|
|
605
|
+
ctx,
|
|
606
|
+
args.questionNode,
|
|
607
|
+
args.topicId,
|
|
608
|
+
args.questionNodeId
|
|
609
|
+
);
|
|
610
|
+
const versionNumber = getNextAnswerVersion(activeAnswers);
|
|
611
|
+
await supersedeActiveAnswers(
|
|
612
|
+
ctx,
|
|
613
|
+
activeAnswers,
|
|
614
|
+
now,
|
|
615
|
+
!!args.clearSupersededByBeforeInsert
|
|
616
|
+
);
|
|
617
|
+
const evidenceGlobalIds = await collectEvidenceGlobalIds(
|
|
618
|
+
ctx,
|
|
619
|
+
args.evidenceNodeIds
|
|
620
|
+
);
|
|
621
|
+
const nodeId = await insertAnswerNodeRecord(ctx, {
|
|
622
|
+
questionNode: args.questionNode,
|
|
623
|
+
topicId: args.topicId,
|
|
624
|
+
answerText,
|
|
625
|
+
contentHash,
|
|
626
|
+
versionNumber,
|
|
627
|
+
evidenceGlobalIds,
|
|
628
|
+
globalId,
|
|
629
|
+
now,
|
|
630
|
+
args: {
|
|
631
|
+
answerSource: args.answerSource,
|
|
632
|
+
sprintId: args.sprintId,
|
|
633
|
+
userId: args.userId,
|
|
634
|
+
backfilledFromQuestionId: args.backfilledFromQuestionId
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
await setAnswerSupersession(ctx, activeAnswers, nodeId);
|
|
638
|
+
await scheduleAnswerSyncJobs(ctx, {
|
|
639
|
+
nodeId,
|
|
640
|
+
topicId: args.topicId,
|
|
641
|
+
questionNode: args.questionNode,
|
|
642
|
+
userId: args.userId,
|
|
643
|
+
answerText
|
|
644
|
+
});
|
|
645
|
+
await scheduleAnswerGraphEdges(ctx, {
|
|
646
|
+
questionNode: args.questionNode,
|
|
647
|
+
questionNodeId: args.questionNodeId,
|
|
648
|
+
userId: args.userId,
|
|
649
|
+
evidenceNodeIds: args.evidenceNodeIds,
|
|
650
|
+
activeAnswers,
|
|
651
|
+
answerGlobalId: globalId,
|
|
652
|
+
edgeTopicId: args.edgeTopicId
|
|
653
|
+
});
|
|
654
|
+
const answerQuality = args.confidence || "moderate";
|
|
655
|
+
await ctx.db.patch(args.questionNodeId, {
|
|
656
|
+
answerQuality,
|
|
657
|
+
updatedAt: now
|
|
658
|
+
});
|
|
659
|
+
const auditState = args.backfilledFromQuestionId ? {
|
|
660
|
+
answerText: answerText.slice(0, 200),
|
|
661
|
+
versionNumber,
|
|
662
|
+
questionNodeId: String(args.questionNodeId),
|
|
663
|
+
backfilled: true
|
|
664
|
+
} : {
|
|
665
|
+
answerText: answerText.slice(0, 200),
|
|
666
|
+
versionNumber,
|
|
667
|
+
questionNodeId: String(args.questionNodeId),
|
|
668
|
+
confidence: answerQuality
|
|
669
|
+
};
|
|
670
|
+
await ctx.db.insert("epistemicAudit", {
|
|
671
|
+
entityType: "answer",
|
|
672
|
+
entityId: nodeId,
|
|
673
|
+
changeType: "created",
|
|
674
|
+
changedAt: now,
|
|
675
|
+
changedBy: args.userId,
|
|
676
|
+
isAgent: false,
|
|
677
|
+
projectId: args.topicId,
|
|
678
|
+
newState: auditState
|
|
679
|
+
});
|
|
680
|
+
return { nodeId, globalId, versionNumber };
|
|
681
|
+
}
|
|
346
682
|
var create = mutation({
|
|
347
683
|
args: {
|
|
348
684
|
...scopeArgs,
|
|
@@ -367,190 +703,25 @@ var create = mutation({
|
|
|
367
703
|
const scope = await resolveScope(ctx, args);
|
|
368
704
|
const topicId = scope?.topicId ? String(scope.topicId) : args.topicId || "";
|
|
369
705
|
await checkScopeAccess(ctx, topicId, args.userId);
|
|
370
|
-
const questionNode =
|
|
371
|
-
|
|
372
|
-
throw new Error("Question node not found or not a question type");
|
|
373
|
-
}
|
|
374
|
-
const now = Date.now();
|
|
375
|
-
const globalId = generateGlobalId();
|
|
376
|
-
const contentHash = generateContentHash(args.answerText);
|
|
377
|
-
const existingAnswers = await ctx.db.query("epistemicNodes").withIndex(
|
|
378
|
-
"by_topic_type",
|
|
379
|
-
(q) => q.eq("topicId", questionNode.topicId ?? topicId).eq("nodeType", "answer")
|
|
380
|
-
).collect();
|
|
381
|
-
const activeAnswersForQuestion = existingAnswers.filter((n) => {
|
|
382
|
-
const meta = n.metadata || {};
|
|
383
|
-
return meta.questionNodeId === args.questionNodeId && meta.isLatest === true && n.status === "active";
|
|
384
|
-
});
|
|
385
|
-
const versionNumber = activeAnswersForQuestion.length > 0 ? Math.max(
|
|
386
|
-
...activeAnswersForQuestion.map((a) => {
|
|
387
|
-
const meta = a.metadata || {};
|
|
388
|
-
return meta.versionNumber || 0;
|
|
389
|
-
})
|
|
390
|
-
) + 1 : 1;
|
|
391
|
-
for (const oldAnswer of activeAnswersForQuestion) {
|
|
392
|
-
await ctx.db.patch(oldAnswer._id, {
|
|
393
|
-
status: "superseded",
|
|
394
|
-
supersededBy: void 0,
|
|
395
|
-
// Will be set after insert
|
|
396
|
-
updatedAt: now,
|
|
397
|
-
metadata: {
|
|
398
|
-
...oldAnswer.metadata || {},
|
|
399
|
-
isLatest: false
|
|
400
|
-
}
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
const evidenceGlobalIds = [];
|
|
404
|
-
if (args.evidenceNodeIds) {
|
|
405
|
-
for (const evId of args.evidenceNodeIds) {
|
|
406
|
-
const evNode = await ctx.db.get(evId);
|
|
407
|
-
if (evNode) {
|
|
408
|
-
evidenceGlobalIds.push(evNode.globalId);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
const nodeId = await insertEpistemicNode(ctx, {
|
|
413
|
-
globalId,
|
|
414
|
-
projectId: topicId,
|
|
415
|
-
topicId: questionNode.topicId,
|
|
416
|
-
nodeType: "answer",
|
|
417
|
-
epistemicLayer: "L2",
|
|
418
|
-
canonicalText: args.answerText,
|
|
419
|
-
contentHash,
|
|
420
|
-
status: "active",
|
|
421
|
-
sourceType: args.answerSource === "human" ? "human" : "ai_generated",
|
|
422
|
-
createdBy: args.userId,
|
|
423
|
-
createdAt: now,
|
|
424
|
-
updatedAt: now,
|
|
425
|
-
metadata: {
|
|
426
|
-
questionGlobalId: questionNode.globalId,
|
|
427
|
-
questionNodeId: String(args.questionNodeId),
|
|
428
|
-
versionNumber,
|
|
429
|
-
isLatest: true,
|
|
430
|
-
evidenceGlobalIds,
|
|
431
|
-
evidenceCount: evidenceGlobalIds.length,
|
|
432
|
-
answeredBy: args.userId,
|
|
433
|
-
answerSource: args.answerSource || "human",
|
|
434
|
-
sprintId: args.sprintId
|
|
435
|
-
}
|
|
436
|
-
});
|
|
437
|
-
for (const oldAnswer of activeAnswersForQuestion) {
|
|
438
|
-
await ctx.db.patch(oldAnswer._id, {
|
|
439
|
-
supersededBy: nodeId
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
|
|
443
|
-
nodeId,
|
|
444
|
-
operation: "upsert"
|
|
445
|
-
});
|
|
446
|
-
await ctx.scheduler.runAfter(
|
|
447
|
-
0,
|
|
448
|
-
api.embeddingActions.generateEpistemicNodeEmbedding,
|
|
449
|
-
{
|
|
450
|
-
nodeId,
|
|
451
|
-
projectId: topicId,
|
|
452
|
-
topicId: questionNode.topicId ? String(questionNode.topicId) : void 0,
|
|
453
|
-
createdBy: args.userId,
|
|
454
|
-
nodeType: "answer",
|
|
455
|
-
text: args.answerText.slice(0, 2e4)
|
|
456
|
-
}
|
|
457
|
-
);
|
|
458
|
-
await ctx.scheduler.runAfter(
|
|
459
|
-
0,
|
|
460
|
-
internal.sprints.embedProjectThemeCollectiveInternal,
|
|
461
|
-
{
|
|
462
|
-
projectId: topicId,
|
|
463
|
-
userId: args.userId
|
|
464
|
-
}
|
|
706
|
+
const questionNode = readAnswerNodeRow(
|
|
707
|
+
await ctx.db.get(args.questionNodeId)
|
|
465
708
|
);
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
fromGlobalId: globalId,
|
|
469
|
-
toGlobalId: questionNode.globalId,
|
|
470
|
-
edgeType: "responds_to",
|
|
471
|
-
weight: 1,
|
|
472
|
-
createdBy: args.userId,
|
|
473
|
-
topicId: String(args.topicId ?? ""),
|
|
474
|
-
fromNodeType: "answer",
|
|
475
|
-
toNodeType: "question",
|
|
476
|
-
fromLayer: "L2",
|
|
477
|
-
toLayer: "L3"
|
|
478
|
-
});
|
|
479
|
-
for (const oldAnswer of activeAnswersForQuestion) {
|
|
480
|
-
await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
|
|
481
|
-
globalId: generateGlobalId(),
|
|
482
|
-
fromGlobalId: globalId,
|
|
483
|
-
toGlobalId: oldAnswer.globalId,
|
|
484
|
-
edgeType: "supersedes",
|
|
485
|
-
createdBy: args.userId,
|
|
486
|
-
topicId: String(args.topicId ?? ""),
|
|
487
|
-
fromNodeType: "answer",
|
|
488
|
-
toNodeType: "answer",
|
|
489
|
-
fromLayer: "L2",
|
|
490
|
-
toLayer: "L2"
|
|
491
|
-
});
|
|
492
|
-
}
|
|
493
|
-
if (args.evidenceNodeIds) {
|
|
494
|
-
for (const evId of args.evidenceNodeIds) {
|
|
495
|
-
const evNode = await ctx.db.get(evId);
|
|
496
|
-
if (evNode) {
|
|
497
|
-
await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
|
|
498
|
-
globalId: generateGlobalId(),
|
|
499
|
-
fromGlobalId: globalId,
|
|
500
|
-
toGlobalId: evNode.globalId,
|
|
501
|
-
edgeType: "derived_from",
|
|
502
|
-
createdBy: args.userId,
|
|
503
|
-
topicId: String(args.topicId ?? ""),
|
|
504
|
-
fromNodeType: "answer",
|
|
505
|
-
toNodeType: "evidence",
|
|
506
|
-
fromLayer: "L2",
|
|
507
|
-
toLayer: "L2"
|
|
508
|
-
});
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
const evidenceEdges = await ctx.db.query("epistemicEdges").withIndex(
|
|
513
|
-
"by_to_type",
|
|
514
|
-
(q) => q.eq("toNodeId", args.questionNodeId).eq("edgeType", "derived_from")
|
|
515
|
-
).collect();
|
|
516
|
-
for (const edge of evidenceEdges) {
|
|
517
|
-
const evidenceNode = await ctx.db.get(edge.fromNodeId);
|
|
518
|
-
if (evidenceNode) {
|
|
519
|
-
await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
|
|
520
|
-
globalId: generateGlobalId(),
|
|
521
|
-
fromGlobalId: evidenceNode.globalId,
|
|
522
|
-
toGlobalId: globalId,
|
|
523
|
-
edgeType: "derived_from",
|
|
524
|
-
createdBy: args.userId,
|
|
525
|
-
topicId: String(args.topicId ?? ""),
|
|
526
|
-
fromNodeType: "evidence",
|
|
527
|
-
toNodeType: "answer",
|
|
528
|
-
fromLayer: "L2",
|
|
529
|
-
toLayer: "L2"
|
|
530
|
-
});
|
|
531
|
-
}
|
|
709
|
+
if (questionNode?.nodeType !== "question") {
|
|
710
|
+
throw new Error("Question node not found or not a question type");
|
|
532
711
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
projectId: topicId,
|
|
546
|
-
newState: {
|
|
547
|
-
answerText: args.answerText.slice(0, 200),
|
|
548
|
-
versionNumber,
|
|
549
|
-
questionNodeId: String(args.questionNodeId),
|
|
550
|
-
confidence: answerQuality
|
|
551
|
-
}
|
|
712
|
+
return createAnswerMutationResult(ctx, {
|
|
713
|
+
questionNode,
|
|
714
|
+
questionNodeId: args.questionNodeId,
|
|
715
|
+
answerText: args.answerText,
|
|
716
|
+
confidence: args.confidence,
|
|
717
|
+
evidenceNodeIds: args.evidenceNodeIds,
|
|
718
|
+
answerSource: args.answerSource,
|
|
719
|
+
sprintId: args.sprintId,
|
|
720
|
+
userId: args.userId,
|
|
721
|
+
topicId,
|
|
722
|
+
edgeTopicId: String(args.topicId ?? ""),
|
|
723
|
+
clearSupersededByBeforeInsert: true
|
|
552
724
|
});
|
|
553
|
-
return { nodeId, globalId, versionNumber };
|
|
554
725
|
}
|
|
555
726
|
});
|
|
556
727
|
var createInternal = internalMutation({
|
|
@@ -569,189 +740,25 @@ var createInternal = internalMutation({
|
|
|
569
740
|
handler: async (ctx, args) => {
|
|
570
741
|
const scope = await resolveScope(ctx, args);
|
|
571
742
|
const topicId = scope?.topicId ? String(scope.topicId) : args.topicId || "";
|
|
572
|
-
const questionNode =
|
|
573
|
-
|
|
574
|
-
throw new Error("Question node not found or not a question type");
|
|
575
|
-
}
|
|
576
|
-
const now = Date.now();
|
|
577
|
-
const globalId = generateGlobalId();
|
|
578
|
-
const contentHash = generateContentHash(args.answerText);
|
|
579
|
-
const existingAnswers = await ctx.db.query("epistemicNodes").withIndex(
|
|
580
|
-
"by_topic_type",
|
|
581
|
-
(q) => q.eq("topicId", questionNode.topicId ?? topicId).eq("nodeType", "answer")
|
|
582
|
-
).collect();
|
|
583
|
-
const activeAnswersForQuestion = existingAnswers.filter((n) => {
|
|
584
|
-
const meta = n.metadata || {};
|
|
585
|
-
return meta.questionNodeId === String(args.questionNodeId) && meta.isLatest === true && n.status === "active";
|
|
586
|
-
});
|
|
587
|
-
const versionNumber = activeAnswersForQuestion.length > 0 ? Math.max(
|
|
588
|
-
...activeAnswersForQuestion.map((a) => {
|
|
589
|
-
const meta = a.metadata || {};
|
|
590
|
-
return meta.versionNumber || 0;
|
|
591
|
-
})
|
|
592
|
-
) + 1 : 1;
|
|
593
|
-
for (const oldAnswer of activeAnswersForQuestion) {
|
|
594
|
-
await ctx.db.patch(oldAnswer._id, {
|
|
595
|
-
status: "superseded",
|
|
596
|
-
updatedAt: now,
|
|
597
|
-
metadata: {
|
|
598
|
-
...oldAnswer.metadata || {},
|
|
599
|
-
isLatest: false
|
|
600
|
-
}
|
|
601
|
-
});
|
|
602
|
-
}
|
|
603
|
-
const evidenceGlobalIds = [];
|
|
604
|
-
if (args.evidenceNodeIds) {
|
|
605
|
-
for (const evId of args.evidenceNodeIds) {
|
|
606
|
-
const evNode = await ctx.db.get(evId);
|
|
607
|
-
if (evNode) {
|
|
608
|
-
evidenceGlobalIds.push(evNode.globalId);
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
const nodeId = await insertEpistemicNode(ctx, {
|
|
613
|
-
globalId,
|
|
614
|
-
projectId: topicId,
|
|
615
|
-
topicId: questionNode.topicId,
|
|
616
|
-
nodeType: "answer",
|
|
617
|
-
epistemicLayer: "L2",
|
|
618
|
-
canonicalText: args.answerText,
|
|
619
|
-
contentHash,
|
|
620
|
-
status: "active",
|
|
621
|
-
sourceType: args.answerSource === "human" ? "human" : "ai_generated",
|
|
622
|
-
createdBy: args.userId,
|
|
623
|
-
createdAt: now,
|
|
624
|
-
updatedAt: now,
|
|
625
|
-
metadata: {
|
|
626
|
-
questionGlobalId: questionNode.globalId,
|
|
627
|
-
questionNodeId: String(args.questionNodeId),
|
|
628
|
-
versionNumber,
|
|
629
|
-
isLatest: true,
|
|
630
|
-
evidenceGlobalIds,
|
|
631
|
-
evidenceCount: evidenceGlobalIds.length,
|
|
632
|
-
answeredBy: args.userId,
|
|
633
|
-
answerSource: args.answerSource || "human",
|
|
634
|
-
sprintId: args.sprintId,
|
|
635
|
-
backfilledFromQuestionId: args.backfilledFromQuestionId
|
|
636
|
-
}
|
|
637
|
-
});
|
|
638
|
-
for (const oldAnswer of activeAnswersForQuestion) {
|
|
639
|
-
await ctx.db.patch(oldAnswer._id, {
|
|
640
|
-
supersededBy: nodeId
|
|
641
|
-
});
|
|
642
|
-
}
|
|
643
|
-
await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
|
|
644
|
-
nodeId,
|
|
645
|
-
operation: "upsert"
|
|
646
|
-
});
|
|
647
|
-
await ctx.scheduler.runAfter(
|
|
648
|
-
0,
|
|
649
|
-
api.embeddingActions.generateEpistemicNodeEmbedding,
|
|
650
|
-
{
|
|
651
|
-
nodeId,
|
|
652
|
-
projectId: topicId,
|
|
653
|
-
topicId: questionNode.topicId ? String(questionNode.topicId) : void 0,
|
|
654
|
-
createdBy: args.userId,
|
|
655
|
-
nodeType: "answer",
|
|
656
|
-
text: args.answerText.slice(0, 2e4)
|
|
657
|
-
}
|
|
658
|
-
);
|
|
659
|
-
await ctx.scheduler.runAfter(
|
|
660
|
-
0,
|
|
661
|
-
internal.sprints.embedProjectThemeCollectiveInternal,
|
|
662
|
-
{
|
|
663
|
-
projectId: topicId,
|
|
664
|
-
userId: args.userId
|
|
665
|
-
}
|
|
743
|
+
const questionNode = readAnswerNodeRow(
|
|
744
|
+
await ctx.db.get(args.questionNodeId)
|
|
666
745
|
);
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
fromGlobalId: globalId,
|
|
670
|
-
toGlobalId: questionNode.globalId,
|
|
671
|
-
edgeType: "responds_to",
|
|
672
|
-
weight: 1,
|
|
673
|
-
createdBy: args.userId,
|
|
674
|
-
topicId: String(args.topicId ?? ""),
|
|
675
|
-
fromNodeType: "answer",
|
|
676
|
-
toNodeType: "question",
|
|
677
|
-
fromLayer: "L2",
|
|
678
|
-
toLayer: "L3"
|
|
679
|
-
});
|
|
680
|
-
for (const oldAnswer of activeAnswersForQuestion) {
|
|
681
|
-
await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
|
|
682
|
-
globalId: generateGlobalId(),
|
|
683
|
-
fromGlobalId: globalId,
|
|
684
|
-
toGlobalId: oldAnswer.globalId,
|
|
685
|
-
edgeType: "supersedes",
|
|
686
|
-
createdBy: args.userId,
|
|
687
|
-
topicId: String(args.topicId ?? ""),
|
|
688
|
-
fromNodeType: "answer",
|
|
689
|
-
toNodeType: "answer",
|
|
690
|
-
fromLayer: "L2",
|
|
691
|
-
toLayer: "L2"
|
|
692
|
-
});
|
|
693
|
-
}
|
|
694
|
-
if (args.evidenceNodeIds) {
|
|
695
|
-
for (const evId of args.evidenceNodeIds) {
|
|
696
|
-
const evNode = await ctx.db.get(evId);
|
|
697
|
-
if (evNode) {
|
|
698
|
-
await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
|
|
699
|
-
globalId: generateGlobalId(),
|
|
700
|
-
fromGlobalId: globalId,
|
|
701
|
-
toGlobalId: evNode.globalId,
|
|
702
|
-
edgeType: "derived_from",
|
|
703
|
-
createdBy: args.userId,
|
|
704
|
-
topicId: String(args.topicId ?? ""),
|
|
705
|
-
fromNodeType: "answer",
|
|
706
|
-
toNodeType: "evidence",
|
|
707
|
-
fromLayer: "L2",
|
|
708
|
-
toLayer: "L2"
|
|
709
|
-
});
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
const evidenceEdges = await ctx.db.query("epistemicEdges").withIndex(
|
|
714
|
-
"by_to_type",
|
|
715
|
-
(q) => q.eq("toNodeId", args.questionNodeId).eq("edgeType", "derived_from")
|
|
716
|
-
).collect();
|
|
717
|
-
for (const edge of evidenceEdges) {
|
|
718
|
-
const evidenceNode = await ctx.db.get(edge.fromNodeId);
|
|
719
|
-
if (evidenceNode) {
|
|
720
|
-
await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
|
|
721
|
-
globalId: generateGlobalId(),
|
|
722
|
-
fromGlobalId: evidenceNode.globalId,
|
|
723
|
-
toGlobalId: globalId,
|
|
724
|
-
edgeType: "derived_from",
|
|
725
|
-
createdBy: args.userId,
|
|
726
|
-
topicId: String(args.topicId ?? ""),
|
|
727
|
-
fromNodeType: "evidence",
|
|
728
|
-
toNodeType: "answer",
|
|
729
|
-
fromLayer: "L2",
|
|
730
|
-
toLayer: "L2"
|
|
731
|
-
});
|
|
732
|
-
}
|
|
746
|
+
if (questionNode?.nodeType !== "question") {
|
|
747
|
+
throw new Error("Question node not found or not a question type");
|
|
733
748
|
}
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
projectId: topicId,
|
|
747
|
-
newState: {
|
|
748
|
-
answerText: args.answerText.slice(0, 200),
|
|
749
|
-
versionNumber,
|
|
750
|
-
questionNodeId: String(args.questionNodeId),
|
|
751
|
-
backfilled: !!args.backfilledFromQuestionId
|
|
752
|
-
}
|
|
749
|
+
return createAnswerMutationResult(ctx, {
|
|
750
|
+
questionNode,
|
|
751
|
+
questionNodeId: args.questionNodeId,
|
|
752
|
+
answerText: args.answerText,
|
|
753
|
+
confidence: args.confidence,
|
|
754
|
+
evidenceNodeIds: args.evidenceNodeIds,
|
|
755
|
+
answerSource: args.answerSource,
|
|
756
|
+
sprintId: args.sprintId,
|
|
757
|
+
userId: args.userId,
|
|
758
|
+
topicId,
|
|
759
|
+
edgeTopicId: String(args.topicId ?? ""),
|
|
760
|
+
backfilledFromQuestionId: args.backfilledFromQuestionId
|
|
753
761
|
});
|
|
754
|
-
return { nodeId, globalId, versionNumber };
|
|
755
762
|
}
|
|
756
763
|
});
|
|
757
764
|
var getByQuestion = query({
|
|
@@ -761,30 +768,35 @@ var getByQuestion = query({
|
|
|
761
768
|
},
|
|
762
769
|
returns: permissiveReturn,
|
|
763
770
|
handler: async (ctx, args) => {
|
|
764
|
-
const questionNode =
|
|
765
|
-
|
|
771
|
+
const questionNode = readAnswerNodeRow(
|
|
772
|
+
await ctx.db.get(args.questionNodeId)
|
|
773
|
+
);
|
|
774
|
+
if (questionNode?.nodeType !== "question") {
|
|
766
775
|
return [];
|
|
767
776
|
}
|
|
768
777
|
const topicId = questionNode.topicId;
|
|
769
778
|
if (!topicId) {
|
|
770
779
|
return [];
|
|
771
780
|
}
|
|
772
|
-
const answers =
|
|
773
|
-
"
|
|
774
|
-
|
|
775
|
-
|
|
781
|
+
const answers = readRowList(
|
|
782
|
+
await ctx.db.query("epistemicNodes").withIndex(
|
|
783
|
+
"by_topic_type",
|
|
784
|
+
(q) => q.eq("topicId", topicId).eq("nodeType", "answer")
|
|
785
|
+
).collect(),
|
|
786
|
+
readAnswerNodeRow
|
|
787
|
+
);
|
|
776
788
|
const questionIdStr = String(args.questionNodeId);
|
|
777
789
|
const filtered = answers.filter((a) => {
|
|
778
|
-
const meta = a.metadata
|
|
790
|
+
const meta = a.metadata ?? {};
|
|
779
791
|
return meta.questionNodeId === questionIdStr;
|
|
780
792
|
});
|
|
781
793
|
if (!args.includeSuperseded) {
|
|
782
794
|
return filtered.filter((a) => a.status === "active");
|
|
783
795
|
}
|
|
784
796
|
filtered.sort((a, b) => {
|
|
785
|
-
const metaA = a.metadata
|
|
786
|
-
const metaB = b.metadata
|
|
787
|
-
return (metaB.versionNumber
|
|
797
|
+
const metaA = a.metadata ?? {};
|
|
798
|
+
const metaB = b.metadata ?? {};
|
|
799
|
+
return (readOptionalNumber(metaB.versionNumber) ?? 0) - (readOptionalNumber(metaA.versionNumber) ?? 0);
|
|
788
800
|
});
|
|
789
801
|
return filtered;
|
|
790
802
|
}
|
|
@@ -795,20 +807,26 @@ var getLatestForQuestion = query({
|
|
|
795
807
|
},
|
|
796
808
|
returns: permissiveReturn,
|
|
797
809
|
handler: async (ctx, args) => {
|
|
798
|
-
const questionNode =
|
|
799
|
-
|
|
810
|
+
const questionNode = readAnswerNodeRow(
|
|
811
|
+
await ctx.db.get(args.questionNodeId)
|
|
812
|
+
);
|
|
813
|
+
if (questionNode?.nodeType !== "question") {
|
|
800
814
|
return null;
|
|
801
815
|
}
|
|
802
|
-
|
|
816
|
+
const topicId = questionNode.topicId;
|
|
817
|
+
if (!topicId) {
|
|
803
818
|
return null;
|
|
804
819
|
}
|
|
805
|
-
const answers =
|
|
806
|
-
"
|
|
807
|
-
|
|
808
|
-
|
|
820
|
+
const answers = readRowList(
|
|
821
|
+
await ctx.db.query("epistemicNodes").withIndex(
|
|
822
|
+
"by_topic_type",
|
|
823
|
+
(q) => q.eq("topicId", topicId).eq("nodeType", "answer")
|
|
824
|
+
).filter((q) => q.eq(q.field("status"), "active")).collect(),
|
|
825
|
+
readAnswerNodeRow
|
|
826
|
+
);
|
|
809
827
|
const questionIdStr = String(args.questionNodeId);
|
|
810
828
|
const latest = answers.find((a) => {
|
|
811
|
-
const meta = a.metadata
|
|
829
|
+
const meta = a.metadata ?? {};
|
|
812
830
|
return meta.questionNodeId === questionIdStr && meta.isLatest === true;
|
|
813
831
|
});
|
|
814
832
|
return latest || null;
|
|
@@ -820,38 +838,44 @@ var getVersionHistory = query({
|
|
|
820
838
|
},
|
|
821
839
|
returns: permissiveReturn,
|
|
822
840
|
handler: async (ctx, args) => {
|
|
823
|
-
const questionNode =
|
|
824
|
-
|
|
841
|
+
const questionNode = readAnswerNodeRow(
|
|
842
|
+
await ctx.db.get(args.questionNodeId)
|
|
843
|
+
);
|
|
844
|
+
if (questionNode?.nodeType !== "question") {
|
|
825
845
|
return [];
|
|
826
846
|
}
|
|
827
|
-
|
|
847
|
+
const topicId = questionNode.topicId;
|
|
848
|
+
if (!topicId) {
|
|
828
849
|
return [];
|
|
829
850
|
}
|
|
830
|
-
const answers =
|
|
831
|
-
"
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
851
|
+
const answers = readRowList(
|
|
852
|
+
await ctx.db.query("epistemicNodes").withIndex(
|
|
853
|
+
"by_topic_type",
|
|
854
|
+
(q) => q.eq("topicId", topicId).eq("nodeType", "answer")
|
|
855
|
+
).filter(
|
|
856
|
+
(q) => q.or(
|
|
857
|
+
q.eq(q.field("status"), "active"),
|
|
858
|
+
q.eq(q.field("status"), "superseded")
|
|
859
|
+
)
|
|
860
|
+
).collect(),
|
|
861
|
+
readAnswerNodeRow
|
|
862
|
+
);
|
|
839
863
|
const questionIdStr = String(args.questionNodeId);
|
|
840
864
|
const history = answers.filter((a) => {
|
|
841
|
-
const meta = a.metadata
|
|
865
|
+
const meta = a.metadata ?? {};
|
|
842
866
|
return meta.questionNodeId === questionIdStr;
|
|
843
867
|
}).map((a) => {
|
|
844
|
-
const meta = a.metadata
|
|
868
|
+
const meta = a.metadata ?? {};
|
|
845
869
|
return {
|
|
846
870
|
_id: a._id,
|
|
847
871
|
globalId: a.globalId,
|
|
848
872
|
answerText: a.canonicalText,
|
|
849
|
-
versionNumber: meta.versionNumber
|
|
850
|
-
isLatest: meta.isLatest
|
|
873
|
+
versionNumber: readOptionalNumber(meta.versionNumber) ?? 1,
|
|
874
|
+
isLatest: meta.isLatest === true,
|
|
851
875
|
status: a.status,
|
|
852
|
-
answeredBy: meta.answeredBy
|
|
853
|
-
answerSource: meta.answerSource,
|
|
854
|
-
evidenceCount: meta.evidenceCount
|
|
876
|
+
answeredBy: readOptionalString(meta.answeredBy) ?? a.createdBy,
|
|
877
|
+
answerSource: readOptionalString(meta.answerSource),
|
|
878
|
+
evidenceCount: readOptionalNumber(meta.evidenceCount) ?? 0,
|
|
855
879
|
createdAt: a.createdAt,
|
|
856
880
|
supersededBy: a.supersededBy
|
|
857
881
|
};
|
|
@@ -868,25 +892,39 @@ var backfillAnswerNodes = internalMutation({
|
|
|
868
892
|
returns: permissiveReturn,
|
|
869
893
|
handler: async (ctx, args) => {
|
|
870
894
|
const scope = await resolveScope(ctx, args);
|
|
871
|
-
const topicId = scope?.topicId
|
|
895
|
+
const topicId = scope?.topicId ? String(scope.topicId) : args.topicId;
|
|
872
896
|
const dryRun = args.dryRun ?? false;
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
897
|
+
if (!topicId) {
|
|
898
|
+
return {
|
|
899
|
+
questionsWithAnswers: 0,
|
|
900
|
+
alreadyHaveNodes: 0,
|
|
901
|
+
needsBackfill: 0,
|
|
902
|
+
backfilled: 0
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
const questions = readRowList(
|
|
906
|
+
await ctx.db.query("epistemicNodes").withIndex(
|
|
907
|
+
"by_topic_type",
|
|
908
|
+
(q) => q.eq("topicId", topicId).eq("nodeType", "question")
|
|
909
|
+
).collect(),
|
|
910
|
+
readAnswerNodeRow
|
|
911
|
+
);
|
|
877
912
|
const questionsWithAnswers = questions.filter((q) => {
|
|
878
|
-
const meta = q.metadata
|
|
879
|
-
const answer = meta.answer;
|
|
913
|
+
const meta = q.metadata ?? {};
|
|
914
|
+
const answer = readOptionalString(meta.answer);
|
|
880
915
|
return answer && answer.trim().length > 0;
|
|
881
916
|
});
|
|
882
|
-
const existingAnswers =
|
|
883
|
-
"
|
|
884
|
-
|
|
885
|
-
|
|
917
|
+
const existingAnswers = readRowList(
|
|
918
|
+
await ctx.db.query("epistemicNodes").withIndex(
|
|
919
|
+
"by_topic_type",
|
|
920
|
+
(q) => q.eq("topicId", topicId).eq("nodeType", "answer")
|
|
921
|
+
).collect(),
|
|
922
|
+
readAnswerNodeRow
|
|
923
|
+
);
|
|
886
924
|
const questionIdsWithAnswerNodes = new Set(
|
|
887
925
|
existingAnswers.map((a) => {
|
|
888
|
-
const meta = a.metadata
|
|
889
|
-
return meta.questionNodeId;
|
|
926
|
+
const meta = a.metadata ?? {};
|
|
927
|
+
return readOptionalString(meta.questionNodeId);
|
|
890
928
|
})
|
|
891
929
|
);
|
|
892
930
|
const needsBackfill = questionsWithAnswers.filter(
|
|
@@ -904,14 +942,18 @@ var backfillAnswerNodes = internalMutation({
|
|
|
904
942
|
id: q._id,
|
|
905
943
|
globalId: q.globalId,
|
|
906
944
|
text: q.canonicalText?.slice(0, 80),
|
|
907
|
-
answer: q.metadata?.answer?.slice(0, 80)
|
|
945
|
+
answer: readOptionalString(q.metadata?.answer)?.slice(0, 80)
|
|
908
946
|
}))
|
|
909
947
|
};
|
|
910
948
|
}
|
|
911
949
|
let created = 0;
|
|
912
950
|
for (const question of needsBackfill) {
|
|
913
|
-
const meta = question.metadata
|
|
914
|
-
const answerText = meta.answer;
|
|
951
|
+
const meta = question.metadata ?? {};
|
|
952
|
+
const answerText = readOptionalString(meta.answer);
|
|
953
|
+
if (!answerText) {
|
|
954
|
+
continue;
|
|
955
|
+
}
|
|
956
|
+
const answeredBy = readOptionalString(meta.answeredBy);
|
|
915
957
|
await ctx.scheduler.runAfter(
|
|
916
958
|
0,
|
|
917
959
|
internal.epistemicAnswers.createInternal,
|
|
@@ -920,8 +962,8 @@ var backfillAnswerNodes = internalMutation({
|
|
|
920
962
|
// TC-C: migrated from projectId
|
|
921
963
|
questionNodeId: question._id,
|
|
922
964
|
answerText,
|
|
923
|
-
answerSource:
|
|
924
|
-
userId:
|
|
965
|
+
answerSource: answeredBy ? "human" : "ai_generated",
|
|
966
|
+
userId: answeredBy ?? args.userId,
|
|
925
967
|
backfilledFromQuestionId: String(question._id)
|
|
926
968
|
}
|
|
927
969
|
);
|