@lucern/graph-primitives 0.3.0-alpha.0 → 0.3.0-alpha.10
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-Q_26RTc-.d.ts → beliefDecay-DZ6tkLYq.d.ts} +1 -1
- package/dist/beliefDecay.d.ts +1 -1
- package/dist/beliefDecay.js +188 -1144
- package/dist/beliefDecay.js.map +1 -1
- package/dist/{beliefEvidenceLinks-42FlR48t.d.ts → beliefEvidenceLinks-CWOXxxJg.d.ts} +1 -1
- package/dist/beliefEvidenceLinks.d.ts +1 -1
- package/dist/beliefEvidenceLinks.js +186 -871
- package/dist/beliefEvidenceLinks.js.map +1 -1
- package/dist/{beliefLifecycle-C-AehZgF.d.ts → beliefLifecycle-y8WLXqQj.d.ts} +1 -1
- package/dist/beliefLifecycle.d.ts +1 -1
- package/dist/confidencePropagationDispatch.d.ts +4 -4
- package/dist/confidencePropagationDispatch.js +31 -311
- package/dist/confidencePropagationDispatch.js.map +1 -1
- package/dist/{contradictions-Hdwl7zid.d.ts → contradictions-51VLsESq.d.ts} +1 -1
- package/dist/contradictions.d.ts +1 -1
- package/dist/contradictions.js +67 -800
- package/dist/contradictions.js.map +1 -1
- package/dist/debug.d.ts +4 -0
- package/dist/debug.js +34 -0
- package/dist/debug.js.map +1 -0
- package/dist/edges/contradicts.js +1 -122
- package/dist/edges/contradicts.js.map +1 -1
- package/dist/edges/dependsOn.js +14 -172
- package/dist/edges/dependsOn.js.map +1 -1
- package/dist/edges/elaborates.js +1 -49
- package/dist/edges/elaborates.js.map +1 -1
- package/dist/edges/index.js +15 -280
- package/dist/edges/index.js.map +1 -1
- package/dist/edges/informs.js +2 -65
- package/dist/edges/informs.js.map +1 -1
- package/dist/edges/propagationTypes.d.ts +2 -2
- package/dist/edges/propagationTypes.js.map +1 -1
- package/dist/edges/refutes.js +2 -65
- package/dist/edges/refutes.js.map +1 -1
- package/dist/edges/supports.js +1 -122
- package/dist/edges/supports.js.map +1 -1
- package/dist/edges/utils.d.ts +7 -7
- package/dist/edges/utils.js +2 -133
- package/dist/edges/utils.js.map +1 -1
- package/dist/embeddingTrigger.js +21 -1
- package/dist/embeddingTrigger.js.map +1 -1
- package/dist/entityBridge.js +3 -18
- package/dist/entityBridge.js.map +1 -1
- package/dist/{entityLifecycle-BkhRJ-XI.d.ts → entityLifecycle-CvgSK5FV.d.ts} +1 -1
- package/dist/entityLifecycle.d.ts +1 -1
- package/dist/entityLifecycle.js +193 -892
- package/dist/entityLifecycle.js.map +1 -1
- package/dist/{epistemicAnswers-DSP1slZ9.d.ts → epistemicAnswers-C5ib4z6_.d.ts} +1 -1
- package/dist/epistemicAnswers.d.ts +1 -1
- package/dist/epistemicAnswers.js +73 -810
- package/dist/epistemicAnswers.js.map +1 -1
- package/dist/{epistemicBeliefs-DtFVTp-k.d.ts → epistemicBeliefs-DzKjZAeC.d.ts} +3 -3
- package/dist/epistemicBeliefs.d.ts +2 -2
- package/dist/epistemicBeliefs.js +404 -1698
- package/dist/epistemicBeliefs.js.map +1 -1
- package/dist/epistemicContractHelpers.js +1 -318
- package/dist/epistemicContractHelpers.js.map +1 -1
- package/dist/epistemicContracts.d.ts +1 -1
- package/dist/epistemicContracts.js +417 -1980
- package/dist/epistemicContracts.js.map +1 -1
- package/dist/{epistemicEdges-DcA8ErUG.d.ts → epistemicEdges-CD5vxmlH.d.ts} +3 -3
- package/dist/epistemicEdges.d.ts +1 -1
- package/dist/epistemicEdges.js +248 -919
- package/dist/epistemicEdges.js.map +1 -1
- package/dist/{epistemicEvidence-Bo638XDP.d.ts → epistemicEvidence-xw6UUrwh.d.ts} +1 -1
- package/dist/epistemicEvidence.d.ts +1 -1
- package/dist/epistemicEvidence.js +229 -1087
- package/dist/epistemicEvidence.js.map +1 -1
- package/dist/{epistemicHelpers-Bd9xbaib.d.ts → epistemicHelpers-DevrYgPN.d.ts} +1 -1
- package/dist/epistemicHelpers.d.ts +1 -1
- package/dist/{epistemicLinking-CyeLOIzN.d.ts → epistemicLinking-CfE00tHJ.d.ts} +1 -1
- package/dist/epistemicLinking.d.ts +1 -1
- package/dist/epistemicLinking.js +3 -786
- package/dist/epistemicLinking.js.map +1 -1
- package/dist/{epistemicNodes-BpD6Koud.d.ts → epistemicNodes-NBrPW7fk.d.ts} +2 -2
- package/dist/epistemicNodes.d.ts +1 -1
- package/dist/epistemicNodes.js +172 -899
- package/dist/epistemicNodes.js.map +1 -1
- package/dist/{epistemicQuestions-CmEeY6zQ.d.ts → epistemicQuestions-B_nUclrH.d.ts} +1 -1
- package/dist/epistemicQuestions.d.ts +1 -1
- package/dist/epistemicQuestions.js +369 -1125
- package/dist/epistemicQuestions.js.map +1 -1
- package/dist/{epistemicSources-ZazxHOK1.d.ts → epistemicSources-dlKj58Jp.d.ts} +1 -1
- package/dist/epistemicSources.d.ts +1 -1
- package/dist/epistemicSources.js +86 -886
- package/dist/epistemicSources.js.map +1 -1
- package/dist/evaluators/index.js +417 -1980
- package/dist/evaluators/index.js.map +1 -1
- package/dist/evaluators/lintCheckerEvaluator.js.map +1 -1
- package/dist/evaluators/sentryCheckerEvaluator.js.map +1 -1
- package/dist/evaluators/shared.js +20 -1
- package/dist/evaluators/shared.js.map +1 -1
- package/dist/evaluators/testRunnerEvaluator.js +20 -1
- package/dist/evaluators/testRunnerEvaluator.js.map +1 -1
- package/dist/evaluators/tscCheckerEvaluator.js.map +1 -1
- package/dist/index.d.ts +20 -20
- package/dist/index.js +965 -3004
- package/dist/index.js.map +1 -1
- package/dist/{ontology-matching-Buhu23ss.d.ts → ontology-matching-C6rrz2VP.d.ts} +1 -1
- package/dist/ontology-matching.d.ts +1 -1
- package/dist/ontology-matching.js +1 -344
- package/dist/ontology-matching.js.map +1 -1
- package/dist/{ontologyApproval-Ba0Jjk1k.d.ts → ontologyApproval-CFYmqKmk.d.ts} +1 -1
- package/dist/ontologyApproval.d.ts +1 -1
- package/dist/ontologyApproval.js +1 -13
- package/dist/ontologyApproval.js.map +1 -1
- package/dist/ontologyDefinitions.js +6 -20
- package/dist/ontologyDefinitions.js.map +1 -1
- package/dist/ontologyHelpers.d.ts +1 -1
- package/dist/ontologyHelpers.js +4 -3
- package/dist/ontologyHelpers.js.map +1 -1
- package/dist/ontologyRegistry.js +2 -17
- package/dist/ontologyRegistry.js.map +1 -1
- package/dist/{projectionReconciliation-CxrXYGaB.d.ts → projectionReconciliation-jww2fBI0.d.ts} +1 -1
- package/dist/projectionReconciliation.d.ts +1 -1
- package/dist/projectionReconciliation.js +16 -37
- package/dist/projectionReconciliation.js.map +1 -1
- package/dist/{projectionStaleness-CAdpIsaW.d.ts → projectionStaleness-CmdbpjVK.d.ts} +1 -1
- package/dist/projectionStaleness.d.ts +1 -1
- package/dist/{questionEvidenceLinks-BdQD0TkM.d.ts → questionEvidenceLinks-DFlyPpAj.d.ts} +1 -1
- package/dist/questionEvidenceLinks.d.ts +1 -1
- package/dist/questionEvidenceLinks.js +199 -881
- package/dist/questionEvidenceLinks.js.map +1 -1
- package/dist/resolvers.js +86 -37
- package/dist/resolvers.js.map +1 -1
- package/dist/scopeResolverCompat.js +64 -7
- package/dist/scopeResolverCompat.js.map +1 -1
- package/dist/{text-matching-CMn2WnVD.d.ts → text-matching-DNg4M5Wd.d.ts} +1 -1
- package/dist/text-matching.d.ts +1 -1
- package/dist/text-matching.js +1 -244
- package/dist/text-matching.js.map +1 -1
- package/dist/topicProjectOverlay.js +56 -13
- package/dist/topicProjectOverlay.js.map +1 -1
- package/dist/topicScope.js +55 -6
- package/dist/topicScope.js.map +1 -1
- package/dist/workflowBridge.d.ts +27 -0
- package/dist/workflowBridge.js +352 -0
- package/dist/workflowBridge.js.map +1 -0
- package/dist/workspaceIsolation.js +56 -57
- package/dist/workspaceIsolation.js.map +1 -1
- package/package.json +6 -5
package/dist/epistemicBeliefs.js
CHANGED
|
@@ -1,1440 +1,16 @@
|
|
|
1
1
|
import { v } from 'convex/values';
|
|
2
|
-
import {
|
|
2
|
+
import { normalizeTupleContradictionPolicy, mkOpinion, createInheritedContractRecord, confidenceFromSL, conditionalDeduction, project, dampedDependencyCascade, hasProjectedOpinionChanged, detectTupleContradiction, evaluateTupleContradictionTransition, readOpinionFromRecord, trustDiscount, applyNegativeSupport, cumulativeFusion, applyNegativeEvidence } from '@lucern/confidence';
|
|
3
|
+
import { checkScopeAccess, checkProjectAccess } from '@lucern/access-control/access';
|
|
4
|
+
import { canAudienceClassAccess, normalizeAudienceKey, classFromAudienceKey } from '@lucern/access-control/audience';
|
|
5
|
+
import { listAudienceRegistryRows } from '@lucern/access-control/audienceRegistry';
|
|
6
|
+
import { getCurrentUserId } from '@lucern/access-control/auth';
|
|
7
|
+
import { assertSchemaEnumValue } from '@lucern/contracts/schema-helpers/enumValidation';
|
|
8
|
+
import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
|
|
9
|
+
import { componentsGeneric, anyApi, internalMutationGeneric, mutationGeneric, queryGeneric, internalQueryGeneric } from 'convex/server';
|
|
10
|
+
import { isNodeType, getLayerForNodeType } from '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
|
|
3
11
|
|
|
4
12
|
// src/epistemicBeliefs.ts
|
|
5
13
|
|
|
6
|
-
// ../confidence/src/v1/operations/subjectiveLogic/index.ts
|
|
7
|
-
function opinion(belief, disbelief, uncertainty, baseRate = 0.5) {
|
|
8
|
-
const b = Math.max(0, Math.min(1, belief));
|
|
9
|
-
const d = Math.max(0, Math.min(1, disbelief));
|
|
10
|
-
const u = Math.max(0, Math.min(1, uncertainty));
|
|
11
|
-
const a = Math.max(0, Math.min(1, baseRate));
|
|
12
|
-
const sum = b + d + u;
|
|
13
|
-
if (sum === 0) {
|
|
14
|
-
return { b: 0, d: 0, u: 1, a };
|
|
15
|
-
}
|
|
16
|
-
return {
|
|
17
|
-
b: b / sum,
|
|
18
|
-
d: d / sum,
|
|
19
|
-
u: u / sum,
|
|
20
|
-
a
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
function vacuous(baseRate = 0.5) {
|
|
24
|
-
return { b: 0, d: 0, u: 1, a: baseRate };
|
|
25
|
-
}
|
|
26
|
-
function project(o) {
|
|
27
|
-
return o.b + o.a * o.u;
|
|
28
|
-
}
|
|
29
|
-
function cumulativeFusion(left, right) {
|
|
30
|
-
if (left.u === 0 && right.u === 0) {
|
|
31
|
-
return opinion(
|
|
32
|
-
(left.b + right.b) / 2,
|
|
33
|
-
(left.d + right.d) / 2,
|
|
34
|
-
0,
|
|
35
|
-
(left.a + right.a) / 2
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
const k = left.u + right.u - left.u * right.u;
|
|
39
|
-
if (k === 0) {
|
|
40
|
-
return vacuous((left.a + right.a) / 2);
|
|
41
|
-
}
|
|
42
|
-
return opinion(
|
|
43
|
-
(left.b * right.u + right.b * left.u) / k,
|
|
44
|
-
(left.d * right.u + right.d * left.u) / k,
|
|
45
|
-
left.u * right.u / k,
|
|
46
|
-
(left.a + right.a) / 2
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
function trustDiscount(sourceOpinion, trust) {
|
|
50
|
-
const weight = Math.max(0, Math.min(1, Math.abs(trust)));
|
|
51
|
-
return opinion(
|
|
52
|
-
weight * sourceOpinion.b,
|
|
53
|
-
weight * sourceOpinion.d,
|
|
54
|
-
1 - weight * (sourceOpinion.b + sourceOpinion.d),
|
|
55
|
-
sourceOpinion.a
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
var EPSILON = 1e-9;
|
|
59
|
-
function childBaseRateFallback(ifTrue, ifFalse, fallbackBaseRate) {
|
|
60
|
-
if (fallbackBaseRate !== void 0) {
|
|
61
|
-
return Math.max(0, Math.min(1, fallbackBaseRate));
|
|
62
|
-
}
|
|
63
|
-
if (Math.abs(ifTrue.a - ifFalse.a) <= EPSILON) {
|
|
64
|
-
return ifTrue.a;
|
|
65
|
-
}
|
|
66
|
-
return (ifTrue.a + ifFalse.a) / 2;
|
|
67
|
-
}
|
|
68
|
-
function computeConditionalDeductionBaseRate(opinionA, ifTrue, ifFalse, fallbackBaseRate) {
|
|
69
|
-
const denominator = 1 - opinionA.a * ifTrue.u - (1 - opinionA.a) * ifFalse.u;
|
|
70
|
-
if (ifTrue.u + ifFalse.u < 2 - EPSILON && Math.abs(denominator) > EPSILON) {
|
|
71
|
-
const baseRate = (opinionA.a * ifTrue.b + (1 - opinionA.a) * ifFalse.b) / denominator;
|
|
72
|
-
if (baseRate >= -EPSILON && baseRate <= 1 + EPSILON) {
|
|
73
|
-
return Math.max(0, Math.min(1, baseRate));
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return fallbackBaseRate;
|
|
77
|
-
}
|
|
78
|
-
function safeCorrectionTerm(numerator, denominator) {
|
|
79
|
-
if (Math.abs(denominator) <= EPSILON) {
|
|
80
|
-
return void 0;
|
|
81
|
-
}
|
|
82
|
-
const value = numerator / denominator;
|
|
83
|
-
if (!Number.isFinite(value)) {
|
|
84
|
-
return void 0;
|
|
85
|
-
}
|
|
86
|
-
return Math.max(0, value);
|
|
87
|
-
}
|
|
88
|
-
function conditionalDeduction(opinionA, ifTrue, ifFalse, fallbackBaseRate) {
|
|
89
|
-
const fallbackChildBaseRate = childBaseRateFallback(
|
|
90
|
-
ifTrue,
|
|
91
|
-
ifFalse,
|
|
92
|
-
fallbackBaseRate
|
|
93
|
-
);
|
|
94
|
-
const childBaseRate = computeConditionalDeductionBaseRate(
|
|
95
|
-
opinionA,
|
|
96
|
-
ifTrue,
|
|
97
|
-
ifFalse,
|
|
98
|
-
fallbackChildBaseRate
|
|
99
|
-
);
|
|
100
|
-
const projectedAntecedent = project(opinionA);
|
|
101
|
-
const projectedAntecedentComplement = 1 - projectedAntecedent;
|
|
102
|
-
const intermediateBelief = opinionA.b * ifTrue.b + opinionA.d * ifFalse.b + opinionA.u * (ifTrue.b * opinionA.a + ifFalse.b * (1 - opinionA.a));
|
|
103
|
-
const intermediateDisbelief = opinionA.b * ifTrue.d + opinionA.d * ifFalse.d + opinionA.u * (ifTrue.d * opinionA.a + ifFalse.d * (1 - opinionA.a));
|
|
104
|
-
const intermediateUncertainty = opinionA.b * ifTrue.u + opinionA.d * ifFalse.u + opinionA.u * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));
|
|
105
|
-
const projectedVacuousDeduction = ifTrue.b * opinionA.a + ifFalse.b * (1 - opinionA.a) + childBaseRate * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));
|
|
106
|
-
const projectedConditionalA = ifTrue.b + childBaseRate * (1 - ifTrue.b - ifTrue.d);
|
|
107
|
-
let correction = 0;
|
|
108
|
-
if (ifTrue.b > ifFalse.b && ifTrue.d > ifFalse.d || ifTrue.b <= ifFalse.b && ifTrue.d <= ifFalse.d) {
|
|
109
|
-
correction = 0;
|
|
110
|
-
} else if (ifTrue.b > ifFalse.b && ifTrue.d <= ifFalse.d) {
|
|
111
|
-
const beliefGap = ifTrue.b - ifFalse.b;
|
|
112
|
-
const disbeliefGap = ifFalse.d - ifTrue.d;
|
|
113
|
-
if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
114
|
-
correction = safeCorrectionTerm(
|
|
115
|
-
opinionA.a * opinionA.u * (intermediateBelief - ifTrue.b),
|
|
116
|
-
projectedAntecedent * childBaseRate
|
|
117
|
-
) ?? 0;
|
|
118
|
-
} else if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent > opinionA.a) {
|
|
119
|
-
correction = safeCorrectionTerm(
|
|
120
|
-
opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d) * beliefGap,
|
|
121
|
-
projectedAntecedentComplement * childBaseRate * disbeliefGap
|
|
122
|
-
) ?? 0;
|
|
123
|
-
} else if (projectedVacuousDeduction > projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
124
|
-
correction = safeCorrectionTerm(
|
|
125
|
-
(1 - opinionA.a) * opinionA.u * (intermediateBelief - ifTrue.b) * disbeliefGap,
|
|
126
|
-
projectedAntecedent * (1 - childBaseRate) * beliefGap
|
|
127
|
-
) ?? 0;
|
|
128
|
-
} else {
|
|
129
|
-
correction = safeCorrectionTerm(
|
|
130
|
-
(1 - opinionA.a) * opinionA.u * (intermediateDisbelief - ifTrue.d),
|
|
131
|
-
projectedAntecedentComplement * (1 - childBaseRate)
|
|
132
|
-
) ?? 0;
|
|
133
|
-
}
|
|
134
|
-
} else {
|
|
135
|
-
const beliefGap = ifFalse.b - ifTrue.b;
|
|
136
|
-
const disbeliefGap = ifTrue.d - ifFalse.d;
|
|
137
|
-
if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
138
|
-
correction = safeCorrectionTerm(
|
|
139
|
-
(1 - opinionA.a) * opinionA.u * (intermediateDisbelief - ifTrue.d) * beliefGap,
|
|
140
|
-
projectedAntecedent * childBaseRate * disbeliefGap
|
|
141
|
-
) ?? 0;
|
|
142
|
-
} else if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent > opinionA.a) {
|
|
143
|
-
correction = safeCorrectionTerm(
|
|
144
|
-
(1 - opinionA.a) * opinionA.u * (intermediateBelief - ifTrue.b),
|
|
145
|
-
projectedAntecedentComplement * childBaseRate
|
|
146
|
-
) ?? 0;
|
|
147
|
-
} else if (projectedVacuousDeduction > projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
148
|
-
correction = safeCorrectionTerm(
|
|
149
|
-
opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d),
|
|
150
|
-
projectedAntecedent * (1 - childBaseRate)
|
|
151
|
-
) ?? 0;
|
|
152
|
-
} else {
|
|
153
|
-
correction = safeCorrectionTerm(
|
|
154
|
-
opinionA.a * opinionA.u * (intermediateBelief - ifTrue.b) * disbeliefGap,
|
|
155
|
-
projectedAntecedentComplement * (1 - childBaseRate) * beliefGap
|
|
156
|
-
) ?? 0;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
return opinion(
|
|
160
|
-
intermediateBelief - childBaseRate * correction,
|
|
161
|
-
intermediateDisbelief - (1 - childBaseRate) * correction,
|
|
162
|
-
intermediateUncertainty + correction,
|
|
163
|
-
childBaseRate
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
function negate(o) {
|
|
167
|
-
return { b: o.d, d: o.b, u: o.u, a: 1 - o.a };
|
|
168
|
-
}
|
|
169
|
-
function constraintFusion(left, right, mode = "pressure") {
|
|
170
|
-
if (mode === "redistribute") {
|
|
171
|
-
const leftProjected = project(left);
|
|
172
|
-
const rightProjected = project(right);
|
|
173
|
-
const total = leftProjected + rightProjected;
|
|
174
|
-
if (total <= 1) {
|
|
175
|
-
return { o1: left, o2: right };
|
|
176
|
-
}
|
|
177
|
-
const scale = 1 / total;
|
|
178
|
-
return {
|
|
179
|
-
o1: opinion(
|
|
180
|
-
left.b * scale,
|
|
181
|
-
left.d + left.b * (1 - scale),
|
|
182
|
-
left.u,
|
|
183
|
-
left.a
|
|
184
|
-
),
|
|
185
|
-
o2: opinion(
|
|
186
|
-
right.b * scale,
|
|
187
|
-
right.d + right.b * (1 - scale),
|
|
188
|
-
right.u,
|
|
189
|
-
right.a
|
|
190
|
-
)
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
const pressureLeft = right.b * 0.5;
|
|
194
|
-
const pressureRight = left.b * 0.5;
|
|
195
|
-
return {
|
|
196
|
-
o1: opinion(
|
|
197
|
-
left.b - pressureLeft * 0.3,
|
|
198
|
-
left.d + pressureLeft * 0.3,
|
|
199
|
-
left.u,
|
|
200
|
-
left.a
|
|
201
|
-
),
|
|
202
|
-
o2: opinion(
|
|
203
|
-
right.b - pressureRight * 0.3,
|
|
204
|
-
right.d + pressureRight * 0.3,
|
|
205
|
-
right.u,
|
|
206
|
-
right.a
|
|
207
|
-
)
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// ../confidence/src/v1/operations/scoring.ts
|
|
212
|
-
function finiteNumber(value) {
|
|
213
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
214
|
-
}
|
|
215
|
-
function clamp01(value) {
|
|
216
|
-
return Math.max(0, Math.min(1, value));
|
|
217
|
-
}
|
|
218
|
-
function confidenceFromOpinion(opinion2) {
|
|
219
|
-
return clamp01(opinion2.b + opinion2.a * opinion2.u);
|
|
220
|
-
}
|
|
221
|
-
function confidenceFromSL(belief, _disbelief, uncertainty, baseRate = 0.5) {
|
|
222
|
-
return confidenceFromOpinion({
|
|
223
|
-
b: belief,
|
|
224
|
-
u: uncertainty,
|
|
225
|
-
a: baseRate
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
function readOpinionFromRecord(source, fallback = {}) {
|
|
229
|
-
const record = source && typeof source === "object" ? source : {};
|
|
230
|
-
return {
|
|
231
|
-
b: finiteNumber(record.b) ?? finiteNumber(record.belief) ?? finiteNumber(record.slBelief) ?? finiteNumber(record.opinion_b) ?? fallback.b ?? 0,
|
|
232
|
-
d: finiteNumber(record.d) ?? finiteNumber(record.disbelief) ?? finiteNumber(record.slDisbelief) ?? finiteNumber(record.opinion_d) ?? fallback.d ?? 0,
|
|
233
|
-
u: finiteNumber(record.u) ?? finiteNumber(record.uncertainty) ?? finiteNumber(record.slUncertainty) ?? finiteNumber(record.opinion_u) ?? fallback.u ?? 1,
|
|
234
|
-
a: finiteNumber(record.a) ?? finiteNumber(record.baseRate) ?? finiteNumber(record.slBaseRate) ?? finiteNumber(record.opinion_a) ?? fallback.a ?? 0.5
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
function hasProjectedOpinionChanged(current, next, tolerance = 0.01) {
|
|
238
|
-
return Math.abs(confidenceFromOpinion(next) - confidenceFromOpinion(current)) >= tolerance;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// ../confidence/src/v1/operations/contradiction/detectTupleContradiction.ts
|
|
242
|
-
var DEFAULT_TUPLE_CONTRADICTION_BELIEF_THRESHOLD = 0.7;
|
|
243
|
-
var DEFAULT_TUPLE_CONTRADICTION_DISBELIEF_THRESHOLD = 0.7;
|
|
244
|
-
function normalizeTupleContradictionPolicy(policy = {}) {
|
|
245
|
-
return {
|
|
246
|
-
beliefThreshold: clamp01(
|
|
247
|
-
policy.beliefThreshold ?? DEFAULT_TUPLE_CONTRADICTION_BELIEF_THRESHOLD
|
|
248
|
-
),
|
|
249
|
-
disbeliefThreshold: clamp01(
|
|
250
|
-
policy.disbeliefThreshold ?? DEFAULT_TUPLE_CONTRADICTION_DISBELIEF_THRESHOLD
|
|
251
|
-
)
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
function detectTupleContradiction(opinion2, tauB = DEFAULT_TUPLE_CONTRADICTION_BELIEF_THRESHOLD, tauD = DEFAULT_TUPLE_CONTRADICTION_DISBELIEF_THRESHOLD) {
|
|
255
|
-
return opinion2.b > tauB && opinion2.d > tauD;
|
|
256
|
-
}
|
|
257
|
-
function evaluateTupleContradictionTransition(args) {
|
|
258
|
-
const policy = normalizeTupleContradictionPolicy(args.policy);
|
|
259
|
-
const tupleContradicted = detectTupleContradiction(
|
|
260
|
-
args.opinion,
|
|
261
|
-
policy.beliefThreshold,
|
|
262
|
-
policy.disbeliefThreshold
|
|
263
|
-
);
|
|
264
|
-
const previousTupleContradicted = Boolean(args.previousTupleContradicted);
|
|
265
|
-
return {
|
|
266
|
-
tupleContradicted,
|
|
267
|
-
crossedIntoTupleContradiction: !previousTupleContradicted && tupleContradicted,
|
|
268
|
-
crossedOutOfTupleContradiction: previousTupleContradicted && !tupleContradicted,
|
|
269
|
-
policy
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// ../confidence/src/v1/operations/dynamics/cascade.ts
|
|
274
|
-
function dampedDependencyOpinion(dependencyOpinion, beliefOpinion, mode = "continuous", threshold = 0.3) {
|
|
275
|
-
const dependencyProjection = project(dependencyOpinion);
|
|
276
|
-
if (mode === "threshold") {
|
|
277
|
-
if (dependencyProjection < threshold) {
|
|
278
|
-
return opinion(
|
|
279
|
-
0,
|
|
280
|
-
beliefOpinion.d + beliefOpinion.b * 0.5,
|
|
281
|
-
0.5,
|
|
282
|
-
beliefOpinion.a
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
return beliefOpinion;
|
|
286
|
-
}
|
|
287
|
-
const dampingFactor = Math.pow(dependencyProjection, 0.5);
|
|
288
|
-
return opinion(
|
|
289
|
-
beliefOpinion.b * dampingFactor,
|
|
290
|
-
beliefOpinion.d + beliefOpinion.b * (1 - dampingFactor) * 0.3,
|
|
291
|
-
beliefOpinion.u + beliefOpinion.b * (1 - dampingFactor) * 0.7,
|
|
292
|
-
beliefOpinion.a
|
|
293
|
-
);
|
|
294
|
-
}
|
|
295
|
-
function dampedDependencyCascade(dependencyOpinion, beliefOpinion, mode = "continuous") {
|
|
296
|
-
return {
|
|
297
|
-
opinion: dampedDependencyOpinion(dependencyOpinion, beliefOpinion, mode),
|
|
298
|
-
operator: "dependency_cascade",
|
|
299
|
-
rationale: `Damped dependency cascade (${mode}): prerequisite at ${project(
|
|
300
|
-
dependencyOpinion
|
|
301
|
-
).toFixed(2)}`
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// ../confidence/src/v1/operations/dynamics/defeat.ts
|
|
306
|
-
function applyNegativeSupport(source, target, weight, metadata = {}) {
|
|
307
|
-
if (metadata.constraint === "xor") {
|
|
308
|
-
const result = constraintFusion(
|
|
309
|
-
source,
|
|
310
|
-
target,
|
|
311
|
-
metadata.normalization ?? "pressure"
|
|
312
|
-
);
|
|
313
|
-
return {
|
|
314
|
-
opinion: result.o2,
|
|
315
|
-
operator: "constraint_fusion",
|
|
316
|
-
rationale: `XOR constraint: source belief at ${project(source).toFixed(
|
|
317
|
-
2
|
|
318
|
-
)} pressures target`
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
const discounted = trustDiscount(negate(source), Math.abs(weight));
|
|
322
|
-
return {
|
|
323
|
-
opinion: cumulativeFusion(target, discounted),
|
|
324
|
-
operator: "cumulative_fusion",
|
|
325
|
-
rationale: `Contradicting evidence (weight=${weight.toFixed(
|
|
326
|
-
2
|
|
327
|
-
)}) from source at ${project(source).toFixed(2)}`
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
function applyNegativeEvidence(source, target, weight) {
|
|
331
|
-
const discounted = trustDiscount(negate(source), Math.abs(weight));
|
|
332
|
-
return {
|
|
333
|
-
opinion: cumulativeFusion(target, discounted),
|
|
334
|
-
operator: "cumulative_fusion",
|
|
335
|
-
rationale: `Contradicting evidence (weight=${weight.toFixed(2)})`
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// ../confidence/src/v1/operations/contracts/epistemicContract.ts
|
|
340
|
-
function generateContractId() {
|
|
341
|
-
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
342
|
-
return crypto.randomUUID();
|
|
343
|
-
}
|
|
344
|
-
return `contract-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
345
|
-
}
|
|
346
|
-
function createInheritedContractRecord(contract, args) {
|
|
347
|
-
return {
|
|
348
|
-
beliefNodeId: args.beliefNodeId,
|
|
349
|
-
contractId: generateContractId(),
|
|
350
|
-
title: contract.title,
|
|
351
|
-
description: contract.description,
|
|
352
|
-
conditionType: contract.conditionType,
|
|
353
|
-
direction: contract.direction,
|
|
354
|
-
condition: contract.condition,
|
|
355
|
-
deadline: contract.deadline,
|
|
356
|
-
compositeOf: contract.compositeOf,
|
|
357
|
-
compositeOperator: contract.compositeOperator,
|
|
358
|
-
modulation: contract.modulation,
|
|
359
|
-
evaluationSchedule: contract.evaluationSchedule,
|
|
360
|
-
periodicIntervalMs: contract.periodicIntervalMs,
|
|
361
|
-
status: "active",
|
|
362
|
-
lineageSource: "inherited",
|
|
363
|
-
inheritedFromContractId: contract.contractId,
|
|
364
|
-
inheritedFromBeliefNodeId: contract.beliefNodeId,
|
|
365
|
-
inheritedAt: args.now,
|
|
366
|
-
topicId: args.topicId,
|
|
367
|
-
createdAt: args.now,
|
|
368
|
-
createdBy: args.createdBy,
|
|
369
|
-
updatedAt: args.now
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
var api = anyApi;
|
|
373
|
-
componentsGeneric();
|
|
374
|
-
|
|
375
|
-
// ../access-control/src/topicProjectOverlay.ts
|
|
376
|
-
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
377
|
-
function readNonEmptyString(value) {
|
|
378
|
-
if (typeof value !== "string") {
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
const normalized = value.trim();
|
|
382
|
-
return normalized.length > 0 ? normalized : void 0;
|
|
383
|
-
}
|
|
384
|
-
function readStringArray(value) {
|
|
385
|
-
if (!Array.isArray(value)) {
|
|
386
|
-
return [];
|
|
387
|
-
}
|
|
388
|
-
return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
|
|
389
|
-
}
|
|
390
|
-
function readMetadata(topic) {
|
|
391
|
-
return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
|
|
392
|
-
}
|
|
393
|
-
function readLegacyProjectId(value) {
|
|
394
|
-
if (!value) {
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
return readNonEmptyString(value[LEGACY_SCOPE_FIELD]);
|
|
398
|
-
}
|
|
399
|
-
function coerceVisibility(value) {
|
|
400
|
-
return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
|
|
401
|
-
}
|
|
402
|
-
function coerceStatus(value) {
|
|
403
|
-
return value === "active" || value === "archived" || value === "watching" ? value : void 0;
|
|
404
|
-
}
|
|
405
|
-
function mapProjectType(topic, metadata) {
|
|
406
|
-
const explicit = readNonEmptyString(metadata.projectType);
|
|
407
|
-
if (explicit) {
|
|
408
|
-
return explicit;
|
|
409
|
-
}
|
|
410
|
-
if (topic.type === "theme") {
|
|
411
|
-
return "thematic";
|
|
412
|
-
}
|
|
413
|
-
return readNonEmptyString(topic.type) || "general";
|
|
414
|
-
}
|
|
415
|
-
function isProjectLikeTopic(topic) {
|
|
416
|
-
const metadata = readMetadata(topic);
|
|
417
|
-
return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
|
|
418
|
-
}
|
|
419
|
-
async function resolveTopicDoc(ctx, scopeId) {
|
|
420
|
-
if (ctx?.db && typeof ctx.db.get === "function") {
|
|
421
|
-
try {
|
|
422
|
-
const directTopic = await ctx.db.get(scopeId);
|
|
423
|
-
if (directTopic) {
|
|
424
|
-
return directTopic;
|
|
425
|
-
}
|
|
426
|
-
} catch {
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
if (typeof ctx.runQuery !== "function") {
|
|
430
|
-
return null;
|
|
431
|
-
}
|
|
432
|
-
try {
|
|
433
|
-
const topic = await ctx.runQuery(api.topics.get, {
|
|
434
|
-
id: String(scopeId)
|
|
435
|
-
});
|
|
436
|
-
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
437
|
-
return topic;
|
|
438
|
-
}
|
|
439
|
-
} catch {
|
|
440
|
-
}
|
|
441
|
-
try {
|
|
442
|
-
const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
443
|
-
projectId: String(scopeId)
|
|
444
|
-
});
|
|
445
|
-
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
446
|
-
return topic;
|
|
447
|
-
}
|
|
448
|
-
} catch {
|
|
449
|
-
}
|
|
450
|
-
return null;
|
|
451
|
-
}
|
|
452
|
-
function materializeTopicProjectOverlay(topic, idMode = "legacy") {
|
|
453
|
-
const metadata = readMetadata(topic);
|
|
454
|
-
const topicId = String(topic._id);
|
|
455
|
-
const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
|
|
456
|
-
const storageProjectId = legacyProjectId || topicId;
|
|
457
|
-
const outwardId = idMode === "topic" ? topicId : storageProjectId;
|
|
458
|
-
const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
|
|
459
|
-
const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
|
|
460
|
-
const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
|
|
461
|
-
const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
|
|
462
|
-
return {
|
|
463
|
-
...metadata,
|
|
464
|
-
_id: outwardId,
|
|
465
|
-
projectId: outwardId,
|
|
466
|
-
topicId,
|
|
467
|
-
storageProjectId,
|
|
468
|
-
legacyProjectId,
|
|
469
|
-
name: readNonEmptyString(topic.name) || "Untitled Theme",
|
|
470
|
-
type: mapProjectType(topic, metadata),
|
|
471
|
-
description: readNonEmptyString(topic.description),
|
|
472
|
-
ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
|
|
473
|
-
sharedWith: readStringArray(metadata.sharedWith),
|
|
474
|
-
visibility,
|
|
475
|
-
tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
|
|
476
|
-
workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
|
|
477
|
-
status,
|
|
478
|
-
tags: readStringArray(metadata.tags),
|
|
479
|
-
chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
|
|
480
|
-
artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
|
|
481
|
-
lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
|
|
482
|
-
_creationTime: typeof topic._creationTime === "number" ? topic._creationTime : createdAt,
|
|
483
|
-
createdAt,
|
|
484
|
-
updatedAt
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
|
|
488
|
-
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
489
|
-
if (!topic) {
|
|
490
|
-
return null;
|
|
491
|
-
}
|
|
492
|
-
if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
|
|
493
|
-
return null;
|
|
494
|
-
}
|
|
495
|
-
return materializeTopicProjectOverlay(topic, options.idMode);
|
|
496
|
-
}
|
|
497
|
-
async function listTopicProjectOverlays(ctx, options = {}) {
|
|
498
|
-
let allTopics = [];
|
|
499
|
-
if (ctx?.db?.query && typeof ctx.db.query === "function") {
|
|
500
|
-
try {
|
|
501
|
-
allTopics = await ctx.db.query("topics").collect();
|
|
502
|
-
} catch {
|
|
503
|
-
allTopics = [];
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
|
|
507
|
-
allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
|
|
508
|
-
}
|
|
509
|
-
return allTopics.filter(
|
|
510
|
-
(topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
|
|
511
|
-
).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
// ../access-control/src/projectGrantsBridge.ts
|
|
515
|
-
var PROJECT_GRANT_STATUSES = ["active", "revoked", "expired"];
|
|
516
|
-
function normalizeString(value) {
|
|
517
|
-
if (typeof value !== "string") {
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
520
|
-
const trimmed = value.trim();
|
|
521
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
522
|
-
}
|
|
523
|
-
async function resolveGrantScopeIds(ctx, args) {
|
|
524
|
-
const topicId = normalizeString(args.topicId);
|
|
525
|
-
const projectId = normalizeString(args.projectId);
|
|
526
|
-
for (const scopeId of [topicId, projectId]) {
|
|
527
|
-
if (!scopeId) {
|
|
528
|
-
continue;
|
|
529
|
-
}
|
|
530
|
-
try {
|
|
531
|
-
const overlay = await resolveTopicProjectOverlay(ctx, scopeId, {
|
|
532
|
-
idMode: "legacy",
|
|
533
|
-
projectLikeOnly: false
|
|
534
|
-
});
|
|
535
|
-
if (overlay) {
|
|
536
|
-
return {
|
|
537
|
-
topicId: normalizeString(overlay.topicId) ?? topicId,
|
|
538
|
-
projectId: normalizeString(overlay.projectId) ?? projectId ?? scopeId
|
|
539
|
-
};
|
|
540
|
-
}
|
|
541
|
-
} catch {
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
return { topicId, projectId };
|
|
545
|
-
}
|
|
546
|
-
async function normalizeProjectGrantRow(ctx, row) {
|
|
547
|
-
const scope = await resolveGrantScopeIds(ctx, {
|
|
548
|
-
topicId: row.topicId,
|
|
549
|
-
projectId: row.projectId
|
|
550
|
-
});
|
|
551
|
-
return {
|
|
552
|
-
...row,
|
|
553
|
-
...scope.topicId ? { topicId: scope.topicId } : {},
|
|
554
|
-
...scope.projectId ?? scope.topicId ? { projectId: scope.projectId ?? scope.topicId } : {}
|
|
555
|
-
};
|
|
556
|
-
}
|
|
557
|
-
async function normalizeProjectGrantRows(ctx, rows) {
|
|
558
|
-
return await Promise.all(rows.map((row) => normalizeProjectGrantRow(ctx, row)));
|
|
559
|
-
}
|
|
560
|
-
async function listProjectGrantsByPrincipal(ctx, principalId) {
|
|
561
|
-
const rows = await Promise.all(
|
|
562
|
-
PROJECT_GRANT_STATUSES.map(
|
|
563
|
-
(status) => ctx.db.query("projectGrants").withIndex(
|
|
564
|
-
"by_principal_status",
|
|
565
|
-
(q) => q.eq("principalId", principalId).eq("status", status)
|
|
566
|
-
).collect()
|
|
567
|
-
)
|
|
568
|
-
);
|
|
569
|
-
return await normalizeProjectGrantRows(ctx, rows.flat());
|
|
570
|
-
}
|
|
571
|
-
async function listProjectGrantsByGroup(ctx, groupId) {
|
|
572
|
-
const rows = await Promise.all(
|
|
573
|
-
PROJECT_GRANT_STATUSES.map(
|
|
574
|
-
(status) => ctx.db.query("projectGrants").withIndex(
|
|
575
|
-
"by_group_status",
|
|
576
|
-
(q) => q.eq("groupId", groupId).eq("status", status)
|
|
577
|
-
).collect()
|
|
578
|
-
)
|
|
579
|
-
);
|
|
580
|
-
return await normalizeProjectGrantRows(ctx, rows.flat());
|
|
581
|
-
}
|
|
582
|
-
function buildScopeMatchers(inputScopeId, resolved) {
|
|
583
|
-
return new Set(
|
|
584
|
-
[inputScopeId, resolved.topicId, resolved.projectId].map((value) => normalizeString(value)).filter((value) => Boolean(value))
|
|
585
|
-
);
|
|
586
|
-
}
|
|
587
|
-
function matchesResolvedScope(row, scopeIds) {
|
|
588
|
-
const rowTopicId = normalizeString(row.topicId);
|
|
589
|
-
const rowProjectId = normalizeString(row.projectId);
|
|
590
|
-
return rowTopicId !== void 0 && scopeIds.has(rowTopicId) || rowProjectId !== void 0 && scopeIds.has(rowProjectId);
|
|
591
|
-
}
|
|
592
|
-
async function bridgeListProjectGrantsByTopicAndPrincipal(ctx, topicId, principalId) {
|
|
593
|
-
const resolved = await resolveGrantScopeIds(ctx, { topicId });
|
|
594
|
-
const scopeIds = buildScopeMatchers(topicId, resolved);
|
|
595
|
-
const rows = await listProjectGrantsByPrincipal(ctx, principalId);
|
|
596
|
-
return rows.filter((row) => matchesResolvedScope(row, scopeIds));
|
|
597
|
-
}
|
|
598
|
-
async function bridgeListProjectGrantsByTopicAndGroup(ctx, topicId, groupId) {
|
|
599
|
-
const resolved = await resolveGrantScopeIds(ctx, { topicId });
|
|
600
|
-
const scopeIds = buildScopeMatchers(topicId, resolved);
|
|
601
|
-
const rows = await listProjectGrantsByGroup(ctx, groupId);
|
|
602
|
-
return rows.filter((row) => matchesResolvedScope(row, scopeIds));
|
|
603
|
-
}
|
|
604
|
-
async function bridgeListProjectGrantsByPrincipalStatus(ctx, principalId, status) {
|
|
605
|
-
const rows = await listProjectGrantsByPrincipal(ctx, principalId);
|
|
606
|
-
return rows.filter((row) => row.status === status);
|
|
607
|
-
}
|
|
608
|
-
async function bridgeListProjectGrantsByGroupStatus(ctx, groupId, status) {
|
|
609
|
-
const rows = await listProjectGrantsByGroup(ctx, groupId);
|
|
610
|
-
return rows.filter((row) => row.status === status);
|
|
611
|
-
}
|
|
612
|
-
async function bridgeInsertProjectGrant(ctx, value) {
|
|
613
|
-
const resolved = await resolveGrantScopeIds(ctx, value);
|
|
614
|
-
return await ctx.db.insert("projectGrants", {
|
|
615
|
-
...value,
|
|
616
|
-
...resolved.topicId ? { topicId: resolved.topicId } : {},
|
|
617
|
-
...resolved.projectId ?? resolved.topicId ? { projectId: resolved.projectId ?? resolved.topicId } : {}
|
|
618
|
-
});
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
// ../access-control/src/resolvers.ts
|
|
622
|
-
async function findUserByClerkId(ctx, clerkId) {
|
|
623
|
-
const normalizedClerkId = clerkId.trim();
|
|
624
|
-
if (!normalizedClerkId) {
|
|
625
|
-
return null;
|
|
626
|
-
}
|
|
627
|
-
if (typeof ctx.runQuery === "function") {
|
|
628
|
-
try {
|
|
629
|
-
const bridgedUser = await ctx.runQuery(api.users.getUserByClerkId, {
|
|
630
|
-
clerkId: normalizedClerkId
|
|
631
|
-
});
|
|
632
|
-
if (bridgedUser) {
|
|
633
|
-
return bridgedUser;
|
|
634
|
-
}
|
|
635
|
-
} catch {
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
try {
|
|
639
|
-
const users = await ctx.db.query("users").collect();
|
|
640
|
-
return users.find((user) => String(user.clerkId ?? "") === normalizedClerkId) ?? null;
|
|
641
|
-
} catch {
|
|
642
|
-
return null;
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
async function findUserByPrincipalId(ctx, principalId) {
|
|
646
|
-
const normalizedPrincipalId = principalId.trim();
|
|
647
|
-
if (!normalizedPrincipalId) {
|
|
648
|
-
return null;
|
|
649
|
-
}
|
|
650
|
-
try {
|
|
651
|
-
const users = await ctx.db.query("users").collect();
|
|
652
|
-
return users.find(
|
|
653
|
-
(user) => String(user.defaultPrincipalId ?? "") === normalizedPrincipalId
|
|
654
|
-
) ?? null;
|
|
655
|
-
} catch {
|
|
656
|
-
return null;
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
async function findAgentByPrincipalId(ctx, principalId) {
|
|
660
|
-
const normalizedPrincipalId = principalId.trim();
|
|
661
|
-
if (!normalizedPrincipalId) {
|
|
662
|
-
return null;
|
|
663
|
-
}
|
|
664
|
-
if (typeof ctx.runQuery === "function") {
|
|
665
|
-
try {
|
|
666
|
-
const bridgedAgent = await ctx.runQuery(
|
|
667
|
-
api.agents.getAgentByPrincipalId,
|
|
668
|
-
{
|
|
669
|
-
principalId: normalizedPrincipalId
|
|
670
|
-
}
|
|
671
|
-
);
|
|
672
|
-
if (bridgedAgent) {
|
|
673
|
-
return bridgedAgent;
|
|
674
|
-
}
|
|
675
|
-
} catch {
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
try {
|
|
679
|
-
const agents = await ctx.db.query("agents").collect();
|
|
680
|
-
return agents.find(
|
|
681
|
-
(agent) => String(agent.principalId ?? "") === normalizedPrincipalId
|
|
682
|
-
) ?? null;
|
|
683
|
-
} catch {
|
|
684
|
-
return null;
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
function defaultResolvers() {
|
|
688
|
-
return {
|
|
689
|
-
async getProject(ctx, topicId) {
|
|
690
|
-
return await resolveTopicProjectOverlay(ctx, topicId, {
|
|
691
|
-
idMode: "legacy",
|
|
692
|
-
projectLikeOnly: false
|
|
693
|
-
});
|
|
694
|
-
},
|
|
695
|
-
async listTopics(ctx) {
|
|
696
|
-
return await listTopicProjectOverlays(ctx, { idMode: "legacy" });
|
|
697
|
-
},
|
|
698
|
-
async listTopicsByOwner(ctx, ownerId) {
|
|
699
|
-
const topics = await listTopicProjectOverlays(ctx, { idMode: "legacy" });
|
|
700
|
-
return topics.filter((topic) => topic.ownerId === ownerId);
|
|
701
|
-
},
|
|
702
|
-
async listTopicsByVisibility(ctx, visibility) {
|
|
703
|
-
const topics = await listTopicProjectOverlays(ctx, { idMode: "legacy" });
|
|
704
|
-
return topics.filter((topic) => topic.visibility === visibility);
|
|
705
|
-
},
|
|
706
|
-
async listProjectGrantsByProjectAndPrincipal(ctx, topicId, principalId) {
|
|
707
|
-
return await bridgeListProjectGrantsByTopicAndPrincipal(
|
|
708
|
-
ctx,
|
|
709
|
-
topicId,
|
|
710
|
-
principalId
|
|
711
|
-
);
|
|
712
|
-
},
|
|
713
|
-
async listProjectGrantsByProjectAndGroup(ctx, topicId, groupId) {
|
|
714
|
-
return await bridgeListProjectGrantsByTopicAndGroup(ctx, topicId, groupId);
|
|
715
|
-
},
|
|
716
|
-
async listProjectGrantsByPrincipalStatus(ctx, principalId, status) {
|
|
717
|
-
return await bridgeListProjectGrantsByPrincipalStatus(
|
|
718
|
-
ctx,
|
|
719
|
-
principalId,
|
|
720
|
-
status
|
|
721
|
-
);
|
|
722
|
-
},
|
|
723
|
-
async listProjectGrantsByGroupStatus(ctx, groupId, status) {
|
|
724
|
-
return await bridgeListProjectGrantsByGroupStatus(ctx, groupId, status);
|
|
725
|
-
},
|
|
726
|
-
async insertProjectGrant(ctx, value) {
|
|
727
|
-
return await bridgeInsertProjectGrant(ctx, value);
|
|
728
|
-
},
|
|
729
|
-
async getAgentByPrincipalId(ctx, principalId) {
|
|
730
|
-
return await findAgentByPrincipalId(ctx, principalId);
|
|
731
|
-
},
|
|
732
|
-
async getUserByClerkId(ctx, clerkId) {
|
|
733
|
-
return await findUserByClerkId(ctx, clerkId);
|
|
734
|
-
},
|
|
735
|
-
async getUserByPrincipalId(ctx, principalId) {
|
|
736
|
-
return await findUserByPrincipalId(ctx, principalId);
|
|
737
|
-
}
|
|
738
|
-
};
|
|
739
|
-
}
|
|
740
|
-
var resolverOverrides = {};
|
|
741
|
-
function resolveAccessControlAppResolvers(_ctx) {
|
|
742
|
-
return {
|
|
743
|
-
...defaultResolvers(),
|
|
744
|
-
...resolverOverrides
|
|
745
|
-
};
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
// ../access-control/src/principalContext.ts
|
|
749
|
-
function requireCanonicalResolvedUser(user, clerkId) {
|
|
750
|
-
const resolved = user;
|
|
751
|
-
if (!resolved) {
|
|
752
|
-
throw new Error(
|
|
753
|
-
`[AccessControl] Canonical user identity required for ${clerkId}. Sync users.upsertUser before user-bound access checks.`
|
|
754
|
-
);
|
|
755
|
-
}
|
|
756
|
-
const { mcRole, defaultTenantId, defaultWorkspaceId, defaultPrincipalId } = resolved;
|
|
757
|
-
if (mcRole !== "platform_admin" && mcRole !== "tenant_admin" && mcRole !== "workspace_admin" && mcRole !== "editor" && mcRole !== "viewer" && mcRole !== "auditor" && mcRole !== "service_agent") {
|
|
758
|
-
throw new Error(
|
|
759
|
-
`[AccessControl] Canonical MC role required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
|
|
760
|
-
);
|
|
761
|
-
}
|
|
762
|
-
if (typeof defaultTenantId !== "string" || defaultTenantId.trim().length === 0) {
|
|
763
|
-
throw new Error(
|
|
764
|
-
`[AccessControl] Canonical home tenant required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
|
|
765
|
-
);
|
|
766
|
-
}
|
|
767
|
-
if (typeof defaultWorkspaceId !== "string" || defaultWorkspaceId.trim().length === 0) {
|
|
768
|
-
throw new Error(
|
|
769
|
-
`[AccessControl] Canonical home workspace required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
|
|
770
|
-
);
|
|
771
|
-
}
|
|
772
|
-
if (typeof defaultPrincipalId !== "string" || defaultPrincipalId.trim().length === 0) {
|
|
773
|
-
throw new Error(
|
|
774
|
-
`[AccessControl] Canonical federated principal required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
|
|
775
|
-
);
|
|
776
|
-
}
|
|
777
|
-
return {
|
|
778
|
-
mcRole,
|
|
779
|
-
defaultTenantId: defaultTenantId.trim(),
|
|
780
|
-
defaultWorkspaceId: defaultWorkspaceId.trim(),
|
|
781
|
-
defaultPrincipalId: defaultPrincipalId.trim()
|
|
782
|
-
};
|
|
783
|
-
}
|
|
784
|
-
function isPrincipalIdInput(value) {
|
|
785
|
-
return value.startsWith("user:") || value.startsWith("group:") || value.startsWith("service:") || value.startsWith("agent:") || value.startsWith("external_viewer:");
|
|
786
|
-
}
|
|
787
|
-
async function resolveCanonicalUserRecord(ctx, actorId) {
|
|
788
|
-
const normalizedActorId = actorId.trim();
|
|
789
|
-
const clerkId = isPrincipalIdInput(normalizedActorId) && normalizedActorId.startsWith("user:") ? normalizedActorId.slice("user:".length) : normalizedActorId;
|
|
790
|
-
const resolvers = resolveAccessControlAppResolvers();
|
|
791
|
-
const resolvedByClerkId = await resolvers.getUserByClerkId(ctx, clerkId);
|
|
792
|
-
if (resolvedByClerkId) {
|
|
793
|
-
return {
|
|
794
|
-
resolvedUser: resolvedByClerkId,
|
|
795
|
-
clerkId,
|
|
796
|
-
contextClerkId: clerkId
|
|
797
|
-
};
|
|
798
|
-
}
|
|
799
|
-
const resolvedByPrincipalId = await resolvers.getUserByPrincipalId(
|
|
800
|
-
ctx,
|
|
801
|
-
normalizedActorId
|
|
802
|
-
);
|
|
803
|
-
return {
|
|
804
|
-
resolvedUser: resolvedByPrincipalId ?? null,
|
|
805
|
-
clerkId,
|
|
806
|
-
contextClerkId: normalizedActorId.startsWith("user:") && clerkId.length > 0 ? clerkId : normalizedActorId
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
function uniqRoles(roles) {
|
|
810
|
-
const roleSet = /* @__PURE__ */ new Set();
|
|
811
|
-
for (const role of roles) {
|
|
812
|
-
if (role === "platform_admin" || role === "tenant_admin" || role === "workspace_admin" || role === "editor" || role === "viewer" || role === "auditor" || role === "service_agent") {
|
|
813
|
-
roleSet.add(role);
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
return [...roleSet];
|
|
817
|
-
}
|
|
818
|
-
function normalizeGroupIds(value) {
|
|
819
|
-
if (!Array.isArray(value)) {
|
|
820
|
-
return [];
|
|
821
|
-
}
|
|
822
|
-
return [...new Set(
|
|
823
|
-
value.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter(Boolean)
|
|
824
|
-
)];
|
|
825
|
-
}
|
|
826
|
-
function requireServiceAgentUser(user, actorId) {
|
|
827
|
-
const canonicalUser = requireCanonicalResolvedUser(user, actorId);
|
|
828
|
-
if (canonicalUser.mcRole !== "service_agent") {
|
|
829
|
-
throw new Error(
|
|
830
|
-
`[AccessControl] Canonical service_agent identity required for ${actorId}. Sync users.upsertUser before agent-bound access checks.`
|
|
831
|
-
);
|
|
832
|
-
}
|
|
833
|
-
return canonicalUser;
|
|
834
|
-
}
|
|
835
|
-
function requireCanonicalResolvedAgent(agent, actorId) {
|
|
836
|
-
const resolved = agent;
|
|
837
|
-
if (!resolved) {
|
|
838
|
-
throw new Error(
|
|
839
|
-
`[AccessControl] Agent "${actorId}" not found in agents or users table.`
|
|
840
|
-
);
|
|
841
|
-
}
|
|
842
|
-
if (typeof resolved.principalId !== "string" || resolved.principalId.trim().length === 0) {
|
|
843
|
-
throw new Error(
|
|
844
|
-
`[AccessControl] Canonical agent principalId required for ${actorId}.`
|
|
845
|
-
);
|
|
846
|
-
}
|
|
847
|
-
if (typeof resolved.tenantId !== "string" || resolved.tenantId.trim().length === 0) {
|
|
848
|
-
throw new Error(
|
|
849
|
-
`[AccessControl] Canonical home tenant required for ${actorId}.`
|
|
850
|
-
);
|
|
851
|
-
}
|
|
852
|
-
if (typeof resolved.workspaceId !== "string" || resolved.workspaceId.trim().length === 0) {
|
|
853
|
-
throw new Error(
|
|
854
|
-
`[AccessControl] Canonical home workspace required for ${actorId}.`
|
|
855
|
-
);
|
|
856
|
-
}
|
|
857
|
-
return {
|
|
858
|
-
principalId: resolved.principalId.trim(),
|
|
859
|
-
tenantId: resolved.tenantId.trim(),
|
|
860
|
-
workspaceId: resolved.workspaceId.trim(),
|
|
861
|
-
roles: uniqRoles(Array.isArray(resolved.roles) ? resolved.roles : []) ?? ["service_agent"],
|
|
862
|
-
groupIds: normalizeGroupIds(resolved.groupIds)
|
|
863
|
-
};
|
|
864
|
-
}
|
|
865
|
-
async function resolvePrincipalContext(ctx, actorId) {
|
|
866
|
-
if (actorId.startsWith("agent:")) {
|
|
867
|
-
const resolvers = resolveAccessControlAppResolvers();
|
|
868
|
-
const resolvedAgent = await resolvers.getAgentByPrincipalId(ctx, actorId);
|
|
869
|
-
if (resolvedAgent) {
|
|
870
|
-
const agent = requireCanonicalResolvedAgent(
|
|
871
|
-
resolvedAgent,
|
|
872
|
-
actorId
|
|
873
|
-
);
|
|
874
|
-
return {
|
|
875
|
-
principalId: agent.principalId,
|
|
876
|
-
principalType: "service",
|
|
877
|
-
clerkId: actorId,
|
|
878
|
-
tenantId: agent.tenantId,
|
|
879
|
-
workspaceId: agent.workspaceId,
|
|
880
|
-
roles: agent.roles.length > 0 ? agent.roles : ["service_agent"],
|
|
881
|
-
groupIds: agent.groupIds,
|
|
882
|
-
isPlatformAdmin: false,
|
|
883
|
-
isTenantAdmin: false,
|
|
884
|
-
isWorkspaceAdmin: false,
|
|
885
|
-
isSystemFallback: false
|
|
886
|
-
};
|
|
887
|
-
}
|
|
888
|
-
const resolvedUser2 = await resolvers.getUserByClerkId(
|
|
889
|
-
ctx,
|
|
890
|
-
actorId
|
|
891
|
-
);
|
|
892
|
-
if (!resolvedUser2) {
|
|
893
|
-
throw new Error(
|
|
894
|
-
`[AccessControl] Agent "${actorId}" not found in agents or users table.`
|
|
895
|
-
);
|
|
896
|
-
}
|
|
897
|
-
const user2 = requireServiceAgentUser(
|
|
898
|
-
resolvedUser2,
|
|
899
|
-
actorId
|
|
900
|
-
);
|
|
901
|
-
console.warn(
|
|
902
|
-
`[AccessControl] Deprecated legacy service-agent fallback for ${actorId}; migrate this principal into identity.agents.`
|
|
903
|
-
);
|
|
904
|
-
return {
|
|
905
|
-
principalId: user2.defaultPrincipalId,
|
|
906
|
-
principalType: "service",
|
|
907
|
-
clerkId: actorId,
|
|
908
|
-
tenantId: user2.defaultTenantId,
|
|
909
|
-
workspaceId: user2.defaultWorkspaceId,
|
|
910
|
-
roles: ["service_agent"],
|
|
911
|
-
groupIds: normalizeGroupIds(resolvedUser2?.principalGroupIds),
|
|
912
|
-
isPlatformAdmin: false,
|
|
913
|
-
isTenantAdmin: false,
|
|
914
|
-
isWorkspaceAdmin: false,
|
|
915
|
-
isSystemFallback: false
|
|
916
|
-
};
|
|
917
|
-
}
|
|
918
|
-
const {
|
|
919
|
-
resolvedUser,
|
|
920
|
-
contextClerkId
|
|
921
|
-
} = await resolveCanonicalUserRecord(ctx, actorId);
|
|
922
|
-
const user = requireCanonicalResolvedUser(
|
|
923
|
-
resolvedUser,
|
|
924
|
-
contextClerkId
|
|
925
|
-
);
|
|
926
|
-
if (!user.defaultPrincipalId) {
|
|
927
|
-
throw new Error(
|
|
928
|
-
`[AccessControl] Canonical federated principal required for ${contextClerkId}. Re-sync Master Control identity before user-bound access checks.`
|
|
929
|
-
);
|
|
930
|
-
}
|
|
931
|
-
if (user.mcRole === "service_agent") {
|
|
932
|
-
return {
|
|
933
|
-
principalId: user.defaultPrincipalId,
|
|
934
|
-
principalType: "service",
|
|
935
|
-
clerkId: contextClerkId,
|
|
936
|
-
tenantId: user.defaultTenantId,
|
|
937
|
-
workspaceId: user.defaultWorkspaceId,
|
|
938
|
-
roles: ["service_agent"],
|
|
939
|
-
groupIds: normalizeGroupIds(resolvedUser?.principalGroupIds),
|
|
940
|
-
isPlatformAdmin: false,
|
|
941
|
-
isTenantAdmin: false,
|
|
942
|
-
isWorkspaceAdmin: false,
|
|
943
|
-
isSystemFallback: false
|
|
944
|
-
};
|
|
945
|
-
}
|
|
946
|
-
const principalId = user.defaultPrincipalId;
|
|
947
|
-
const effectiveRole = user.mcRole;
|
|
948
|
-
const roles = effectiveRole === "platform_admin" ? ["platform_admin", "tenant_admin"] : effectiveRole === "tenant_admin" ? ["tenant_admin"] : [effectiveRole];
|
|
949
|
-
const tenantId = user.defaultTenantId;
|
|
950
|
-
const workspaceId = user.defaultWorkspaceId;
|
|
951
|
-
const isPlatformAdmin = effectiveRole === "platform_admin";
|
|
952
|
-
return {
|
|
953
|
-
principalId,
|
|
954
|
-
principalType: "user",
|
|
955
|
-
clerkId: contextClerkId,
|
|
956
|
-
tenantId,
|
|
957
|
-
workspaceId,
|
|
958
|
-
roles: uniqRoles(roles),
|
|
959
|
-
groupIds: normalizeGroupIds(resolvedUser?.principalGroupIds),
|
|
960
|
-
isPlatformAdmin,
|
|
961
|
-
isTenantAdmin: isPlatformAdmin || effectiveRole === "tenant_admin",
|
|
962
|
-
isWorkspaceAdmin: isPlatformAdmin || effectiveRole === "tenant_admin" || effectiveRole === "workspace_admin",
|
|
963
|
-
isSystemFallback: false
|
|
964
|
-
};
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
// ../access-control/src/access.ts
|
|
968
|
-
function isTopicInPrincipalTenant(topic, principalTenantId) {
|
|
969
|
-
if (!topic.tenantId) {
|
|
970
|
-
return false;
|
|
971
|
-
}
|
|
972
|
-
if (!principalTenantId) {
|
|
973
|
-
return false;
|
|
974
|
-
}
|
|
975
|
-
return String(topic.tenantId) === String(principalTenantId);
|
|
976
|
-
}
|
|
977
|
-
function isTopicInPrincipalWorkspace(topic, principalWorkspaceId) {
|
|
978
|
-
if (!topic.workspaceId) {
|
|
979
|
-
return false;
|
|
980
|
-
}
|
|
981
|
-
if (!principalWorkspaceId) {
|
|
982
|
-
return false;
|
|
983
|
-
}
|
|
984
|
-
return String(topic.workspaceId) === String(principalWorkspaceId);
|
|
985
|
-
}
|
|
986
|
-
function isLegacyUnscopedTopic(topic) {
|
|
987
|
-
return !topic.tenantId || !topic.workspaceId;
|
|
988
|
-
}
|
|
989
|
-
function isGrantScopeAlignedToTopic(topic, grant) {
|
|
990
|
-
if (topic.tenantId && grant.tenantId && String(topic.tenantId) !== String(grant.tenantId)) {
|
|
991
|
-
return false;
|
|
992
|
-
}
|
|
993
|
-
if (topic.workspaceId && grant.workspaceId && String(topic.workspaceId) !== String(grant.workspaceId)) {
|
|
994
|
-
return false;
|
|
995
|
-
}
|
|
996
|
-
return true;
|
|
997
|
-
}
|
|
998
|
-
function isGrantSourceAllowedForVisibility(visibility, source) {
|
|
999
|
-
if (source !== "external_share") {
|
|
1000
|
-
return true;
|
|
1001
|
-
}
|
|
1002
|
-
return visibility === "external" || visibility === "public";
|
|
1003
|
-
}
|
|
1004
|
-
function isGrantActive(grant) {
|
|
1005
|
-
if (grant.status !== "active") {
|
|
1006
|
-
return false;
|
|
1007
|
-
}
|
|
1008
|
-
if (grant.expiresAt !== void 0 && grant.expiresAt <= Date.now()) {
|
|
1009
|
-
return false;
|
|
1010
|
-
}
|
|
1011
|
-
return true;
|
|
1012
|
-
}
|
|
1013
|
-
async function hasPrincipalGrant(ctx, args) {
|
|
1014
|
-
const grants = await resolveAccessControlAppResolvers().listProjectGrantsByProjectAndPrincipal(
|
|
1015
|
-
ctx,
|
|
1016
|
-
args.topic._id,
|
|
1017
|
-
args.principalId
|
|
1018
|
-
);
|
|
1019
|
-
if (grants.some(
|
|
1020
|
-
(grant) => isGrantActive(grant) && isGrantScopeAlignedToTopic(args.topic, grant) && isGrantSourceAllowedForVisibility(
|
|
1021
|
-
args.topic.visibility,
|
|
1022
|
-
grant.source
|
|
1023
|
-
) && (!args.principalIsExternal || args.topic.visibility === "public" || grant.source === "external_share")
|
|
1024
|
-
)) {
|
|
1025
|
-
return true;
|
|
1026
|
-
}
|
|
1027
|
-
return false;
|
|
1028
|
-
}
|
|
1029
|
-
async function hasGroupGrant(ctx, args) {
|
|
1030
|
-
if (args.groupIds.length === 0) {
|
|
1031
|
-
return false;
|
|
1032
|
-
}
|
|
1033
|
-
for (const groupId of args.groupIds) {
|
|
1034
|
-
const grants = await resolveAccessControlAppResolvers().listProjectGrantsByProjectAndGroup(ctx, args.topic._id, groupId);
|
|
1035
|
-
if (grants.some(
|
|
1036
|
-
(grant) => isGrantActive(grant) && isGrantScopeAlignedToTopic(args.topic, grant) && isGrantSourceAllowedForVisibility(
|
|
1037
|
-
args.topic.visibility,
|
|
1038
|
-
grant.source
|
|
1039
|
-
)
|
|
1040
|
-
)) {
|
|
1041
|
-
return true;
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
return false;
|
|
1045
|
-
}
|
|
1046
|
-
function isExternalPrincipal(_ctx, _args) {
|
|
1047
|
-
return false;
|
|
1048
|
-
}
|
|
1049
|
-
async function evaluateTopicAccessDetailed(ctx, args) {
|
|
1050
|
-
if (args.legacyUserId) {
|
|
1051
|
-
return {
|
|
1052
|
-
hasAccess: true,
|
|
1053
|
-
isAdmin: false,
|
|
1054
|
-
isOwner: false,
|
|
1055
|
-
isShared: false,
|
|
1056
|
-
hasGrant: true,
|
|
1057
|
-
isFirmVisible: true,
|
|
1058
|
-
isExternalVisible: false,
|
|
1059
|
-
isPublicVisible: false,
|
|
1060
|
-
isTenantScopeMatch: true,
|
|
1061
|
-
isWorkspaceScopeMatch: true,
|
|
1062
|
-
isPrincipalExternal: false
|
|
1063
|
-
};
|
|
1064
|
-
}
|
|
1065
|
-
const topic = await resolveAccessControlAppResolvers().getProject(
|
|
1066
|
-
ctx,
|
|
1067
|
-
args.topicId
|
|
1068
|
-
);
|
|
1069
|
-
if (!topic) {
|
|
1070
|
-
return {
|
|
1071
|
-
hasAccess: false,
|
|
1072
|
-
isAdmin: false,
|
|
1073
|
-
isOwner: false,
|
|
1074
|
-
isShared: false,
|
|
1075
|
-
hasGrant: false,
|
|
1076
|
-
isFirmVisible: false,
|
|
1077
|
-
isExternalVisible: false,
|
|
1078
|
-
isPublicVisible: false,
|
|
1079
|
-
isTenantScopeMatch: false,
|
|
1080
|
-
isWorkspaceScopeMatch: false,
|
|
1081
|
-
isPrincipalExternal: false
|
|
1082
|
-
};
|
|
1083
|
-
}
|
|
1084
|
-
const { principalContext, legacyUserId } = args;
|
|
1085
|
-
const userIsAdmin = principalContext.isPlatformAdmin;
|
|
1086
|
-
const isOwner = topic.ownerId === legacyUserId;
|
|
1087
|
-
const isShared = (topic.sharedWith ?? []).includes(legacyUserId);
|
|
1088
|
-
const principalIsExternal = await isExternalPrincipal(ctx, {
|
|
1089
|
-
groupIds: principalContext.groupIds,
|
|
1090
|
-
topicTenantId: topic.tenantId,
|
|
1091
|
-
topicWorkspaceId: topic.workspaceId
|
|
1092
|
-
});
|
|
1093
|
-
const hasPrincipalGrantResult = await hasPrincipalGrant(ctx, {
|
|
1094
|
-
topic,
|
|
1095
|
-
principalId: principalContext.principalId,
|
|
1096
|
-
principalIsExternal
|
|
1097
|
-
});
|
|
1098
|
-
const hasGroupGrantResult = await hasGroupGrant(ctx, {
|
|
1099
|
-
topic,
|
|
1100
|
-
groupIds: principalContext.groupIds
|
|
1101
|
-
});
|
|
1102
|
-
const hasGrant = isShared || hasPrincipalGrantResult || hasGroupGrantResult;
|
|
1103
|
-
const legacyUnscoped = isLegacyUnscopedTopic(topic);
|
|
1104
|
-
const tenantScopeMatch = isTopicInPrincipalTenant(
|
|
1105
|
-
topic,
|
|
1106
|
-
principalContext.tenantId
|
|
1107
|
-
);
|
|
1108
|
-
const workspaceScopeMatch = isTopicInPrincipalWorkspace(
|
|
1109
|
-
topic,
|
|
1110
|
-
principalContext.workspaceId
|
|
1111
|
-
);
|
|
1112
|
-
const isPublicVisible = topic.visibility === "public";
|
|
1113
|
-
const isFirmVisible = topic.visibility === "firm" && !legacyUnscoped && tenantScopeMatch && workspaceScopeMatch && !principalIsExternal;
|
|
1114
|
-
const hasScopedGrant = hasGrant && (legacyUnscoped || tenantScopeMatch && workspaceScopeMatch);
|
|
1115
|
-
const isExternalVisible = topic.visibility === "external" && hasScopedGrant;
|
|
1116
|
-
const hasAccess = userIsAdmin || isOwner || hasScopedGrant || isPublicVisible || isFirmVisible;
|
|
1117
|
-
return {
|
|
1118
|
-
hasAccess,
|
|
1119
|
-
isAdmin: userIsAdmin,
|
|
1120
|
-
isOwner,
|
|
1121
|
-
isShared,
|
|
1122
|
-
hasGrant,
|
|
1123
|
-
isFirmVisible,
|
|
1124
|
-
isExternalVisible,
|
|
1125
|
-
isPublicVisible,
|
|
1126
|
-
isTenantScopeMatch: tenantScopeMatch,
|
|
1127
|
-
isWorkspaceScopeMatch: workspaceScopeMatch,
|
|
1128
|
-
isPrincipalExternal: principalIsExternal
|
|
1129
|
-
};
|
|
1130
|
-
}
|
|
1131
|
-
async function checkTopicAccessDetailed(ctx, topicId, userId) {
|
|
1132
|
-
const principalContext = await resolvePrincipalContext(ctx, userId);
|
|
1133
|
-
return evaluateTopicAccessDetailed(ctx, {
|
|
1134
|
-
topicId,
|
|
1135
|
-
legacyUserId: userId,
|
|
1136
|
-
principalContext
|
|
1137
|
-
});
|
|
1138
|
-
}
|
|
1139
|
-
async function checkTopicAccess(ctx, topicId, userId) {
|
|
1140
|
-
const result = await checkTopicAccessDetailed(ctx, topicId, userId);
|
|
1141
|
-
return result.hasAccess;
|
|
1142
|
-
}
|
|
1143
|
-
async function checkScopeAccess(ctx, scopeId, userId) {
|
|
1144
|
-
try {
|
|
1145
|
-
const topic = await ctx.db.get(scopeId);
|
|
1146
|
-
if (topic && topic.name !== void 0 && topic.type !== void 0) {
|
|
1147
|
-
return true;
|
|
1148
|
-
}
|
|
1149
|
-
} catch {
|
|
1150
|
-
}
|
|
1151
|
-
try {
|
|
1152
|
-
return await checkTopicAccess(ctx, scopeId, userId);
|
|
1153
|
-
} catch {
|
|
1154
|
-
return false;
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
var checkProjectAccess = checkTopicAccess;
|
|
1158
|
-
|
|
1159
|
-
// ../access-control/src/audience.ts
|
|
1160
|
-
var AUDIENCE_CLASS_RANK = {
|
|
1161
|
-
public: 0,
|
|
1162
|
-
restricted_external: 1,
|
|
1163
|
-
internal: 2
|
|
1164
|
-
};
|
|
1165
|
-
function normalizeKey(key) {
|
|
1166
|
-
return (key ?? "").trim().toLowerCase().replace(/[^a-z0-9:_-]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
1167
|
-
}
|
|
1168
|
-
function normalizeAudienceKey(key) {
|
|
1169
|
-
return normalizeKey(key);
|
|
1170
|
-
}
|
|
1171
|
-
function classFromAudienceKey(audienceKey, fallback = "internal") {
|
|
1172
|
-
const key = normalizeKey(audienceKey);
|
|
1173
|
-
if (!key) {
|
|
1174
|
-
return fallback;
|
|
1175
|
-
}
|
|
1176
|
-
if (key === "internal") {
|
|
1177
|
-
return "internal";
|
|
1178
|
-
}
|
|
1179
|
-
if (key === "public") {
|
|
1180
|
-
return "public";
|
|
1181
|
-
}
|
|
1182
|
-
if (key === "lp" || key === "external" || key === "client" || key === "partner" || key === "portfolio" || key === "network" || key === "restricted_external") {
|
|
1183
|
-
return "restricted_external";
|
|
1184
|
-
}
|
|
1185
|
-
return fallback;
|
|
1186
|
-
}
|
|
1187
|
-
function canAudienceClassAccess(viewerClass, resourceClass) {
|
|
1188
|
-
return AUDIENCE_CLASS_RANK[viewerClass] >= AUDIENCE_CLASS_RANK[resourceClass];
|
|
1189
|
-
}
|
|
1190
|
-
|
|
1191
|
-
// ../access-control/src/audienceRegistry.ts
|
|
1192
|
-
var DEFAULT_AUDIENCES = [
|
|
1193
|
-
{
|
|
1194
|
-
audienceKey: "internal",
|
|
1195
|
-
audienceLabel: "Internal",
|
|
1196
|
-
audienceClass: "internal"
|
|
1197
|
-
},
|
|
1198
|
-
{
|
|
1199
|
-
audienceKey: "lp",
|
|
1200
|
-
audienceLabel: "Limited Partners",
|
|
1201
|
-
audienceClass: "restricted_external"
|
|
1202
|
-
},
|
|
1203
|
-
{
|
|
1204
|
-
audienceKey: "public",
|
|
1205
|
-
audienceLabel: "Public",
|
|
1206
|
-
audienceClass: "public"
|
|
1207
|
-
}
|
|
1208
|
-
];
|
|
1209
|
-
var AUDIENCE_CLASS_PRIORITY = {
|
|
1210
|
-
internal: 0,
|
|
1211
|
-
restricted_external: 1,
|
|
1212
|
-
public: 2
|
|
1213
|
-
};
|
|
1214
|
-
function normalizeRegistryRow(row) {
|
|
1215
|
-
return {
|
|
1216
|
-
audienceKey: normalizeAudienceKey(row.audienceKey),
|
|
1217
|
-
audienceLabel: row.audienceLabel,
|
|
1218
|
-
audienceClass: row.audienceClass,
|
|
1219
|
-
workspaceId: row.workspaceId
|
|
1220
|
-
};
|
|
1221
|
-
}
|
|
1222
|
-
function dedupeRegistryRows(rows) {
|
|
1223
|
-
const byKey = /* @__PURE__ */ new Map();
|
|
1224
|
-
for (const row of rows) {
|
|
1225
|
-
const key = normalizeAudienceKey(row.audienceKey);
|
|
1226
|
-
if (!key) {
|
|
1227
|
-
continue;
|
|
1228
|
-
}
|
|
1229
|
-
const existing = byKey.get(key);
|
|
1230
|
-
const isWorkspaceScoped = row.workspaceId !== void 0;
|
|
1231
|
-
const existingWorkspaceScoped = existing?.workspaceId !== void 0;
|
|
1232
|
-
if (!existing || isWorkspaceScoped && !existingWorkspaceScoped) {
|
|
1233
|
-
byKey.set(key, {
|
|
1234
|
-
...row,
|
|
1235
|
-
audienceKey: key
|
|
1236
|
-
});
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
const normalized = [...byKey.values()];
|
|
1240
|
-
normalized.sort((a, b) => {
|
|
1241
|
-
const classDelta = AUDIENCE_CLASS_PRIORITY[a.audienceClass] - AUDIENCE_CLASS_PRIORITY[b.audienceClass];
|
|
1242
|
-
if (classDelta !== 0) {
|
|
1243
|
-
return classDelta;
|
|
1244
|
-
}
|
|
1245
|
-
return a.audienceKey.localeCompare(b.audienceKey);
|
|
1246
|
-
});
|
|
1247
|
-
return normalized;
|
|
1248
|
-
}
|
|
1249
|
-
async function queryRegistryRows(ctx, args) {
|
|
1250
|
-
if (!args.tenantId) {
|
|
1251
|
-
return [...DEFAULT_AUDIENCES];
|
|
1252
|
-
}
|
|
1253
|
-
const rows = await ctx.db.query("platformAudiences").withIndex("by_tenantId", (q) => q.eq("tenantId", args.tenantId)).collect();
|
|
1254
|
-
const workspaceIdString = args.workspaceId ? String(args.workspaceId) : null;
|
|
1255
|
-
const tenantScoped = rows.filter((row) => row.status === "active");
|
|
1256
|
-
const applicable = tenantScoped.filter((row) => {
|
|
1257
|
-
if (!row.workspaceId) {
|
|
1258
|
-
return true;
|
|
1259
|
-
}
|
|
1260
|
-
if (!workspaceIdString) {
|
|
1261
|
-
return false;
|
|
1262
|
-
}
|
|
1263
|
-
return String(row.workspaceId) === workspaceIdString;
|
|
1264
|
-
});
|
|
1265
|
-
return dedupeRegistryRows([
|
|
1266
|
-
...DEFAULT_AUDIENCES,
|
|
1267
|
-
...applicable.map(
|
|
1268
|
-
(row) => normalizeRegistryRow({
|
|
1269
|
-
audienceKey: row.audienceKey,
|
|
1270
|
-
audienceLabel: row.audienceLabel,
|
|
1271
|
-
audienceClass: row.audienceClass,
|
|
1272
|
-
workspaceId: row.workspaceId
|
|
1273
|
-
})
|
|
1274
|
-
)
|
|
1275
|
-
]);
|
|
1276
|
-
}
|
|
1277
|
-
async function listAudienceRegistryRows(ctx, args) {
|
|
1278
|
-
return queryRegistryRows(ctx, args);
|
|
1279
|
-
}
|
|
1280
|
-
|
|
1281
|
-
// ../access-control/src/auth.ts
|
|
1282
|
-
async function getCurrentUserId(ctx) {
|
|
1283
|
-
const identity = await ctx.auth.getUserIdentity();
|
|
1284
|
-
return identity?.subject ?? null;
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
// ../../packages/contracts/src/schema-helpers/enumValidation.ts
|
|
1288
|
-
var BUILTIN_ENUM_FALLBACK = {
|
|
1289
|
-
topic_type: /* @__PURE__ */ new Set([
|
|
1290
|
-
"domain",
|
|
1291
|
-
"theme",
|
|
1292
|
-
"deal",
|
|
1293
|
-
"strategy",
|
|
1294
|
-
"constitution",
|
|
1295
|
-
"project",
|
|
1296
|
-
"portfolio",
|
|
1297
|
-
"architecture",
|
|
1298
|
-
"capability",
|
|
1299
|
-
"runtime",
|
|
1300
|
-
"interface",
|
|
1301
|
-
"governance",
|
|
1302
|
-
"operations",
|
|
1303
|
-
"security",
|
|
1304
|
-
"data"
|
|
1305
|
-
]),
|
|
1306
|
-
branch_schema: /* @__PURE__ */ new Set(["pillar", "track", "dimension", "axis", "phase"]),
|
|
1307
|
-
lens_perspective_type: /* @__PURE__ */ new Set([
|
|
1308
|
-
"investigation",
|
|
1309
|
-
"monitoring",
|
|
1310
|
-
"analysis",
|
|
1311
|
-
"comparison",
|
|
1312
|
-
"taxonomy"
|
|
1313
|
-
]),
|
|
1314
|
-
belief_type: /* @__PURE__ */ new Set([
|
|
1315
|
-
"belief",
|
|
1316
|
-
"hypothesis",
|
|
1317
|
-
"principle",
|
|
1318
|
-
"invariant",
|
|
1319
|
-
"assumption",
|
|
1320
|
-
"tenet",
|
|
1321
|
-
"prior",
|
|
1322
|
-
"preference",
|
|
1323
|
-
"goal",
|
|
1324
|
-
"forecast",
|
|
1325
|
-
"decision",
|
|
1326
|
-
"constraint",
|
|
1327
|
-
"tradeoff",
|
|
1328
|
-
"policy",
|
|
1329
|
-
"implementation_choice",
|
|
1330
|
-
"implementation_decision",
|
|
1331
|
-
"interface_contract",
|
|
1332
|
-
"migration_state",
|
|
1333
|
-
"code_pattern",
|
|
1334
|
-
"deprecation_notice"
|
|
1335
|
-
]),
|
|
1336
|
-
edge_type: /* @__PURE__ */ new Set([
|
|
1337
|
-
"supports",
|
|
1338
|
-
"informs",
|
|
1339
|
-
"depends_on",
|
|
1340
|
-
"derived_from",
|
|
1341
|
-
"contains",
|
|
1342
|
-
"tests",
|
|
1343
|
-
"supersedes",
|
|
1344
|
-
"responds_to",
|
|
1345
|
-
"belongs_to",
|
|
1346
|
-
"relates_to_thesis",
|
|
1347
|
-
"works_at",
|
|
1348
|
-
"invested_in",
|
|
1349
|
-
"competes_with",
|
|
1350
|
-
"participates_in",
|
|
1351
|
-
"founded_by",
|
|
1352
|
-
"evaluates",
|
|
1353
|
-
"performs",
|
|
1354
|
-
"function_in",
|
|
1355
|
-
"impacts",
|
|
1356
|
-
"raised_from",
|
|
1357
|
-
"mentioned_in",
|
|
1358
|
-
"perspective_on",
|
|
1359
|
-
"plays_theme"
|
|
1360
|
-
]),
|
|
1361
|
-
worktree_type: /* @__PURE__ */ new Set([
|
|
1362
|
-
"belief_test",
|
|
1363
|
-
"existential",
|
|
1364
|
-
"contradiction",
|
|
1365
|
-
"refinement",
|
|
1366
|
-
"coverage",
|
|
1367
|
-
"discovery",
|
|
1368
|
-
"clarification",
|
|
1369
|
-
"confirmation"
|
|
1370
|
-
]),
|
|
1371
|
-
worktree_phase: /* @__PURE__ */ new Set([
|
|
1372
|
-
"cluster_mapping",
|
|
1373
|
-
"hypothesis_formation",
|
|
1374
|
-
"question_generation",
|
|
1375
|
-
"evidence_collection",
|
|
1376
|
-
"synthesis",
|
|
1377
|
-
"decision",
|
|
1378
|
-
"retrospective"
|
|
1379
|
-
]),
|
|
1380
|
-
activity_type: /* @__PURE__ */ new Set([
|
|
1381
|
-
"create",
|
|
1382
|
-
"update",
|
|
1383
|
-
"review",
|
|
1384
|
-
"merge",
|
|
1385
|
-
"archive",
|
|
1386
|
-
"comment",
|
|
1387
|
-
"status_change",
|
|
1388
|
-
"evidence_added",
|
|
1389
|
-
"question_added"
|
|
1390
|
-
])
|
|
1391
|
-
};
|
|
1392
|
-
function normalizeEnumValue(value) {
|
|
1393
|
-
return value.trim().toLowerCase();
|
|
1394
|
-
}
|
|
1395
|
-
async function validateSchemaEnumValue(_ctx, args) {
|
|
1396
|
-
const normalized = normalizeEnumValue(args.value);
|
|
1397
|
-
if (!normalized) {
|
|
1398
|
-
return { valid: false, source: "none" };
|
|
1399
|
-
}
|
|
1400
|
-
if (BUILTIN_ENUM_FALLBACK[args.category].has(normalized)) {
|
|
1401
|
-
return { valid: true, source: "builtin" };
|
|
1402
|
-
}
|
|
1403
|
-
return { valid: false, source: "none" };
|
|
1404
|
-
}
|
|
1405
|
-
async function assertSchemaEnumValue(ctx, args) {
|
|
1406
|
-
if (typeof args.value !== "string") {
|
|
1407
|
-
return;
|
|
1408
|
-
}
|
|
1409
|
-
const normalized = normalizeEnumValue(args.value);
|
|
1410
|
-
if (!normalized) {
|
|
1411
|
-
return;
|
|
1412
|
-
}
|
|
1413
|
-
const validation = await validateSchemaEnumValue(ctx, {
|
|
1414
|
-
category: args.category,
|
|
1415
|
-
value: normalized,
|
|
1416
|
-
tenantId: args.tenantId
|
|
1417
|
-
});
|
|
1418
|
-
if (!validation.valid) {
|
|
1419
|
-
const tenantHint = args.tenantId ? ` for tenant ${args.tenantId}` : "";
|
|
1420
|
-
throw new Error(
|
|
1421
|
-
`[${args.context}] Invalid value "${normalized}" for category "${args.category}"${tenantHint}. Add it to the contracts schema enum manifest before use.`
|
|
1422
|
-
);
|
|
1423
|
-
}
|
|
1424
|
-
return normalized;
|
|
1425
|
-
}
|
|
1426
|
-
var permissiveReturn = v.optional(v.any());
|
|
1427
|
-
var looseJsonObject = v.record(v.string(), v.any());
|
|
1428
|
-
var looseJsonArray = v.array(v.any());
|
|
1429
|
-
v.union(
|
|
1430
|
-
v.string(),
|
|
1431
|
-
v.number(),
|
|
1432
|
-
v.boolean(),
|
|
1433
|
-
v.null(),
|
|
1434
|
-
looseJsonObject,
|
|
1435
|
-
looseJsonArray
|
|
1436
|
-
);
|
|
1437
|
-
|
|
1438
14
|
// src/beliefLifecycle.ts
|
|
1439
15
|
var BELIEF_STATUS_VALUES = [
|
|
1440
16
|
"assumption",
|
|
@@ -1526,7 +102,7 @@ function promoteBeliefStatusAfterScoring(status, opts) {
|
|
|
1526
102
|
}
|
|
1527
103
|
return status === "fact" ? "fact" : "belief";
|
|
1528
104
|
}
|
|
1529
|
-
var
|
|
105
|
+
var api = anyApi;
|
|
1530
106
|
componentsGeneric();
|
|
1531
107
|
var internal = anyApi;
|
|
1532
108
|
var internalMutation = internalMutationGeneric;
|
|
@@ -1544,8 +120,6 @@ var containsPropagationSpec = {
|
|
|
1544
120
|
operator: () => null,
|
|
1545
121
|
description: "Structural containment only. Traversed for explicit semantics, but it never propagates opinions."
|
|
1546
122
|
};
|
|
1547
|
-
|
|
1548
|
-
// src/edges/utils.ts
|
|
1549
123
|
function readEdgeMetadata(edge) {
|
|
1550
124
|
return {
|
|
1551
125
|
constraint: edge.constraint ?? void 0,
|
|
@@ -1595,9 +169,7 @@ function propagateNegativeSupportWithMetadata(sourceOpinion, targetOpinion, edge
|
|
|
1595
169
|
readEdgeMetadata(edge)
|
|
1596
170
|
);
|
|
1597
171
|
}
|
|
1598
|
-
|
|
1599
|
-
return applyNegativeEvidence(sourceOpinion, targetOpinion, edgeWeight);
|
|
1600
|
-
}
|
|
172
|
+
var propagateNegativeInform = applyNegativeEvidence;
|
|
1601
173
|
|
|
1602
174
|
// src/edges/contradicts.ts
|
|
1603
175
|
var contradictsPropagationSpec = {
|
|
@@ -1622,8 +194,6 @@ var contradictsPropagationSpec = {
|
|
|
1622
194
|
},
|
|
1623
195
|
description: "Legacy contradiction edges move negative pressure in either direction, but never beyond one hop."
|
|
1624
196
|
};
|
|
1625
|
-
|
|
1626
|
-
// src/edges/dependsOn.ts
|
|
1627
197
|
var dependsOnPropagationSpec = {
|
|
1628
198
|
edgeType: "depends_on",
|
|
1629
199
|
direction: "incoming",
|
|
@@ -1639,8 +209,18 @@ var dependsOnPropagationSpec = {
|
|
|
1639
209
|
if (metadata.conditionalA && metadata.conditionalNotA) {
|
|
1640
210
|
const deducedOpinion = conditionalDeduction(
|
|
1641
211
|
dampedSource,
|
|
1642
|
-
|
|
1643
|
-
|
|
212
|
+
mkOpinion(
|
|
213
|
+
metadata.conditionalA.b,
|
|
214
|
+
metadata.conditionalA.d,
|
|
215
|
+
metadata.conditionalA.u,
|
|
216
|
+
metadata.conditionalA.a
|
|
217
|
+
),
|
|
218
|
+
mkOpinion(
|
|
219
|
+
metadata.conditionalNotA.b,
|
|
220
|
+
metadata.conditionalNotA.d,
|
|
221
|
+
metadata.conditionalNotA.u,
|
|
222
|
+
metadata.conditionalNotA.a
|
|
223
|
+
),
|
|
1644
224
|
targetOpinion.a
|
|
1645
225
|
);
|
|
1646
226
|
return annotateRationale(
|
|
@@ -1813,69 +393,30 @@ function getTraversalDirections(direction) {
|
|
|
1813
393
|
return ["outgoing", "incoming"];
|
|
1814
394
|
}
|
|
1815
395
|
|
|
1816
|
-
//
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
"
|
|
1820
|
-
"question",
|
|
1821
|
-
"theme",
|
|
1822
|
-
"deal",
|
|
1823
|
-
"topic",
|
|
1824
|
-
"claim",
|
|
1825
|
-
"evidence",
|
|
1826
|
-
"synthesis",
|
|
1827
|
-
"answer",
|
|
1828
|
-
"atomic_fact",
|
|
1829
|
-
"excerpt",
|
|
1830
|
-
"source",
|
|
1831
|
-
"company",
|
|
1832
|
-
"person",
|
|
1833
|
-
"investor",
|
|
1834
|
-
"function",
|
|
1835
|
-
"value_chain"
|
|
1836
|
-
];
|
|
1837
|
-
function isNodeType(value) {
|
|
1838
|
-
return NODE_TYPES.includes(value);
|
|
396
|
+
// src/debug.ts
|
|
397
|
+
function isGraphPrimitiveDebugEnabled() {
|
|
398
|
+
const env = globalThis.process?.env;
|
|
399
|
+
return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
|
|
1839
400
|
}
|
|
1840
|
-
function
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
return "L4";
|
|
1844
|
-
case "belief":
|
|
1845
|
-
case "question":
|
|
1846
|
-
case "theme":
|
|
1847
|
-
case "deal":
|
|
1848
|
-
return "L3";
|
|
1849
|
-
case "claim":
|
|
1850
|
-
case "evidence":
|
|
1851
|
-
case "synthesis":
|
|
1852
|
-
case "answer":
|
|
1853
|
-
return "L2";
|
|
1854
|
-
case "atomic_fact":
|
|
1855
|
-
case "excerpt":
|
|
1856
|
-
case "source":
|
|
1857
|
-
return "L1";
|
|
1858
|
-
case "topic":
|
|
1859
|
-
return "organizational";
|
|
1860
|
-
case "company":
|
|
1861
|
-
case "person":
|
|
1862
|
-
case "investor":
|
|
1863
|
-
case "function":
|
|
1864
|
-
case "value_chain":
|
|
1865
|
-
return "ontological";
|
|
401
|
+
function debugGraphPrimitiveFallback(message, context) {
|
|
402
|
+
if (!isGraphPrimitiveDebugEnabled()) {
|
|
403
|
+
return;
|
|
1866
404
|
}
|
|
405
|
+
console.debug(message, context ?? {});
|
|
1867
406
|
}
|
|
1868
|
-
|
|
407
|
+
|
|
408
|
+
// src/topicScope.ts
|
|
409
|
+
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
1869
410
|
function asMappedProjectId(topic) {
|
|
1870
411
|
if (!topic) {
|
|
1871
412
|
return;
|
|
1872
413
|
}
|
|
1873
|
-
const directLegacyProjectId = normalizeScopeValue(topic[
|
|
414
|
+
const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
|
|
1874
415
|
if (directLegacyProjectId) {
|
|
1875
416
|
return directLegacyProjectId;
|
|
1876
417
|
}
|
|
1877
418
|
const metadata = topic.metadata || {};
|
|
1878
|
-
const candidate = metadata[
|
|
419
|
+
const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
1879
420
|
return candidate ? candidate : void 0;
|
|
1880
421
|
}
|
|
1881
422
|
function normalizeScopeValue(value) {
|
|
@@ -1904,9 +445,16 @@ async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
|
1904
445
|
try {
|
|
1905
446
|
return await ctx.db.query("topics").withIndex(
|
|
1906
447
|
"by_graph_scope_project",
|
|
1907
|
-
(q) => q.eq(
|
|
448
|
+
(q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
|
|
1908
449
|
).collect();
|
|
1909
|
-
} catch {
|
|
450
|
+
} catch (error) {
|
|
451
|
+
debugGraphPrimitiveFallback(
|
|
452
|
+
"[topicScope] Failed to resolve scope alias via index",
|
|
453
|
+
{
|
|
454
|
+
error,
|
|
455
|
+
scopeId
|
|
456
|
+
}
|
|
457
|
+
);
|
|
1910
458
|
const topics = await ctx.db.query("topics").collect();
|
|
1911
459
|
return topics.filter((topic) => {
|
|
1912
460
|
const normalizedGlobalId = normalizeScopeValue(topic.globalId);
|
|
@@ -1920,10 +468,17 @@ async function tryResolveHostTopicById(ctx, topicId) {
|
|
|
1920
468
|
return null;
|
|
1921
469
|
}
|
|
1922
470
|
try {
|
|
1923
|
-
return await ctx.runQuery(
|
|
471
|
+
return await ctx.runQuery(api.topics.get, {
|
|
1924
472
|
id: topicId
|
|
1925
473
|
}) ?? null;
|
|
1926
|
-
} catch {
|
|
474
|
+
} catch (error) {
|
|
475
|
+
debugGraphPrimitiveFallback(
|
|
476
|
+
"[topicScope] Failed to resolve topic by host query",
|
|
477
|
+
{
|
|
478
|
+
error,
|
|
479
|
+
topicId
|
|
480
|
+
}
|
|
481
|
+
);
|
|
1927
482
|
return null;
|
|
1928
483
|
}
|
|
1929
484
|
}
|
|
@@ -1932,10 +487,17 @@ async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
|
|
|
1932
487
|
return null;
|
|
1933
488
|
}
|
|
1934
489
|
try {
|
|
1935
|
-
return await ctx.runQuery(
|
|
490
|
+
return await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
1936
491
|
projectId: legacyScopeId
|
|
1937
492
|
}) ?? null;
|
|
1938
|
-
} catch {
|
|
493
|
+
} catch (error) {
|
|
494
|
+
debugGraphPrimitiveFallback(
|
|
495
|
+
"[topicScope] Failed to resolve topic by legacy scope",
|
|
496
|
+
{
|
|
497
|
+
error,
|
|
498
|
+
legacyScopeId
|
|
499
|
+
}
|
|
500
|
+
);
|
|
1939
501
|
return null;
|
|
1940
502
|
}
|
|
1941
503
|
}
|
|
@@ -1964,8 +526,17 @@ async function resolveTopicProjectScope(ctx, args) {
|
|
|
1964
526
|
if (args.topicId) {
|
|
1965
527
|
let topic = null;
|
|
1966
528
|
try {
|
|
1967
|
-
topic = await ctx.db.get(
|
|
1968
|
-
|
|
529
|
+
topic = await ctx.db.get(
|
|
530
|
+
args.topicId
|
|
531
|
+
);
|
|
532
|
+
} catch (error) {
|
|
533
|
+
debugGraphPrimitiveFallback(
|
|
534
|
+
"[topicScope] Failed to load topic by direct id",
|
|
535
|
+
{
|
|
536
|
+
error,
|
|
537
|
+
topicId: args.topicId
|
|
538
|
+
}
|
|
539
|
+
);
|
|
1969
540
|
}
|
|
1970
541
|
if (!topic) {
|
|
1971
542
|
topic = await tryResolveHostTopicById(ctx, String(args.topicId));
|
|
@@ -2002,7 +573,14 @@ async function resolveTopicProjectScope(ctx, args) {
|
|
|
2002
573
|
directTopic = await ctx.db.get(
|
|
2003
574
|
args.projectId
|
|
2004
575
|
);
|
|
2005
|
-
} catch {
|
|
576
|
+
} catch (error) {
|
|
577
|
+
debugGraphPrimitiveFallback(
|
|
578
|
+
"[topicScope] Failed to load direct project topic",
|
|
579
|
+
{
|
|
580
|
+
error,
|
|
581
|
+
projectId: args.projectId
|
|
582
|
+
}
|
|
583
|
+
);
|
|
2006
584
|
}
|
|
2007
585
|
if (directTopic) {
|
|
2008
586
|
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
@@ -2209,16 +787,17 @@ function resolveTraversalTargetNodeId(edge, direction) {
|
|
|
2209
787
|
}
|
|
2210
788
|
function readNodeOpinion(node) {
|
|
2211
789
|
const metadata = node.metadata ?? {};
|
|
2212
|
-
|
|
2213
|
-
{
|
|
790
|
+
try {
|
|
791
|
+
return readOpinionFromRecord({
|
|
2214
792
|
...metadata,
|
|
2215
793
|
opinion_b: node.opinion_b,
|
|
2216
794
|
opinion_d: node.opinion_d,
|
|
2217
795
|
opinion_u: node.opinion_u,
|
|
2218
796
|
opinion_a: node.opinion_a
|
|
2219
|
-
}
|
|
2220
|
-
|
|
2221
|
-
|
|
797
|
+
});
|
|
798
|
+
} catch {
|
|
799
|
+
return mkOpinion(0, 0, 1, 0.5);
|
|
800
|
+
}
|
|
2222
801
|
}
|
|
2223
802
|
async function collectConfidencePropagationDispatches(args) {
|
|
2224
803
|
const dispatchesByTargetId = /* @__PURE__ */ new Map();
|
|
@@ -2287,14 +866,20 @@ async function collectConfidencePropagationDispatches(args) {
|
|
|
2287
866
|
if (!result || !hasProjectedOpinionChanged(targetOpinion, result.opinion)) {
|
|
2288
867
|
continue;
|
|
2289
868
|
}
|
|
2290
|
-
|
|
869
|
+
const projectedOpinion = mkOpinion(
|
|
870
|
+
result.opinion.b,
|
|
871
|
+
result.opinion.d,
|
|
872
|
+
result.opinion.u,
|
|
873
|
+
result.opinion.a
|
|
874
|
+
);
|
|
875
|
+
opinionCache.set(cacheKey, projectedOpinion);
|
|
2291
876
|
const existingDispatch = dispatchesByTargetId.get(cacheKey);
|
|
2292
877
|
dispatchesByTargetId.set(cacheKey, {
|
|
2293
878
|
targetNodeId,
|
|
2294
879
|
edgeType: spec.edgeType,
|
|
2295
880
|
traversedDirection: direction,
|
|
2296
881
|
weight: edge.weight ?? 1,
|
|
2297
|
-
opinion:
|
|
882
|
+
opinion: projectedOpinion,
|
|
2298
883
|
operator: result.operator,
|
|
2299
884
|
rationale: existingDispatch ? `${existingDispatch.rationale}; ${result.rationale}` : result.rationale,
|
|
2300
885
|
hop: nextHop
|
|
@@ -2302,7 +887,7 @@ async function collectConfidencePropagationDispatches(args) {
|
|
|
2302
887
|
if (canContinueTransitively(spec, nextHop)) {
|
|
2303
888
|
queue.push({
|
|
2304
889
|
nodeId: targetNodeId,
|
|
2305
|
-
opinion:
|
|
890
|
+
opinion: projectedOpinion,
|
|
2306
891
|
hop: nextHop,
|
|
2307
892
|
visitedNodeIds: /* @__PURE__ */ new Set([
|
|
2308
893
|
...state.visitedNodeIds,
|
|
@@ -2339,7 +924,15 @@ async function scheduleEmbeddingGeneration(args) {
|
|
|
2339
924
|
confidence: args.confidence
|
|
2340
925
|
}
|
|
2341
926
|
);
|
|
2342
|
-
} catch {
|
|
927
|
+
} catch (error) {
|
|
928
|
+
debugGraphPrimitiveFallback(
|
|
929
|
+
"[embeddingTrigger] Failed to schedule embedding generation",
|
|
930
|
+
{
|
|
931
|
+
error,
|
|
932
|
+
nodeId: String(args.nodeId),
|
|
933
|
+
nodeType: args.nodeType
|
|
934
|
+
}
|
|
935
|
+
);
|
|
2343
936
|
}
|
|
2344
937
|
}
|
|
2345
938
|
|
|
@@ -2417,96 +1010,125 @@ async function getQuestionsAnsweredByEvidence(ctx, evidenceId) {
|
|
|
2417
1010
|
}
|
|
2418
1011
|
|
|
2419
1012
|
// src/topicProjectOverlay.ts
|
|
2420
|
-
var
|
|
2421
|
-
function
|
|
1013
|
+
var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
|
|
1014
|
+
function readNonEmptyString(value) {
|
|
2422
1015
|
if (typeof value !== "string") {
|
|
2423
1016
|
return;
|
|
2424
1017
|
}
|
|
2425
1018
|
const normalized = value.trim();
|
|
2426
1019
|
return normalized.length > 0 ? normalized : void 0;
|
|
2427
1020
|
}
|
|
2428
|
-
function
|
|
1021
|
+
function readStringArray(value) {
|
|
2429
1022
|
if (!Array.isArray(value)) {
|
|
2430
1023
|
return [];
|
|
2431
1024
|
}
|
|
2432
|
-
return value.map((entry) =>
|
|
1025
|
+
return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
|
|
2433
1026
|
}
|
|
2434
|
-
function
|
|
1027
|
+
function readMetadata(topic) {
|
|
2435
1028
|
return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
|
|
2436
1029
|
}
|
|
2437
|
-
function
|
|
1030
|
+
function readLegacyProjectId(value) {
|
|
2438
1031
|
if (!value) {
|
|
2439
1032
|
return;
|
|
2440
1033
|
}
|
|
2441
|
-
return
|
|
1034
|
+
return readNonEmptyString(value[LEGACY_SCOPE_FIELD2]);
|
|
2442
1035
|
}
|
|
2443
|
-
function
|
|
1036
|
+
function coerceVisibility(value) {
|
|
2444
1037
|
return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
|
|
2445
1038
|
}
|
|
2446
|
-
function
|
|
1039
|
+
function coerceStatus(value) {
|
|
2447
1040
|
return value === "active" || value === "archived" || value === "watching" ? value : void 0;
|
|
2448
1041
|
}
|
|
2449
|
-
function
|
|
2450
|
-
const explicit =
|
|
1042
|
+
function mapProjectType(topic, metadata) {
|
|
1043
|
+
const explicit = readNonEmptyString(metadata.projectType);
|
|
2451
1044
|
if (explicit) {
|
|
2452
1045
|
return explicit;
|
|
2453
1046
|
}
|
|
2454
1047
|
if (topic.type === "theme") {
|
|
2455
1048
|
return "thematic";
|
|
2456
1049
|
}
|
|
2457
|
-
return
|
|
1050
|
+
return readNonEmptyString(topic.type) || "general";
|
|
2458
1051
|
}
|
|
2459
|
-
function
|
|
2460
|
-
const metadata =
|
|
2461
|
-
return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" ||
|
|
1052
|
+
function isProjectLikeTopic(topic) {
|
|
1053
|
+
const metadata = readMetadata(topic);
|
|
1054
|
+
return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
|
|
2462
1055
|
}
|
|
2463
1056
|
function isMissingLucernChildComponentError(error) {
|
|
2464
|
-
const message =
|
|
1057
|
+
const message = getErrorMessage(error);
|
|
2465
1058
|
return message.includes(
|
|
2466
1059
|
'Child component ComponentName(Identifier("lucern")) not found'
|
|
2467
1060
|
) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
|
|
2468
1061
|
}
|
|
2469
|
-
|
|
1062
|
+
function getErrorMessage(error) {
|
|
1063
|
+
if (error instanceof Error) {
|
|
1064
|
+
return error.message;
|
|
1065
|
+
}
|
|
1066
|
+
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
|
1067
|
+
return error.message;
|
|
1068
|
+
}
|
|
1069
|
+
return "unknown error";
|
|
1070
|
+
}
|
|
1071
|
+
async function resolveTopicDoc(ctx, scopeId) {
|
|
2470
1072
|
if (ctx?.db && typeof ctx.db.get === "function") {
|
|
2471
1073
|
try {
|
|
2472
|
-
const directTopic = await ctx.db.get(
|
|
1074
|
+
const directTopic = await ctx.db.get(
|
|
1075
|
+
scopeId
|
|
1076
|
+
);
|
|
2473
1077
|
if (directTopic) {
|
|
2474
1078
|
return directTopic;
|
|
2475
1079
|
}
|
|
2476
|
-
} catch {
|
|
1080
|
+
} catch (error) {
|
|
1081
|
+
debugGraphPrimitiveFallback(
|
|
1082
|
+
"[topicProjectOverlay] Failed to resolve topic by direct ID",
|
|
1083
|
+
{
|
|
1084
|
+
error,
|
|
1085
|
+
scopeId
|
|
1086
|
+
}
|
|
1087
|
+
);
|
|
2477
1088
|
}
|
|
2478
1089
|
}
|
|
2479
1090
|
if (typeof ctx.runQuery !== "function") {
|
|
2480
1091
|
return null;
|
|
2481
1092
|
}
|
|
2482
1093
|
try {
|
|
2483
|
-
const topic = await ctx.runQuery(
|
|
1094
|
+
const topic = await ctx.runQuery(api.topics.get, {
|
|
2484
1095
|
id: String(scopeId)
|
|
2485
1096
|
});
|
|
2486
1097
|
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
2487
1098
|
return topic;
|
|
2488
1099
|
}
|
|
2489
|
-
} catch {
|
|
1100
|
+
} catch (error) {
|
|
1101
|
+
debugGraphPrimitiveFallback(
|
|
1102
|
+
"[topicProjectOverlay] Failed to resolve topic by ID query",
|
|
1103
|
+
{
|
|
1104
|
+
error,
|
|
1105
|
+
scopeId
|
|
1106
|
+
}
|
|
1107
|
+
);
|
|
2490
1108
|
}
|
|
2491
1109
|
try {
|
|
2492
|
-
const topic = await ctx.runQuery(
|
|
1110
|
+
const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
2493
1111
|
projectId: String(scopeId)
|
|
2494
1112
|
});
|
|
2495
1113
|
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
2496
1114
|
return topic;
|
|
2497
1115
|
}
|
|
2498
|
-
} catch {
|
|
1116
|
+
} catch (error) {
|
|
1117
|
+
debugGraphPrimitiveFallback(
|
|
1118
|
+
"[topicProjectOverlay] Failed to resolve topic by legacy scope ID",
|
|
1119
|
+
{ error, scopeId }
|
|
1120
|
+
);
|
|
2499
1121
|
}
|
|
2500
1122
|
return null;
|
|
2501
1123
|
}
|
|
2502
|
-
function
|
|
2503
|
-
const metadata =
|
|
1124
|
+
function materializeTopicProjectOverlay(topic, idMode = "legacy") {
|
|
1125
|
+
const metadata = readMetadata(topic);
|
|
2504
1126
|
const topicId = String(topic._id);
|
|
2505
|
-
const legacyProjectId =
|
|
1127
|
+
const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
|
|
2506
1128
|
const storageProjectId = legacyProjectId || topicId;
|
|
2507
1129
|
const outwardId = idMode === "topic" ? topicId : storageProjectId;
|
|
2508
|
-
const visibility =
|
|
2509
|
-
const status =
|
|
1130
|
+
const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
|
|
1131
|
+
const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
|
|
2510
1132
|
const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
|
|
2511
1133
|
const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
|
|
2512
1134
|
return {
|
|
@@ -2516,16 +1138,16 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
|
|
|
2516
1138
|
topicId,
|
|
2517
1139
|
storageProjectId,
|
|
2518
1140
|
legacyProjectId,
|
|
2519
|
-
name:
|
|
2520
|
-
type:
|
|
2521
|
-
description:
|
|
2522
|
-
ownerId:
|
|
2523
|
-
sharedWith:
|
|
1141
|
+
name: readNonEmptyString(topic.name) || "Untitled Theme",
|
|
1142
|
+
type: mapProjectType(topic, metadata),
|
|
1143
|
+
description: readNonEmptyString(topic.description),
|
|
1144
|
+
ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
|
|
1145
|
+
sharedWith: readStringArray(metadata.sharedWith),
|
|
2524
1146
|
visibility,
|
|
2525
|
-
tenantId:
|
|
2526
|
-
workspaceId:
|
|
1147
|
+
tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
|
|
1148
|
+
workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
|
|
2527
1149
|
status,
|
|
2528
|
-
tags:
|
|
1150
|
+
tags: readStringArray(metadata.tags),
|
|
2529
1151
|
chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
|
|
2530
1152
|
artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
|
|
2531
1153
|
lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
|
|
@@ -2534,38 +1156,42 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
|
|
|
2534
1156
|
updatedAt
|
|
2535
1157
|
};
|
|
2536
1158
|
}
|
|
2537
|
-
async function
|
|
2538
|
-
const topic = await
|
|
1159
|
+
async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
|
|
1160
|
+
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
2539
1161
|
if (!topic) {
|
|
2540
1162
|
return null;
|
|
2541
1163
|
}
|
|
2542
|
-
if (options.projectLikeOnly !== false && !
|
|
1164
|
+
if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
|
|
2543
1165
|
return null;
|
|
2544
1166
|
}
|
|
2545
|
-
return
|
|
1167
|
+
return materializeTopicProjectOverlay(topic, options.idMode);
|
|
2546
1168
|
}
|
|
2547
|
-
async function
|
|
1169
|
+
async function listTopicProjectOverlays(ctx, options = {}) {
|
|
2548
1170
|
let allTopics = [];
|
|
2549
1171
|
if (ctx?.db?.query && typeof ctx.db.query === "function") {
|
|
2550
1172
|
try {
|
|
2551
1173
|
allTopics = await ctx.db.query("topics").collect();
|
|
2552
|
-
} catch {
|
|
1174
|
+
} catch (error) {
|
|
1175
|
+
debugGraphPrimitiveFallback(
|
|
1176
|
+
"[topicProjectOverlay] Failed to read topics table; falling back to API",
|
|
1177
|
+
{ error }
|
|
1178
|
+
);
|
|
2553
1179
|
allTopics = [];
|
|
2554
1180
|
}
|
|
2555
1181
|
}
|
|
2556
1182
|
if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
|
|
2557
|
-
allTopics = (await ctx.runQuery(
|
|
1183
|
+
allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
|
|
2558
1184
|
}
|
|
2559
1185
|
return allTopics.filter(
|
|
2560
|
-
(topic) => options.projectLikeOnly === false ||
|
|
2561
|
-
).map((topic) =>
|
|
1186
|
+
(topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
|
|
1187
|
+
).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
|
|
2562
1188
|
}
|
|
2563
1189
|
async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
2564
|
-
const topic = await
|
|
1190
|
+
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
2565
1191
|
if (!topic) {
|
|
2566
1192
|
return null;
|
|
2567
1193
|
}
|
|
2568
|
-
const nextMetadata = { ...
|
|
1194
|
+
const nextMetadata = { ...readMetadata(topic) };
|
|
2569
1195
|
const patch = {};
|
|
2570
1196
|
const topicUpdateArgs = {
|
|
2571
1197
|
id: String(topic._id)
|
|
@@ -2590,7 +1216,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2590
1216
|
`patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
|
|
2591
1217
|
);
|
|
2592
1218
|
case "status": {
|
|
2593
|
-
const status =
|
|
1219
|
+
const status = coerceStatus(rawValue);
|
|
2594
1220
|
if (status) {
|
|
2595
1221
|
patch.status = status;
|
|
2596
1222
|
topicUpdateArgs.status = status;
|
|
@@ -2598,7 +1224,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2598
1224
|
break;
|
|
2599
1225
|
}
|
|
2600
1226
|
case "visibility": {
|
|
2601
|
-
const visibility =
|
|
1227
|
+
const visibility = coerceVisibility(rawValue);
|
|
2602
1228
|
if (visibility) {
|
|
2603
1229
|
patch.visibility = visibility;
|
|
2604
1230
|
topicUpdateArgs.visibility = visibility;
|
|
@@ -2606,7 +1232,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2606
1232
|
break;
|
|
2607
1233
|
}
|
|
2608
1234
|
case "type": {
|
|
2609
|
-
const projectType =
|
|
1235
|
+
const projectType = readNonEmptyString(rawValue);
|
|
2610
1236
|
if (projectType) {
|
|
2611
1237
|
nextMetadata.projectType = projectType;
|
|
2612
1238
|
} else {
|
|
@@ -2630,7 +1256,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2630
1256
|
topicUpdateArgs.metadata = nextMetadata;
|
|
2631
1257
|
if (typeof ctx.runMutation === "function") {
|
|
2632
1258
|
try {
|
|
2633
|
-
await ctx.runMutation(
|
|
1259
|
+
await ctx.runMutation(api.topics.update, topicUpdateArgs);
|
|
2634
1260
|
} catch (error) {
|
|
2635
1261
|
if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
|
|
2636
1262
|
throw error;
|
|
@@ -2644,19 +1270,28 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2644
1270
|
"Cannot patch topic without component adapter (ctx.runMutation unavailable)"
|
|
2645
1271
|
);
|
|
2646
1272
|
}
|
|
2647
|
-
return
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
}
|
|
2653
|
-
);
|
|
1273
|
+
return materializeTopicProjectOverlay({
|
|
1274
|
+
...topic,
|
|
1275
|
+
...patch,
|
|
1276
|
+
metadata: nextMetadata
|
|
1277
|
+
});
|
|
2654
1278
|
}
|
|
2655
1279
|
|
|
2656
1280
|
// src/resolvers.ts
|
|
2657
1281
|
function isMissingLucernChildComponentError2(error) {
|
|
2658
|
-
const message =
|
|
2659
|
-
return message.includes(
|
|
1282
|
+
const message = getErrorMessage2(error);
|
|
1283
|
+
return message.includes(
|
|
1284
|
+
'Child component ComponentName(Identifier("lucern")) not found'
|
|
1285
|
+
) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
|
|
1286
|
+
}
|
|
1287
|
+
function getErrorMessage2(error) {
|
|
1288
|
+
if (error instanceof Error) {
|
|
1289
|
+
return error.message;
|
|
1290
|
+
}
|
|
1291
|
+
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
|
1292
|
+
return error.message;
|
|
1293
|
+
}
|
|
1294
|
+
return "unknown error";
|
|
2660
1295
|
}
|
|
2661
1296
|
function isAdvisoryTopicPatch(value) {
|
|
2662
1297
|
const advisoryKeys = /* @__PURE__ */ new Set(["lastActivityAt", "updatedAt"]);
|
|
@@ -2670,39 +1305,34 @@ async function patchProjectWithTolerance(ctx, projectId, value) {
|
|
|
2670
1305
|
if (!isAdvisoryTopicPatch(value) || !isMissingLucernChildComponentError2(error)) {
|
|
2671
1306
|
throw error;
|
|
2672
1307
|
}
|
|
2673
|
-
console.warn(
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
1308
|
+
console.warn(
|
|
1309
|
+
"[lucern graph-primitives] Non-fatal advisory topic patch failure",
|
|
1310
|
+
{
|
|
1311
|
+
projectId,
|
|
1312
|
+
keys: Object.keys(value),
|
|
1313
|
+
error: getErrorMessage2(error)
|
|
1314
|
+
}
|
|
1315
|
+
);
|
|
2678
1316
|
}
|
|
2679
1317
|
}
|
|
2680
|
-
function
|
|
1318
|
+
function defaultResolvers() {
|
|
2681
1319
|
return {
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
async listTopics(ctx) {
|
|
2692
|
-
return await listTopicProjectOverlays2(ctx, {
|
|
2693
|
-
idMode: "legacy"
|
|
2694
|
-
});
|
|
2695
|
-
},
|
|
2696
|
-
async getFinalArtifact(ctx, artifactId) {
|
|
2697
|
-
return await ctx.db.get(artifactId);
|
|
2698
|
-
}
|
|
1320
|
+
getProject: (ctx, projectId) => resolveTopicProjectOverlay(ctx, projectId, {
|
|
1321
|
+
idMode: "legacy",
|
|
1322
|
+
projectLikeOnly: false
|
|
1323
|
+
}),
|
|
1324
|
+
patchProject: (ctx, projectId, value) => patchProjectWithTolerance(ctx, projectId, value),
|
|
1325
|
+
listTopics: (ctx) => listTopicProjectOverlays(ctx, {
|
|
1326
|
+
idMode: "legacy"
|
|
1327
|
+
}),
|
|
1328
|
+
getFinalArtifact: (ctx, artifactId) => ctx.db.get(artifactId)
|
|
2699
1329
|
};
|
|
2700
1330
|
}
|
|
2701
|
-
var
|
|
1331
|
+
var resolverOverrides = {};
|
|
2702
1332
|
function resolveGraphPrimitivesAppResolvers(_ctx) {
|
|
2703
1333
|
return {
|
|
2704
|
-
...
|
|
2705
|
-
...
|
|
1334
|
+
...defaultResolvers(),
|
|
1335
|
+
...resolverOverrides
|
|
2706
1336
|
};
|
|
2707
1337
|
}
|
|
2708
1338
|
|
|
@@ -2727,7 +1357,7 @@ function throwStructuredMutationError(args) {
|
|
|
2727
1357
|
function readFiniteNumber(value) {
|
|
2728
1358
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
2729
1359
|
}
|
|
2730
|
-
function
|
|
1360
|
+
function clamp01(value) {
|
|
2731
1361
|
return Math.max(0, Math.min(1, value));
|
|
2732
1362
|
}
|
|
2733
1363
|
function assertBaseRateInRange(baseRate, field = "baseRate") {
|
|
@@ -2771,26 +1401,35 @@ function buildBeliefConfidenceRow(args) {
|
|
|
2771
1401
|
...args.triggeringWorktreeId ? { triggeringWorktreeId: args.triggeringWorktreeId } : {}
|
|
2772
1402
|
};
|
|
2773
1403
|
}
|
|
1404
|
+
function buildBeliefStatusSuccessResult() {
|
|
1405
|
+
return { success: true };
|
|
1406
|
+
}
|
|
1407
|
+
function buildBeliefEvidenceNotFoundResult() {
|
|
1408
|
+
const result = {};
|
|
1409
|
+
result.success = false;
|
|
1410
|
+
result.message = "Evidence node not found";
|
|
1411
|
+
return result;
|
|
1412
|
+
}
|
|
2774
1413
|
function deriveSyntheticBackfillOpinion(source) {
|
|
2775
1414
|
const belief = readFiniteNumber(source.opinion_b) ?? readFiniteNumber(source.belief);
|
|
2776
1415
|
const disbelief = readFiniteNumber(source.opinion_d) ?? readFiniteNumber(source.disbelief);
|
|
2777
1416
|
const uncertainty = readFiniteNumber(source.opinion_u) ?? readFiniteNumber(source.uncertainty);
|
|
2778
1417
|
const baseRate = readFiniteNumber(source.opinion_a) ?? readFiniteNumber(source.baseRate);
|
|
2779
1418
|
if (belief !== void 0 || disbelief !== void 0 || uncertainty !== void 0 || baseRate !== void 0) {
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
1419
|
+
try {
|
|
1420
|
+
return readOpinionFromRecord(source);
|
|
1421
|
+
} catch (error) {
|
|
1422
|
+
debugGraphPrimitiveFallback(
|
|
1423
|
+
"[epistemicBeliefs] Failed to decode legacy belief opinion",
|
|
1424
|
+
{
|
|
1425
|
+
error
|
|
1426
|
+
}
|
|
1427
|
+
);
|
|
1428
|
+
return mkOpinion(0, 0, 1, 0.5);
|
|
1429
|
+
}
|
|
2786
1430
|
}
|
|
2787
|
-
const confidence =
|
|
2788
|
-
return
|
|
2789
|
-
b: confidence,
|
|
2790
|
-
d: 1 - confidence,
|
|
2791
|
-
u: 0,
|
|
2792
|
-
a: 0.5
|
|
2793
|
-
};
|
|
1431
|
+
const confidence = clamp01(readFiniteNumber(source.confidence) ?? 0);
|
|
1432
|
+
return mkOpinion(confidence, 1 - confidence, 0, 0.5);
|
|
2794
1433
|
}
|
|
2795
1434
|
function clampBeliefLimit(limit, fallback = DEFAULT_PROJECT_BELIEF_LIMIT) {
|
|
2796
1435
|
if (!Number.isFinite(limit)) {
|
|
@@ -2805,16 +1444,24 @@ function readTupleContradictedFlag(value) {
|
|
|
2805
1444
|
return typeof value === "boolean" ? value : void 0;
|
|
2806
1445
|
}
|
|
2807
1446
|
function readBeliefOpinionSnapshot(node, metadata) {
|
|
2808
|
-
|
|
2809
|
-
{
|
|
1447
|
+
try {
|
|
1448
|
+
return readOpinionFromRecord({
|
|
2810
1449
|
...metadata,
|
|
2811
1450
|
opinion_b: node.opinion_b,
|
|
2812
1451
|
opinion_d: node.opinion_d,
|
|
2813
1452
|
opinion_u: node.opinion_u,
|
|
2814
1453
|
opinion_a: node.opinion_a
|
|
2815
|
-
}
|
|
2816
|
-
|
|
2817
|
-
|
|
1454
|
+
});
|
|
1455
|
+
} catch (error) {
|
|
1456
|
+
debugGraphPrimitiveFallback(
|
|
1457
|
+
"[epistemicBeliefs] Failed to read belief opinion snapshot",
|
|
1458
|
+
{
|
|
1459
|
+
error,
|
|
1460
|
+
beliefId: node._id
|
|
1461
|
+
}
|
|
1462
|
+
);
|
|
1463
|
+
return mkOpinion(0, 0, 1, 0.5);
|
|
1464
|
+
}
|
|
2818
1465
|
}
|
|
2819
1466
|
function deriveTupleContradictionSeverity(node) {
|
|
2820
1467
|
const metadata = node.metadata || {};
|
|
@@ -2905,7 +1552,15 @@ async function resolveBeliefScopeOrNull(ctx, args) {
|
|
|
2905
1552
|
projectId: args.projectId ?? void 0,
|
|
2906
1553
|
topicId: args.topicId ?? void 0
|
|
2907
1554
|
});
|
|
2908
|
-
} catch {
|
|
1555
|
+
} catch (error) {
|
|
1556
|
+
debugGraphPrimitiveFallback(
|
|
1557
|
+
"[epistemicBeliefs] Failed to resolve belief scope",
|
|
1558
|
+
{
|
|
1559
|
+
error,
|
|
1560
|
+
projectId: args.projectId,
|
|
1561
|
+
topicId: args.topicId
|
|
1562
|
+
}
|
|
1563
|
+
);
|
|
2909
1564
|
return null;
|
|
2910
1565
|
}
|
|
2911
1566
|
}
|
|
@@ -3005,7 +1660,13 @@ async function getActiveConfidencePolicy(ctx) {
|
|
|
3005
1660
|
activeConfig?.tupleContradictionPolicy
|
|
3006
1661
|
)
|
|
3007
1662
|
};
|
|
3008
|
-
} catch {
|
|
1663
|
+
} catch (error) {
|
|
1664
|
+
debugGraphPrimitiveFallback(
|
|
1665
|
+
"[epistemicBeliefs] Failed to load active confidence policy",
|
|
1666
|
+
{
|
|
1667
|
+
error
|
|
1668
|
+
}
|
|
1669
|
+
);
|
|
3009
1670
|
return DEFAULT_CONFIDENCE_POLICY;
|
|
3010
1671
|
}
|
|
3011
1672
|
}
|
|
@@ -3042,7 +1703,11 @@ async function applyBeliefConfidenceChange(ctx, args) {
|
|
|
3042
1703
|
details: { nodeId: args.nodeId }
|
|
3043
1704
|
});
|
|
3044
1705
|
}
|
|
3045
|
-
await requireProjectWriteAccess(
|
|
1706
|
+
await requireProjectWriteAccess(
|
|
1707
|
+
ctx,
|
|
1708
|
+
node.projectId,
|
|
1709
|
+
args.authenticatedUserId
|
|
1710
|
+
);
|
|
3046
1711
|
const existingMetadata = node.metadata || {};
|
|
3047
1712
|
const currentBeliefStatus = resolveBeliefStatus(node, existingMetadata);
|
|
3048
1713
|
const confidencePolicy = await getActiveConfidencePolicy(ctx);
|
|
@@ -3086,29 +1751,29 @@ async function applyBeliefConfidenceChange(ctx, args) {
|
|
|
3086
1751
|
opinion: nextOpinion,
|
|
3087
1752
|
policy: tupleTransition.policy
|
|
3088
1753
|
});
|
|
3089
|
-
const newBeliefStatus = promoteBeliefStatusAfterScoring(
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
metadata: existingMetadata
|
|
3095
|
-
}
|
|
3096
|
-
);
|
|
1754
|
+
const newBeliefStatus = promoteBeliefStatusAfterScoring(currentBeliefStatus, {
|
|
1755
|
+
confidence: derivedConfidence,
|
|
1756
|
+
predictionMeta,
|
|
1757
|
+
metadata: existingMetadata
|
|
1758
|
+
});
|
|
3097
1759
|
let tupleContradictionId;
|
|
3098
1760
|
if (tupleTransition.crossedIntoTupleContradiction) {
|
|
3099
|
-
tupleContradictionId = await ctx.runMutation(
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
1761
|
+
tupleContradictionId = await ctx.runMutation(
|
|
1762
|
+
"contradictions:create",
|
|
1763
|
+
{
|
|
1764
|
+
projectId: node.projectId,
|
|
1765
|
+
topicId: node.topicId,
|
|
1766
|
+
beliefId: args.nodeId,
|
|
1767
|
+
beliefBId: args.nodeId,
|
|
1768
|
+
supportingInsightIds: [],
|
|
1769
|
+
contradictingInsightIds: [],
|
|
1770
|
+
severity: deriveTupleContradictionSeverity(node),
|
|
1771
|
+
source: "tuple_space",
|
|
1772
|
+
detectionMethod: "agent",
|
|
1773
|
+
description: tupleContradictionDescription,
|
|
1774
|
+
createdBy: args.authenticatedUserId
|
|
1775
|
+
}
|
|
1776
|
+
);
|
|
3112
1777
|
}
|
|
3113
1778
|
await ctx.db.patch(args.nodeId, {
|
|
3114
1779
|
confidence: derivedConfidence,
|
|
@@ -3132,7 +1797,10 @@ async function applyBeliefConfidenceChange(ctx, args) {
|
|
|
3132
1797
|
});
|
|
3133
1798
|
if (isFirstScoring) {
|
|
3134
1799
|
const nodeTopicId = node.topicId;
|
|
3135
|
-
const themeNodes = await ctx.db.query("epistemicNodes").withIndex(
|
|
1800
|
+
const themeNodes = await ctx.db.query("epistemicNodes").withIndex(
|
|
1801
|
+
"by_topic",
|
|
1802
|
+
(q) => q.eq("topicId", nodeTopicId || node.projectId)
|
|
1803
|
+
).filter((q) => q.eq(q.field("nodeType"), "theme")).collect();
|
|
3136
1804
|
for (const theme of themeNodes) {
|
|
3137
1805
|
if (theme.globalId && node.globalId) {
|
|
3138
1806
|
await ctx.scheduler.runAfter(0, internal.neo4jEdgeAPI.createEdge, {
|
|
@@ -3262,12 +1930,12 @@ var propagateConfidenceChange = internalMutation({
|
|
|
3262
1930
|
},
|
|
3263
1931
|
returns: permissiveReturn,
|
|
3264
1932
|
handler: async (ctx, args) => {
|
|
3265
|
-
const sourceOpinion =
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
1933
|
+
const sourceOpinion = mkOpinion(
|
|
1934
|
+
args.opinion_b,
|
|
1935
|
+
args.opinion_d,
|
|
1936
|
+
args.opinion_u,
|
|
1937
|
+
args.opinion_a
|
|
1938
|
+
);
|
|
3271
1939
|
const sourceNode = await ctx.db.get(args.nodeId);
|
|
3272
1940
|
const sourceScope = await resolveNodeScopeForWorkspaceIsolation(
|
|
3273
1941
|
ctx,
|
|
@@ -3374,7 +2042,7 @@ var create = mutation({
|
|
|
3374
2042
|
expectedBy: v.optional(v.number())
|
|
3375
2043
|
})
|
|
3376
2044
|
),
|
|
3377
|
-
baseRate: v.number(),
|
|
2045
|
+
baseRate: v.optional(v.number()),
|
|
3378
2046
|
metadata: v.optional(v.any())
|
|
3379
2047
|
// Additional metadata including isConditional
|
|
3380
2048
|
},
|
|
@@ -3405,7 +2073,7 @@ var create = mutation({
|
|
|
3405
2073
|
);
|
|
3406
2074
|
}
|
|
3407
2075
|
const now = Date.now();
|
|
3408
|
-
const baseRate = assertBaseRateInRange(args.baseRate);
|
|
2076
|
+
const baseRate = assertBaseRateInRange(args.baseRate ?? 0.5);
|
|
3409
2077
|
const initialBeliefStatus = args.worktreeId ? "hypothesis" : "assumption";
|
|
3410
2078
|
const initialEpistemicStatus = args.worktreeId ? "hypothesis" : "assumption";
|
|
3411
2079
|
const seedOpinion = {
|
|
@@ -3730,7 +2398,15 @@ var getByProject = query({
|
|
|
3730
2398
|
projectId: args.projectId,
|
|
3731
2399
|
topicId: args.topicId
|
|
3732
2400
|
});
|
|
3733
|
-
} catch {
|
|
2401
|
+
} catch (error) {
|
|
2402
|
+
debugGraphPrimitiveFallback(
|
|
2403
|
+
"[epistemicBeliefs] Failed to resolve project scope",
|
|
2404
|
+
{
|
|
2405
|
+
error,
|
|
2406
|
+
projectId: args.projectId,
|
|
2407
|
+
topicId: args.topicId
|
|
2408
|
+
}
|
|
2409
|
+
);
|
|
3734
2410
|
return [];
|
|
3735
2411
|
}
|
|
3736
2412
|
if (args.userId) {
|
|
@@ -3774,7 +2450,9 @@ var getByTopic = query({
|
|
|
3774
2450
|
handler: async (ctx, args) => {
|
|
3775
2451
|
const pageSize = clampBeliefLimit(args.limit);
|
|
3776
2452
|
const scanLimit = Math.min(pageSize * 3, MAX_PROJECT_BELIEF_LIMIT);
|
|
3777
|
-
const scope = await resolveTopicProjectScope(ctx, {
|
|
2453
|
+
const scope = await resolveTopicProjectScope(ctx, {
|
|
2454
|
+
topicId: args.topicId
|
|
2455
|
+
});
|
|
3778
2456
|
const query2 = ctx.db.query("epistemicNodes").withIndex(
|
|
3779
2457
|
"by_topic_type",
|
|
3780
2458
|
(q) => q.eq("topicId", args.topicId).eq("nodeType", "belief")
|
|
@@ -3979,8 +2657,8 @@ var modulateConfidence = mutation({
|
|
|
3979
2657
|
// d: evidence AGAINST [0,1]
|
|
3980
2658
|
uncertainty: v.number(),
|
|
3981
2659
|
// u: lack of evidence [0,1]
|
|
3982
|
-
baseRate: v.
|
|
3983
|
-
// a: prior probability [0,1]
|
|
2660
|
+
baseRate: v.number(),
|
|
2661
|
+
// a: prior probability [0,1]
|
|
3984
2662
|
trigger: v.union(
|
|
3985
2663
|
v.literal("evidence_added"),
|
|
3986
2664
|
v.literal("evidence_removed"),
|
|
@@ -4000,16 +2678,18 @@ var modulateConfidence = mutation({
|
|
|
4000
2678
|
rationale: v.optional(v.string()),
|
|
4001
2679
|
userId: v.string(),
|
|
4002
2680
|
// SL operator provenance (optional — defaults to manual_assessment)
|
|
4003
|
-
slOperator: v.optional(
|
|
4004
|
-
v.
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
2681
|
+
slOperator: v.optional(
|
|
2682
|
+
v.union(
|
|
2683
|
+
v.literal("cumulative_fusion"),
|
|
2684
|
+
v.literal("averaging_fusion"),
|
|
2685
|
+
v.literal("trust_discount"),
|
|
2686
|
+
v.literal("conditional_deduction"),
|
|
2687
|
+
v.literal("dependency_cascade"),
|
|
2688
|
+
v.literal("negation"),
|
|
2689
|
+
v.literal("constraint_fusion"),
|
|
2690
|
+
v.literal("manual_assessment")
|
|
2691
|
+
)
|
|
2692
|
+
),
|
|
4013
2693
|
triggeringEvidenceId: v.optional(v.id("epistemicNodes")),
|
|
4014
2694
|
triggeringWorktreeId: v.optional(v.string())
|
|
4015
2695
|
},
|
|
@@ -4240,10 +2920,7 @@ var getConfidenceHistory = query({
|
|
|
4240
2920
|
if (!node || node.nodeType !== "belief") {
|
|
4241
2921
|
return [];
|
|
4242
2922
|
}
|
|
4243
|
-
return await ctx.db.query("beliefConfidence").withIndex(
|
|
4244
|
-
"by_beliefId_time",
|
|
4245
|
-
(q) => q.eq("beliefId", args.nodeId)
|
|
4246
|
-
).order("asc").collect();
|
|
2923
|
+
return await ctx.db.query("beliefConfidence").withIndex("by_beliefId_time", (q) => q.eq("beliefId", args.nodeId)).order("asc").collect();
|
|
4247
2924
|
}
|
|
4248
2925
|
});
|
|
4249
2926
|
var getLineage = query({
|
|
@@ -4547,7 +3224,7 @@ var unlinkEvidence = mutation({
|
|
|
4547
3224
|
const now = Date.now();
|
|
4548
3225
|
const evidenceNode = await ctx.db.get(args.insightId);
|
|
4549
3226
|
if (!evidenceNode || evidenceNode.nodeType !== "evidence") {
|
|
4550
|
-
return
|
|
3227
|
+
return buildBeliefEvidenceNotFoundResult();
|
|
4551
3228
|
}
|
|
4552
3229
|
if (!evidenceNode.projectId) {
|
|
4553
3230
|
throw new Error("Evidence has no project scope");
|
|
@@ -4588,7 +3265,7 @@ var unlinkEvidence = mutation({
|
|
|
4588
3265
|
projectId: evidenceNode.projectId
|
|
4589
3266
|
});
|
|
4590
3267
|
}
|
|
4591
|
-
return
|
|
3268
|
+
return buildBeliefStatusSuccessResult();
|
|
4592
3269
|
}
|
|
4593
3270
|
});
|
|
4594
3271
|
var getWithEvidence = query({
|
|
@@ -4700,7 +3377,7 @@ var deleteRelationship = mutation({
|
|
|
4700
3377
|
changedAt: now,
|
|
4701
3378
|
projectId: edge.projectId
|
|
4702
3379
|
});
|
|
4703
|
-
return
|
|
3380
|
+
return buildBeliefStatusSuccessResult();
|
|
4704
3381
|
}
|
|
4705
3382
|
});
|
|
4706
3383
|
var updateCriticality = mutation({
|
|
@@ -4829,13 +3506,23 @@ var batchUpdateCriticality = mutation({
|
|
|
4829
3506
|
changedAt: now,
|
|
4830
3507
|
projectId: node.projectId
|
|
4831
3508
|
});
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
} catch {
|
|
4838
|
-
|
|
3509
|
+
const result = {};
|
|
3510
|
+
result.beliefId = update.beliefId;
|
|
3511
|
+
result.nodeId = node._id;
|
|
3512
|
+
result.success = true;
|
|
3513
|
+
results.push(result);
|
|
3514
|
+
} catch (error) {
|
|
3515
|
+
debugGraphPrimitiveFallback(
|
|
3516
|
+
"[epistemicBeliefs] Failed to update belief criticality",
|
|
3517
|
+
{
|
|
3518
|
+
error,
|
|
3519
|
+
beliefId: update.beliefId
|
|
3520
|
+
}
|
|
3521
|
+
);
|
|
3522
|
+
const result = {};
|
|
3523
|
+
result.beliefId = update.beliefId;
|
|
3524
|
+
result.success = false;
|
|
3525
|
+
results.push(result);
|
|
4839
3526
|
}
|
|
4840
3527
|
}
|
|
4841
3528
|
return { results, updatedCount: results.filter((r) => r.success).length };
|
|
@@ -4950,7 +3637,15 @@ var getByPillar = query({
|
|
|
4950
3637
|
projectId: args.projectId,
|
|
4951
3638
|
topicId: args.topicId
|
|
4952
3639
|
});
|
|
4953
|
-
} catch {
|
|
3640
|
+
} catch (error) {
|
|
3641
|
+
debugGraphPrimitiveFallback(
|
|
3642
|
+
"[epistemicBeliefs] Failed to resolve criticality scope",
|
|
3643
|
+
{
|
|
3644
|
+
error,
|
|
3645
|
+
projectId: args.projectId,
|
|
3646
|
+
topicId: args.topicId
|
|
3647
|
+
}
|
|
3648
|
+
);
|
|
4954
3649
|
return [];
|
|
4955
3650
|
}
|
|
4956
3651
|
const nodes = await ctx.db.query("epistemicNodes").withIndex(
|
|
@@ -5071,7 +3766,9 @@ var internalGetByTopic = internalQuery({
|
|
|
5071
3766
|
const pageSize = clampBeliefLimit(args.limit, 500);
|
|
5072
3767
|
const scanLimit = Math.min(pageSize * 3, MAX_PROJECT_BELIEF_LIMIT);
|
|
5073
3768
|
const audienceMode = args.audienceMode ?? "internal";
|
|
5074
|
-
const scope = await resolveTopicProjectScope(ctx, {
|
|
3769
|
+
const scope = await resolveTopicProjectScope(ctx, {
|
|
3770
|
+
topicId: args.topicId
|
|
3771
|
+
});
|
|
5075
3772
|
const registryRows = await listAudienceRegistryRows(ctx, {
|
|
5076
3773
|
tenantId: scope.tenantId,
|
|
5077
3774
|
workspaceId: scope.workspaceId
|
|
@@ -5131,7 +3828,7 @@ var internalCreate = internalMutation({
|
|
|
5131
3828
|
args: {
|
|
5132
3829
|
...optionalBeliefScopeArgs,
|
|
5133
3830
|
formulation: v.string(),
|
|
5134
|
-
baseRate: v.number(),
|
|
3831
|
+
baseRate: v.optional(v.number()),
|
|
5135
3832
|
confidence: v.optional(
|
|
5136
3833
|
v.union(v.literal("high"), v.literal("medium"), v.literal("low"))
|
|
5137
3834
|
),
|
|
@@ -5167,7 +3864,7 @@ var internalCreate = internalMutation({
|
|
|
5167
3864
|
returns: permissiveReturn,
|
|
5168
3865
|
handler: async (ctx, args) => {
|
|
5169
3866
|
const now = Date.now();
|
|
5170
|
-
const baseRate = assertBaseRateInRange(args.baseRate);
|
|
3867
|
+
const baseRate = assertBaseRateInRange(args.baseRate ?? 0.5);
|
|
5171
3868
|
const scope = await resolveTopicProjectScope(ctx, {
|
|
5172
3869
|
topicId: args.topicId,
|
|
5173
3870
|
projectId: args.projectId
|
|
@@ -5416,15 +4113,17 @@ var backfillSyntheticOpinionHistory = internalMutation({
|
|
|
5416
4113
|
skippedHasHistory++;
|
|
5417
4114
|
continue;
|
|
5418
4115
|
}
|
|
5419
|
-
const
|
|
4116
|
+
const opinion = deriveSyntheticBackfillOpinion(
|
|
4117
|
+
node
|
|
4118
|
+
);
|
|
5420
4119
|
await ctx.db.insert(
|
|
5421
4120
|
"beliefConfidence",
|
|
5422
4121
|
buildBeliefConfidenceRow({
|
|
5423
4122
|
beliefId: node._id,
|
|
5424
|
-
belief:
|
|
5425
|
-
disbelief:
|
|
5426
|
-
uncertainty:
|
|
5427
|
-
baseRate:
|
|
4123
|
+
belief: opinion.b,
|
|
4124
|
+
disbelief: opinion.d,
|
|
4125
|
+
uncertainty: opinion.u,
|
|
4126
|
+
baseRate: opinion.a,
|
|
5428
4127
|
trigger: "backfill_synthetic",
|
|
5429
4128
|
rationale: "LK-6 backfill: synthesized t0 from node-level opinion fields (no prior beliefConfidence row found).",
|
|
5430
4129
|
assessedAt: readFiniteNumber(node.createdAt) ?? readFiniteNumber(node.updatedAt) ?? Date.now(),
|
|
@@ -5722,7 +4421,13 @@ var getBeliefClusterPositions = query({
|
|
|
5722
4421
|
return {
|
|
5723
4422
|
positions: {},
|
|
5724
4423
|
hasClusters: false,
|
|
5725
|
-
counts: {
|
|
4424
|
+
counts: {
|
|
4425
|
+
raw: 0,
|
|
4426
|
+
hypothesis: 0,
|
|
4427
|
+
conditional: 0,
|
|
4428
|
+
unprocessed: 0,
|
|
4429
|
+
total: 0
|
|
4430
|
+
}
|
|
5726
4431
|
};
|
|
5727
4432
|
}
|
|
5728
4433
|
const allBeliefNodes = await getBeliefNodesForScope(ctx, scope);
|
|
@@ -5784,14 +4489,8 @@ var reassignBeliefsTopic = mutation({
|
|
|
5784
4489
|
let connectedReassigned = 0;
|
|
5785
4490
|
const alreadyReassigned = /* @__PURE__ */ new Set();
|
|
5786
4491
|
for (const beliefId of movedBeliefIds) {
|
|
5787
|
-
const outbound = await ctx.db.query("epistemicEdges").withIndex(
|
|
5788
|
-
|
|
5789
|
-
(q) => q.eq("fromNodeId", beliefId)
|
|
5790
|
-
).collect();
|
|
5791
|
-
const inbound = await ctx.db.query("epistemicEdges").withIndex(
|
|
5792
|
-
"by_to",
|
|
5793
|
-
(q) => q.eq("toNodeId", beliefId)
|
|
5794
|
-
).collect();
|
|
4492
|
+
const outbound = await ctx.db.query("epistemicEdges").withIndex("by_from", (q) => q.eq("fromNodeId", beliefId)).collect();
|
|
4493
|
+
const inbound = await ctx.db.query("epistemicEdges").withIndex("by_to", (q) => q.eq("toNodeId", beliefId)).collect();
|
|
5795
4494
|
const connectedNodeIds = /* @__PURE__ */ new Set();
|
|
5796
4495
|
for (const edge of outbound) {
|
|
5797
4496
|
if (edge.toNodeId) connectedNodeIds.add(String(edge.toNodeId));
|
|
@@ -5814,7 +4513,14 @@ var reassignBeliefsTopic = mutation({
|
|
|
5814
4513
|
alreadyReassigned.add(connectedId);
|
|
5815
4514
|
connectedReassigned++;
|
|
5816
4515
|
}
|
|
5817
|
-
} catch {
|
|
4516
|
+
} catch (error) {
|
|
4517
|
+
debugGraphPrimitiveFallback(
|
|
4518
|
+
"[epistemicBeliefs] Failed to reassign connected node",
|
|
4519
|
+
{
|
|
4520
|
+
error,
|
|
4521
|
+
connectedId
|
|
4522
|
+
}
|
|
4523
|
+
);
|
|
5818
4524
|
}
|
|
5819
4525
|
}
|
|
5820
4526
|
}
|