@lucern/graph-primitives 0.3.0-alpha.0 → 0.3.0-alpha.1
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.js +37 -1104
- package/dist/beliefDecay.js.map +1 -1
- package/dist/beliefEvidenceLinks.js +53 -834
- package/dist/beliefEvidenceLinks.js.map +1 -1
- package/dist/confidencePropagationDispatch.d.ts +3 -3
- package/dist/confidencePropagationDispatch.js +30 -308
- package/dist/confidencePropagationDispatch.js.map +1 -1
- package/dist/contradictions.js +5 -797
- package/dist/contradictions.js.map +1 -1
- 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 +14 -277
- package/dist/edges/index.js.map +1 -1
- package/dist/edges/informs.js +1 -62
- 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 +1 -62
- 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 +6 -6
- package/dist/edges/utils.js +1 -130
- package/dist/edges/utils.js.map +1 -1
- package/dist/entityBridge.js +2 -17
- package/dist/entityBridge.js.map +1 -1
- package/dist/entityLifecycle.js +62 -848
- package/dist/entityLifecycle.js.map +1 -1
- package/dist/epistemicAnswers.js +6 -802
- package/dist/epistemicAnswers.js.map +1 -1
- package/dist/epistemicBeliefs.js +125 -1594
- package/dist/epistemicBeliefs.js.map +1 -1
- package/dist/epistemicContractHelpers.js +1 -318
- package/dist/epistemicContractHelpers.js.map +1 -1
- package/dist/epistemicContracts.js +129 -1874
- package/dist/epistemicContracts.js.map +1 -1
- package/dist/epistemicEdges.js +60 -863
- package/dist/epistemicEdges.js.map +1 -1
- package/dist/epistemicEvidence.js +69 -1041
- package/dist/epistemicEvidence.js.map +1 -1
- package/dist/epistemicLinking.js +2 -785
- package/dist/epistemicLinking.js.map +1 -1
- package/dist/epistemicNodes.js +9 -866
- package/dist/epistemicNodes.js.map +1 -1
- package/dist/epistemicQuestions.js +66 -1071
- package/dist/epistemicQuestions.js.map +1 -1
- package/dist/epistemicSources.js +23 -880
- package/dist/epistemicSources.js.map +1 -1
- package/dist/evaluators/index.js +129 -1874
- package/dist/evaluators/index.js.map +1 -1
- package/dist/index.js +182 -2744
- package/dist/index.js.map +1 -1
- package/dist/ontology-matching.js +1 -344
- package/dist/ontology-matching.js.map +1 -1
- package/dist/ontologyApproval.js +1 -13
- package/dist/ontologyApproval.js.map +1 -1
- package/dist/ontologyDefinitions.js +2 -17
- package/dist/ontologyDefinitions.js.map +1 -1
- package/dist/ontologyRegistry.js +2 -17
- package/dist/ontologyRegistry.js.map +1 -1
- package/dist/projectionReconciliation.js +2 -17
- package/dist/projectionReconciliation.js.map +1 -1
- package/dist/questionEvidenceLinks.js +60 -841
- package/dist/questionEvidenceLinks.js.map +1 -1
- package/dist/text-matching.js +1 -244
- package/dist/text-matching.js.map +1 -1
- package/dist/workflowBridge.d.ts +27 -0
- package/dist/workflowBridge.js +303 -0
- package/dist/workflowBridge.js.map +1 -0
- package/dist/workspaceIsolation.js +2 -52
- package/dist/workspaceIsolation.js.map +1 -1
- package/package.json +6 -5
package/dist/index.js
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { v } from 'convex/values';
|
|
2
|
+
import { getRescoringSchedule, normalizeTupleContradictionPolicy, mkOpinion, createInheritedContractRecord, confidenceFromSL, conditionalDeduction, project, dampedDependencyCascade, hasProjectedOpinionChanged, detectTupleContradiction, evaluateTupleContradictionTransition, readOpinionFromRecord, deriveContractModulationPlan, deriveContractStatus, trustDiscount, applyNegativeSupport, cumulativeFusion, applyNegativeEvidence, parseEvidentialEvaluatorConfig, compareMetricValue, buildEvidentialRationale, parseMetricCheckerConfig, getEvaluatorInputRecord, pickFiniteNumber, resolveComparisonResult, buildComparisonRationale, parseReferenceCheckCounterConfig, parseTemporalDeadlineConfig, parseMarketIndexComparatorConfig, computeEffectiveDecay, computeDeadlineUrgency, computeBaseDecay, bayesianUpdate, DECAY_TIERS, DEADLINE_URGENCY } from '@lucern/confidence';
|
|
3
|
+
import { getAccessibleProjectIds, checkProjectAccess, requireProjectAccess, checkScopeAccess } from '@lucern/access-control/access';
|
|
4
|
+
import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
|
|
2
5
|
import { componentsGeneric, queryGeneric, mutationGeneric, anyApi, internalMutationGeneric, internalQueryGeneric, internalActionGeneric } from 'convex/server';
|
|
6
|
+
import { getCurrentUserId } from '@lucern/access-control/auth';
|
|
7
|
+
import { canAudienceClassAccess, normalizeAudienceKey, classFromAudienceKey } from '@lucern/access-control/audience';
|
|
8
|
+
import { listAudienceRegistryRows } from '@lucern/access-control/audienceRegistry';
|
|
9
|
+
import { assertSchemaEnumValue } from '@lucern/contracts/schema-helpers/enumValidation';
|
|
10
|
+
import { isNodeType, getLayerForNodeType } from '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
|
|
11
|
+
import { scoreEntityTypeMatch, scoreEntityConnection, rankEntityTypeMatches, rankEntityConnections } from '@lucern/contracts/v1/ontologies/v1';
|
|
12
|
+
import { wordTokenize, wordOverlapScore, tokenizeSearchText, tokenOverlapScore, stemToken, scoreLexicalSignals, scoreLexicalSignal, rerankLexicalWindow, rankWindowScore, prepareLexicalQuery, jaccardSimilarity, bigramTokenize } from '@lucern/contracts/text-matching.contract';
|
|
3
13
|
|
|
4
14
|
var __defProp = Object.defineProperty;
|
|
5
15
|
var __export = (target, all) => {
|
|
@@ -21,1867 +31,7 @@ __export(beliefDecay_exports, {
|
|
|
21
31
|
getRescoringSchedule: () => getRescoringSchedule,
|
|
22
32
|
identifyBeliefsNeedingRescore: () => identifyBeliefsNeedingRescore
|
|
23
33
|
});
|
|
24
|
-
|
|
25
|
-
// ../confidence/src/v1/operations/subjectiveLogic/index.ts
|
|
26
|
-
function opinion(belief, disbelief, uncertainty, baseRate = 0.5) {
|
|
27
|
-
const b = Math.max(0, Math.min(1, belief));
|
|
28
|
-
const d = Math.max(0, Math.min(1, disbelief));
|
|
29
|
-
const u = Math.max(0, Math.min(1, uncertainty));
|
|
30
|
-
const a = Math.max(0, Math.min(1, baseRate));
|
|
31
|
-
const sum = b + d + u;
|
|
32
|
-
if (sum === 0) {
|
|
33
|
-
return { b: 0, d: 0, u: 1, a };
|
|
34
|
-
}
|
|
35
|
-
return {
|
|
36
|
-
b: b / sum,
|
|
37
|
-
d: d / sum,
|
|
38
|
-
u: u / sum,
|
|
39
|
-
a
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
function vacuous(baseRate = 0.5) {
|
|
43
|
-
return { b: 0, d: 0, u: 1, a: baseRate };
|
|
44
|
-
}
|
|
45
|
-
function project(o) {
|
|
46
|
-
return o.b + o.a * o.u;
|
|
47
|
-
}
|
|
48
|
-
function cumulativeFusion(left, right) {
|
|
49
|
-
if (left.u === 0 && right.u === 0) {
|
|
50
|
-
return opinion(
|
|
51
|
-
(left.b + right.b) / 2,
|
|
52
|
-
(left.d + right.d) / 2,
|
|
53
|
-
0,
|
|
54
|
-
(left.a + right.a) / 2
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
const k = left.u + right.u - left.u * right.u;
|
|
58
|
-
if (k === 0) {
|
|
59
|
-
return vacuous((left.a + right.a) / 2);
|
|
60
|
-
}
|
|
61
|
-
return opinion(
|
|
62
|
-
(left.b * right.u + right.b * left.u) / k,
|
|
63
|
-
(left.d * right.u + right.d * left.u) / k,
|
|
64
|
-
left.u * right.u / k,
|
|
65
|
-
(left.a + right.a) / 2
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
function trustDiscount(sourceOpinion, trust) {
|
|
69
|
-
const weight = Math.max(0, Math.min(1, Math.abs(trust)));
|
|
70
|
-
return opinion(
|
|
71
|
-
weight * sourceOpinion.b,
|
|
72
|
-
weight * sourceOpinion.d,
|
|
73
|
-
1 - weight * (sourceOpinion.b + sourceOpinion.d),
|
|
74
|
-
sourceOpinion.a
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
var EPSILON = 1e-9;
|
|
78
|
-
function childBaseRateFallback(ifTrue, ifFalse, fallbackBaseRate) {
|
|
79
|
-
if (fallbackBaseRate !== void 0) {
|
|
80
|
-
return Math.max(0, Math.min(1, fallbackBaseRate));
|
|
81
|
-
}
|
|
82
|
-
if (Math.abs(ifTrue.a - ifFalse.a) <= EPSILON) {
|
|
83
|
-
return ifTrue.a;
|
|
84
|
-
}
|
|
85
|
-
return (ifTrue.a + ifFalse.a) / 2;
|
|
86
|
-
}
|
|
87
|
-
function computeConditionalDeductionBaseRate(opinionA, ifTrue, ifFalse, fallbackBaseRate) {
|
|
88
|
-
const denominator = 1 - opinionA.a * ifTrue.u - (1 - opinionA.a) * ifFalse.u;
|
|
89
|
-
if (ifTrue.u + ifFalse.u < 2 - EPSILON && Math.abs(denominator) > EPSILON) {
|
|
90
|
-
const baseRate = (opinionA.a * ifTrue.b + (1 - opinionA.a) * ifFalse.b) / denominator;
|
|
91
|
-
if (baseRate >= -EPSILON && baseRate <= 1 + EPSILON) {
|
|
92
|
-
return Math.max(0, Math.min(1, baseRate));
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return fallbackBaseRate;
|
|
96
|
-
}
|
|
97
|
-
function safeCorrectionTerm(numerator, denominator) {
|
|
98
|
-
if (Math.abs(denominator) <= EPSILON) {
|
|
99
|
-
return void 0;
|
|
100
|
-
}
|
|
101
|
-
const value = numerator / denominator;
|
|
102
|
-
if (!Number.isFinite(value)) {
|
|
103
|
-
return void 0;
|
|
104
|
-
}
|
|
105
|
-
return Math.max(0, value);
|
|
106
|
-
}
|
|
107
|
-
function conditionalDeduction(opinionA, ifTrue, ifFalse, fallbackBaseRate) {
|
|
108
|
-
const fallbackChildBaseRate = childBaseRateFallback(
|
|
109
|
-
ifTrue,
|
|
110
|
-
ifFalse,
|
|
111
|
-
fallbackBaseRate
|
|
112
|
-
);
|
|
113
|
-
const childBaseRate = computeConditionalDeductionBaseRate(
|
|
114
|
-
opinionA,
|
|
115
|
-
ifTrue,
|
|
116
|
-
ifFalse,
|
|
117
|
-
fallbackChildBaseRate
|
|
118
|
-
);
|
|
119
|
-
const projectedAntecedent = project(opinionA);
|
|
120
|
-
const projectedAntecedentComplement = 1 - projectedAntecedent;
|
|
121
|
-
const intermediateBelief = opinionA.b * ifTrue.b + opinionA.d * ifFalse.b + opinionA.u * (ifTrue.b * opinionA.a + ifFalse.b * (1 - opinionA.a));
|
|
122
|
-
const intermediateDisbelief = opinionA.b * ifTrue.d + opinionA.d * ifFalse.d + opinionA.u * (ifTrue.d * opinionA.a + ifFalse.d * (1 - opinionA.a));
|
|
123
|
-
const intermediateUncertainty = opinionA.b * ifTrue.u + opinionA.d * ifFalse.u + opinionA.u * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));
|
|
124
|
-
const projectedVacuousDeduction = ifTrue.b * opinionA.a + ifFalse.b * (1 - opinionA.a) + childBaseRate * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));
|
|
125
|
-
const projectedConditionalA = ifTrue.b + childBaseRate * (1 - ifTrue.b - ifTrue.d);
|
|
126
|
-
let correction = 0;
|
|
127
|
-
if (ifTrue.b > ifFalse.b && ifTrue.d > ifFalse.d || ifTrue.b <= ifFalse.b && ifTrue.d <= ifFalse.d) {
|
|
128
|
-
correction = 0;
|
|
129
|
-
} else if (ifTrue.b > ifFalse.b && ifTrue.d <= ifFalse.d) {
|
|
130
|
-
const beliefGap = ifTrue.b - ifFalse.b;
|
|
131
|
-
const disbeliefGap = ifFalse.d - ifTrue.d;
|
|
132
|
-
if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
133
|
-
correction = safeCorrectionTerm(
|
|
134
|
-
opinionA.a * opinionA.u * (intermediateBelief - ifTrue.b),
|
|
135
|
-
projectedAntecedent * childBaseRate
|
|
136
|
-
) ?? 0;
|
|
137
|
-
} else if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent > opinionA.a) {
|
|
138
|
-
correction = safeCorrectionTerm(
|
|
139
|
-
opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d) * beliefGap,
|
|
140
|
-
projectedAntecedentComplement * childBaseRate * disbeliefGap
|
|
141
|
-
) ?? 0;
|
|
142
|
-
} else if (projectedVacuousDeduction > projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
143
|
-
correction = safeCorrectionTerm(
|
|
144
|
-
(1 - opinionA.a) * opinionA.u * (intermediateBelief - ifTrue.b) * disbeliefGap,
|
|
145
|
-
projectedAntecedent * (1 - childBaseRate) * beliefGap
|
|
146
|
-
) ?? 0;
|
|
147
|
-
} else {
|
|
148
|
-
correction = safeCorrectionTerm(
|
|
149
|
-
(1 - opinionA.a) * opinionA.u * (intermediateDisbelief - ifTrue.d),
|
|
150
|
-
projectedAntecedentComplement * (1 - childBaseRate)
|
|
151
|
-
) ?? 0;
|
|
152
|
-
}
|
|
153
|
-
} else {
|
|
154
|
-
const beliefGap = ifFalse.b - ifTrue.b;
|
|
155
|
-
const disbeliefGap = ifTrue.d - ifFalse.d;
|
|
156
|
-
if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
157
|
-
correction = safeCorrectionTerm(
|
|
158
|
-
(1 - opinionA.a) * opinionA.u * (intermediateDisbelief - ifTrue.d) * beliefGap,
|
|
159
|
-
projectedAntecedent * childBaseRate * disbeliefGap
|
|
160
|
-
) ?? 0;
|
|
161
|
-
} else if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent > opinionA.a) {
|
|
162
|
-
correction = safeCorrectionTerm(
|
|
163
|
-
(1 - opinionA.a) * opinionA.u * (intermediateBelief - ifTrue.b),
|
|
164
|
-
projectedAntecedentComplement * childBaseRate
|
|
165
|
-
) ?? 0;
|
|
166
|
-
} else if (projectedVacuousDeduction > projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
167
|
-
correction = safeCorrectionTerm(
|
|
168
|
-
opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d),
|
|
169
|
-
projectedAntecedent * (1 - childBaseRate)
|
|
170
|
-
) ?? 0;
|
|
171
|
-
} else {
|
|
172
|
-
correction = safeCorrectionTerm(
|
|
173
|
-
opinionA.a * opinionA.u * (intermediateBelief - ifTrue.b) * disbeliefGap,
|
|
174
|
-
projectedAntecedentComplement * (1 - childBaseRate) * beliefGap
|
|
175
|
-
) ?? 0;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
return opinion(
|
|
179
|
-
intermediateBelief - childBaseRate * correction,
|
|
180
|
-
intermediateDisbelief - (1 - childBaseRate) * correction,
|
|
181
|
-
intermediateUncertainty + correction,
|
|
182
|
-
childBaseRate
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
function negate(o) {
|
|
186
|
-
return { b: o.d, d: o.b, u: o.u, a: 1 - o.a };
|
|
187
|
-
}
|
|
188
|
-
function constraintFusion(left, right, mode = "pressure") {
|
|
189
|
-
if (mode === "redistribute") {
|
|
190
|
-
const leftProjected = project(left);
|
|
191
|
-
const rightProjected = project(right);
|
|
192
|
-
const total = leftProjected + rightProjected;
|
|
193
|
-
if (total <= 1) {
|
|
194
|
-
return { o1: left, o2: right };
|
|
195
|
-
}
|
|
196
|
-
const scale = 1 / total;
|
|
197
|
-
return {
|
|
198
|
-
o1: opinion(
|
|
199
|
-
left.b * scale,
|
|
200
|
-
left.d + left.b * (1 - scale),
|
|
201
|
-
left.u,
|
|
202
|
-
left.a
|
|
203
|
-
),
|
|
204
|
-
o2: opinion(
|
|
205
|
-
right.b * scale,
|
|
206
|
-
right.d + right.b * (1 - scale),
|
|
207
|
-
right.u,
|
|
208
|
-
right.a
|
|
209
|
-
)
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
const pressureLeft = right.b * 0.5;
|
|
213
|
-
const pressureRight = left.b * 0.5;
|
|
214
|
-
return {
|
|
215
|
-
o1: opinion(
|
|
216
|
-
left.b - pressureLeft * 0.3,
|
|
217
|
-
left.d + pressureLeft * 0.3,
|
|
218
|
-
left.u,
|
|
219
|
-
left.a
|
|
220
|
-
),
|
|
221
|
-
o2: opinion(
|
|
222
|
-
right.b - pressureRight * 0.3,
|
|
223
|
-
right.d + pressureRight * 0.3,
|
|
224
|
-
right.u,
|
|
225
|
-
right.a
|
|
226
|
-
)
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// ../confidence/src/v1/operations/bridge/index.ts
|
|
231
|
-
var DEFAULT_NON_INFORMATIVE_WEIGHT = 2;
|
|
232
|
-
function clamp01(value) {
|
|
233
|
-
return Math.max(0, Math.min(1, value));
|
|
234
|
-
}
|
|
235
|
-
function clampNonNegative(value) {
|
|
236
|
-
return Number.isFinite(value) ? Math.max(0, value) : 0;
|
|
237
|
-
}
|
|
238
|
-
function normalizeNonInformativeWeight(weight) {
|
|
239
|
-
if (weight === void 0) {
|
|
240
|
-
return DEFAULT_NON_INFORMATIVE_WEIGHT;
|
|
241
|
-
}
|
|
242
|
-
return Number.isFinite(weight) ? Math.max(0, weight) : DEFAULT_NON_INFORMATIVE_WEIGHT;
|
|
243
|
-
}
|
|
244
|
-
function normalizeBaseRateVector(baseRate, size) {
|
|
245
|
-
if (size === 0) {
|
|
246
|
-
return [];
|
|
247
|
-
}
|
|
248
|
-
const fallback = Array.from({ length: size }, () => 1 / size);
|
|
249
|
-
if (!baseRate) {
|
|
250
|
-
return fallback;
|
|
251
|
-
}
|
|
252
|
-
if (baseRate.length !== size) {
|
|
253
|
-
throw new Error(
|
|
254
|
-
`Base-rate vector length ${baseRate.length} must match evidence vector length ${size}.`
|
|
255
|
-
);
|
|
256
|
-
}
|
|
257
|
-
const normalized = baseRate.map((value) => clampNonNegative(value));
|
|
258
|
-
const total = normalized.reduce((sum, value) => sum + value, 0);
|
|
259
|
-
if (total === 0) {
|
|
260
|
-
return fallback;
|
|
261
|
-
}
|
|
262
|
-
return normalized.map((value) => value / total);
|
|
263
|
-
}
|
|
264
|
-
function opinionFromDirichlet(alpha, nonInformativeWeight = DEFAULT_NON_INFORMATIVE_WEIGHT, baseRate) {
|
|
265
|
-
const evidence = alpha.map((value) => clampNonNegative(value));
|
|
266
|
-
const safeWeight = normalizeNonInformativeWeight(nonInformativeWeight);
|
|
267
|
-
const normalizedBaseRate = normalizeBaseRateVector(baseRate, evidence.length);
|
|
268
|
-
const totalEvidence = evidence.reduce((sum, value) => sum + value, 0);
|
|
269
|
-
const denominator = totalEvidence + safeWeight;
|
|
270
|
-
if (denominator === 0) {
|
|
271
|
-
return {
|
|
272
|
-
b: evidence.map(() => 0),
|
|
273
|
-
u: 1,
|
|
274
|
-
a: normalizedBaseRate
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
return {
|
|
278
|
-
b: evidence.map((value) => value / denominator),
|
|
279
|
-
u: safeWeight / denominator,
|
|
280
|
-
a: normalizedBaseRate
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
function opinionFromBeta(alpha, beta, nonInformativeWeight = DEFAULT_NON_INFORMATIVE_WEIGHT, baseRate = 0.5) {
|
|
284
|
-
const dirichlet = opinionFromDirichlet(
|
|
285
|
-
[alpha, beta],
|
|
286
|
-
nonInformativeWeight,
|
|
287
|
-
[clamp01(baseRate), 1 - clamp01(baseRate)]
|
|
288
|
-
);
|
|
289
|
-
return {
|
|
290
|
-
b: dirichlet.b[0] ?? 0,
|
|
291
|
-
d: dirichlet.b[1] ?? 0,
|
|
292
|
-
u: dirichlet.u,
|
|
293
|
-
a: dirichlet.a[0] ?? clamp01(baseRate)
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// ../confidence/src/v1/operations/scoring.ts
|
|
298
|
-
function finiteNumber(value) {
|
|
299
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
300
|
-
}
|
|
301
|
-
function clamp012(value) {
|
|
302
|
-
return Math.max(0, Math.min(1, value));
|
|
303
|
-
}
|
|
304
|
-
function confidenceFromOpinion(opinion2) {
|
|
305
|
-
return clamp012(opinion2.b + opinion2.a * opinion2.u);
|
|
306
|
-
}
|
|
307
|
-
function confidenceFromSL(belief, _disbelief, uncertainty, baseRate = 0.5) {
|
|
308
|
-
return confidenceFromOpinion({
|
|
309
|
-
b: belief,
|
|
310
|
-
u: uncertainty,
|
|
311
|
-
a: baseRate
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
function readOpinionFromRecord(source, fallback = {}) {
|
|
315
|
-
const record = source && typeof source === "object" ? source : {};
|
|
316
|
-
return {
|
|
317
|
-
b: finiteNumber(record.b) ?? finiteNumber(record.belief) ?? finiteNumber(record.slBelief) ?? finiteNumber(record.opinion_b) ?? fallback.b ?? 0,
|
|
318
|
-
d: finiteNumber(record.d) ?? finiteNumber(record.disbelief) ?? finiteNumber(record.slDisbelief) ?? finiteNumber(record.opinion_d) ?? fallback.d ?? 0,
|
|
319
|
-
u: finiteNumber(record.u) ?? finiteNumber(record.uncertainty) ?? finiteNumber(record.slUncertainty) ?? finiteNumber(record.opinion_u) ?? fallback.u ?? 1,
|
|
320
|
-
a: finiteNumber(record.a) ?? finiteNumber(record.baseRate) ?? finiteNumber(record.slBaseRate) ?? finiteNumber(record.opinion_a) ?? fallback.a ?? 0.5
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
function hasProjectedOpinionChanged(current, next, tolerance = 0.01) {
|
|
324
|
-
return Math.abs(confidenceFromOpinion(next) - confidenceFromOpinion(current)) >= tolerance;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// ../confidence/src/v1/operations/contradiction/detectTupleContradiction.ts
|
|
328
|
-
var DEFAULT_TUPLE_CONTRADICTION_BELIEF_THRESHOLD = 0.7;
|
|
329
|
-
var DEFAULT_TUPLE_CONTRADICTION_DISBELIEF_THRESHOLD = 0.7;
|
|
330
|
-
function normalizeTupleContradictionPolicy(policy = {}) {
|
|
331
|
-
return {
|
|
332
|
-
beliefThreshold: clamp012(
|
|
333
|
-
policy.beliefThreshold ?? DEFAULT_TUPLE_CONTRADICTION_BELIEF_THRESHOLD
|
|
334
|
-
),
|
|
335
|
-
disbeliefThreshold: clamp012(
|
|
336
|
-
policy.disbeliefThreshold ?? DEFAULT_TUPLE_CONTRADICTION_DISBELIEF_THRESHOLD
|
|
337
|
-
)
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
function detectTupleContradiction(opinion2, tauB = DEFAULT_TUPLE_CONTRADICTION_BELIEF_THRESHOLD, tauD = DEFAULT_TUPLE_CONTRADICTION_DISBELIEF_THRESHOLD) {
|
|
341
|
-
return opinion2.b > tauB && opinion2.d > tauD;
|
|
342
|
-
}
|
|
343
|
-
function evaluateTupleContradictionTransition(args) {
|
|
344
|
-
const policy = normalizeTupleContradictionPolicy(args.policy);
|
|
345
|
-
const tupleContradicted = detectTupleContradiction(
|
|
346
|
-
args.opinion,
|
|
347
|
-
policy.beliefThreshold,
|
|
348
|
-
policy.disbeliefThreshold
|
|
349
|
-
);
|
|
350
|
-
const previousTupleContradicted = Boolean(args.previousTupleContradicted);
|
|
351
|
-
return {
|
|
352
|
-
tupleContradicted,
|
|
353
|
-
crossedIntoTupleContradiction: !previousTupleContradicted && tupleContradicted,
|
|
354
|
-
crossedOutOfTupleContradiction: previousTupleContradicted && !tupleContradicted,
|
|
355
|
-
policy
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// ../confidence/src/v1/operations/dynamics/cascade.ts
|
|
360
|
-
function dampedDependencyOpinion(dependencyOpinion, beliefOpinion, mode = "continuous", threshold = 0.3) {
|
|
361
|
-
const dependencyProjection = project(dependencyOpinion);
|
|
362
|
-
if (mode === "threshold") {
|
|
363
|
-
if (dependencyProjection < threshold) {
|
|
364
|
-
return opinion(
|
|
365
|
-
0,
|
|
366
|
-
beliefOpinion.d + beliefOpinion.b * 0.5,
|
|
367
|
-
0.5,
|
|
368
|
-
beliefOpinion.a
|
|
369
|
-
);
|
|
370
|
-
}
|
|
371
|
-
return beliefOpinion;
|
|
372
|
-
}
|
|
373
|
-
const dampingFactor = Math.pow(dependencyProjection, 0.5);
|
|
374
|
-
return opinion(
|
|
375
|
-
beliefOpinion.b * dampingFactor,
|
|
376
|
-
beliefOpinion.d + beliefOpinion.b * (1 - dampingFactor) * 0.3,
|
|
377
|
-
beliefOpinion.u + beliefOpinion.b * (1 - dampingFactor) * 0.7,
|
|
378
|
-
beliefOpinion.a
|
|
379
|
-
);
|
|
380
|
-
}
|
|
381
|
-
function dampedDependencyCascade(dependencyOpinion, beliefOpinion, mode = "continuous") {
|
|
382
|
-
return {
|
|
383
|
-
opinion: dampedDependencyOpinion(dependencyOpinion, beliefOpinion, mode),
|
|
384
|
-
operator: "dependency_cascade",
|
|
385
|
-
rationale: `Damped dependency cascade (${mode}): prerequisite at ${project(
|
|
386
|
-
dependencyOpinion
|
|
387
|
-
).toFixed(2)}`
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// ../confidence/src/v1/operations/dynamics/defeat.ts
|
|
392
|
-
function applyNegativeSupport(source, target, weight, metadata = {}) {
|
|
393
|
-
if (metadata.constraint === "xor") {
|
|
394
|
-
const result = constraintFusion(
|
|
395
|
-
source,
|
|
396
|
-
target,
|
|
397
|
-
metadata.normalization ?? "pressure"
|
|
398
|
-
);
|
|
399
|
-
return {
|
|
400
|
-
opinion: result.o2,
|
|
401
|
-
operator: "constraint_fusion",
|
|
402
|
-
rationale: `XOR constraint: source belief at ${project(source).toFixed(
|
|
403
|
-
2
|
|
404
|
-
)} pressures target`
|
|
405
|
-
};
|
|
406
|
-
}
|
|
407
|
-
const discounted = trustDiscount(negate(source), Math.abs(weight));
|
|
408
|
-
return {
|
|
409
|
-
opinion: cumulativeFusion(target, discounted),
|
|
410
|
-
operator: "cumulative_fusion",
|
|
411
|
-
rationale: `Contradicting evidence (weight=${weight.toFixed(
|
|
412
|
-
2
|
|
413
|
-
)}) from source at ${project(source).toFixed(2)}`
|
|
414
|
-
};
|
|
415
|
-
}
|
|
416
|
-
function applyNegativeEvidence(source, target, weight) {
|
|
417
|
-
const discounted = trustDiscount(negate(source), Math.abs(weight));
|
|
418
|
-
return {
|
|
419
|
-
opinion: cumulativeFusion(target, discounted),
|
|
420
|
-
operator: "cumulative_fusion",
|
|
421
|
-
rationale: `Contradicting evidence (weight=${weight.toFixed(2)})`
|
|
422
|
-
};
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// ../confidence/src/v1/operations/dynamics/revision.ts
|
|
426
|
-
function clamp013(value) {
|
|
427
|
-
return Math.max(0, Math.min(1, value));
|
|
428
|
-
}
|
|
429
|
-
function toEvidence(probability, weight) {
|
|
430
|
-
const safeProbability = clamp013(probability);
|
|
431
|
-
const safeWeight = Number.isFinite(weight) ? Math.max(0, weight) : 0;
|
|
432
|
-
return {
|
|
433
|
-
alpha: safeProbability * safeWeight,
|
|
434
|
-
beta: (1 - safeProbability) * safeWeight
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
function bayesianUpdate(priorConfidence, priorWeight, newAssessment, newWeight, options) {
|
|
438
|
-
return reviseConfidence({
|
|
439
|
-
priorConfidence,
|
|
440
|
-
priorWeight,
|
|
441
|
-
newAssessment,
|
|
442
|
-
newWeight,
|
|
443
|
-
baseRate: options?.baseRate,
|
|
444
|
-
nonInformativeWeight: options?.nonInformativeWeight
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
|
-
function reviseConfidence(args) {
|
|
448
|
-
return project(reviseConfidenceOpinion(args));
|
|
449
|
-
}
|
|
450
|
-
function reviseConfidenceOpinion(args) {
|
|
451
|
-
const priorEvidence = toEvidence(args.priorConfidence, args.priorWeight);
|
|
452
|
-
const newEvidence = toEvidence(args.newAssessment, args.newWeight ?? 1);
|
|
453
|
-
return opinionFromBeta(
|
|
454
|
-
priorEvidence.alpha + newEvidence.alpha,
|
|
455
|
-
priorEvidence.beta + newEvidence.beta,
|
|
456
|
-
args.nonInformativeWeight ?? DEFAULT_NON_INFORMATIVE_WEIGHT,
|
|
457
|
-
args.baseRate ?? 0.5
|
|
458
|
-
);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
// ../confidence/src/v1/operations/dynamics/decay.ts
|
|
462
|
-
var DECAY_TIERS = {
|
|
463
|
-
FRESH: {
|
|
464
|
-
maxAgeDays: 30,
|
|
465
|
-
weight: 1,
|
|
466
|
-
label: "fresh",
|
|
467
|
-
action: "Full confidence \u2014 recently validated",
|
|
468
|
-
rescoreInDays: 30
|
|
469
|
-
},
|
|
470
|
-
AGING: {
|
|
471
|
-
maxAgeDays: 90,
|
|
472
|
-
weight: 0.8,
|
|
473
|
-
label: "aging",
|
|
474
|
-
action: "Evidence refresh recommended",
|
|
475
|
-
rescoreInDays: 14
|
|
476
|
-
},
|
|
477
|
-
STALE: {
|
|
478
|
-
maxAgeDays: 180,
|
|
479
|
-
weight: 0.5,
|
|
480
|
-
label: "stale",
|
|
481
|
-
action: "Evidence update required before trusting",
|
|
482
|
-
rescoreInDays: 7
|
|
483
|
-
},
|
|
484
|
-
EXPIRED: {
|
|
485
|
-
maxAgeDays: Number.POSITIVE_INFINITY,
|
|
486
|
-
weight: 0.2,
|
|
487
|
-
label: "expired",
|
|
488
|
-
action: "Full re-evaluation needed",
|
|
489
|
-
rescoreInDays: 0
|
|
490
|
-
}
|
|
491
|
-
};
|
|
492
|
-
var DEADLINE_URGENCY = {
|
|
493
|
-
DISTANT: {
|
|
494
|
-
minDaysToDeadline: 365,
|
|
495
|
-
urgencyMultiplier: 1,
|
|
496
|
-
label: "distant",
|
|
497
|
-
rescoreIntervalDays: 90,
|
|
498
|
-
action: "Quarterly confidence check"
|
|
499
|
-
},
|
|
500
|
-
APPROACHING: {
|
|
501
|
-
minDaysToDeadline: 180,
|
|
502
|
-
urgencyMultiplier: 0.9,
|
|
503
|
-
label: "approaching",
|
|
504
|
-
rescoreIntervalDays: 30,
|
|
505
|
-
action: "Monthly confidence check \u2014 conditions may be shifting"
|
|
506
|
-
},
|
|
507
|
-
NEAR: {
|
|
508
|
-
minDaysToDeadline: 90,
|
|
509
|
-
urgencyMultiplier: 0.75,
|
|
510
|
-
label: "near",
|
|
511
|
-
rescoreIntervalDays: 14,
|
|
512
|
-
action: "Biweekly rescore \u2014 deadline within 3 months"
|
|
513
|
-
},
|
|
514
|
-
IMMINENT: {
|
|
515
|
-
minDaysToDeadline: 30,
|
|
516
|
-
urgencyMultiplier: 0.6,
|
|
517
|
-
label: "imminent",
|
|
518
|
-
rescoreIntervalDays: 7,
|
|
519
|
-
action: "Weekly rescore \u2014 deadline within 1 month"
|
|
520
|
-
},
|
|
521
|
-
CRITICAL: {
|
|
522
|
-
minDaysToDeadline: 7,
|
|
523
|
-
urgencyMultiplier: 0.4,
|
|
524
|
-
label: "critical",
|
|
525
|
-
rescoreIntervalDays: 1,
|
|
526
|
-
action: "Daily rescore \u2014 deadline THIS WEEK"
|
|
527
|
-
},
|
|
528
|
-
OVERDUE: {
|
|
529
|
-
minDaysToDeadline: Number.NEGATIVE_INFINITY,
|
|
530
|
-
urgencyMultiplier: 0.2,
|
|
531
|
-
label: "overdue",
|
|
532
|
-
rescoreIntervalDays: 0,
|
|
533
|
-
action: "OVERDUE \u2014 must validate or archive immediately"
|
|
534
|
-
}
|
|
535
|
-
};
|
|
536
|
-
function normalizeBeliefConfidence(confidence) {
|
|
537
|
-
if (typeof confidence !== "number" || !Number.isFinite(confidence)) {
|
|
538
|
-
return null;
|
|
539
|
-
}
|
|
540
|
-
if (confidence >= 0 && confidence <= 1) {
|
|
541
|
-
return confidence;
|
|
542
|
-
}
|
|
543
|
-
if (confidence > 1 && confidence <= 100) {
|
|
544
|
-
return confidence / 100;
|
|
545
|
-
}
|
|
546
|
-
return null;
|
|
547
|
-
}
|
|
548
|
-
function hasResolvedPredictionOutcome(predictionMeta) {
|
|
549
|
-
if (!predictionMeta || typeof predictionMeta !== "object") {
|
|
550
|
-
return false;
|
|
551
|
-
}
|
|
552
|
-
const outcome = predictionMeta.outcome;
|
|
553
|
-
return outcome === "confirmed" || outcome === "disconfirmed" || outcome === "partial" || outcome === "expired";
|
|
554
|
-
}
|
|
555
|
-
function resolveLifecycleBucket(args) {
|
|
556
|
-
if (normalizeBeliefConfidence(args.confidence) === 0 || normalizeBeliefConfidence(args.confidence) === 1 || hasResolvedPredictionOutcome(args.predictionMeta)) {
|
|
557
|
-
return "fact";
|
|
558
|
-
}
|
|
559
|
-
if (args.beliefStatus === "assumption" || args.beliefStatus === "hypothesis" || args.beliefStatus === "belief" || args.beliefStatus === "fact") {
|
|
560
|
-
if (normalizeBeliefConfidence(args.confidence) !== null && (args.beliefStatus === "assumption" || args.beliefStatus === "hypothesis")) {
|
|
561
|
-
return "belief";
|
|
562
|
-
}
|
|
563
|
-
return args.beliefStatus;
|
|
564
|
-
}
|
|
565
|
-
return "assumption";
|
|
566
|
-
}
|
|
567
|
-
function lifecycleMultiplier(status) {
|
|
568
|
-
if (status === "assumption") {
|
|
569
|
-
return 0.65;
|
|
570
|
-
}
|
|
571
|
-
if (status === "hypothesis") {
|
|
572
|
-
return 0.8;
|
|
573
|
-
}
|
|
574
|
-
return 1;
|
|
575
|
-
}
|
|
576
|
-
function computeBaseDecay(lastScoredAt) {
|
|
577
|
-
const ageDays = (Date.now() - lastScoredAt) / (1e3 * 60 * 60 * 24);
|
|
578
|
-
let tier = DECAY_TIERS.EXPIRED;
|
|
579
|
-
if (ageDays <= DECAY_TIERS.FRESH.maxAgeDays) {
|
|
580
|
-
tier = DECAY_TIERS.FRESH;
|
|
581
|
-
} else if (ageDays <= DECAY_TIERS.AGING.maxAgeDays) {
|
|
582
|
-
tier = DECAY_TIERS.AGING;
|
|
583
|
-
} else if (ageDays <= DECAY_TIERS.STALE.maxAgeDays) {
|
|
584
|
-
tier = DECAY_TIERS.STALE;
|
|
585
|
-
}
|
|
586
|
-
return { ageDays, tier, weight: tier.weight };
|
|
587
|
-
}
|
|
588
|
-
function computeDeadlineUrgency(expectedBy) {
|
|
589
|
-
if (!expectedBy) {
|
|
590
|
-
return null;
|
|
591
|
-
}
|
|
592
|
-
const daysToDeadline = (expectedBy - Date.now()) / (1e3 * 60 * 60 * 24);
|
|
593
|
-
let urgencyTier = DEADLINE_URGENCY.DISTANT;
|
|
594
|
-
if (daysToDeadline < 0) {
|
|
595
|
-
urgencyTier = DEADLINE_URGENCY.OVERDUE;
|
|
596
|
-
} else if (daysToDeadline <= 7) {
|
|
597
|
-
urgencyTier = DEADLINE_URGENCY.CRITICAL;
|
|
598
|
-
} else if (daysToDeadline <= 30) {
|
|
599
|
-
urgencyTier = DEADLINE_URGENCY.IMMINENT;
|
|
600
|
-
} else if (daysToDeadline <= 90) {
|
|
601
|
-
urgencyTier = DEADLINE_URGENCY.NEAR;
|
|
602
|
-
} else if (daysToDeadline <= 180) {
|
|
603
|
-
urgencyTier = DEADLINE_URGENCY.APPROACHING;
|
|
604
|
-
}
|
|
605
|
-
return {
|
|
606
|
-
daysToDeadline: Math.round(daysToDeadline),
|
|
607
|
-
urgencyTier,
|
|
608
|
-
urgencyMultiplier: urgencyTier.urgencyMultiplier,
|
|
609
|
-
isOverdue: daysToDeadline < 0
|
|
610
|
-
};
|
|
611
|
-
}
|
|
612
|
-
function computeEffectiveDecay(lastScoredAt, expectedBy, beliefStatus) {
|
|
613
|
-
const base = computeBaseDecay(lastScoredAt);
|
|
614
|
-
const urgency = computeDeadlineUrgency(expectedBy);
|
|
615
|
-
const effectiveWeight = (urgency ? base.weight * urgency.urgencyMultiplier : base.weight) * lifecycleMultiplier(beliefStatus);
|
|
616
|
-
const urgencyRescoreDays = urgency?.urgencyTier.rescoreIntervalDays ?? Number.POSITIVE_INFINITY;
|
|
617
|
-
const rescoreInDays = Math.min(base.tier.rescoreInDays, urgencyRescoreDays);
|
|
618
|
-
const rescoreByDate = lastScoredAt + rescoreInDays * 24 * 60 * 60 * 1e3;
|
|
619
|
-
let action = base.tier.action;
|
|
620
|
-
if (urgency && urgency.urgencyTier.label !== "distant") {
|
|
621
|
-
action = `${urgency.urgencyTier.action}. ${base.tier.action}`;
|
|
622
|
-
}
|
|
623
|
-
return {
|
|
624
|
-
ageDays: base.ageDays,
|
|
625
|
-
baseTier: base.tier,
|
|
626
|
-
baseWeight: base.weight,
|
|
627
|
-
urgency,
|
|
628
|
-
effectiveWeight: Math.max(0, Math.min(1, effectiveWeight)),
|
|
629
|
-
rescoreByDate,
|
|
630
|
-
rescoreInDays,
|
|
631
|
-
action
|
|
632
|
-
};
|
|
633
|
-
}
|
|
634
|
-
function getRescoringSchedule(opts) {
|
|
635
|
-
const lifecycleStatus = resolveLifecycleBucket({
|
|
636
|
-
beliefStatus: opts.beliefStatus,
|
|
637
|
-
confidence: opts.confidence,
|
|
638
|
-
predictionMeta: opts.predictionMeta
|
|
639
|
-
});
|
|
640
|
-
const decayState = computeEffectiveDecay(
|
|
641
|
-
opts.lastScoredAt,
|
|
642
|
-
opts.expectedBy,
|
|
643
|
-
lifecycleStatus
|
|
644
|
-
);
|
|
645
|
-
const daysUntilRescore = (decayState.rescoreByDate - Date.now()) / (1e3 * 60 * 60 * 24);
|
|
646
|
-
const isOverdue = daysUntilRescore < 0;
|
|
647
|
-
let priority;
|
|
648
|
-
let reason;
|
|
649
|
-
if (isOverdue && lifecycleStatus === "assumption") {
|
|
650
|
-
priority = "critical";
|
|
651
|
-
reason = `Untested assumption is stale (${Math.round(decayState.ageDays)}d) \u2014 validate or supersede`;
|
|
652
|
-
} else if (isOverdue && lifecycleStatus === "hypothesis" || lifecycleStatus === "hypothesis" && daysUntilRescore < 7) {
|
|
653
|
-
priority = "high";
|
|
654
|
-
reason = `Hypothesis aging without validation (${Math.round(decayState.ageDays)}d) \u2014 run/finish sprint testing`;
|
|
655
|
-
} else if (decayState.urgency?.isOverdue) {
|
|
656
|
-
priority = "critical";
|
|
657
|
-
reason = `Prediction deadline passed ${Math.abs(decayState.urgency.daysToDeadline)}d ago \u2014 validate or archive`;
|
|
658
|
-
} else if (isOverdue && decayState.baseTier.label === "expired") {
|
|
659
|
-
priority = "critical";
|
|
660
|
-
reason = `Not scored in ${Math.round(decayState.ageDays)}d \u2014 confidence severely degraded`;
|
|
661
|
-
} else if (isOverdue && decayState.baseTier.label === "stale") {
|
|
662
|
-
priority = "high";
|
|
663
|
-
reason = `Stale (${Math.round(decayState.ageDays)}d since scoring) \u2014 evidence update required`;
|
|
664
|
-
} else if (decayState.urgency && decayState.urgency.urgencyTier.label === "critical") {
|
|
665
|
-
priority = "critical";
|
|
666
|
-
reason = `Deadline in ${decayState.urgency.daysToDeadline}d \u2014 needs immediate rescoring`;
|
|
667
|
-
} else if (decayState.urgency && decayState.urgency.urgencyTier.label === "imminent") {
|
|
668
|
-
priority = "high";
|
|
669
|
-
reason = `Deadline in ${decayState.urgency.daysToDeadline}d \u2014 weekly rescoring required`;
|
|
670
|
-
} else if (isOverdue) {
|
|
671
|
-
priority = "high";
|
|
672
|
-
reason = `Rescore overdue by ${Math.abs(Math.round(daysUntilRescore))}d`;
|
|
673
|
-
} else if (opts.temporalNature === "forecast" && daysUntilRescore < 7) {
|
|
674
|
-
priority = "medium";
|
|
675
|
-
reason = `Forecast belief \u2014 next rescore due in ${Math.round(daysUntilRescore)}d`;
|
|
676
|
-
} else if (daysUntilRescore < 14) {
|
|
677
|
-
priority = "medium";
|
|
678
|
-
reason = `Rescore due in ${Math.round(daysUntilRescore)}d`;
|
|
679
|
-
} else {
|
|
680
|
-
priority = "low";
|
|
681
|
-
reason = `On schedule \u2014 next rescore in ${Math.round(daysUntilRescore)}d`;
|
|
682
|
-
}
|
|
683
|
-
if ((opts.confidence ?? 0) >= 0.8 && decayState.baseTier.label !== "fresh" && priority === "medium") {
|
|
684
|
-
priority = "high";
|
|
685
|
-
reason = `High-confidence belief (${((opts.confidence ?? 0) * 100).toFixed(0)}%) aging without rescore \u2014 ${reason}`;
|
|
686
|
-
}
|
|
687
|
-
return {
|
|
688
|
-
nextRescoreBy: decayState.rescoreByDate,
|
|
689
|
-
daysUntilRescore: Math.round(daysUntilRescore),
|
|
690
|
-
isOverdue,
|
|
691
|
-
priority,
|
|
692
|
-
reason,
|
|
693
|
-
decay: decayState
|
|
694
|
-
};
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
// ../confidence/src/v1/operations/contracts/epistemicContract.ts
|
|
698
|
-
var BUILT_IN_METRIC_CHECKER = "metric_checker";
|
|
699
|
-
var BUILT_IN_REFERENCE_CHECK_COUNTER = "reference_check_counter";
|
|
700
|
-
var BUILT_IN_MARKET_INDEX_COMPARATOR = "market_index_comparator";
|
|
701
|
-
function clampConfidence(value) {
|
|
702
|
-
return Math.max(0, Math.min(1, value));
|
|
703
|
-
}
|
|
704
|
-
function generateContractId() {
|
|
705
|
-
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
706
|
-
return crypto.randomUUID();
|
|
707
|
-
}
|
|
708
|
-
return `contract-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
709
|
-
}
|
|
710
|
-
function deriveContractStatus(result, currentStatus) {
|
|
711
|
-
if (currentStatus === "archived") {
|
|
712
|
-
return currentStatus;
|
|
713
|
-
}
|
|
714
|
-
switch (result) {
|
|
715
|
-
case "confirmed":
|
|
716
|
-
return "satisfied";
|
|
717
|
-
case "disconfirmed":
|
|
718
|
-
return "violated";
|
|
719
|
-
case "expired":
|
|
720
|
-
return "expired";
|
|
721
|
-
default:
|
|
722
|
-
return currentStatus === "satisfied" || currentStatus === "violated" || currentStatus === "expired" ? "active" : currentStatus;
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
function deriveVerificationTrigger(result) {
|
|
726
|
-
switch (result) {
|
|
727
|
-
case "confirmed":
|
|
728
|
-
return "verification_confirmed";
|
|
729
|
-
case "disconfirmed":
|
|
730
|
-
return "verification_disconfirmed";
|
|
731
|
-
case "expired":
|
|
732
|
-
return "verification_expired";
|
|
733
|
-
case "partial":
|
|
734
|
-
return "verification_partial";
|
|
735
|
-
default:
|
|
736
|
-
return null;
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
function deriveContractModulationPlan(args) {
|
|
740
|
-
const trigger = deriveVerificationTrigger(args.result);
|
|
741
|
-
if (!trigger) {
|
|
742
|
-
return null;
|
|
743
|
-
}
|
|
744
|
-
if (args.result === "confirmed") {
|
|
745
|
-
const rawNext = args.currentConfidence + args.modulation.onConfirmed.delta;
|
|
746
|
-
const confidenceAfter = clampConfidence(
|
|
747
|
-
Math.min(
|
|
748
|
-
args.modulation.onConfirmed.ceiling ?? Number.POSITIVE_INFINITY,
|
|
749
|
-
rawNext
|
|
750
|
-
)
|
|
751
|
-
);
|
|
752
|
-
return {
|
|
753
|
-
trigger,
|
|
754
|
-
confidenceBefore: args.currentConfidence,
|
|
755
|
-
confidenceAfter,
|
|
756
|
-
confidenceDelta: confidenceAfter - args.currentConfidence
|
|
757
|
-
};
|
|
758
|
-
}
|
|
759
|
-
if (args.result === "disconfirmed") {
|
|
760
|
-
const rawNext = args.currentConfidence + args.modulation.onDisconfirmed.delta;
|
|
761
|
-
const confidenceAfter = clampConfidence(
|
|
762
|
-
Math.max(args.modulation.onDisconfirmed.floor ?? 0, rawNext)
|
|
763
|
-
);
|
|
764
|
-
return {
|
|
765
|
-
trigger,
|
|
766
|
-
confidenceBefore: args.currentConfidence,
|
|
767
|
-
confidenceAfter,
|
|
768
|
-
confidenceDelta: confidenceAfter - args.currentConfidence
|
|
769
|
-
};
|
|
770
|
-
}
|
|
771
|
-
if (args.result === "expired" && args.modulation.onExpired) {
|
|
772
|
-
const confidenceAfter = clampConfidence(
|
|
773
|
-
args.currentConfidence + args.modulation.onExpired.delta
|
|
774
|
-
);
|
|
775
|
-
return {
|
|
776
|
-
trigger,
|
|
777
|
-
confidenceBefore: args.currentConfidence,
|
|
778
|
-
confidenceAfter,
|
|
779
|
-
confidenceDelta: confidenceAfter - args.currentConfidence
|
|
780
|
-
};
|
|
781
|
-
}
|
|
782
|
-
if (args.result === "partial" && args.modulation.onPartial) {
|
|
783
|
-
if ((args.resultConfidence ?? 0) < args.modulation.onPartial.threshold) {
|
|
784
|
-
return null;
|
|
785
|
-
}
|
|
786
|
-
const confidenceAfter = clampConfidence(
|
|
787
|
-
args.currentConfidence + args.modulation.onPartial.delta
|
|
788
|
-
);
|
|
789
|
-
return {
|
|
790
|
-
trigger,
|
|
791
|
-
confidenceBefore: args.currentConfidence,
|
|
792
|
-
confidenceAfter,
|
|
793
|
-
confidenceDelta: confidenceAfter - args.currentConfidence
|
|
794
|
-
};
|
|
795
|
-
}
|
|
796
|
-
return null;
|
|
797
|
-
}
|
|
798
|
-
function createInheritedContractRecord(contract, args) {
|
|
799
|
-
return {
|
|
800
|
-
beliefNodeId: args.beliefNodeId,
|
|
801
|
-
contractId: generateContractId(),
|
|
802
|
-
title: contract.title,
|
|
803
|
-
description: contract.description,
|
|
804
|
-
conditionType: contract.conditionType,
|
|
805
|
-
direction: contract.direction,
|
|
806
|
-
condition: contract.condition,
|
|
807
|
-
deadline: contract.deadline,
|
|
808
|
-
compositeOf: contract.compositeOf,
|
|
809
|
-
compositeOperator: contract.compositeOperator,
|
|
810
|
-
modulation: contract.modulation,
|
|
811
|
-
evaluationSchedule: contract.evaluationSchedule,
|
|
812
|
-
periodicIntervalMs: contract.periodicIntervalMs,
|
|
813
|
-
status: "active",
|
|
814
|
-
lineageSource: "inherited",
|
|
815
|
-
inheritedFromContractId: contract.contractId,
|
|
816
|
-
inheritedFromBeliefNodeId: contract.beliefNodeId,
|
|
817
|
-
inheritedAt: args.now,
|
|
818
|
-
topicId: args.topicId,
|
|
819
|
-
createdAt: args.now,
|
|
820
|
-
createdBy: args.createdBy,
|
|
821
|
-
updatedAt: args.now
|
|
822
|
-
};
|
|
823
|
-
}
|
|
824
|
-
function normalizeEvidentialAction(value) {
|
|
825
|
-
return value === "modulate_confidence" || value === "flag_review" || value === "archive" ? value : void 0;
|
|
826
|
-
}
|
|
827
|
-
function parseComparisonOperator(value, evaluatorName) {
|
|
828
|
-
if (value === "gte" || value === "lte" || value === "eq" || value === "gt" || value === "lt") {
|
|
829
|
-
return value;
|
|
830
|
-
}
|
|
831
|
-
throw new Error(
|
|
832
|
-
`${evaluatorName} requires operator to be one of gte, lte, eq, gt, or lt.`
|
|
833
|
-
);
|
|
834
|
-
}
|
|
835
|
-
function parseNumericThreshold(value, evaluatorName) {
|
|
836
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
837
|
-
return value;
|
|
838
|
-
}
|
|
839
|
-
throw new Error(`${evaluatorName} requires a finite numeric threshold.`);
|
|
840
|
-
}
|
|
841
|
-
function pickFiniteNumber(config, keys) {
|
|
842
|
-
for (const key of keys) {
|
|
843
|
-
const value = config[key];
|
|
844
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
845
|
-
return value;
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
return void 0;
|
|
849
|
-
}
|
|
850
|
-
function getEvaluatorInputRecord(inputData, nestedKey) {
|
|
851
|
-
if (!inputData || typeof inputData !== "object") {
|
|
852
|
-
return {};
|
|
853
|
-
}
|
|
854
|
-
const root = inputData;
|
|
855
|
-
const nested = root[nestedKey];
|
|
856
|
-
if (nested && typeof nested === "object") {
|
|
857
|
-
return nested;
|
|
858
|
-
}
|
|
859
|
-
return root;
|
|
860
|
-
}
|
|
861
|
-
function parseEvidentialEvaluatorConfig(value) {
|
|
862
|
-
if (!value || typeof value !== "object") {
|
|
863
|
-
throw new Error(
|
|
864
|
-
"Evidential contracts require condition.evaluatorConfig with metric/operator/threshold."
|
|
865
|
-
);
|
|
866
|
-
}
|
|
867
|
-
const config = value;
|
|
868
|
-
const metric = config.metric;
|
|
869
|
-
const operator = config.operator;
|
|
870
|
-
const threshold = config.threshold;
|
|
871
|
-
if (metric !== "evidence_count" && metric !== "contradiction_status" && metric !== "edge_freshness" && metric !== "dependent_count") {
|
|
872
|
-
throw new Error(`Unsupported evidential metric: ${String(metric)}`);
|
|
873
|
-
}
|
|
874
|
-
if (operator !== "gte" && operator !== "lte" && operator !== "eq" && operator !== "gt" && operator !== "lt") {
|
|
875
|
-
throw new Error(`Unsupported evidential operator: ${String(operator)}`);
|
|
876
|
-
}
|
|
877
|
-
if (typeof threshold !== "number" || !Number.isFinite(threshold)) {
|
|
878
|
-
throw new Error("Evidential contracts require a numeric threshold.");
|
|
879
|
-
}
|
|
880
|
-
const actionParams = config.actionParams && typeof config.actionParams === "object" && config.actionParams !== null ? config.actionParams : void 0;
|
|
881
|
-
return {
|
|
882
|
-
metric,
|
|
883
|
-
operator,
|
|
884
|
-
threshold,
|
|
885
|
-
action: normalizeEvidentialAction(config.action),
|
|
886
|
-
actionParams: actionParams && (typeof actionParams.targetConfidence === "number" || typeof actionParams.rationale === "string") ? {
|
|
887
|
-
targetConfidence: typeof actionParams.targetConfidence === "number" ? actionParams.targetConfidence : void 0,
|
|
888
|
-
rationale: typeof actionParams.rationale === "string" ? actionParams.rationale : void 0
|
|
889
|
-
} : void 0
|
|
890
|
-
};
|
|
891
|
-
}
|
|
892
|
-
function compareMetricValue(operator, left, right) {
|
|
893
|
-
switch (operator) {
|
|
894
|
-
case "gte":
|
|
895
|
-
return left >= right;
|
|
896
|
-
case "lte":
|
|
897
|
-
return left <= right;
|
|
898
|
-
case "eq":
|
|
899
|
-
return left === right;
|
|
900
|
-
case "gt":
|
|
901
|
-
return left > right;
|
|
902
|
-
case "lt":
|
|
903
|
-
return left < right;
|
|
904
|
-
default:
|
|
905
|
-
return false;
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
function resolveComparisonResult(direction, comparisonSatisfied) {
|
|
909
|
-
return direction === "falsifies" ? comparisonSatisfied ? "disconfirmed" : "confirmed" : comparisonSatisfied ? "confirmed" : "disconfirmed";
|
|
910
|
-
}
|
|
911
|
-
function buildComparisonRationale(args) {
|
|
912
|
-
const renderedObserved = args.observedValue === null ? "no data" : `${args.observedValue}${args.unit ? ` ${args.unit}` : ""}`;
|
|
913
|
-
const renderedThreshold = `${args.threshold}${args.unit ? ` ${args.unit}` : ""}`;
|
|
914
|
-
const clause = `${args.label} observed ${renderedObserved} against ${args.operator} ${renderedThreshold}`;
|
|
915
|
-
if (args.observedValue === null) {
|
|
916
|
-
return `${clause}; evaluator returned ${args.result} because no current data was available.`;
|
|
917
|
-
}
|
|
918
|
-
return `${clause}; comparison ${args.comparisonSatisfied ? "passed" : "failed"}, resulting in ${args.result}.`;
|
|
919
|
-
}
|
|
920
|
-
function buildEvidentialRationale(args) {
|
|
921
|
-
const observed = args.snapshot.value === null ? "no data" : String(args.snapshot.value);
|
|
922
|
-
const clause = `${args.snapshot.metric} observed ${observed} against ${args.config.operator} ${args.config.threshold}`;
|
|
923
|
-
if (args.snapshot.value === null) {
|
|
924
|
-
return `${clause}; evidential evaluator treated the comparison as unmet, resulting in ${args.result}.`;
|
|
925
|
-
}
|
|
926
|
-
return `${clause}; comparison ${args.comparisonSatisfied ? "passed" : "failed"}, resulting in ${args.result}.`;
|
|
927
|
-
}
|
|
928
|
-
function parseMetricCheckerConfig(value) {
|
|
929
|
-
if (!value || typeof value !== "object") {
|
|
930
|
-
throw new Error(
|
|
931
|
-
"metric_checker requires condition.evaluatorConfig with observedValue/operator/threshold."
|
|
932
|
-
);
|
|
933
|
-
}
|
|
934
|
-
const config = value;
|
|
935
|
-
return {
|
|
936
|
-
metric: typeof config.metric === "string" && config.metric.length > 0 ? config.metric : void 0,
|
|
937
|
-
operator: parseComparisonOperator(config.operator, BUILT_IN_METRIC_CHECKER),
|
|
938
|
-
threshold: parseNumericThreshold(config.threshold, BUILT_IN_METRIC_CHECKER),
|
|
939
|
-
observedValue: pickFiniteNumber(config, ["observedValue", "value"]),
|
|
940
|
-
currentValue: pickFiniteNumber(config, ["currentValue"]),
|
|
941
|
-
metricValue: pickFiniteNumber(config, ["metricValue"]),
|
|
942
|
-
unit: typeof config.unit === "string" && config.unit.length > 0 ? config.unit : void 0
|
|
943
|
-
};
|
|
944
|
-
}
|
|
945
|
-
function parseReferenceCheckCounterConfig(value) {
|
|
946
|
-
if (!value || typeof value !== "object") {
|
|
947
|
-
throw new Error(
|
|
948
|
-
"reference_check_counter requires condition.evaluatorConfig with tag/operator/threshold."
|
|
949
|
-
);
|
|
950
|
-
}
|
|
951
|
-
const config = value;
|
|
952
|
-
if (typeof config.tag !== "string" || config.tag.trim().length === 0) {
|
|
953
|
-
throw new Error("reference_check_counter requires a non-empty tag.");
|
|
954
|
-
}
|
|
955
|
-
return {
|
|
956
|
-
tag: config.tag.trim(),
|
|
957
|
-
operator: parseComparisonOperator(
|
|
958
|
-
config.operator,
|
|
959
|
-
BUILT_IN_REFERENCE_CHECK_COUNTER
|
|
960
|
-
),
|
|
961
|
-
threshold: parseNumericThreshold(
|
|
962
|
-
config.threshold,
|
|
963
|
-
BUILT_IN_REFERENCE_CHECK_COUNTER
|
|
964
|
-
),
|
|
965
|
-
caseSensitive: config.caseSensitive === true
|
|
966
|
-
};
|
|
967
|
-
}
|
|
968
|
-
function parseTemporalDeadlineConfig(value) {
|
|
969
|
-
if (!value || typeof value !== "object") {
|
|
970
|
-
return {};
|
|
971
|
-
}
|
|
972
|
-
const config = value;
|
|
973
|
-
return {
|
|
974
|
-
label: typeof config.label === "string" && config.label.length > 0 ? config.label : void 0,
|
|
975
|
-
completed: config.completed === true,
|
|
976
|
-
completedAt: pickFiniteNumber(config, ["completedAt"]),
|
|
977
|
-
observedAt: pickFiniteNumber(config, ["observedAt"]),
|
|
978
|
-
satisfiedAt: pickFiniteNumber(config, ["satisfiedAt"]),
|
|
979
|
-
achievedAt: pickFiniteNumber(config, ["achievedAt"])
|
|
980
|
-
};
|
|
981
|
-
}
|
|
982
|
-
function parseMarketIndexComparatorConfig(value) {
|
|
983
|
-
if (!value || typeof value !== "object") {
|
|
984
|
-
throw new Error(
|
|
985
|
-
"market_index_comparator requires condition.evaluatorConfig with subjectValue/benchmarkValue/operator/threshold."
|
|
986
|
-
);
|
|
987
|
-
}
|
|
988
|
-
const config = value;
|
|
989
|
-
return {
|
|
990
|
-
subject: typeof config.subject === "string" && config.subject.length > 0 ? config.subject : void 0,
|
|
991
|
-
subjectValue: pickFiniteNumber(config, ["subjectValue", "leftValue"]),
|
|
992
|
-
primaryValue: pickFiniteNumber(config, ["primaryValue"]),
|
|
993
|
-
benchmark: typeof config.benchmark === "string" && config.benchmark.length > 0 ? config.benchmark : void 0,
|
|
994
|
-
benchmarkValue: pickFiniteNumber(config, ["benchmarkValue", "rightValue"]),
|
|
995
|
-
comparisonValue: pickFiniteNumber(config, ["comparisonValue"]),
|
|
996
|
-
operator: parseComparisonOperator(
|
|
997
|
-
config.operator,
|
|
998
|
-
BUILT_IN_MARKET_INDEX_COMPARATOR
|
|
999
|
-
),
|
|
1000
|
-
threshold: parseNumericThreshold(
|
|
1001
|
-
config.threshold,
|
|
1002
|
-
BUILT_IN_MARKET_INDEX_COMPARATOR
|
|
1003
|
-
)
|
|
1004
|
-
};
|
|
1005
|
-
}
|
|
1006
|
-
var api = anyApi;
|
|
1007
|
-
componentsGeneric();
|
|
1008
|
-
|
|
1009
|
-
// ../access-control/src/topicProjectOverlay.ts
|
|
1010
|
-
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
1011
|
-
function readNonEmptyString(value) {
|
|
1012
|
-
if (typeof value !== "string") {
|
|
1013
|
-
return;
|
|
1014
|
-
}
|
|
1015
|
-
const normalized = value.trim();
|
|
1016
|
-
return normalized.length > 0 ? normalized : void 0;
|
|
1017
|
-
}
|
|
1018
|
-
function readStringArray(value) {
|
|
1019
|
-
if (!Array.isArray(value)) {
|
|
1020
|
-
return [];
|
|
1021
|
-
}
|
|
1022
|
-
return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
|
|
1023
|
-
}
|
|
1024
|
-
function readMetadata(topic) {
|
|
1025
|
-
return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
|
|
1026
|
-
}
|
|
1027
|
-
function readLegacyProjectId(value) {
|
|
1028
|
-
if (!value) {
|
|
1029
|
-
return;
|
|
1030
|
-
}
|
|
1031
|
-
return readNonEmptyString(value[LEGACY_SCOPE_FIELD]);
|
|
1032
|
-
}
|
|
1033
|
-
function coerceVisibility(value) {
|
|
1034
|
-
return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
|
|
1035
|
-
}
|
|
1036
|
-
function coerceStatus(value) {
|
|
1037
|
-
return value === "active" || value === "archived" || value === "watching" ? value : void 0;
|
|
1038
|
-
}
|
|
1039
|
-
function mapProjectType(topic, metadata) {
|
|
1040
|
-
const explicit = readNonEmptyString(metadata.projectType);
|
|
1041
|
-
if (explicit) {
|
|
1042
|
-
return explicit;
|
|
1043
|
-
}
|
|
1044
|
-
if (topic.type === "theme") {
|
|
1045
|
-
return "thematic";
|
|
1046
|
-
}
|
|
1047
|
-
return readNonEmptyString(topic.type) || "general";
|
|
1048
|
-
}
|
|
1049
|
-
function isProjectLikeTopic(topic) {
|
|
1050
|
-
const metadata = readMetadata(topic);
|
|
1051
|
-
return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
|
|
1052
|
-
}
|
|
1053
|
-
async function resolveTopicDoc(ctx, scopeId) {
|
|
1054
|
-
if (ctx?.db && typeof ctx.db.get === "function") {
|
|
1055
|
-
try {
|
|
1056
|
-
const directTopic = await ctx.db.get(scopeId);
|
|
1057
|
-
if (directTopic) {
|
|
1058
|
-
return directTopic;
|
|
1059
|
-
}
|
|
1060
|
-
} catch {
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
if (typeof ctx.runQuery !== "function") {
|
|
1064
|
-
return null;
|
|
1065
|
-
}
|
|
1066
|
-
try {
|
|
1067
|
-
const topic = await ctx.runQuery(api.topics.get, {
|
|
1068
|
-
id: String(scopeId)
|
|
1069
|
-
});
|
|
1070
|
-
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
1071
|
-
return topic;
|
|
1072
|
-
}
|
|
1073
|
-
} catch {
|
|
1074
|
-
}
|
|
1075
|
-
try {
|
|
1076
|
-
const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
1077
|
-
projectId: String(scopeId)
|
|
1078
|
-
});
|
|
1079
|
-
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
1080
|
-
return topic;
|
|
1081
|
-
}
|
|
1082
|
-
} catch {
|
|
1083
|
-
}
|
|
1084
|
-
return null;
|
|
1085
|
-
}
|
|
1086
|
-
function materializeTopicProjectOverlay(topic, idMode = "legacy") {
|
|
1087
|
-
const metadata = readMetadata(topic);
|
|
1088
|
-
const topicId = String(topic._id);
|
|
1089
|
-
const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
|
|
1090
|
-
const storageProjectId = legacyProjectId || topicId;
|
|
1091
|
-
const outwardId = idMode === "topic" ? topicId : storageProjectId;
|
|
1092
|
-
const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
|
|
1093
|
-
const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
|
|
1094
|
-
const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
|
|
1095
|
-
const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
|
|
1096
|
-
return {
|
|
1097
|
-
...metadata,
|
|
1098
|
-
_id: outwardId,
|
|
1099
|
-
projectId: outwardId,
|
|
1100
|
-
topicId,
|
|
1101
|
-
storageProjectId,
|
|
1102
|
-
legacyProjectId,
|
|
1103
|
-
name: readNonEmptyString(topic.name) || "Untitled Theme",
|
|
1104
|
-
type: mapProjectType(topic, metadata),
|
|
1105
|
-
description: readNonEmptyString(topic.description),
|
|
1106
|
-
ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
|
|
1107
|
-
sharedWith: readStringArray(metadata.sharedWith),
|
|
1108
|
-
visibility,
|
|
1109
|
-
tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
|
|
1110
|
-
workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
|
|
1111
|
-
status,
|
|
1112
|
-
tags: readStringArray(metadata.tags),
|
|
1113
|
-
chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
|
|
1114
|
-
artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
|
|
1115
|
-
lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
|
|
1116
|
-
_creationTime: typeof topic._creationTime === "number" ? topic._creationTime : createdAt,
|
|
1117
|
-
createdAt,
|
|
1118
|
-
updatedAt
|
|
1119
|
-
};
|
|
1120
|
-
}
|
|
1121
|
-
async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
|
|
1122
|
-
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
1123
|
-
if (!topic) {
|
|
1124
|
-
return null;
|
|
1125
|
-
}
|
|
1126
|
-
if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
|
|
1127
|
-
return null;
|
|
1128
|
-
}
|
|
1129
|
-
return materializeTopicProjectOverlay(topic, options.idMode);
|
|
1130
|
-
}
|
|
1131
|
-
async function listTopicProjectOverlays(ctx, options = {}) {
|
|
1132
|
-
let allTopics = [];
|
|
1133
|
-
if (ctx?.db?.query && typeof ctx.db.query === "function") {
|
|
1134
|
-
try {
|
|
1135
|
-
allTopics = await ctx.db.query("topics").collect();
|
|
1136
|
-
} catch {
|
|
1137
|
-
allTopics = [];
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
|
|
1141
|
-
allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
|
|
1142
|
-
}
|
|
1143
|
-
return allTopics.filter(
|
|
1144
|
-
(topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
|
|
1145
|
-
).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
|
-
// ../access-control/src/projectGrantsBridge.ts
|
|
1149
|
-
var PROJECT_GRANT_STATUSES = ["active", "revoked", "expired"];
|
|
1150
|
-
function normalizeString(value) {
|
|
1151
|
-
if (typeof value !== "string") {
|
|
1152
|
-
return;
|
|
1153
|
-
}
|
|
1154
|
-
const trimmed = value.trim();
|
|
1155
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
1156
|
-
}
|
|
1157
|
-
async function resolveGrantScopeIds(ctx, args) {
|
|
1158
|
-
const topicId = normalizeString(args.topicId);
|
|
1159
|
-
const projectId = normalizeString(args.projectId);
|
|
1160
|
-
for (const scopeId of [topicId, projectId]) {
|
|
1161
|
-
if (!scopeId) {
|
|
1162
|
-
continue;
|
|
1163
|
-
}
|
|
1164
|
-
try {
|
|
1165
|
-
const overlay = await resolveTopicProjectOverlay(ctx, scopeId, {
|
|
1166
|
-
idMode: "legacy",
|
|
1167
|
-
projectLikeOnly: false
|
|
1168
|
-
});
|
|
1169
|
-
if (overlay) {
|
|
1170
|
-
return {
|
|
1171
|
-
topicId: normalizeString(overlay.topicId) ?? topicId,
|
|
1172
|
-
projectId: normalizeString(overlay.projectId) ?? projectId ?? scopeId
|
|
1173
|
-
};
|
|
1174
|
-
}
|
|
1175
|
-
} catch {
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
return { topicId, projectId };
|
|
1179
|
-
}
|
|
1180
|
-
async function normalizeProjectGrantRow(ctx, row) {
|
|
1181
|
-
const scope = await resolveGrantScopeIds(ctx, {
|
|
1182
|
-
topicId: row.topicId,
|
|
1183
|
-
projectId: row.projectId
|
|
1184
|
-
});
|
|
1185
|
-
return {
|
|
1186
|
-
...row,
|
|
1187
|
-
...scope.topicId ? { topicId: scope.topicId } : {},
|
|
1188
|
-
...scope.projectId ?? scope.topicId ? { projectId: scope.projectId ?? scope.topicId } : {}
|
|
1189
|
-
};
|
|
1190
|
-
}
|
|
1191
|
-
async function normalizeProjectGrantRows(ctx, rows) {
|
|
1192
|
-
return await Promise.all(rows.map((row) => normalizeProjectGrantRow(ctx, row)));
|
|
1193
|
-
}
|
|
1194
|
-
async function listProjectGrantsByPrincipal(ctx, principalId) {
|
|
1195
|
-
const rows = await Promise.all(
|
|
1196
|
-
PROJECT_GRANT_STATUSES.map(
|
|
1197
|
-
(status) => ctx.db.query("projectGrants").withIndex(
|
|
1198
|
-
"by_principal_status",
|
|
1199
|
-
(q) => q.eq("principalId", principalId).eq("status", status)
|
|
1200
|
-
).collect()
|
|
1201
|
-
)
|
|
1202
|
-
);
|
|
1203
|
-
return await normalizeProjectGrantRows(ctx, rows.flat());
|
|
1204
|
-
}
|
|
1205
|
-
async function listProjectGrantsByGroup(ctx, groupId) {
|
|
1206
|
-
const rows = await Promise.all(
|
|
1207
|
-
PROJECT_GRANT_STATUSES.map(
|
|
1208
|
-
(status) => ctx.db.query("projectGrants").withIndex(
|
|
1209
|
-
"by_group_status",
|
|
1210
|
-
(q) => q.eq("groupId", groupId).eq("status", status)
|
|
1211
|
-
).collect()
|
|
1212
|
-
)
|
|
1213
|
-
);
|
|
1214
|
-
return await normalizeProjectGrantRows(ctx, rows.flat());
|
|
1215
|
-
}
|
|
1216
|
-
function buildScopeMatchers(inputScopeId, resolved) {
|
|
1217
|
-
return new Set(
|
|
1218
|
-
[inputScopeId, resolved.topicId, resolved.projectId].map((value) => normalizeString(value)).filter((value) => Boolean(value))
|
|
1219
|
-
);
|
|
1220
|
-
}
|
|
1221
|
-
function matchesResolvedScope(row, scopeIds) {
|
|
1222
|
-
const rowTopicId = normalizeString(row.topicId);
|
|
1223
|
-
const rowProjectId = normalizeString(row.projectId);
|
|
1224
|
-
return rowTopicId !== void 0 && scopeIds.has(rowTopicId) || rowProjectId !== void 0 && scopeIds.has(rowProjectId);
|
|
1225
|
-
}
|
|
1226
|
-
async function bridgeListProjectGrantsByTopicAndPrincipal(ctx, topicId, principalId) {
|
|
1227
|
-
const resolved = await resolveGrantScopeIds(ctx, { topicId });
|
|
1228
|
-
const scopeIds = buildScopeMatchers(topicId, resolved);
|
|
1229
|
-
const rows = await listProjectGrantsByPrincipal(ctx, principalId);
|
|
1230
|
-
return rows.filter((row) => matchesResolvedScope(row, scopeIds));
|
|
1231
|
-
}
|
|
1232
|
-
async function bridgeListProjectGrantsByTopicAndGroup(ctx, topicId, groupId) {
|
|
1233
|
-
const resolved = await resolveGrantScopeIds(ctx, { topicId });
|
|
1234
|
-
const scopeIds = buildScopeMatchers(topicId, resolved);
|
|
1235
|
-
const rows = await listProjectGrantsByGroup(ctx, groupId);
|
|
1236
|
-
return rows.filter((row) => matchesResolvedScope(row, scopeIds));
|
|
1237
|
-
}
|
|
1238
|
-
async function bridgeListProjectGrantsByPrincipalStatus(ctx, principalId, status) {
|
|
1239
|
-
const rows = await listProjectGrantsByPrincipal(ctx, principalId);
|
|
1240
|
-
return rows.filter((row) => row.status === status);
|
|
1241
|
-
}
|
|
1242
|
-
async function bridgeListProjectGrantsByGroupStatus(ctx, groupId, status) {
|
|
1243
|
-
const rows = await listProjectGrantsByGroup(ctx, groupId);
|
|
1244
|
-
return rows.filter((row) => row.status === status);
|
|
1245
|
-
}
|
|
1246
|
-
async function bridgeInsertProjectGrant(ctx, value) {
|
|
1247
|
-
const resolved = await resolveGrantScopeIds(ctx, value);
|
|
1248
|
-
return await ctx.db.insert("projectGrants", {
|
|
1249
|
-
...value,
|
|
1250
|
-
...resolved.topicId ? { topicId: resolved.topicId } : {},
|
|
1251
|
-
...resolved.projectId ?? resolved.topicId ? { projectId: resolved.projectId ?? resolved.topicId } : {}
|
|
1252
|
-
});
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
|
-
// ../access-control/src/resolvers.ts
|
|
1256
|
-
async function findUserByClerkId(ctx, clerkId) {
|
|
1257
|
-
const normalizedClerkId = clerkId.trim();
|
|
1258
|
-
if (!normalizedClerkId) {
|
|
1259
|
-
return null;
|
|
1260
|
-
}
|
|
1261
|
-
if (typeof ctx.runQuery === "function") {
|
|
1262
|
-
try {
|
|
1263
|
-
const bridgedUser = await ctx.runQuery(api.users.getUserByClerkId, {
|
|
1264
|
-
clerkId: normalizedClerkId
|
|
1265
|
-
});
|
|
1266
|
-
if (bridgedUser) {
|
|
1267
|
-
return bridgedUser;
|
|
1268
|
-
}
|
|
1269
|
-
} catch {
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
try {
|
|
1273
|
-
const users = await ctx.db.query("users").collect();
|
|
1274
|
-
return users.find((user) => String(user.clerkId ?? "") === normalizedClerkId) ?? null;
|
|
1275
|
-
} catch {
|
|
1276
|
-
return null;
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
1279
|
-
async function findUserByPrincipalId(ctx, principalId) {
|
|
1280
|
-
const normalizedPrincipalId = principalId.trim();
|
|
1281
|
-
if (!normalizedPrincipalId) {
|
|
1282
|
-
return null;
|
|
1283
|
-
}
|
|
1284
|
-
try {
|
|
1285
|
-
const users = await ctx.db.query("users").collect();
|
|
1286
|
-
return users.find(
|
|
1287
|
-
(user) => String(user.defaultPrincipalId ?? "") === normalizedPrincipalId
|
|
1288
|
-
) ?? null;
|
|
1289
|
-
} catch {
|
|
1290
|
-
return null;
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
async function findAgentByPrincipalId(ctx, principalId) {
|
|
1294
|
-
const normalizedPrincipalId = principalId.trim();
|
|
1295
|
-
if (!normalizedPrincipalId) {
|
|
1296
|
-
return null;
|
|
1297
|
-
}
|
|
1298
|
-
if (typeof ctx.runQuery === "function") {
|
|
1299
|
-
try {
|
|
1300
|
-
const bridgedAgent = await ctx.runQuery(
|
|
1301
|
-
api.agents.getAgentByPrincipalId,
|
|
1302
|
-
{
|
|
1303
|
-
principalId: normalizedPrincipalId
|
|
1304
|
-
}
|
|
1305
|
-
);
|
|
1306
|
-
if (bridgedAgent) {
|
|
1307
|
-
return bridgedAgent;
|
|
1308
|
-
}
|
|
1309
|
-
} catch {
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
try {
|
|
1313
|
-
const agents = await ctx.db.query("agents").collect();
|
|
1314
|
-
return agents.find(
|
|
1315
|
-
(agent) => String(agent.principalId ?? "") === normalizedPrincipalId
|
|
1316
|
-
) ?? null;
|
|
1317
|
-
} catch {
|
|
1318
|
-
return null;
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
function defaultResolvers() {
|
|
1322
|
-
return {
|
|
1323
|
-
async getProject(ctx, topicId) {
|
|
1324
|
-
return await resolveTopicProjectOverlay(ctx, topicId, {
|
|
1325
|
-
idMode: "legacy",
|
|
1326
|
-
projectLikeOnly: false
|
|
1327
|
-
});
|
|
1328
|
-
},
|
|
1329
|
-
async listTopics(ctx) {
|
|
1330
|
-
return await listTopicProjectOverlays(ctx, { idMode: "legacy" });
|
|
1331
|
-
},
|
|
1332
|
-
async listTopicsByOwner(ctx, ownerId) {
|
|
1333
|
-
const topics = await listTopicProjectOverlays(ctx, { idMode: "legacy" });
|
|
1334
|
-
return topics.filter((topic) => topic.ownerId === ownerId);
|
|
1335
|
-
},
|
|
1336
|
-
async listTopicsByVisibility(ctx, visibility) {
|
|
1337
|
-
const topics = await listTopicProjectOverlays(ctx, { idMode: "legacy" });
|
|
1338
|
-
return topics.filter((topic) => topic.visibility === visibility);
|
|
1339
|
-
},
|
|
1340
|
-
async listProjectGrantsByProjectAndPrincipal(ctx, topicId, principalId) {
|
|
1341
|
-
return await bridgeListProjectGrantsByTopicAndPrincipal(
|
|
1342
|
-
ctx,
|
|
1343
|
-
topicId,
|
|
1344
|
-
principalId
|
|
1345
|
-
);
|
|
1346
|
-
},
|
|
1347
|
-
async listProjectGrantsByProjectAndGroup(ctx, topicId, groupId) {
|
|
1348
|
-
return await bridgeListProjectGrantsByTopicAndGroup(ctx, topicId, groupId);
|
|
1349
|
-
},
|
|
1350
|
-
async listProjectGrantsByPrincipalStatus(ctx, principalId, status) {
|
|
1351
|
-
return await bridgeListProjectGrantsByPrincipalStatus(
|
|
1352
|
-
ctx,
|
|
1353
|
-
principalId,
|
|
1354
|
-
status
|
|
1355
|
-
);
|
|
1356
|
-
},
|
|
1357
|
-
async listProjectGrantsByGroupStatus(ctx, groupId, status) {
|
|
1358
|
-
return await bridgeListProjectGrantsByGroupStatus(ctx, groupId, status);
|
|
1359
|
-
},
|
|
1360
|
-
async insertProjectGrant(ctx, value) {
|
|
1361
|
-
return await bridgeInsertProjectGrant(ctx, value);
|
|
1362
|
-
},
|
|
1363
|
-
async getAgentByPrincipalId(ctx, principalId) {
|
|
1364
|
-
return await findAgentByPrincipalId(ctx, principalId);
|
|
1365
|
-
},
|
|
1366
|
-
async getUserByClerkId(ctx, clerkId) {
|
|
1367
|
-
return await findUserByClerkId(ctx, clerkId);
|
|
1368
|
-
},
|
|
1369
|
-
async getUserByPrincipalId(ctx, principalId) {
|
|
1370
|
-
return await findUserByPrincipalId(ctx, principalId);
|
|
1371
|
-
}
|
|
1372
|
-
};
|
|
1373
|
-
}
|
|
1374
|
-
var resolverOverrides = {};
|
|
1375
|
-
function resolveAccessControlAppResolvers(_ctx) {
|
|
1376
|
-
return {
|
|
1377
|
-
...defaultResolvers(),
|
|
1378
|
-
...resolverOverrides
|
|
1379
|
-
};
|
|
1380
|
-
}
|
|
1381
|
-
|
|
1382
|
-
// ../access-control/src/principalContext.ts
|
|
1383
|
-
function requireCanonicalResolvedUser(user, clerkId) {
|
|
1384
|
-
const resolved = user;
|
|
1385
|
-
if (!resolved) {
|
|
1386
|
-
throw new Error(
|
|
1387
|
-
`[AccessControl] Canonical user identity required for ${clerkId}. Sync users.upsertUser before user-bound access checks.`
|
|
1388
|
-
);
|
|
1389
|
-
}
|
|
1390
|
-
const { mcRole, defaultTenantId, defaultWorkspaceId, defaultPrincipalId } = resolved;
|
|
1391
|
-
if (mcRole !== "platform_admin" && mcRole !== "tenant_admin" && mcRole !== "workspace_admin" && mcRole !== "editor" && mcRole !== "viewer" && mcRole !== "auditor" && mcRole !== "service_agent") {
|
|
1392
|
-
throw new Error(
|
|
1393
|
-
`[AccessControl] Canonical MC role required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
|
|
1394
|
-
);
|
|
1395
|
-
}
|
|
1396
|
-
if (typeof defaultTenantId !== "string" || defaultTenantId.trim().length === 0) {
|
|
1397
|
-
throw new Error(
|
|
1398
|
-
`[AccessControl] Canonical home tenant required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
|
|
1399
|
-
);
|
|
1400
|
-
}
|
|
1401
|
-
if (typeof defaultWorkspaceId !== "string" || defaultWorkspaceId.trim().length === 0) {
|
|
1402
|
-
throw new Error(
|
|
1403
|
-
`[AccessControl] Canonical home workspace required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
|
|
1404
|
-
);
|
|
1405
|
-
}
|
|
1406
|
-
if (typeof defaultPrincipalId !== "string" || defaultPrincipalId.trim().length === 0) {
|
|
1407
|
-
throw new Error(
|
|
1408
|
-
`[AccessControl] Canonical federated principal required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
|
|
1409
|
-
);
|
|
1410
|
-
}
|
|
1411
|
-
return {
|
|
1412
|
-
mcRole,
|
|
1413
|
-
defaultTenantId: defaultTenantId.trim(),
|
|
1414
|
-
defaultWorkspaceId: defaultWorkspaceId.trim(),
|
|
1415
|
-
defaultPrincipalId: defaultPrincipalId.trim()
|
|
1416
|
-
};
|
|
1417
|
-
}
|
|
1418
|
-
function isPrincipalIdInput(value) {
|
|
1419
|
-
return value.startsWith("user:") || value.startsWith("group:") || value.startsWith("service:") || value.startsWith("agent:") || value.startsWith("external_viewer:");
|
|
1420
|
-
}
|
|
1421
|
-
async function resolveCanonicalUserRecord(ctx, actorId) {
|
|
1422
|
-
const normalizedActorId = actorId.trim();
|
|
1423
|
-
const clerkId = isPrincipalIdInput(normalizedActorId) && normalizedActorId.startsWith("user:") ? normalizedActorId.slice("user:".length) : normalizedActorId;
|
|
1424
|
-
const resolvers = resolveAccessControlAppResolvers();
|
|
1425
|
-
const resolvedByClerkId = await resolvers.getUserByClerkId(ctx, clerkId);
|
|
1426
|
-
if (resolvedByClerkId) {
|
|
1427
|
-
return {
|
|
1428
|
-
resolvedUser: resolvedByClerkId,
|
|
1429
|
-
clerkId,
|
|
1430
|
-
contextClerkId: clerkId
|
|
1431
|
-
};
|
|
1432
|
-
}
|
|
1433
|
-
const resolvedByPrincipalId = await resolvers.getUserByPrincipalId(
|
|
1434
|
-
ctx,
|
|
1435
|
-
normalizedActorId
|
|
1436
|
-
);
|
|
1437
|
-
return {
|
|
1438
|
-
resolvedUser: resolvedByPrincipalId ?? null,
|
|
1439
|
-
clerkId,
|
|
1440
|
-
contextClerkId: normalizedActorId.startsWith("user:") && clerkId.length > 0 ? clerkId : normalizedActorId
|
|
1441
|
-
};
|
|
1442
|
-
}
|
|
1443
|
-
function uniqRoles(roles) {
|
|
1444
|
-
const roleSet = /* @__PURE__ */ new Set();
|
|
1445
|
-
for (const role of roles) {
|
|
1446
|
-
if (role === "platform_admin" || role === "tenant_admin" || role === "workspace_admin" || role === "editor" || role === "viewer" || role === "auditor" || role === "service_agent") {
|
|
1447
|
-
roleSet.add(role);
|
|
1448
|
-
}
|
|
1449
|
-
}
|
|
1450
|
-
return [...roleSet];
|
|
1451
|
-
}
|
|
1452
|
-
function normalizeGroupIds(value) {
|
|
1453
|
-
if (!Array.isArray(value)) {
|
|
1454
|
-
return [];
|
|
1455
|
-
}
|
|
1456
|
-
return [...new Set(
|
|
1457
|
-
value.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter(Boolean)
|
|
1458
|
-
)];
|
|
1459
|
-
}
|
|
1460
|
-
function requireServiceAgentUser(user, actorId) {
|
|
1461
|
-
const canonicalUser = requireCanonicalResolvedUser(user, actorId);
|
|
1462
|
-
if (canonicalUser.mcRole !== "service_agent") {
|
|
1463
|
-
throw new Error(
|
|
1464
|
-
`[AccessControl] Canonical service_agent identity required for ${actorId}. Sync users.upsertUser before agent-bound access checks.`
|
|
1465
|
-
);
|
|
1466
|
-
}
|
|
1467
|
-
return canonicalUser;
|
|
1468
|
-
}
|
|
1469
|
-
function requireCanonicalResolvedAgent(agent, actorId) {
|
|
1470
|
-
const resolved = agent;
|
|
1471
|
-
if (!resolved) {
|
|
1472
|
-
throw new Error(
|
|
1473
|
-
`[AccessControl] Agent "${actorId}" not found in agents or users table.`
|
|
1474
|
-
);
|
|
1475
|
-
}
|
|
1476
|
-
if (typeof resolved.principalId !== "string" || resolved.principalId.trim().length === 0) {
|
|
1477
|
-
throw new Error(
|
|
1478
|
-
`[AccessControl] Canonical agent principalId required for ${actorId}.`
|
|
1479
|
-
);
|
|
1480
|
-
}
|
|
1481
|
-
if (typeof resolved.tenantId !== "string" || resolved.tenantId.trim().length === 0) {
|
|
1482
|
-
throw new Error(
|
|
1483
|
-
`[AccessControl] Canonical home tenant required for ${actorId}.`
|
|
1484
|
-
);
|
|
1485
|
-
}
|
|
1486
|
-
if (typeof resolved.workspaceId !== "string" || resolved.workspaceId.trim().length === 0) {
|
|
1487
|
-
throw new Error(
|
|
1488
|
-
`[AccessControl] Canonical home workspace required for ${actorId}.`
|
|
1489
|
-
);
|
|
1490
|
-
}
|
|
1491
|
-
return {
|
|
1492
|
-
principalId: resolved.principalId.trim(),
|
|
1493
|
-
tenantId: resolved.tenantId.trim(),
|
|
1494
|
-
workspaceId: resolved.workspaceId.trim(),
|
|
1495
|
-
roles: uniqRoles(Array.isArray(resolved.roles) ? resolved.roles : []) ?? ["service_agent"],
|
|
1496
|
-
groupIds: normalizeGroupIds(resolved.groupIds)
|
|
1497
|
-
};
|
|
1498
|
-
}
|
|
1499
|
-
async function resolvePrincipalContext(ctx, actorId) {
|
|
1500
|
-
if (actorId.startsWith("agent:")) {
|
|
1501
|
-
const resolvers = resolveAccessControlAppResolvers();
|
|
1502
|
-
const resolvedAgent = await resolvers.getAgentByPrincipalId(ctx, actorId);
|
|
1503
|
-
if (resolvedAgent) {
|
|
1504
|
-
const agent = requireCanonicalResolvedAgent(
|
|
1505
|
-
resolvedAgent,
|
|
1506
|
-
actorId
|
|
1507
|
-
);
|
|
1508
|
-
return {
|
|
1509
|
-
principalId: agent.principalId,
|
|
1510
|
-
principalType: "service",
|
|
1511
|
-
clerkId: actorId,
|
|
1512
|
-
tenantId: agent.tenantId,
|
|
1513
|
-
workspaceId: agent.workspaceId,
|
|
1514
|
-
roles: agent.roles.length > 0 ? agent.roles : ["service_agent"],
|
|
1515
|
-
groupIds: agent.groupIds,
|
|
1516
|
-
isPlatformAdmin: false,
|
|
1517
|
-
isTenantAdmin: false,
|
|
1518
|
-
isWorkspaceAdmin: false,
|
|
1519
|
-
isSystemFallback: false
|
|
1520
|
-
};
|
|
1521
|
-
}
|
|
1522
|
-
const resolvedUser2 = await resolvers.getUserByClerkId(
|
|
1523
|
-
ctx,
|
|
1524
|
-
actorId
|
|
1525
|
-
);
|
|
1526
|
-
if (!resolvedUser2) {
|
|
1527
|
-
throw new Error(
|
|
1528
|
-
`[AccessControl] Agent "${actorId}" not found in agents or users table.`
|
|
1529
|
-
);
|
|
1530
|
-
}
|
|
1531
|
-
const user2 = requireServiceAgentUser(
|
|
1532
|
-
resolvedUser2,
|
|
1533
|
-
actorId
|
|
1534
|
-
);
|
|
1535
|
-
console.warn(
|
|
1536
|
-
`[AccessControl] Deprecated legacy service-agent fallback for ${actorId}; migrate this principal into identity.agents.`
|
|
1537
|
-
);
|
|
1538
|
-
return {
|
|
1539
|
-
principalId: user2.defaultPrincipalId,
|
|
1540
|
-
principalType: "service",
|
|
1541
|
-
clerkId: actorId,
|
|
1542
|
-
tenantId: user2.defaultTenantId,
|
|
1543
|
-
workspaceId: user2.defaultWorkspaceId,
|
|
1544
|
-
roles: ["service_agent"],
|
|
1545
|
-
groupIds: normalizeGroupIds(resolvedUser2?.principalGroupIds),
|
|
1546
|
-
isPlatformAdmin: false,
|
|
1547
|
-
isTenantAdmin: false,
|
|
1548
|
-
isWorkspaceAdmin: false,
|
|
1549
|
-
isSystemFallback: false
|
|
1550
|
-
};
|
|
1551
|
-
}
|
|
1552
|
-
const {
|
|
1553
|
-
resolvedUser,
|
|
1554
|
-
contextClerkId
|
|
1555
|
-
} = await resolveCanonicalUserRecord(ctx, actorId);
|
|
1556
|
-
const user = requireCanonicalResolvedUser(
|
|
1557
|
-
resolvedUser,
|
|
1558
|
-
contextClerkId
|
|
1559
|
-
);
|
|
1560
|
-
if (!user.defaultPrincipalId) {
|
|
1561
|
-
throw new Error(
|
|
1562
|
-
`[AccessControl] Canonical federated principal required for ${contextClerkId}. Re-sync Master Control identity before user-bound access checks.`
|
|
1563
|
-
);
|
|
1564
|
-
}
|
|
1565
|
-
if (user.mcRole === "service_agent") {
|
|
1566
|
-
return {
|
|
1567
|
-
principalId: user.defaultPrincipalId,
|
|
1568
|
-
principalType: "service",
|
|
1569
|
-
clerkId: contextClerkId,
|
|
1570
|
-
tenantId: user.defaultTenantId,
|
|
1571
|
-
workspaceId: user.defaultWorkspaceId,
|
|
1572
|
-
roles: ["service_agent"],
|
|
1573
|
-
groupIds: normalizeGroupIds(resolvedUser?.principalGroupIds),
|
|
1574
|
-
isPlatformAdmin: false,
|
|
1575
|
-
isTenantAdmin: false,
|
|
1576
|
-
isWorkspaceAdmin: false,
|
|
1577
|
-
isSystemFallback: false
|
|
1578
|
-
};
|
|
1579
|
-
}
|
|
1580
|
-
const principalId = user.defaultPrincipalId;
|
|
1581
|
-
const effectiveRole = user.mcRole;
|
|
1582
|
-
const roles = effectiveRole === "platform_admin" ? ["platform_admin", "tenant_admin"] : effectiveRole === "tenant_admin" ? ["tenant_admin"] : [effectiveRole];
|
|
1583
|
-
const tenantId = user.defaultTenantId;
|
|
1584
|
-
const workspaceId = user.defaultWorkspaceId;
|
|
1585
|
-
const isPlatformAdmin = effectiveRole === "platform_admin";
|
|
1586
|
-
return {
|
|
1587
|
-
principalId,
|
|
1588
|
-
principalType: "user",
|
|
1589
|
-
clerkId: contextClerkId,
|
|
1590
|
-
tenantId,
|
|
1591
|
-
workspaceId,
|
|
1592
|
-
roles: uniqRoles(roles),
|
|
1593
|
-
groupIds: normalizeGroupIds(resolvedUser?.principalGroupIds),
|
|
1594
|
-
isPlatformAdmin,
|
|
1595
|
-
isTenantAdmin: isPlatformAdmin || effectiveRole === "tenant_admin",
|
|
1596
|
-
isWorkspaceAdmin: isPlatformAdmin || effectiveRole === "tenant_admin" || effectiveRole === "workspace_admin",
|
|
1597
|
-
isSystemFallback: false
|
|
1598
|
-
};
|
|
1599
|
-
}
|
|
1600
|
-
|
|
1601
|
-
// ../access-control/src/access.ts
|
|
1602
|
-
function isTopicInPrincipalTenant(topic, principalTenantId) {
|
|
1603
|
-
if (!topic.tenantId) {
|
|
1604
|
-
return false;
|
|
1605
|
-
}
|
|
1606
|
-
if (!principalTenantId) {
|
|
1607
|
-
return false;
|
|
1608
|
-
}
|
|
1609
|
-
return String(topic.tenantId) === String(principalTenantId);
|
|
1610
|
-
}
|
|
1611
|
-
function isTopicInPrincipalWorkspace(topic, principalWorkspaceId) {
|
|
1612
|
-
if (!topic.workspaceId) {
|
|
1613
|
-
return false;
|
|
1614
|
-
}
|
|
1615
|
-
if (!principalWorkspaceId) {
|
|
1616
|
-
return false;
|
|
1617
|
-
}
|
|
1618
|
-
return String(topic.workspaceId) === String(principalWorkspaceId);
|
|
1619
|
-
}
|
|
1620
|
-
function isLegacyUnscopedTopic(topic) {
|
|
1621
|
-
return !topic.tenantId || !topic.workspaceId;
|
|
1622
|
-
}
|
|
1623
|
-
function isGrantScopeAlignedToTopic(topic, grant) {
|
|
1624
|
-
if (topic.tenantId && grant.tenantId && String(topic.tenantId) !== String(grant.tenantId)) {
|
|
1625
|
-
return false;
|
|
1626
|
-
}
|
|
1627
|
-
if (topic.workspaceId && grant.workspaceId && String(topic.workspaceId) !== String(grant.workspaceId)) {
|
|
1628
|
-
return false;
|
|
1629
|
-
}
|
|
1630
|
-
return true;
|
|
1631
|
-
}
|
|
1632
|
-
function isGrantSourceAllowedForVisibility(visibility, source) {
|
|
1633
|
-
if (source !== "external_share") {
|
|
1634
|
-
return true;
|
|
1635
|
-
}
|
|
1636
|
-
return visibility === "external" || visibility === "public";
|
|
1637
|
-
}
|
|
1638
|
-
function isGrantActive(grant) {
|
|
1639
|
-
if (grant.status !== "active") {
|
|
1640
|
-
return false;
|
|
1641
|
-
}
|
|
1642
|
-
if (grant.expiresAt !== void 0 && grant.expiresAt <= Date.now()) {
|
|
1643
|
-
return false;
|
|
1644
|
-
}
|
|
1645
|
-
return true;
|
|
1646
|
-
}
|
|
1647
|
-
async function hasPrincipalGrant(ctx, args) {
|
|
1648
|
-
const grants = await resolveAccessControlAppResolvers().listProjectGrantsByProjectAndPrincipal(
|
|
1649
|
-
ctx,
|
|
1650
|
-
args.topic._id,
|
|
1651
|
-
args.principalId
|
|
1652
|
-
);
|
|
1653
|
-
if (grants.some(
|
|
1654
|
-
(grant) => isGrantActive(grant) && isGrantScopeAlignedToTopic(args.topic, grant) && isGrantSourceAllowedForVisibility(
|
|
1655
|
-
args.topic.visibility,
|
|
1656
|
-
grant.source
|
|
1657
|
-
) && (!args.principalIsExternal || args.topic.visibility === "public" || grant.source === "external_share")
|
|
1658
|
-
)) {
|
|
1659
|
-
return true;
|
|
1660
|
-
}
|
|
1661
|
-
return false;
|
|
1662
|
-
}
|
|
1663
|
-
async function hasGroupGrant(ctx, args) {
|
|
1664
|
-
if (args.groupIds.length === 0) {
|
|
1665
|
-
return false;
|
|
1666
|
-
}
|
|
1667
|
-
for (const groupId of args.groupIds) {
|
|
1668
|
-
const grants = await resolveAccessControlAppResolvers().listProjectGrantsByProjectAndGroup(ctx, args.topic._id, groupId);
|
|
1669
|
-
if (grants.some(
|
|
1670
|
-
(grant) => isGrantActive(grant) && isGrantScopeAlignedToTopic(args.topic, grant) && isGrantSourceAllowedForVisibility(
|
|
1671
|
-
args.topic.visibility,
|
|
1672
|
-
grant.source
|
|
1673
|
-
)
|
|
1674
|
-
)) {
|
|
1675
|
-
return true;
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1678
|
-
return false;
|
|
1679
|
-
}
|
|
1680
|
-
function isExternalPrincipal(_ctx, _args) {
|
|
1681
|
-
return false;
|
|
1682
|
-
}
|
|
1683
|
-
async function evaluateTopicAccessDetailed(ctx, args) {
|
|
1684
|
-
if (args.legacyUserId) {
|
|
1685
|
-
return {
|
|
1686
|
-
hasAccess: true,
|
|
1687
|
-
isAdmin: false,
|
|
1688
|
-
isOwner: false,
|
|
1689
|
-
isShared: false,
|
|
1690
|
-
hasGrant: true,
|
|
1691
|
-
isFirmVisible: true,
|
|
1692
|
-
isExternalVisible: false,
|
|
1693
|
-
isPublicVisible: false,
|
|
1694
|
-
isTenantScopeMatch: true,
|
|
1695
|
-
isWorkspaceScopeMatch: true,
|
|
1696
|
-
isPrincipalExternal: false
|
|
1697
|
-
};
|
|
1698
|
-
}
|
|
1699
|
-
const topic = await resolveAccessControlAppResolvers().getProject(
|
|
1700
|
-
ctx,
|
|
1701
|
-
args.topicId
|
|
1702
|
-
);
|
|
1703
|
-
if (!topic) {
|
|
1704
|
-
return {
|
|
1705
|
-
hasAccess: false,
|
|
1706
|
-
isAdmin: false,
|
|
1707
|
-
isOwner: false,
|
|
1708
|
-
isShared: false,
|
|
1709
|
-
hasGrant: false,
|
|
1710
|
-
isFirmVisible: false,
|
|
1711
|
-
isExternalVisible: false,
|
|
1712
|
-
isPublicVisible: false,
|
|
1713
|
-
isTenantScopeMatch: false,
|
|
1714
|
-
isWorkspaceScopeMatch: false,
|
|
1715
|
-
isPrincipalExternal: false
|
|
1716
|
-
};
|
|
1717
|
-
}
|
|
1718
|
-
const { principalContext, legacyUserId } = args;
|
|
1719
|
-
const userIsAdmin = principalContext.isPlatformAdmin;
|
|
1720
|
-
const isOwner = topic.ownerId === legacyUserId;
|
|
1721
|
-
const isShared = (topic.sharedWith ?? []).includes(legacyUserId);
|
|
1722
|
-
const principalIsExternal = await isExternalPrincipal(ctx, {
|
|
1723
|
-
groupIds: principalContext.groupIds,
|
|
1724
|
-
topicTenantId: topic.tenantId,
|
|
1725
|
-
topicWorkspaceId: topic.workspaceId
|
|
1726
|
-
});
|
|
1727
|
-
const hasPrincipalGrantResult = await hasPrincipalGrant(ctx, {
|
|
1728
|
-
topic,
|
|
1729
|
-
principalId: principalContext.principalId,
|
|
1730
|
-
principalIsExternal
|
|
1731
|
-
});
|
|
1732
|
-
const hasGroupGrantResult = await hasGroupGrant(ctx, {
|
|
1733
|
-
topic,
|
|
1734
|
-
groupIds: principalContext.groupIds
|
|
1735
|
-
});
|
|
1736
|
-
const hasGrant = isShared || hasPrincipalGrantResult || hasGroupGrantResult;
|
|
1737
|
-
const legacyUnscoped = isLegacyUnscopedTopic(topic);
|
|
1738
|
-
const tenantScopeMatch = isTopicInPrincipalTenant(
|
|
1739
|
-
topic,
|
|
1740
|
-
principalContext.tenantId
|
|
1741
|
-
);
|
|
1742
|
-
const workspaceScopeMatch = isTopicInPrincipalWorkspace(
|
|
1743
|
-
topic,
|
|
1744
|
-
principalContext.workspaceId
|
|
1745
|
-
);
|
|
1746
|
-
const isPublicVisible = topic.visibility === "public";
|
|
1747
|
-
const isFirmVisible = topic.visibility === "firm" && !legacyUnscoped && tenantScopeMatch && workspaceScopeMatch && !principalIsExternal;
|
|
1748
|
-
const hasScopedGrant = hasGrant && (legacyUnscoped || tenantScopeMatch && workspaceScopeMatch);
|
|
1749
|
-
const isExternalVisible = topic.visibility === "external" && hasScopedGrant;
|
|
1750
|
-
const hasAccess = userIsAdmin || isOwner || hasScopedGrant || isPublicVisible || isFirmVisible;
|
|
1751
|
-
return {
|
|
1752
|
-
hasAccess,
|
|
1753
|
-
isAdmin: userIsAdmin,
|
|
1754
|
-
isOwner,
|
|
1755
|
-
isShared,
|
|
1756
|
-
hasGrant,
|
|
1757
|
-
isFirmVisible,
|
|
1758
|
-
isExternalVisible,
|
|
1759
|
-
isPublicVisible,
|
|
1760
|
-
isTenantScopeMatch: tenantScopeMatch,
|
|
1761
|
-
isWorkspaceScopeMatch: workspaceScopeMatch,
|
|
1762
|
-
isPrincipalExternal: principalIsExternal
|
|
1763
|
-
};
|
|
1764
|
-
}
|
|
1765
|
-
async function checkTopicAccessDetailed(ctx, topicId, userId) {
|
|
1766
|
-
const principalContext = await resolvePrincipalContext(ctx, userId);
|
|
1767
|
-
return evaluateTopicAccessDetailed(ctx, {
|
|
1768
|
-
topicId,
|
|
1769
|
-
legacyUserId: userId,
|
|
1770
|
-
principalContext
|
|
1771
|
-
});
|
|
1772
|
-
}
|
|
1773
|
-
async function checkTopicAccess(ctx, topicId, userId) {
|
|
1774
|
-
const result = await checkTopicAccessDetailed(ctx, topicId, userId);
|
|
1775
|
-
return result.hasAccess;
|
|
1776
|
-
}
|
|
1777
|
-
async function checkScopeAccess(ctx, scopeId, userId) {
|
|
1778
|
-
try {
|
|
1779
|
-
const topic = await ctx.db.get(scopeId);
|
|
1780
|
-
if (topic && topic.name !== void 0 && topic.type !== void 0) {
|
|
1781
|
-
return true;
|
|
1782
|
-
}
|
|
1783
|
-
} catch {
|
|
1784
|
-
}
|
|
1785
|
-
try {
|
|
1786
|
-
return await checkTopicAccess(ctx, scopeId, userId);
|
|
1787
|
-
} catch {
|
|
1788
|
-
return false;
|
|
1789
|
-
}
|
|
1790
|
-
}
|
|
1791
|
-
async function requireTopicAccess(ctx, topicId, userId) {
|
|
1792
|
-
const hasAccess = await checkTopicAccess(ctx, topicId, userId);
|
|
1793
|
-
if (!hasAccess) {
|
|
1794
|
-
throw new Error(
|
|
1795
|
-
"Access denied: You don't have permission to access this topic"
|
|
1796
|
-
);
|
|
1797
|
-
}
|
|
1798
|
-
}
|
|
1799
|
-
var checkProjectAccess = checkTopicAccess;
|
|
1800
|
-
var requireProjectAccess = requireTopicAccess;
|
|
1801
|
-
async function getAccessibleTopicIds(ctx, userId) {
|
|
1802
|
-
const principalContext = await resolvePrincipalContext(ctx, userId);
|
|
1803
|
-
if (principalContext.isPlatformAdmin) {
|
|
1804
|
-
const allTopics2 = await resolveAccessControlAppResolvers().listTopics(ctx);
|
|
1805
|
-
return new Set(allTopics2.map((topic) => topic._id));
|
|
1806
|
-
}
|
|
1807
|
-
const topicIds = /* @__PURE__ */ new Set();
|
|
1808
|
-
const ownedTopics = await resolveAccessControlAppResolvers().listTopicsByOwner(ctx, userId);
|
|
1809
|
-
for (const topic of ownedTopics) {
|
|
1810
|
-
topicIds.add(topic._id);
|
|
1811
|
-
}
|
|
1812
|
-
const publicTopics = await resolveAccessControlAppResolvers().listTopicsByVisibility(ctx, "public");
|
|
1813
|
-
for (const topic of publicTopics) {
|
|
1814
|
-
topicIds.add(topic._id);
|
|
1815
|
-
}
|
|
1816
|
-
const principalIsExternal = await isExternalPrincipal(ctx, {
|
|
1817
|
-
groupIds: principalContext.groupIds,
|
|
1818
|
-
topicTenantId: principalContext.tenantId ?? void 0,
|
|
1819
|
-
topicWorkspaceId: principalContext.workspaceId ?? void 0
|
|
1820
|
-
});
|
|
1821
|
-
if (!principalIsExternal) {
|
|
1822
|
-
const firmTopics = await resolveAccessControlAppResolvers().listTopicsByVisibility(ctx, "firm");
|
|
1823
|
-
for (const topic of firmTopics) {
|
|
1824
|
-
if (isTopicInPrincipalTenant(topic, principalContext.tenantId) && isTopicInPrincipalWorkspace(topic, principalContext.workspaceId)) {
|
|
1825
|
-
topicIds.add(topic._id);
|
|
1826
|
-
}
|
|
1827
|
-
}
|
|
1828
|
-
}
|
|
1829
|
-
const directGrants = await resolveAccessControlAppResolvers().listProjectGrantsByPrincipalStatus(
|
|
1830
|
-
ctx,
|
|
1831
|
-
principalContext.principalId,
|
|
1832
|
-
"active"
|
|
1833
|
-
);
|
|
1834
|
-
for (const grant of directGrants) {
|
|
1835
|
-
if (!isGrantActive(grant)) {
|
|
1836
|
-
continue;
|
|
1837
|
-
}
|
|
1838
|
-
const topic = await resolveAccessControlAppResolvers().getProject(
|
|
1839
|
-
ctx,
|
|
1840
|
-
grant.projectId
|
|
1841
|
-
);
|
|
1842
|
-
if (!topic) {
|
|
1843
|
-
continue;
|
|
1844
|
-
}
|
|
1845
|
-
if (!isLegacyUnscopedTopic(topic)) {
|
|
1846
|
-
if (!isTopicInPrincipalTenant(topic, principalContext.tenantId)) {
|
|
1847
|
-
continue;
|
|
1848
|
-
}
|
|
1849
|
-
if (!isTopicInPrincipalWorkspace(topic, principalContext.workspaceId)) {
|
|
1850
|
-
continue;
|
|
1851
|
-
}
|
|
1852
|
-
}
|
|
1853
|
-
if (!isGrantScopeAlignedToTopic(topic, grant)) {
|
|
1854
|
-
continue;
|
|
1855
|
-
}
|
|
1856
|
-
if (!isGrantSourceAllowedForVisibility(topic.visibility, grant.source)) {
|
|
1857
|
-
continue;
|
|
1858
|
-
}
|
|
1859
|
-
if (principalIsExternal && topic.visibility !== "public" && grant.source !== "external_share") {
|
|
1860
|
-
continue;
|
|
1861
|
-
}
|
|
1862
|
-
topicIds.add(grant.projectId);
|
|
1863
|
-
}
|
|
1864
|
-
const allTopics = await resolveAccessControlAppResolvers().listTopics(ctx);
|
|
1865
|
-
for (const topic of allTopics) {
|
|
1866
|
-
if ((topic.sharedWith ?? []).includes(userId) && (isLegacyUnscopedTopic(topic) || isTopicInPrincipalTenant(topic, principalContext.tenantId) && isTopicInPrincipalWorkspace(topic, principalContext.workspaceId))) {
|
|
1867
|
-
topicIds.add(topic._id);
|
|
1868
|
-
}
|
|
1869
|
-
}
|
|
1870
|
-
return topicIds;
|
|
1871
|
-
}
|
|
1872
|
-
var getAccessibleProjectIds = getAccessibleTopicIds;
|
|
1873
|
-
var permissiveReturn = v.optional(v.any());
|
|
1874
|
-
var looseJsonObject = v.record(v.string(), v.any());
|
|
1875
|
-
var looseJsonArray = v.array(v.any());
|
|
1876
|
-
v.union(
|
|
1877
|
-
v.string(),
|
|
1878
|
-
v.number(),
|
|
1879
|
-
v.boolean(),
|
|
1880
|
-
v.null(),
|
|
1881
|
-
looseJsonObject,
|
|
1882
|
-
looseJsonArray
|
|
1883
|
-
);
|
|
1884
|
-
var api2 = anyApi;
|
|
34
|
+
var api = anyApi;
|
|
1885
35
|
componentsGeneric();
|
|
1886
36
|
var internal = anyApi;
|
|
1887
37
|
var internalAction = internalActionGeneric;
|
|
@@ -1899,48 +49,48 @@ __export(resolvers_exports, {
|
|
|
1899
49
|
});
|
|
1900
50
|
|
|
1901
51
|
// src/topicProjectOverlay.ts
|
|
1902
|
-
var
|
|
1903
|
-
function
|
|
52
|
+
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
53
|
+
function readNonEmptyString(value) {
|
|
1904
54
|
if (typeof value !== "string") {
|
|
1905
55
|
return;
|
|
1906
56
|
}
|
|
1907
57
|
const normalized = value.trim();
|
|
1908
58
|
return normalized.length > 0 ? normalized : void 0;
|
|
1909
59
|
}
|
|
1910
|
-
function
|
|
60
|
+
function readStringArray(value) {
|
|
1911
61
|
if (!Array.isArray(value)) {
|
|
1912
62
|
return [];
|
|
1913
63
|
}
|
|
1914
|
-
return value.map((entry) =>
|
|
64
|
+
return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
|
|
1915
65
|
}
|
|
1916
|
-
function
|
|
66
|
+
function readMetadata(topic) {
|
|
1917
67
|
return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
|
|
1918
68
|
}
|
|
1919
|
-
function
|
|
69
|
+
function readLegacyProjectId(value) {
|
|
1920
70
|
if (!value) {
|
|
1921
71
|
return;
|
|
1922
72
|
}
|
|
1923
|
-
return
|
|
73
|
+
return readNonEmptyString(value[LEGACY_SCOPE_FIELD]);
|
|
1924
74
|
}
|
|
1925
|
-
function
|
|
75
|
+
function coerceVisibility(value) {
|
|
1926
76
|
return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
|
|
1927
77
|
}
|
|
1928
|
-
function
|
|
78
|
+
function coerceStatus(value) {
|
|
1929
79
|
return value === "active" || value === "archived" || value === "watching" ? value : void 0;
|
|
1930
80
|
}
|
|
1931
|
-
function
|
|
1932
|
-
const explicit =
|
|
81
|
+
function mapProjectType(topic, metadata) {
|
|
82
|
+
const explicit = readNonEmptyString(metadata.projectType);
|
|
1933
83
|
if (explicit) {
|
|
1934
84
|
return explicit;
|
|
1935
85
|
}
|
|
1936
86
|
if (topic.type === "theme") {
|
|
1937
87
|
return "thematic";
|
|
1938
88
|
}
|
|
1939
|
-
return
|
|
89
|
+
return readNonEmptyString(topic.type) || "general";
|
|
1940
90
|
}
|
|
1941
|
-
function
|
|
1942
|
-
const metadata =
|
|
1943
|
-
return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" ||
|
|
91
|
+
function isProjectLikeTopic(topic) {
|
|
92
|
+
const metadata = readMetadata(topic);
|
|
93
|
+
return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
|
|
1944
94
|
}
|
|
1945
95
|
function isMissingLucernChildComponentError(error) {
|
|
1946
96
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1948,7 +98,7 @@ function isMissingLucernChildComponentError(error) {
|
|
|
1948
98
|
'Child component ComponentName(Identifier("lucern")) not found'
|
|
1949
99
|
) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
|
|
1950
100
|
}
|
|
1951
|
-
async function
|
|
101
|
+
async function resolveTopicDoc(ctx, scopeId) {
|
|
1952
102
|
if (ctx?.db && typeof ctx.db.get === "function") {
|
|
1953
103
|
try {
|
|
1954
104
|
const directTopic = await ctx.db.get(scopeId);
|
|
@@ -1962,7 +112,7 @@ async function resolveTopicDoc2(ctx, scopeId) {
|
|
|
1962
112
|
return null;
|
|
1963
113
|
}
|
|
1964
114
|
try {
|
|
1965
|
-
const topic = await ctx.runQuery(
|
|
115
|
+
const topic = await ctx.runQuery(api.topics.get, {
|
|
1966
116
|
id: String(scopeId)
|
|
1967
117
|
});
|
|
1968
118
|
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
@@ -1971,7 +121,7 @@ async function resolveTopicDoc2(ctx, scopeId) {
|
|
|
1971
121
|
} catch {
|
|
1972
122
|
}
|
|
1973
123
|
try {
|
|
1974
|
-
const topic = await ctx.runQuery(
|
|
124
|
+
const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
1975
125
|
projectId: String(scopeId)
|
|
1976
126
|
});
|
|
1977
127
|
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
@@ -1981,14 +131,14 @@ async function resolveTopicDoc2(ctx, scopeId) {
|
|
|
1981
131
|
}
|
|
1982
132
|
return null;
|
|
1983
133
|
}
|
|
1984
|
-
function
|
|
1985
|
-
const metadata =
|
|
134
|
+
function materializeTopicProjectOverlay(topic, idMode = "legacy") {
|
|
135
|
+
const metadata = readMetadata(topic);
|
|
1986
136
|
const topicId = String(topic._id);
|
|
1987
|
-
const legacyProjectId =
|
|
137
|
+
const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
|
|
1988
138
|
const storageProjectId = legacyProjectId || topicId;
|
|
1989
139
|
const outwardId = idMode === "topic" ? topicId : storageProjectId;
|
|
1990
|
-
const visibility =
|
|
1991
|
-
const status =
|
|
140
|
+
const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
|
|
141
|
+
const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
|
|
1992
142
|
const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
|
|
1993
143
|
const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
|
|
1994
144
|
return {
|
|
@@ -1998,16 +148,16 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
|
|
|
1998
148
|
topicId,
|
|
1999
149
|
storageProjectId,
|
|
2000
150
|
legacyProjectId,
|
|
2001
|
-
name:
|
|
2002
|
-
type:
|
|
2003
|
-
description:
|
|
2004
|
-
ownerId:
|
|
2005
|
-
sharedWith:
|
|
151
|
+
name: readNonEmptyString(topic.name) || "Untitled Theme",
|
|
152
|
+
type: mapProjectType(topic, metadata),
|
|
153
|
+
description: readNonEmptyString(topic.description),
|
|
154
|
+
ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
|
|
155
|
+
sharedWith: readStringArray(metadata.sharedWith),
|
|
2006
156
|
visibility,
|
|
2007
|
-
tenantId:
|
|
2008
|
-
workspaceId:
|
|
157
|
+
tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
|
|
158
|
+
workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
|
|
2009
159
|
status,
|
|
2010
|
-
tags:
|
|
160
|
+
tags: readStringArray(metadata.tags),
|
|
2011
161
|
chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
|
|
2012
162
|
artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
|
|
2013
163
|
lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
|
|
@@ -2016,17 +166,17 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
|
|
|
2016
166
|
updatedAt
|
|
2017
167
|
};
|
|
2018
168
|
}
|
|
2019
|
-
async function
|
|
2020
|
-
const topic = await
|
|
169
|
+
async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
|
|
170
|
+
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
2021
171
|
if (!topic) {
|
|
2022
172
|
return null;
|
|
2023
173
|
}
|
|
2024
|
-
if (options.projectLikeOnly !== false && !
|
|
174
|
+
if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
|
|
2025
175
|
return null;
|
|
2026
176
|
}
|
|
2027
|
-
return
|
|
177
|
+
return materializeTopicProjectOverlay(topic, options.idMode);
|
|
2028
178
|
}
|
|
2029
|
-
async function
|
|
179
|
+
async function listTopicProjectOverlays(ctx, options = {}) {
|
|
2030
180
|
let allTopics = [];
|
|
2031
181
|
if (ctx?.db?.query && typeof ctx.db.query === "function") {
|
|
2032
182
|
try {
|
|
@@ -2036,18 +186,18 @@ async function listTopicProjectOverlays2(ctx, options = {}) {
|
|
|
2036
186
|
}
|
|
2037
187
|
}
|
|
2038
188
|
if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
|
|
2039
|
-
allTopics = (await ctx.runQuery(
|
|
189
|
+
allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
|
|
2040
190
|
}
|
|
2041
191
|
return allTopics.filter(
|
|
2042
|
-
(topic) => options.projectLikeOnly === false ||
|
|
2043
|
-
).map((topic) =>
|
|
192
|
+
(topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
|
|
193
|
+
).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
|
|
2044
194
|
}
|
|
2045
195
|
async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
2046
|
-
const topic = await
|
|
196
|
+
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
2047
197
|
if (!topic) {
|
|
2048
198
|
return null;
|
|
2049
199
|
}
|
|
2050
|
-
const nextMetadata = { ...
|
|
200
|
+
const nextMetadata = { ...readMetadata(topic) };
|
|
2051
201
|
const patch = {};
|
|
2052
202
|
const topicUpdateArgs = {
|
|
2053
203
|
id: String(topic._id)
|
|
@@ -2072,7 +222,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2072
222
|
`patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
|
|
2073
223
|
);
|
|
2074
224
|
case "status": {
|
|
2075
|
-
const status =
|
|
225
|
+
const status = coerceStatus(rawValue);
|
|
2076
226
|
if (status) {
|
|
2077
227
|
patch.status = status;
|
|
2078
228
|
topicUpdateArgs.status = status;
|
|
@@ -2080,7 +230,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2080
230
|
break;
|
|
2081
231
|
}
|
|
2082
232
|
case "visibility": {
|
|
2083
|
-
const visibility =
|
|
233
|
+
const visibility = coerceVisibility(rawValue);
|
|
2084
234
|
if (visibility) {
|
|
2085
235
|
patch.visibility = visibility;
|
|
2086
236
|
topicUpdateArgs.visibility = visibility;
|
|
@@ -2088,7 +238,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2088
238
|
break;
|
|
2089
239
|
}
|
|
2090
240
|
case "type": {
|
|
2091
|
-
const projectType =
|
|
241
|
+
const projectType = readNonEmptyString(rawValue);
|
|
2092
242
|
if (projectType) {
|
|
2093
243
|
nextMetadata.projectType = projectType;
|
|
2094
244
|
} else {
|
|
@@ -2112,7 +262,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2112
262
|
topicUpdateArgs.metadata = nextMetadata;
|
|
2113
263
|
if (typeof ctx.runMutation === "function") {
|
|
2114
264
|
try {
|
|
2115
|
-
await ctx.runMutation(
|
|
265
|
+
await ctx.runMutation(api.topics.update, topicUpdateArgs);
|
|
2116
266
|
} catch (error) {
|
|
2117
267
|
if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
|
|
2118
268
|
throw error;
|
|
@@ -2126,7 +276,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2126
276
|
"Cannot patch topic without component adapter (ctx.runMutation unavailable)"
|
|
2127
277
|
);
|
|
2128
278
|
}
|
|
2129
|
-
return
|
|
279
|
+
return materializeTopicProjectOverlay(
|
|
2130
280
|
{
|
|
2131
281
|
...topic,
|
|
2132
282
|
...patch,
|
|
@@ -2159,10 +309,10 @@ async function patchProjectWithTolerance(ctx, projectId, value) {
|
|
|
2159
309
|
});
|
|
2160
310
|
}
|
|
2161
311
|
}
|
|
2162
|
-
function
|
|
312
|
+
function defaultResolvers() {
|
|
2163
313
|
return {
|
|
2164
314
|
async getProject(ctx, projectId) {
|
|
2165
|
-
return await
|
|
315
|
+
return await resolveTopicProjectOverlay(ctx, projectId, {
|
|
2166
316
|
idMode: "legacy",
|
|
2167
317
|
projectLikeOnly: false
|
|
2168
318
|
});
|
|
@@ -2171,7 +321,7 @@ function defaultResolvers2() {
|
|
|
2171
321
|
await patchProjectWithTolerance(ctx, projectId, value);
|
|
2172
322
|
},
|
|
2173
323
|
async listTopics(ctx) {
|
|
2174
|
-
return await
|
|
324
|
+
return await listTopicProjectOverlays(ctx, {
|
|
2175
325
|
idMode: "legacy"
|
|
2176
326
|
});
|
|
2177
327
|
},
|
|
@@ -2180,20 +330,20 @@ function defaultResolvers2() {
|
|
|
2180
330
|
}
|
|
2181
331
|
};
|
|
2182
332
|
}
|
|
2183
|
-
var
|
|
333
|
+
var resolverOverrides = {};
|
|
2184
334
|
function configureGraphPrimitivesAppResolvers(overrides) {
|
|
2185
|
-
|
|
2186
|
-
...
|
|
335
|
+
resolverOverrides = {
|
|
336
|
+
...resolverOverrides,
|
|
2187
337
|
...overrides
|
|
2188
338
|
};
|
|
2189
339
|
}
|
|
2190
340
|
function resetGraphPrimitivesAppResolvers() {
|
|
2191
|
-
|
|
341
|
+
resolverOverrides = {};
|
|
2192
342
|
}
|
|
2193
343
|
function resolveGraphPrimitivesAppResolvers(_ctx) {
|
|
2194
344
|
return {
|
|
2195
|
-
...
|
|
2196
|
-
...
|
|
345
|
+
...defaultResolvers(),
|
|
346
|
+
...resolverOverrides
|
|
2197
347
|
};
|
|
2198
348
|
}
|
|
2199
349
|
|
|
@@ -2204,17 +354,17 @@ __export(topicScope_exports, {
|
|
|
2204
354
|
readMaterializedTopicTableId: () => readMaterializedTopicTableId,
|
|
2205
355
|
resolveTopicProjectScope: () => resolveTopicProjectScope
|
|
2206
356
|
});
|
|
2207
|
-
var
|
|
357
|
+
var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
|
|
2208
358
|
function asMappedProjectId(topic) {
|
|
2209
359
|
if (!topic) {
|
|
2210
360
|
return;
|
|
2211
361
|
}
|
|
2212
|
-
const directLegacyProjectId = normalizeScopeValue(topic[
|
|
362
|
+
const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD2]);
|
|
2213
363
|
if (directLegacyProjectId) {
|
|
2214
364
|
return directLegacyProjectId;
|
|
2215
365
|
}
|
|
2216
366
|
const metadata = topic.metadata || {};
|
|
2217
|
-
const candidate = metadata[
|
|
367
|
+
const candidate = metadata[LEGACY_SCOPE_FIELD2] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
2218
368
|
return candidate ? candidate : void 0;
|
|
2219
369
|
}
|
|
2220
370
|
function normalizeScopeValue(value) {
|
|
@@ -2243,7 +393,7 @@ async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
|
2243
393
|
try {
|
|
2244
394
|
return await ctx.db.query("topics").withIndex(
|
|
2245
395
|
"by_graph_scope_project",
|
|
2246
|
-
(q) => q.eq(
|
|
396
|
+
(q) => q.eq(LEGACY_SCOPE_FIELD2, scopeId)
|
|
2247
397
|
).collect();
|
|
2248
398
|
} catch {
|
|
2249
399
|
const topics = await ctx.db.query("topics").collect();
|
|
@@ -2259,7 +409,7 @@ async function tryResolveHostTopicById(ctx, topicId) {
|
|
|
2259
409
|
return null;
|
|
2260
410
|
}
|
|
2261
411
|
try {
|
|
2262
|
-
return await ctx.runQuery(
|
|
412
|
+
return await ctx.runQuery(api.topics.get, {
|
|
2263
413
|
id: topicId
|
|
2264
414
|
}) ?? null;
|
|
2265
415
|
} catch {
|
|
@@ -2271,7 +421,7 @@ async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
|
|
|
2271
421
|
return null;
|
|
2272
422
|
}
|
|
2273
423
|
try {
|
|
2274
|
-
return await ctx.runQuery(
|
|
424
|
+
return await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
2275
425
|
projectId: legacyScopeId
|
|
2276
426
|
}) ?? null;
|
|
2277
427
|
} catch {
|
|
@@ -2516,12 +666,12 @@ var getGlobalBeliefHealth = query({
|
|
|
2516
666
|
const allProjects = await resolveGraphPrimitivesAppResolvers().listTopics(ctx);
|
|
2517
667
|
const accessibleProjectIds = await getAccessibleProjectIds(ctx, clerkId);
|
|
2518
668
|
const accessibleProjects = allProjects.filter(
|
|
2519
|
-
(
|
|
669
|
+
(project3) => accessibleProjectIds.has(project3._id)
|
|
2520
670
|
);
|
|
2521
671
|
const allResults = [];
|
|
2522
|
-
for (const
|
|
672
|
+
for (const project3 of accessibleProjects.slice(0, 20)) {
|
|
2523
673
|
const scope = await resolveTopicProjectScope(ctx, {
|
|
2524
|
-
projectId: String(
|
|
674
|
+
projectId: String(project3._id)
|
|
2525
675
|
}).catch(() => null);
|
|
2526
676
|
if (!scope) {
|
|
2527
677
|
continue;
|
|
@@ -2547,8 +697,8 @@ var getGlobalBeliefHealth = query({
|
|
|
2547
697
|
});
|
|
2548
698
|
if (priorityRank[schedule.priority] >= minRank) {
|
|
2549
699
|
allResults.push({
|
|
2550
|
-
projectId:
|
|
2551
|
-
projectName:
|
|
700
|
+
projectId: project3._id,
|
|
701
|
+
projectName: project3.name,
|
|
2552
702
|
beliefId: belief._id,
|
|
2553
703
|
beliefText: belief.canonicalText,
|
|
2554
704
|
confidence,
|
|
@@ -2743,8 +893,6 @@ async function resolveScopeSoft(ctx, args) {
|
|
|
2743
893
|
...projectId ? { projectId } : {}
|
|
2744
894
|
};
|
|
2745
895
|
}
|
|
2746
|
-
|
|
2747
|
-
// src/beliefEvidenceLinks.ts
|
|
2748
896
|
var beliefIdUnion = v.id("epistemicNodes");
|
|
2749
897
|
var insightIdUnion = v.id("epistemicNodes");
|
|
2750
898
|
var suggestionStatusValidator = v.union(
|
|
@@ -3377,7 +1525,7 @@ var getLinkedBeliefsForInsight = query({
|
|
|
3377
1525
|
// src/beliefLifecycle.ts
|
|
3378
1526
|
var beliefLifecycle_exports = {};
|
|
3379
1527
|
__export(beliefLifecycle_exports, {
|
|
3380
|
-
hasResolvedPredictionOutcome: () =>
|
|
1528
|
+
hasResolvedPredictionOutcome: () => hasResolvedPredictionOutcome,
|
|
3381
1529
|
isBeliefLifecycleStatus: () => isBeliefLifecycleStatus,
|
|
3382
1530
|
isPreValidationBeliefStatus: () => isPreValidationBeliefStatus,
|
|
3383
1531
|
isPropagationEligibleBeliefStatus: () => isPropagationEligibleBeliefStatus,
|
|
@@ -3401,7 +1549,7 @@ var RESOLVED_PREDICTION_OUTCOMES = [
|
|
|
3401
1549
|
function isBeliefLifecycleStatus(value) {
|
|
3402
1550
|
return typeof value === "string" && BELIEF_STATUS_VALUES.includes(value);
|
|
3403
1551
|
}
|
|
3404
|
-
function
|
|
1552
|
+
function normalizeBeliefConfidence(confidence) {
|
|
3405
1553
|
if (typeof confidence !== "number" || !Number.isFinite(confidence)) {
|
|
3406
1554
|
return null;
|
|
3407
1555
|
}
|
|
@@ -3414,13 +1562,13 @@ function normalizeBeliefConfidence2(confidence) {
|
|
|
3414
1562
|
return null;
|
|
3415
1563
|
}
|
|
3416
1564
|
function isResolvedByConfidence(confidence) {
|
|
3417
|
-
const normalized =
|
|
1565
|
+
const normalized = normalizeBeliefConfidence(confidence);
|
|
3418
1566
|
if (normalized === null) {
|
|
3419
1567
|
return false;
|
|
3420
1568
|
}
|
|
3421
1569
|
return normalized <= 0 || normalized >= 1;
|
|
3422
1570
|
}
|
|
3423
|
-
function
|
|
1571
|
+
function hasResolvedPredictionOutcome(predictionMeta) {
|
|
3424
1572
|
if (!predictionMeta || typeof predictionMeta !== "object") {
|
|
3425
1573
|
return false;
|
|
3426
1574
|
}
|
|
@@ -3434,10 +1582,10 @@ function shouldTreatBeliefAsFact(opts) {
|
|
|
3434
1582
|
if (isResolvedByConfidence(opts.confidence)) {
|
|
3435
1583
|
return true;
|
|
3436
1584
|
}
|
|
3437
|
-
if (
|
|
1585
|
+
if (hasResolvedPredictionOutcome(opts.predictionMeta)) {
|
|
3438
1586
|
return true;
|
|
3439
1587
|
}
|
|
3440
|
-
if (
|
|
1588
|
+
if (hasResolvedPredictionOutcome(getPredictionMetaFromMetadata(opts.metadata))) {
|
|
3441
1589
|
return true;
|
|
3442
1590
|
}
|
|
3443
1591
|
return false;
|
|
@@ -3448,7 +1596,7 @@ function resolveBeliefLifecycleStatus(opts) {
|
|
|
3448
1596
|
}
|
|
3449
1597
|
const direct = opts.beliefStatus;
|
|
3450
1598
|
if (isBeliefLifecycleStatus(direct)) {
|
|
3451
|
-
const normalized =
|
|
1599
|
+
const normalized = normalizeBeliefConfidence(opts.confidence);
|
|
3452
1600
|
if (normalized !== null && isPreValidationBeliefStatus(direct)) {
|
|
3453
1601
|
return "belief";
|
|
3454
1602
|
}
|
|
@@ -3456,7 +1604,7 @@ function resolveBeliefLifecycleStatus(opts) {
|
|
|
3456
1604
|
}
|
|
3457
1605
|
const metaStatus = opts.metadata?.beliefStatus;
|
|
3458
1606
|
if (isBeliefLifecycleStatus(metaStatus)) {
|
|
3459
|
-
const normalized =
|
|
1607
|
+
const normalized = normalizeBeliefConfidence(opts.confidence);
|
|
3460
1608
|
if (normalized !== null && isPreValidationBeliefStatus(metaStatus)) {
|
|
3461
1609
|
return "belief";
|
|
3462
1610
|
}
|
|
@@ -4037,8 +2185,6 @@ var containsPropagationSpec = {
|
|
|
4037
2185
|
operator: () => null,
|
|
4038
2186
|
description: "Structural containment only. Traversed for explicit semantics, but it never propagates opinions."
|
|
4039
2187
|
};
|
|
4040
|
-
|
|
4041
|
-
// src/edges/utils.ts
|
|
4042
2188
|
function readEdgeMetadata(edge) {
|
|
4043
2189
|
return {
|
|
4044
2190
|
constraint: edge.constraint ?? void 0,
|
|
@@ -4115,8 +2261,6 @@ var contradictsPropagationSpec = {
|
|
|
4115
2261
|
},
|
|
4116
2262
|
description: "Legacy contradiction edges move negative pressure in either direction, but never beyond one hop."
|
|
4117
2263
|
};
|
|
4118
|
-
|
|
4119
|
-
// src/edges/dependsOn.ts
|
|
4120
2264
|
var dependsOnPropagationSpec = {
|
|
4121
2265
|
edgeType: "depends_on",
|
|
4122
2266
|
direction: "incoming",
|
|
@@ -4132,8 +2276,18 @@ var dependsOnPropagationSpec = {
|
|
|
4132
2276
|
if (metadata.conditionalA && metadata.conditionalNotA) {
|
|
4133
2277
|
const deducedOpinion = conditionalDeduction(
|
|
4134
2278
|
dampedSource,
|
|
4135
|
-
|
|
4136
|
-
|
|
2279
|
+
mkOpinion(
|
|
2280
|
+
metadata.conditionalA.b,
|
|
2281
|
+
metadata.conditionalA.d,
|
|
2282
|
+
metadata.conditionalA.u,
|
|
2283
|
+
metadata.conditionalA.a
|
|
2284
|
+
),
|
|
2285
|
+
mkOpinion(
|
|
2286
|
+
metadata.conditionalNotA.b,
|
|
2287
|
+
metadata.conditionalNotA.d,
|
|
2288
|
+
metadata.conditionalNotA.u,
|
|
2289
|
+
metadata.conditionalNotA.a
|
|
2290
|
+
),
|
|
4137
2291
|
targetOpinion.a
|
|
4138
2292
|
);
|
|
4139
2293
|
return annotateRationale(
|
|
@@ -4706,14 +2860,6 @@ __export(entityBridge_exports, {
|
|
|
4706
2860
|
getEvidenceForEntity: () => getEvidenceForEntity,
|
|
4707
2861
|
linkEntityToBelief: () => linkEntityToBelief
|
|
4708
2862
|
});
|
|
4709
|
-
|
|
4710
|
-
// ../access-control/src/auth.ts
|
|
4711
|
-
async function getCurrentUserId(ctx) {
|
|
4712
|
-
const identity = await ctx.auth.getUserIdentity();
|
|
4713
|
-
return identity?.subject ?? null;
|
|
4714
|
-
}
|
|
4715
|
-
|
|
4716
|
-
// src/entityBridge.ts
|
|
4717
2863
|
var ENTITY_BRIDGE_EDGE_TYPES = [
|
|
4718
2864
|
"contains",
|
|
4719
2865
|
"evaluates",
|
|
@@ -5211,7 +3357,7 @@ async function resolveTopicOntologyInternal(ctx, topicId) {
|
|
|
5211
3357
|
"by_ontologyId",
|
|
5212
3358
|
(q) => q.eq("ontologyId", current?.ontologyId)
|
|
5213
3359
|
).collect();
|
|
5214
|
-
const published = versions.filter((
|
|
3360
|
+
const published = versions.filter((v20) => v20.status === "published").sort((a, b) => (b.publishedAt ?? 0) - (a.publishedAt ?? 0));
|
|
5215
3361
|
const latestPublished = published[0] ?? null;
|
|
5216
3362
|
return {
|
|
5217
3363
|
ontologyId: ontologyDef._id,
|
|
@@ -5313,16 +3459,16 @@ function buildEntityTitle(canonicalText) {
|
|
|
5313
3459
|
async function resolveCanonicalEntityScope(ctx, args) {
|
|
5314
3460
|
const scope = await resolveTopicProjectScope(ctx, args);
|
|
5315
3461
|
const topic = scope.topicId ? await ctx.db.get(scope.topicId) ?? null : null;
|
|
5316
|
-
const
|
|
3462
|
+
const project3 = scope.projectId !== void 0 ? await resolveGraphPrimitivesAppResolvers().getProject(
|
|
5317
3463
|
ctx,
|
|
5318
3464
|
scope.projectId
|
|
5319
3465
|
) ?? null : null;
|
|
5320
|
-
const tenantId = typeof topic?.tenantId === "string" && topic.tenantId.trim().length > 0 ? topic.tenantId.trim() : typeof
|
|
3466
|
+
const tenantId = typeof topic?.tenantId === "string" && topic.tenantId.trim().length > 0 ? topic.tenantId.trim() : typeof project3?.tenantId === "string" && project3.tenantId.trim().length > 0 ? project3.tenantId.trim() : void 0;
|
|
5321
3467
|
return {
|
|
5322
3468
|
...scope,
|
|
5323
3469
|
tenantId,
|
|
5324
3470
|
topic,
|
|
5325
|
-
project:
|
|
3471
|
+
project: project3
|
|
5326
3472
|
};
|
|
5327
3473
|
}
|
|
5328
3474
|
function matchesCanonicalEntityRecord(node, args) {
|
|
@@ -5980,323 +4126,6 @@ __export(epistemicBeliefs_exports, {
|
|
|
5980
4126
|
updateStatus: () => updateStatus2,
|
|
5981
4127
|
updateStatusInternal: () => updateStatusInternal
|
|
5982
4128
|
});
|
|
5983
|
-
|
|
5984
|
-
// ../access-control/src/audience.ts
|
|
5985
|
-
var AUDIENCE_CLASS_RANK = {
|
|
5986
|
-
public: 0,
|
|
5987
|
-
restricted_external: 1,
|
|
5988
|
-
internal: 2
|
|
5989
|
-
};
|
|
5990
|
-
function normalizeKey(key) {
|
|
5991
|
-
return (key ?? "").trim().toLowerCase().replace(/[^a-z0-9:_-]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
5992
|
-
}
|
|
5993
|
-
function normalizeAudienceKey(key) {
|
|
5994
|
-
return normalizeKey(key);
|
|
5995
|
-
}
|
|
5996
|
-
function classFromAudienceKey(audienceKey, fallback = "internal") {
|
|
5997
|
-
const key = normalizeKey(audienceKey);
|
|
5998
|
-
if (!key) {
|
|
5999
|
-
return fallback;
|
|
6000
|
-
}
|
|
6001
|
-
if (key === "internal") {
|
|
6002
|
-
return "internal";
|
|
6003
|
-
}
|
|
6004
|
-
if (key === "public") {
|
|
6005
|
-
return "public";
|
|
6006
|
-
}
|
|
6007
|
-
if (key === "lp" || key === "external" || key === "client" || key === "partner" || key === "portfolio" || key === "network" || key === "restricted_external") {
|
|
6008
|
-
return "restricted_external";
|
|
6009
|
-
}
|
|
6010
|
-
return fallback;
|
|
6011
|
-
}
|
|
6012
|
-
function canAudienceClassAccess(viewerClass, resourceClass) {
|
|
6013
|
-
return AUDIENCE_CLASS_RANK[viewerClass] >= AUDIENCE_CLASS_RANK[resourceClass];
|
|
6014
|
-
}
|
|
6015
|
-
|
|
6016
|
-
// ../access-control/src/audienceRegistry.ts
|
|
6017
|
-
var DEFAULT_AUDIENCES = [
|
|
6018
|
-
{
|
|
6019
|
-
audienceKey: "internal",
|
|
6020
|
-
audienceLabel: "Internal",
|
|
6021
|
-
audienceClass: "internal"
|
|
6022
|
-
},
|
|
6023
|
-
{
|
|
6024
|
-
audienceKey: "lp",
|
|
6025
|
-
audienceLabel: "Limited Partners",
|
|
6026
|
-
audienceClass: "restricted_external"
|
|
6027
|
-
},
|
|
6028
|
-
{
|
|
6029
|
-
audienceKey: "public",
|
|
6030
|
-
audienceLabel: "Public",
|
|
6031
|
-
audienceClass: "public"
|
|
6032
|
-
}
|
|
6033
|
-
];
|
|
6034
|
-
var AUDIENCE_CLASS_PRIORITY = {
|
|
6035
|
-
internal: 0,
|
|
6036
|
-
restricted_external: 1,
|
|
6037
|
-
public: 2
|
|
6038
|
-
};
|
|
6039
|
-
function normalizeRegistryRow(row) {
|
|
6040
|
-
return {
|
|
6041
|
-
audienceKey: normalizeAudienceKey(row.audienceKey),
|
|
6042
|
-
audienceLabel: row.audienceLabel,
|
|
6043
|
-
audienceClass: row.audienceClass,
|
|
6044
|
-
workspaceId: row.workspaceId
|
|
6045
|
-
};
|
|
6046
|
-
}
|
|
6047
|
-
function dedupeRegistryRows(rows) {
|
|
6048
|
-
const byKey = /* @__PURE__ */ new Map();
|
|
6049
|
-
for (const row of rows) {
|
|
6050
|
-
const key = normalizeAudienceKey(row.audienceKey);
|
|
6051
|
-
if (!key) {
|
|
6052
|
-
continue;
|
|
6053
|
-
}
|
|
6054
|
-
const existing = byKey.get(key);
|
|
6055
|
-
const isWorkspaceScoped = row.workspaceId !== void 0;
|
|
6056
|
-
const existingWorkspaceScoped = existing?.workspaceId !== void 0;
|
|
6057
|
-
if (!existing || isWorkspaceScoped && !existingWorkspaceScoped) {
|
|
6058
|
-
byKey.set(key, {
|
|
6059
|
-
...row,
|
|
6060
|
-
audienceKey: key
|
|
6061
|
-
});
|
|
6062
|
-
}
|
|
6063
|
-
}
|
|
6064
|
-
const normalized = [...byKey.values()];
|
|
6065
|
-
normalized.sort((a, b) => {
|
|
6066
|
-
const classDelta = AUDIENCE_CLASS_PRIORITY[a.audienceClass] - AUDIENCE_CLASS_PRIORITY[b.audienceClass];
|
|
6067
|
-
if (classDelta !== 0) {
|
|
6068
|
-
return classDelta;
|
|
6069
|
-
}
|
|
6070
|
-
return a.audienceKey.localeCompare(b.audienceKey);
|
|
6071
|
-
});
|
|
6072
|
-
return normalized;
|
|
6073
|
-
}
|
|
6074
|
-
async function queryRegistryRows(ctx, args) {
|
|
6075
|
-
if (!args.tenantId) {
|
|
6076
|
-
return [...DEFAULT_AUDIENCES];
|
|
6077
|
-
}
|
|
6078
|
-
const rows = await ctx.db.query("platformAudiences").withIndex("by_tenantId", (q) => q.eq("tenantId", args.tenantId)).collect();
|
|
6079
|
-
const workspaceIdString = args.workspaceId ? String(args.workspaceId) : null;
|
|
6080
|
-
const tenantScoped = rows.filter((row) => row.status === "active");
|
|
6081
|
-
const applicable = tenantScoped.filter((row) => {
|
|
6082
|
-
if (!row.workspaceId) {
|
|
6083
|
-
return true;
|
|
6084
|
-
}
|
|
6085
|
-
if (!workspaceIdString) {
|
|
6086
|
-
return false;
|
|
6087
|
-
}
|
|
6088
|
-
return String(row.workspaceId) === workspaceIdString;
|
|
6089
|
-
});
|
|
6090
|
-
return dedupeRegistryRows([
|
|
6091
|
-
...DEFAULT_AUDIENCES,
|
|
6092
|
-
...applicable.map(
|
|
6093
|
-
(row) => normalizeRegistryRow({
|
|
6094
|
-
audienceKey: row.audienceKey,
|
|
6095
|
-
audienceLabel: row.audienceLabel,
|
|
6096
|
-
audienceClass: row.audienceClass,
|
|
6097
|
-
workspaceId: row.workspaceId
|
|
6098
|
-
})
|
|
6099
|
-
)
|
|
6100
|
-
]);
|
|
6101
|
-
}
|
|
6102
|
-
async function listAudienceRegistryRows(ctx, args) {
|
|
6103
|
-
return queryRegistryRows(ctx, args);
|
|
6104
|
-
}
|
|
6105
|
-
|
|
6106
|
-
// ../../packages/contracts/src/schema-helpers/enumValidation.ts
|
|
6107
|
-
var BUILTIN_ENUM_FALLBACK = {
|
|
6108
|
-
topic_type: /* @__PURE__ */ new Set([
|
|
6109
|
-
"domain",
|
|
6110
|
-
"theme",
|
|
6111
|
-
"deal",
|
|
6112
|
-
"strategy",
|
|
6113
|
-
"constitution",
|
|
6114
|
-
"project",
|
|
6115
|
-
"portfolio",
|
|
6116
|
-
"architecture",
|
|
6117
|
-
"capability",
|
|
6118
|
-
"runtime",
|
|
6119
|
-
"interface",
|
|
6120
|
-
"governance",
|
|
6121
|
-
"operations",
|
|
6122
|
-
"security",
|
|
6123
|
-
"data"
|
|
6124
|
-
]),
|
|
6125
|
-
branch_schema: /* @__PURE__ */ new Set(["pillar", "track", "dimension", "axis", "phase"]),
|
|
6126
|
-
lens_perspective_type: /* @__PURE__ */ new Set([
|
|
6127
|
-
"investigation",
|
|
6128
|
-
"monitoring",
|
|
6129
|
-
"analysis",
|
|
6130
|
-
"comparison",
|
|
6131
|
-
"taxonomy"
|
|
6132
|
-
]),
|
|
6133
|
-
belief_type: /* @__PURE__ */ new Set([
|
|
6134
|
-
"belief",
|
|
6135
|
-
"hypothesis",
|
|
6136
|
-
"principle",
|
|
6137
|
-
"invariant",
|
|
6138
|
-
"assumption",
|
|
6139
|
-
"tenet",
|
|
6140
|
-
"prior",
|
|
6141
|
-
"preference",
|
|
6142
|
-
"goal",
|
|
6143
|
-
"forecast",
|
|
6144
|
-
"decision",
|
|
6145
|
-
"constraint",
|
|
6146
|
-
"tradeoff",
|
|
6147
|
-
"policy",
|
|
6148
|
-
"implementation_choice",
|
|
6149
|
-
"implementation_decision",
|
|
6150
|
-
"interface_contract",
|
|
6151
|
-
"migration_state",
|
|
6152
|
-
"code_pattern",
|
|
6153
|
-
"deprecation_notice"
|
|
6154
|
-
]),
|
|
6155
|
-
edge_type: /* @__PURE__ */ new Set([
|
|
6156
|
-
"supports",
|
|
6157
|
-
"informs",
|
|
6158
|
-
"depends_on",
|
|
6159
|
-
"derived_from",
|
|
6160
|
-
"contains",
|
|
6161
|
-
"tests",
|
|
6162
|
-
"supersedes",
|
|
6163
|
-
"responds_to",
|
|
6164
|
-
"belongs_to",
|
|
6165
|
-
"relates_to_thesis",
|
|
6166
|
-
"works_at",
|
|
6167
|
-
"invested_in",
|
|
6168
|
-
"competes_with",
|
|
6169
|
-
"participates_in",
|
|
6170
|
-
"founded_by",
|
|
6171
|
-
"evaluates",
|
|
6172
|
-
"performs",
|
|
6173
|
-
"function_in",
|
|
6174
|
-
"impacts",
|
|
6175
|
-
"raised_from",
|
|
6176
|
-
"mentioned_in",
|
|
6177
|
-
"perspective_on",
|
|
6178
|
-
"plays_theme"
|
|
6179
|
-
]),
|
|
6180
|
-
worktree_type: /* @__PURE__ */ new Set([
|
|
6181
|
-
"belief_test",
|
|
6182
|
-
"existential",
|
|
6183
|
-
"contradiction",
|
|
6184
|
-
"refinement",
|
|
6185
|
-
"coverage",
|
|
6186
|
-
"discovery",
|
|
6187
|
-
"clarification",
|
|
6188
|
-
"confirmation"
|
|
6189
|
-
]),
|
|
6190
|
-
worktree_phase: /* @__PURE__ */ new Set([
|
|
6191
|
-
"cluster_mapping",
|
|
6192
|
-
"hypothesis_formation",
|
|
6193
|
-
"question_generation",
|
|
6194
|
-
"evidence_collection",
|
|
6195
|
-
"synthesis",
|
|
6196
|
-
"decision",
|
|
6197
|
-
"retrospective"
|
|
6198
|
-
]),
|
|
6199
|
-
activity_type: /* @__PURE__ */ new Set([
|
|
6200
|
-
"create",
|
|
6201
|
-
"update",
|
|
6202
|
-
"review",
|
|
6203
|
-
"merge",
|
|
6204
|
-
"archive",
|
|
6205
|
-
"comment",
|
|
6206
|
-
"status_change",
|
|
6207
|
-
"evidence_added",
|
|
6208
|
-
"question_added"
|
|
6209
|
-
])
|
|
6210
|
-
};
|
|
6211
|
-
function normalizeEnumValue(value) {
|
|
6212
|
-
return value.trim().toLowerCase();
|
|
6213
|
-
}
|
|
6214
|
-
async function validateSchemaEnumValue(_ctx, args) {
|
|
6215
|
-
const normalized = normalizeEnumValue(args.value);
|
|
6216
|
-
if (!normalized) {
|
|
6217
|
-
return { valid: false, source: "none" };
|
|
6218
|
-
}
|
|
6219
|
-
if (BUILTIN_ENUM_FALLBACK[args.category].has(normalized)) {
|
|
6220
|
-
return { valid: true, source: "builtin" };
|
|
6221
|
-
}
|
|
6222
|
-
return { valid: false, source: "none" };
|
|
6223
|
-
}
|
|
6224
|
-
async function assertSchemaEnumValue(ctx, args) {
|
|
6225
|
-
if (typeof args.value !== "string") {
|
|
6226
|
-
return;
|
|
6227
|
-
}
|
|
6228
|
-
const normalized = normalizeEnumValue(args.value);
|
|
6229
|
-
if (!normalized) {
|
|
6230
|
-
return;
|
|
6231
|
-
}
|
|
6232
|
-
const validation = await validateSchemaEnumValue(ctx, {
|
|
6233
|
-
category: args.category,
|
|
6234
|
-
value: normalized,
|
|
6235
|
-
tenantId: args.tenantId
|
|
6236
|
-
});
|
|
6237
|
-
if (!validation.valid) {
|
|
6238
|
-
const tenantHint = args.tenantId ? ` for tenant ${args.tenantId}` : "";
|
|
6239
|
-
throw new Error(
|
|
6240
|
-
`[${args.context}] Invalid value "${normalized}" for category "${args.category}"${tenantHint}. Add it to the contracts schema enum manifest before use.`
|
|
6241
|
-
);
|
|
6242
|
-
}
|
|
6243
|
-
return normalized;
|
|
6244
|
-
}
|
|
6245
|
-
|
|
6246
|
-
// ../../packages/contracts/src/schema-helpers/spine/tables/epistemicNodes.ts
|
|
6247
|
-
var NODE_TYPES = [
|
|
6248
|
-
"decision",
|
|
6249
|
-
"belief",
|
|
6250
|
-
"question",
|
|
6251
|
-
"theme",
|
|
6252
|
-
"deal",
|
|
6253
|
-
"topic",
|
|
6254
|
-
"claim",
|
|
6255
|
-
"evidence",
|
|
6256
|
-
"synthesis",
|
|
6257
|
-
"answer",
|
|
6258
|
-
"atomic_fact",
|
|
6259
|
-
"excerpt",
|
|
6260
|
-
"source",
|
|
6261
|
-
"company",
|
|
6262
|
-
"person",
|
|
6263
|
-
"investor",
|
|
6264
|
-
"function",
|
|
6265
|
-
"value_chain"
|
|
6266
|
-
];
|
|
6267
|
-
function isNodeType(value) {
|
|
6268
|
-
return NODE_TYPES.includes(value);
|
|
6269
|
-
}
|
|
6270
|
-
function getLayerForNodeType(type) {
|
|
6271
|
-
switch (type) {
|
|
6272
|
-
case "decision":
|
|
6273
|
-
return "L4";
|
|
6274
|
-
case "belief":
|
|
6275
|
-
case "question":
|
|
6276
|
-
case "theme":
|
|
6277
|
-
case "deal":
|
|
6278
|
-
return "L3";
|
|
6279
|
-
case "claim":
|
|
6280
|
-
case "evidence":
|
|
6281
|
-
case "synthesis":
|
|
6282
|
-
case "answer":
|
|
6283
|
-
return "L2";
|
|
6284
|
-
case "atomic_fact":
|
|
6285
|
-
case "excerpt":
|
|
6286
|
-
case "source":
|
|
6287
|
-
return "L1";
|
|
6288
|
-
case "topic":
|
|
6289
|
-
return "organizational";
|
|
6290
|
-
case "company":
|
|
6291
|
-
case "person":
|
|
6292
|
-
case "investor":
|
|
6293
|
-
case "function":
|
|
6294
|
-
case "value_chain":
|
|
6295
|
-
return "ontological";
|
|
6296
|
-
}
|
|
6297
|
-
}
|
|
6298
|
-
|
|
6299
|
-
// src/workspaceIsolation.ts
|
|
6300
4129
|
function normalizeScopeValue3(value) {
|
|
6301
4130
|
if (typeof value !== "string") {
|
|
6302
4131
|
return;
|
|
@@ -6453,16 +4282,17 @@ function resolveTraversalTargetNodeId(edge, direction) {
|
|
|
6453
4282
|
}
|
|
6454
4283
|
function readNodeOpinion(node) {
|
|
6455
4284
|
const metadata = node.metadata ?? {};
|
|
6456
|
-
|
|
6457
|
-
{
|
|
4285
|
+
try {
|
|
4286
|
+
return readOpinionFromRecord({
|
|
6458
4287
|
...metadata,
|
|
6459
4288
|
opinion_b: node.opinion_b,
|
|
6460
4289
|
opinion_d: node.opinion_d,
|
|
6461
4290
|
opinion_u: node.opinion_u,
|
|
6462
4291
|
opinion_a: node.opinion_a
|
|
6463
|
-
}
|
|
6464
|
-
|
|
6465
|
-
|
|
4292
|
+
});
|
|
4293
|
+
} catch {
|
|
4294
|
+
return mkOpinion(0, 0, 1, 0.5);
|
|
4295
|
+
}
|
|
6466
4296
|
}
|
|
6467
4297
|
async function collectConfidencePropagationDispatches(args) {
|
|
6468
4298
|
const dispatchesByTargetId = /* @__PURE__ */ new Map();
|
|
@@ -6531,14 +4361,20 @@ async function collectConfidencePropagationDispatches(args) {
|
|
|
6531
4361
|
if (!result || !hasProjectedOpinionChanged(targetOpinion, result.opinion)) {
|
|
6532
4362
|
continue;
|
|
6533
4363
|
}
|
|
6534
|
-
|
|
4364
|
+
const projectedOpinion = mkOpinion(
|
|
4365
|
+
result.opinion.b,
|
|
4366
|
+
result.opinion.d,
|
|
4367
|
+
result.opinion.u,
|
|
4368
|
+
result.opinion.a
|
|
4369
|
+
);
|
|
4370
|
+
opinionCache.set(cacheKey, projectedOpinion);
|
|
6535
4371
|
const existingDispatch = dispatchesByTargetId.get(cacheKey);
|
|
6536
4372
|
dispatchesByTargetId.set(cacheKey, {
|
|
6537
4373
|
targetNodeId,
|
|
6538
4374
|
edgeType: spec.edgeType,
|
|
6539
4375
|
traversedDirection: direction,
|
|
6540
4376
|
weight: edge.weight ?? 1,
|
|
6541
|
-
opinion:
|
|
4377
|
+
opinion: projectedOpinion,
|
|
6542
4378
|
operator: result.operator,
|
|
6543
4379
|
rationale: existingDispatch ? `${existingDispatch.rationale}; ${result.rationale}` : result.rationale,
|
|
6544
4380
|
hop: nextHop
|
|
@@ -6546,7 +4382,7 @@ async function collectConfidencePropagationDispatches(args) {
|
|
|
6546
4382
|
if (canContinueTransitively(spec, nextHop)) {
|
|
6547
4383
|
queue.push({
|
|
6548
4384
|
nodeId: targetNodeId,
|
|
6549
|
-
opinion:
|
|
4385
|
+
opinion: projectedOpinion,
|
|
6550
4386
|
hop: nextHop,
|
|
6551
4387
|
visitedNodeIds: /* @__PURE__ */ new Set([
|
|
6552
4388
|
...state.visitedNodeIds,
|
|
@@ -6673,7 +4509,7 @@ function throwStructuredMutationError2(args) {
|
|
|
6673
4509
|
function readFiniteNumber(value) {
|
|
6674
4510
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
6675
4511
|
}
|
|
6676
|
-
function
|
|
4512
|
+
function clamp01(value) {
|
|
6677
4513
|
return Math.max(0, Math.min(1, value));
|
|
6678
4514
|
}
|
|
6679
4515
|
function assertBaseRateInRange(baseRate, field = "baseRate") {
|
|
@@ -6723,20 +4559,14 @@ function deriveSyntheticBackfillOpinion(source) {
|
|
|
6723
4559
|
const uncertainty = readFiniteNumber(source.opinion_u) ?? readFiniteNumber(source.uncertainty);
|
|
6724
4560
|
const baseRate = readFiniteNumber(source.opinion_a) ?? readFiniteNumber(source.baseRate);
|
|
6725
4561
|
if (belief !== void 0 || disbelief !== void 0 || uncertainty !== void 0 || baseRate !== void 0) {
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
});
|
|
4562
|
+
try {
|
|
4563
|
+
return readOpinionFromRecord(source);
|
|
4564
|
+
} catch {
|
|
4565
|
+
return mkOpinion(0, 0, 1, 0.5);
|
|
4566
|
+
}
|
|
6732
4567
|
}
|
|
6733
|
-
const confidence =
|
|
6734
|
-
return
|
|
6735
|
-
b: confidence,
|
|
6736
|
-
d: 1 - confidence,
|
|
6737
|
-
u: 0,
|
|
6738
|
-
a: 0.5
|
|
6739
|
-
};
|
|
4568
|
+
const confidence = clamp01(readFiniteNumber(source.confidence) ?? 0);
|
|
4569
|
+
return mkOpinion(confidence, 1 - confidence, 0, 0.5);
|
|
6740
4570
|
}
|
|
6741
4571
|
function clampBeliefLimit(limit, fallback = DEFAULT_PROJECT_BELIEF_LIMIT) {
|
|
6742
4572
|
if (!Number.isFinite(limit)) {
|
|
@@ -6751,16 +4581,17 @@ function readTupleContradictedFlag(value) {
|
|
|
6751
4581
|
return typeof value === "boolean" ? value : void 0;
|
|
6752
4582
|
}
|
|
6753
4583
|
function readBeliefOpinionSnapshot(node, metadata) {
|
|
6754
|
-
|
|
6755
|
-
{
|
|
4584
|
+
try {
|
|
4585
|
+
return readOpinionFromRecord({
|
|
6756
4586
|
...metadata,
|
|
6757
4587
|
opinion_b: node.opinion_b,
|
|
6758
4588
|
opinion_d: node.opinion_d,
|
|
6759
4589
|
opinion_u: node.opinion_u,
|
|
6760
4590
|
opinion_a: node.opinion_a
|
|
6761
|
-
}
|
|
6762
|
-
|
|
6763
|
-
|
|
4591
|
+
});
|
|
4592
|
+
} catch {
|
|
4593
|
+
return mkOpinion(0, 0, 1, 0.5);
|
|
4594
|
+
}
|
|
6764
4595
|
}
|
|
6765
4596
|
function deriveTupleContradictionSeverity(node) {
|
|
6766
4597
|
const metadata = node.metadata || {};
|
|
@@ -7208,12 +5039,12 @@ var propagateConfidenceChange = internalMutation({
|
|
|
7208
5039
|
},
|
|
7209
5040
|
returns: permissiveReturn,
|
|
7210
5041
|
handler: async (ctx, args) => {
|
|
7211
|
-
const sourceOpinion =
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
5042
|
+
const sourceOpinion = mkOpinion(
|
|
5043
|
+
args.opinion_b,
|
|
5044
|
+
args.opinion_d,
|
|
5045
|
+
args.opinion_u,
|
|
5046
|
+
args.opinion_a
|
|
5047
|
+
);
|
|
7217
5048
|
const sourceNode = await ctx.db.get(args.nodeId);
|
|
7218
5049
|
const sourceScope = await resolveNodeScopeForWorkspaceIsolation(
|
|
7219
5050
|
ctx,
|
|
@@ -7320,7 +5151,7 @@ var create3 = mutation({
|
|
|
7320
5151
|
expectedBy: v.optional(v.number())
|
|
7321
5152
|
})
|
|
7322
5153
|
),
|
|
7323
|
-
baseRate: v.number(),
|
|
5154
|
+
baseRate: v.optional(v.number()),
|
|
7324
5155
|
metadata: v.optional(v.any())
|
|
7325
5156
|
// Additional metadata including isConditional
|
|
7326
5157
|
},
|
|
@@ -7351,7 +5182,7 @@ var create3 = mutation({
|
|
|
7351
5182
|
);
|
|
7352
5183
|
}
|
|
7353
5184
|
const now = Date.now();
|
|
7354
|
-
const baseRate = assertBaseRateInRange(args.baseRate);
|
|
5185
|
+
const baseRate = assertBaseRateInRange(args.baseRate ?? 0.5);
|
|
7355
5186
|
const initialBeliefStatus = args.worktreeId ? "hypothesis" : "assumption";
|
|
7356
5187
|
const initialEpistemicStatus = args.worktreeId ? "hypothesis" : "assumption";
|
|
7357
5188
|
const seedOpinion = {
|
|
@@ -7925,8 +5756,8 @@ var modulateConfidence = mutation({
|
|
|
7925
5756
|
// d: evidence AGAINST [0,1]
|
|
7926
5757
|
uncertainty: v.number(),
|
|
7927
5758
|
// u: lack of evidence [0,1]
|
|
7928
|
-
baseRate: v.
|
|
7929
|
-
// a: prior probability [0,1]
|
|
5759
|
+
baseRate: v.number(),
|
|
5760
|
+
// a: prior probability [0,1]
|
|
7930
5761
|
trigger: v.union(
|
|
7931
5762
|
v.literal("evidence_added"),
|
|
7932
5763
|
v.literal("evidence_removed"),
|
|
@@ -9077,7 +6908,7 @@ var internalCreate = internalMutation({
|
|
|
9077
6908
|
args: {
|
|
9078
6909
|
...optionalBeliefScopeArgs,
|
|
9079
6910
|
formulation: v.string(),
|
|
9080
|
-
baseRate: v.number(),
|
|
6911
|
+
baseRate: v.optional(v.number()),
|
|
9081
6912
|
confidence: v.optional(
|
|
9082
6913
|
v.union(v.literal("high"), v.literal("medium"), v.literal("low"))
|
|
9083
6914
|
),
|
|
@@ -9113,7 +6944,7 @@ var internalCreate = internalMutation({
|
|
|
9113
6944
|
returns: permissiveReturn,
|
|
9114
6945
|
handler: async (ctx, args) => {
|
|
9115
6946
|
const now = Date.now();
|
|
9116
|
-
const baseRate = assertBaseRateInRange(args.baseRate);
|
|
6947
|
+
const baseRate = assertBaseRateInRange(args.baseRate ?? 0.5);
|
|
9117
6948
|
const scope = await resolveTopicProjectScope(ctx, {
|
|
9118
6949
|
topicId: args.topicId,
|
|
9119
6950
|
projectId: args.projectId
|
|
@@ -9362,15 +7193,15 @@ var backfillSyntheticOpinionHistory = internalMutation({
|
|
|
9362
7193
|
skippedHasHistory++;
|
|
9363
7194
|
continue;
|
|
9364
7195
|
}
|
|
9365
|
-
const
|
|
7196
|
+
const opinion = deriveSyntheticBackfillOpinion(node);
|
|
9366
7197
|
await ctx.db.insert(
|
|
9367
7198
|
"beliefConfidence",
|
|
9368
7199
|
buildBeliefConfidenceRow({
|
|
9369
7200
|
beliefId: node._id,
|
|
9370
|
-
belief:
|
|
9371
|
-
disbelief:
|
|
9372
|
-
uncertainty:
|
|
9373
|
-
baseRate:
|
|
7201
|
+
belief: opinion.b,
|
|
7202
|
+
disbelief: opinion.d,
|
|
7203
|
+
uncertainty: opinion.u,
|
|
7204
|
+
baseRate: opinion.a,
|
|
9374
7205
|
trigger: "backfill_synthetic",
|
|
9375
7206
|
rationale: "LK-6 backfill: synthesized t0 from node-level opinion fields (no prior beliefConfidence row found).",
|
|
9376
7207
|
assessedAt: readFiniteNumber(node.createdAt) ?? readFiniteNumber(node.updatedAt) ?? Date.now(),
|
|
@@ -11667,7 +9498,7 @@ var create4 = mutation({
|
|
|
11667
9498
|
});
|
|
11668
9499
|
await ctx.scheduler.runAfter(
|
|
11669
9500
|
0,
|
|
11670
|
-
|
|
9501
|
+
api.embeddingActions.generateEpistemicNodeEmbedding,
|
|
11671
9502
|
{
|
|
11672
9503
|
nodeId,
|
|
11673
9504
|
projectId: topicId,
|
|
@@ -11868,7 +9699,7 @@ var createInternal = internalMutation({
|
|
|
11868
9699
|
});
|
|
11869
9700
|
await ctx.scheduler.runAfter(
|
|
11870
9701
|
0,
|
|
11871
|
-
|
|
9702
|
+
api.embeddingActions.generateEpistemicNodeEmbedding,
|
|
11872
9703
|
{
|
|
11873
9704
|
nodeId,
|
|
11874
9705
|
projectId: topicId,
|
|
@@ -13498,6 +11329,7 @@ var create6 = mutation({
|
|
|
13498
11329
|
sourceUrl: v.optional(v.string()),
|
|
13499
11330
|
sourceQuestionId: v.optional(v.string()),
|
|
13500
11331
|
userId: v.string(),
|
|
11332
|
+
rationale: v.string(),
|
|
13501
11333
|
// Classification fields (from AI tools)
|
|
13502
11334
|
methodology: v.optional(v.string()),
|
|
13503
11335
|
informationAsymmetry: v.optional(v.string()),
|
|
@@ -13555,6 +11387,7 @@ var create6 = mutation({
|
|
|
13555
11387
|
externalSourceType: args.externalSourceType,
|
|
13556
11388
|
sourceUrl: args.sourceUrl,
|
|
13557
11389
|
sourceQuestionId: args.sourceQuestionId,
|
|
11390
|
+
rationale: args.rationale,
|
|
13558
11391
|
linkedBeliefNodeId: args.linkedBeliefNodeId,
|
|
13559
11392
|
evidenceRelation: args.evidenceRelation,
|
|
13560
11393
|
confidence: args.confidence,
|
|
@@ -13608,6 +11441,7 @@ var create6 = mutation({
|
|
|
13608
11441
|
changedBy: args.userId,
|
|
13609
11442
|
isAgent: false,
|
|
13610
11443
|
projectId: scope.projectId,
|
|
11444
|
+
rationale: args.rationale,
|
|
13611
11445
|
newState: {
|
|
13612
11446
|
text: args.text.slice(0, 200),
|
|
13613
11447
|
kind,
|
|
@@ -13892,21 +11726,21 @@ var internalGetByProject2 = internalQuery({
|
|
|
13892
11726
|
return [];
|
|
13893
11727
|
}
|
|
13894
11728
|
const audienceMode = args.audienceMode ?? "internal";
|
|
13895
|
-
const
|
|
11729
|
+
const project3 = await resolveGraphPrimitivesAppResolvers().getProject(
|
|
13896
11730
|
ctx,
|
|
13897
11731
|
scope.topicId ? String(scope.topicId) : scope.projectId
|
|
13898
11732
|
);
|
|
13899
11733
|
const registryRows = await listAudienceRegistryRows(ctx, {
|
|
13900
|
-
tenantId:
|
|
13901
|
-
workspaceId:
|
|
11734
|
+
tenantId: project3?.tenantId,
|
|
11735
|
+
workspaceId: project3?.workspaceId
|
|
13902
11736
|
});
|
|
13903
11737
|
const resolveAudienceClass = createEvidenceAudienceResolver(registryRows);
|
|
13904
11738
|
const viewerClass = resolveAudienceClass(audienceMode, "public");
|
|
13905
11739
|
const nodes = await getEvidenceNodesForScope(ctx, scope, { scanLimit });
|
|
13906
11740
|
const workspaceScopedNodes = nodes.filter(
|
|
13907
11741
|
(node) => nodeMatchesWorkspaceReasoningScope(node, {
|
|
13908
|
-
tenantId:
|
|
13909
|
-
workspaceId:
|
|
11742
|
+
tenantId: project3?.tenantId,
|
|
11743
|
+
workspaceId: project3?.workspaceId
|
|
13910
11744
|
})
|
|
13911
11745
|
);
|
|
13912
11746
|
return workspaceScopedNodes.filter(
|
|
@@ -14014,6 +11848,7 @@ var internalCreate2 = internalMutation({
|
|
|
14014
11848
|
sourceUrl: v.optional(v.string()),
|
|
14015
11849
|
sourceQuestionId: v.optional(v.string()),
|
|
14016
11850
|
userId: v.string(),
|
|
11851
|
+
rationale: v.string(),
|
|
14017
11852
|
linkedBeliefNodeId: v.optional(v.id("epistemicNodes")),
|
|
14018
11853
|
evidenceRelation: v.optional(v.string()),
|
|
14019
11854
|
confidence: v.optional(v.number()),
|
|
@@ -14077,6 +11912,7 @@ var internalCreate2 = internalMutation({
|
|
|
14077
11912
|
externalSourceType: args.externalSourceType,
|
|
14078
11913
|
sourceUrl: args.sourceUrl,
|
|
14079
11914
|
sourceQuestionId: args.sourceQuestionId,
|
|
11915
|
+
rationale: args.rationale,
|
|
14080
11916
|
linkedBeliefNodeId: args.linkedBeliefNodeId,
|
|
14081
11917
|
evidenceRelation: args.evidenceRelation,
|
|
14082
11918
|
confidence: args.confidence,
|
|
@@ -14091,6 +11927,7 @@ var internalCreate2 = internalMutation({
|
|
|
14091
11927
|
changedBy: args.userId,
|
|
14092
11928
|
isAgent: false,
|
|
14093
11929
|
projectId: scope.projectId,
|
|
11930
|
+
rationale: args.rationale,
|
|
14094
11931
|
newState: {
|
|
14095
11932
|
text: args.text.slice(0, 200),
|
|
14096
11933
|
kind,
|
|
@@ -15716,7 +13553,7 @@ function isScoredBeliefNode(node) {
|
|
|
15716
13553
|
if (numericConfidence) {
|
|
15717
13554
|
return true;
|
|
15718
13555
|
}
|
|
15719
|
-
return
|
|
13556
|
+
return hasResolvedPredictionOutcome(node.predictionMeta) || hasResolvedPredictionOutcome(metadata?.predictionMeta);
|
|
15720
13557
|
}
|
|
15721
13558
|
function getForbiddenMetadataKeys(metadata) {
|
|
15722
13559
|
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) {
|
|
@@ -16937,38 +14774,7 @@ __export(epistemicQuestions_exports, {
|
|
|
16937
14774
|
updateStatus: () => updateStatus4
|
|
16938
14775
|
});
|
|
16939
14776
|
|
|
16940
|
-
//
|
|
16941
|
-
function normalizeString2(value) {
|
|
16942
|
-
if (typeof value !== "string") {
|
|
16943
|
-
return void 0;
|
|
16944
|
-
}
|
|
16945
|
-
const trimmed = value.trim();
|
|
16946
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
16947
|
-
}
|
|
16948
|
-
function requireScopeId(...ids) {
|
|
16949
|
-
for (const id of ids) {
|
|
16950
|
-
const normalized = normalizeString2(id);
|
|
16951
|
-
if (normalized) {
|
|
16952
|
-
return normalized;
|
|
16953
|
-
}
|
|
16954
|
-
}
|
|
16955
|
-
throw new Error("No scope identifier provided (topicId or projectId required)");
|
|
16956
|
-
}
|
|
16957
|
-
async function resolveTopicProjectScope2(ctx, args) {
|
|
16958
|
-
const resolved = await resolveTopicProjectScope(ctx, {
|
|
16959
|
-
topicId: normalizeString2(args.topicId),
|
|
16960
|
-
projectId: normalizeString2(args.projectId)
|
|
16961
|
-
});
|
|
16962
|
-
const topicId = normalizeString2(resolved.topicId);
|
|
16963
|
-
const projectId = requireScopeId(
|
|
16964
|
-
resolved.projectId,
|
|
16965
|
-
args.projectId,
|
|
16966
|
-
topicId
|
|
16967
|
-
);
|
|
16968
|
-
return { projectId, ...topicId ? { topicId } : {} };
|
|
16969
|
-
}
|
|
16970
|
-
|
|
16971
|
-
// ../worktrees/src/v1/engine/worktreeWorkflowBridge.ts
|
|
14777
|
+
// src/workflowBridge.ts
|
|
16972
14778
|
function isLegacySprintDoc(doc) {
|
|
16973
14779
|
if (!doc || typeof doc !== "object") {
|
|
16974
14780
|
return false;
|
|
@@ -16994,7 +14800,7 @@ async function findPairedWorktreeForSprint(ctx, sprint) {
|
|
|
16994
14800
|
let topicId = getStringField(sprint, "topicId");
|
|
16995
14801
|
if (!topicId) {
|
|
16996
14802
|
try {
|
|
16997
|
-
const scope = await
|
|
14803
|
+
const scope = await resolveTopicProjectScope(ctx, {
|
|
16998
14804
|
topicId: getStringField(sprint, "topicId"),
|
|
16999
14805
|
projectId: getStringField(sprint, "projectId")
|
|
17000
14806
|
});
|
|
@@ -17854,13 +15660,13 @@ var internalGetByProject3 = internalQuery({
|
|
|
17854
15660
|
if (!projectScopeId) {
|
|
17855
15661
|
return [];
|
|
17856
15662
|
}
|
|
17857
|
-
const
|
|
15663
|
+
const project3 = await resolveGraphPrimitivesAppResolvers().getProject(
|
|
17858
15664
|
ctx,
|
|
17859
15665
|
projectScopeId
|
|
17860
15666
|
);
|
|
17861
15667
|
const registryRows = await listAudienceRegistryRows(ctx, {
|
|
17862
|
-
tenantId:
|
|
17863
|
-
workspaceId:
|
|
15668
|
+
tenantId: project3?.tenantId,
|
|
15669
|
+
workspaceId: project3?.workspaceId
|
|
17864
15670
|
});
|
|
17865
15671
|
const audienceClassByKey = new Map(
|
|
17866
15672
|
registryRows.map((row) => [
|
|
@@ -17879,8 +15685,8 @@ var internalGetByProject3 = internalQuery({
|
|
|
17879
15685
|
const nodes = await getQuestionNodesForScope(ctx, scope, { scanLimit });
|
|
17880
15686
|
const workspaceScopedNodes = nodes.filter(
|
|
17881
15687
|
(node) => nodeMatchesWorkspaceReasoningScope(node, {
|
|
17882
|
-
tenantId:
|
|
17883
|
-
workspaceId:
|
|
15688
|
+
tenantId: project3?.tenantId,
|
|
15689
|
+
workspaceId: project3?.workspaceId
|
|
17884
15690
|
})
|
|
17885
15691
|
);
|
|
17886
15692
|
const visibleNodes = workspaceScopedNodes.filter(
|
|
@@ -19768,7 +17574,7 @@ function throwStructuredSourceError(args) {
|
|
|
19768
17574
|
error.details = args.details;
|
|
19769
17575
|
throw error;
|
|
19770
17576
|
}
|
|
19771
|
-
function
|
|
17577
|
+
function normalizeString(value) {
|
|
19772
17578
|
if (typeof value !== "string") {
|
|
19773
17579
|
return void 0;
|
|
19774
17580
|
}
|
|
@@ -19796,7 +17602,7 @@ function generateSourceContentHash(identity) {
|
|
|
19796
17602
|
return Math.abs(hash).toString(16).padStart(8, "0");
|
|
19797
17603
|
}
|
|
19798
17604
|
function normalizeSourceUrl(url) {
|
|
19799
|
-
const trimmed =
|
|
17605
|
+
const trimmed = normalizeString(url);
|
|
19800
17606
|
if (!trimmed) {
|
|
19801
17607
|
throwStructuredSourceError({
|
|
19802
17608
|
message: "Source URL is required.",
|
|
@@ -19839,10 +17645,10 @@ async function findSourceByIdentity(ctx, args) {
|
|
|
19839
17645
|
let shaMatch = null;
|
|
19840
17646
|
for (const node of sourceNodes) {
|
|
19841
17647
|
const metadata = asRecord2(node.metadata);
|
|
19842
|
-
if (args.normalizedUrl &&
|
|
17648
|
+
if (args.normalizedUrl && normalizeString(metadata.url) === args.normalizedUrl && !urlMatch) {
|
|
19843
17649
|
urlMatch = node;
|
|
19844
17650
|
}
|
|
19845
|
-
if (args.sha &&
|
|
17651
|
+
if (args.sha && normalizeString(metadata.contentSha) === args.sha && !shaMatch) {
|
|
19846
17652
|
shaMatch = node;
|
|
19847
17653
|
}
|
|
19848
17654
|
if (urlMatch && (!args.sha || shaMatch)) {
|
|
@@ -19888,10 +17694,10 @@ function buildSourceMetadata(args) {
|
|
|
19888
17694
|
}
|
|
19889
17695
|
function sourceEmbeddingText(args) {
|
|
19890
17696
|
const lines = [
|
|
19891
|
-
|
|
19892
|
-
|
|
17697
|
+
normalizeString(args.title),
|
|
17698
|
+
normalizeString(args.url),
|
|
19893
17699
|
args.kind,
|
|
19894
|
-
|
|
17700
|
+
normalizeString(args.metadata.sourceDescription)
|
|
19895
17701
|
].filter((value) => Boolean(value));
|
|
19896
17702
|
return lines.join("\n");
|
|
19897
17703
|
}
|
|
@@ -19908,8 +17714,8 @@ var upsertSource = mutation({
|
|
|
19908
17714
|
},
|
|
19909
17715
|
returns: permissiveReturn,
|
|
19910
17716
|
handler: async (ctx, args) => {
|
|
19911
|
-
const normalizedUrl =
|
|
19912
|
-
const sha =
|
|
17717
|
+
const normalizedUrl = normalizeString(args.url) ? normalizeSourceUrl(args.url) : void 0;
|
|
17718
|
+
const sha = normalizeString(args.sha);
|
|
19913
17719
|
if (!normalizedUrl && !sha) {
|
|
19914
17720
|
throwStructuredSourceError({
|
|
19915
17721
|
message: "Source identity requires a URL or content SHA.",
|
|
@@ -19934,7 +17740,7 @@ var upsertSource = mutation({
|
|
|
19934
17740
|
});
|
|
19935
17741
|
}
|
|
19936
17742
|
const existingMetadata = asRecord2(existing.metadata);
|
|
19937
|
-
const existingSha =
|
|
17743
|
+
const existingSha = normalizeString(existingMetadata.contentSha);
|
|
19938
17744
|
if (sha && existingSha && existingSha !== sha) {
|
|
19939
17745
|
throwStructuredSourceError({
|
|
19940
17746
|
message: "Same URL cannot be reused with a different content hash.",
|
|
@@ -19960,7 +17766,7 @@ var upsertSource = mutation({
|
|
|
19960
17766
|
metadata: nextMetadata2,
|
|
19961
17767
|
updatedAt: now
|
|
19962
17768
|
};
|
|
19963
|
-
const title2 =
|
|
17769
|
+
const title2 = normalizeString(args.title);
|
|
19964
17770
|
if (title2) {
|
|
19965
17771
|
patch.title = title2;
|
|
19966
17772
|
patch.canonicalText = title2;
|
|
@@ -19989,7 +17795,7 @@ var upsertSource = mutation({
|
|
|
19989
17795
|
});
|
|
19990
17796
|
return await ctx.db.get(existing._id) ?? existing;
|
|
19991
17797
|
}
|
|
19992
|
-
if (!
|
|
17798
|
+
if (!normalizeString(args.topicId)) {
|
|
19993
17799
|
throwStructuredSourceError({
|
|
19994
17800
|
message: "topicId is required when creating a new source.",
|
|
19995
17801
|
status: 400,
|
|
@@ -20010,7 +17816,7 @@ var upsertSource = mutation({
|
|
|
20010
17816
|
await requireProjectAccess(ctx, scope.projectId, args.userId);
|
|
20011
17817
|
}
|
|
20012
17818
|
const globalId = generateGlobalId();
|
|
20013
|
-
const title =
|
|
17819
|
+
const title = normalizeString(args.title);
|
|
20014
17820
|
const nextMetadata = buildSourceMetadata({
|
|
20015
17821
|
normalizedUrl,
|
|
20016
17822
|
sha,
|
|
@@ -20109,7 +17915,7 @@ var findBySha = query({
|
|
|
20109
17915
|
},
|
|
20110
17916
|
returns: permissiveReturn,
|
|
20111
17917
|
handler: async (ctx, args) => {
|
|
20112
|
-
const sha =
|
|
17918
|
+
const sha = normalizeString(args.sha);
|
|
20113
17919
|
if (!sha) {
|
|
20114
17920
|
return null;
|
|
20115
17921
|
}
|
|
@@ -20195,374 +18001,6 @@ __export(ontology_matching_exports, {
|
|
|
20195
18001
|
scoreEntityTypeMatch: () => scoreEntityTypeMatch
|
|
20196
18002
|
});
|
|
20197
18003
|
|
|
20198
|
-
// ../../packages/contracts/src/text-matching.contract.ts
|
|
20199
|
-
var TOKEN_SPLIT_REGEX = /[^a-z0-9]+/;
|
|
20200
|
-
var NON_ALPHANUMERIC_REGEX = /[^a-z0-9]/g;
|
|
20201
|
-
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
20202
|
-
"the",
|
|
20203
|
-
"a",
|
|
20204
|
-
"an",
|
|
20205
|
-
"and",
|
|
20206
|
-
"or",
|
|
20207
|
-
"but",
|
|
20208
|
-
"in",
|
|
20209
|
-
"on",
|
|
20210
|
-
"at",
|
|
20211
|
-
"to",
|
|
20212
|
-
"for",
|
|
20213
|
-
"of",
|
|
20214
|
-
"with",
|
|
20215
|
-
"by",
|
|
20216
|
-
"from",
|
|
20217
|
-
"is",
|
|
20218
|
-
"it",
|
|
20219
|
-
"as",
|
|
20220
|
-
"be",
|
|
20221
|
-
"was",
|
|
20222
|
-
"are",
|
|
20223
|
-
"this",
|
|
20224
|
-
"that",
|
|
20225
|
-
"has",
|
|
20226
|
-
"had",
|
|
20227
|
-
"have",
|
|
20228
|
-
"not",
|
|
20229
|
-
"all",
|
|
20230
|
-
"can",
|
|
20231
|
-
"do",
|
|
20232
|
-
"its",
|
|
20233
|
-
"may",
|
|
20234
|
-
"will",
|
|
20235
|
-
"how",
|
|
20236
|
-
"what",
|
|
20237
|
-
"which",
|
|
20238
|
-
"who",
|
|
20239
|
-
"when",
|
|
20240
|
-
"where",
|
|
20241
|
-
"than",
|
|
20242
|
-
"then",
|
|
20243
|
-
"each",
|
|
20244
|
-
"into",
|
|
20245
|
-
"such",
|
|
20246
|
-
"any",
|
|
20247
|
-
"been",
|
|
20248
|
-
"if",
|
|
20249
|
-
"would",
|
|
20250
|
-
"about",
|
|
20251
|
-
"should",
|
|
20252
|
-
"these",
|
|
20253
|
-
"those",
|
|
20254
|
-
"their",
|
|
20255
|
-
"we",
|
|
20256
|
-
"our",
|
|
20257
|
-
"so"
|
|
20258
|
-
]);
|
|
20259
|
-
function tokenizeSearchText(text) {
|
|
20260
|
-
return text.toLowerCase().split(TOKEN_SPLIT_REGEX).filter((token) => token.length >= 2 && !STOP_WORDS.has(token));
|
|
20261
|
-
}
|
|
20262
|
-
function stemToken(word) {
|
|
20263
|
-
if (word.length <= 4) {
|
|
20264
|
-
return word;
|
|
20265
|
-
}
|
|
20266
|
-
if (word.endsWith("ation")) {
|
|
20267
|
-
return word.slice(0, -5);
|
|
20268
|
-
}
|
|
20269
|
-
if (word.endsWith("ment")) {
|
|
20270
|
-
return word.slice(0, -4);
|
|
20271
|
-
}
|
|
20272
|
-
if (word.endsWith("ness")) {
|
|
20273
|
-
return word.slice(0, -4);
|
|
20274
|
-
}
|
|
20275
|
-
if (word.endsWith("ical")) {
|
|
20276
|
-
return word.slice(0, -4);
|
|
20277
|
-
}
|
|
20278
|
-
if (word.endsWith("tion")) {
|
|
20279
|
-
return word.slice(0, -4);
|
|
20280
|
-
}
|
|
20281
|
-
if (word.endsWith("sion")) {
|
|
20282
|
-
return word.slice(0, -4);
|
|
20283
|
-
}
|
|
20284
|
-
if (word.endsWith("ing")) {
|
|
20285
|
-
return word.slice(0, -3);
|
|
20286
|
-
}
|
|
20287
|
-
if (word.endsWith("ous")) {
|
|
20288
|
-
return word.slice(0, -3);
|
|
20289
|
-
}
|
|
20290
|
-
if (word.endsWith("ive")) {
|
|
20291
|
-
return word.slice(0, -3);
|
|
20292
|
-
}
|
|
20293
|
-
if (word.endsWith("ity")) {
|
|
20294
|
-
return word.slice(0, -3);
|
|
20295
|
-
}
|
|
20296
|
-
if (word.endsWith("ics")) {
|
|
20297
|
-
return word.slice(0, -3);
|
|
20298
|
-
}
|
|
20299
|
-
if (word.endsWith("ly")) {
|
|
20300
|
-
return word.slice(0, -2);
|
|
20301
|
-
}
|
|
20302
|
-
if (word.endsWith("ed")) {
|
|
20303
|
-
return word.slice(0, -2);
|
|
20304
|
-
}
|
|
20305
|
-
if (word.endsWith("er")) {
|
|
20306
|
-
return word.slice(0, -2);
|
|
20307
|
-
}
|
|
20308
|
-
if (word.endsWith("es")) {
|
|
20309
|
-
return word.slice(0, -2);
|
|
20310
|
-
}
|
|
20311
|
-
if (word.endsWith("al")) {
|
|
20312
|
-
return word.slice(0, -2);
|
|
20313
|
-
}
|
|
20314
|
-
if (word.endsWith("ic")) {
|
|
20315
|
-
return word.slice(0, -2);
|
|
20316
|
-
}
|
|
20317
|
-
if (word.endsWith("s") && !word.endsWith("ss")) {
|
|
20318
|
-
return word.slice(0, -1);
|
|
20319
|
-
}
|
|
20320
|
-
return word;
|
|
20321
|
-
}
|
|
20322
|
-
function tokenOverlapScore(queryTokens, textTokens) {
|
|
20323
|
-
if (queryTokens.length === 0 || textTokens.length === 0) {
|
|
20324
|
-
return 0;
|
|
20325
|
-
}
|
|
20326
|
-
const stemmedText = new Set(textTokens.map(stemToken));
|
|
20327
|
-
let matchCount = 0;
|
|
20328
|
-
for (const queryToken of queryTokens) {
|
|
20329
|
-
const stemmedQuery = stemToken(queryToken);
|
|
20330
|
-
if (stemmedText.has(stemmedQuery)) {
|
|
20331
|
-
matchCount += 1;
|
|
20332
|
-
continue;
|
|
20333
|
-
}
|
|
20334
|
-
for (const textToken of stemmedText) {
|
|
20335
|
-
if (textToken.startsWith(stemmedQuery) || stemmedQuery.startsWith(textToken)) {
|
|
20336
|
-
matchCount += 0.5;
|
|
20337
|
-
break;
|
|
20338
|
-
}
|
|
20339
|
-
}
|
|
20340
|
-
}
|
|
20341
|
-
return matchCount / queryTokens.length;
|
|
20342
|
-
}
|
|
20343
|
-
function bigramTokenize(text) {
|
|
20344
|
-
const normalized = text.toLowerCase().replace(NON_ALPHANUMERIC_REGEX, "");
|
|
20345
|
-
const bigrams = /* @__PURE__ */ new Set();
|
|
20346
|
-
for (let i = 0; i < normalized.length - 1; i++) {
|
|
20347
|
-
bigrams.add(normalized.slice(i, i + 2));
|
|
20348
|
-
}
|
|
20349
|
-
return bigrams;
|
|
20350
|
-
}
|
|
20351
|
-
function wordTokenize(text) {
|
|
20352
|
-
return text.toLowerCase().split(TOKEN_SPLIT_REGEX).filter((token) => token.length > 1);
|
|
20353
|
-
}
|
|
20354
|
-
function jaccardSimilarity(setA, setB) {
|
|
20355
|
-
if (setA.size === 0 && setB.size === 0) {
|
|
20356
|
-
return 0;
|
|
20357
|
-
}
|
|
20358
|
-
let intersectionSize = 0;
|
|
20359
|
-
const smaller = setA.size <= setB.size ? setA : setB;
|
|
20360
|
-
const larger = setA.size <= setB.size ? setB : setA;
|
|
20361
|
-
for (const item of smaller) {
|
|
20362
|
-
if (larger.has(item)) {
|
|
20363
|
-
intersectionSize++;
|
|
20364
|
-
}
|
|
20365
|
-
}
|
|
20366
|
-
const unionSize = setA.size + setB.size - intersectionSize;
|
|
20367
|
-
return unionSize === 0 ? 0 : intersectionSize / unionSize;
|
|
20368
|
-
}
|
|
20369
|
-
function wordOverlapScore(inputWords, typeWords) {
|
|
20370
|
-
if (typeWords.length === 0) {
|
|
20371
|
-
return 0;
|
|
20372
|
-
}
|
|
20373
|
-
let matches = 0;
|
|
20374
|
-
for (const word of typeWords) {
|
|
20375
|
-
if (inputWords.includes(word)) {
|
|
20376
|
-
matches++;
|
|
20377
|
-
}
|
|
20378
|
-
}
|
|
20379
|
-
return matches / typeWords.length;
|
|
20380
|
-
}
|
|
20381
|
-
function prepareLexicalQuery(query2) {
|
|
20382
|
-
return {
|
|
20383
|
-
raw: query2,
|
|
20384
|
-
tokens: tokenizeSearchText(query2),
|
|
20385
|
-
words: wordTokenize(query2),
|
|
20386
|
-
bigrams: bigramTokenize(query2)
|
|
20387
|
-
};
|
|
20388
|
-
}
|
|
20389
|
-
function scoreLexicalSignal(query2, signal) {
|
|
20390
|
-
const text = signal.text?.trim();
|
|
20391
|
-
if (!text) {
|
|
20392
|
-
return 0;
|
|
20393
|
-
}
|
|
20394
|
-
switch (signal.strategy ?? "tokenOverlap") {
|
|
20395
|
-
case "bigramJaccard":
|
|
20396
|
-
return jaccardSimilarity(query2.bigrams, bigramTokenize(text));
|
|
20397
|
-
case "wordOverlap":
|
|
20398
|
-
return wordOverlapScore(query2.words, wordTokenize(text));
|
|
20399
|
-
default:
|
|
20400
|
-
return tokenOverlapScore(query2.tokens, tokenizeSearchText(text));
|
|
20401
|
-
}
|
|
20402
|
-
}
|
|
20403
|
-
function scoreLexicalSignals(query2, signals) {
|
|
20404
|
-
let weightedScore = 0;
|
|
20405
|
-
let totalWeight = 0;
|
|
20406
|
-
for (const signal of signals) {
|
|
20407
|
-
if (!signal.text?.trim() || signal.weight <= 0) {
|
|
20408
|
-
continue;
|
|
20409
|
-
}
|
|
20410
|
-
weightedScore += scoreLexicalSignal(query2, signal) * signal.weight;
|
|
20411
|
-
totalWeight += signal.weight;
|
|
20412
|
-
}
|
|
20413
|
-
return totalWeight === 0 ? 0 : weightedScore / totalWeight;
|
|
20414
|
-
}
|
|
20415
|
-
function rankWindowScore(index, total) {
|
|
20416
|
-
if (total <= 1) {
|
|
20417
|
-
return 1;
|
|
20418
|
-
}
|
|
20419
|
-
const clampedIndex = Math.max(0, Math.min(index, total - 1));
|
|
20420
|
-
return 1 - clampedIndex / (total - 1);
|
|
20421
|
-
}
|
|
20422
|
-
function rerankLexicalWindow(query2, items, getText, options) {
|
|
20423
|
-
const preparedQuery = prepareLexicalQuery(query2);
|
|
20424
|
-
if (preparedQuery.tokens.length === 0 || items.length <= 1) {
|
|
20425
|
-
return items;
|
|
20426
|
-
}
|
|
20427
|
-
const lexicalWeight = options?.lexicalWeight ?? 0.65;
|
|
20428
|
-
const rankWeight = options?.rankWeight ?? 0.35;
|
|
20429
|
-
return items.map((item, index) => {
|
|
20430
|
-
const lexicalScore = scoreLexicalSignals(preparedQuery, [
|
|
20431
|
-
{ text: getText(item) ?? "", weight: 1, strategy: "tokenOverlap" }
|
|
20432
|
-
]);
|
|
20433
|
-
const rankScore = rankWindowScore(index, items.length);
|
|
20434
|
-
return {
|
|
20435
|
-
item,
|
|
20436
|
-
combinedScore: lexicalScore * lexicalWeight + rankScore * rankWeight
|
|
20437
|
-
};
|
|
20438
|
-
}).sort((left, right) => right.combinedScore - left.combinedScore).map(({ item }) => item);
|
|
20439
|
-
}
|
|
20440
|
-
|
|
20441
|
-
// ../../packages/contracts/src/v1/ontologies/v1.ts
|
|
20442
|
-
var MATCH_WEIGHTS = {
|
|
20443
|
-
tokenOverlap: 0.35,
|
|
20444
|
-
bigramSimilarity: 0.25,
|
|
20445
|
-
wordOverlap: 0.2,
|
|
20446
|
-
descriptionBonus: 0.2
|
|
20447
|
-
};
|
|
20448
|
-
function scoreEntityTypeMatch(inputText, entityType) {
|
|
20449
|
-
const preparedQuery = prepareLexicalQuery(inputText);
|
|
20450
|
-
const labelText = `${entityType.label} ${entityType.value}`;
|
|
20451
|
-
const tokenScore = scoreLexicalSignals(preparedQuery, [
|
|
20452
|
-
{ text: labelText, weight: 1, strategy: "tokenOverlap" }
|
|
20453
|
-
]);
|
|
20454
|
-
const labelBigrams = bigramTokenize(entityType.label);
|
|
20455
|
-
const bigramScore = jaccardSimilarity(preparedQuery.bigrams, labelBigrams);
|
|
20456
|
-
const labelWords = wordTokenize(labelText);
|
|
20457
|
-
const wordScore = wordOverlapScore(preparedQuery.words, labelWords);
|
|
20458
|
-
let descScore = 0;
|
|
20459
|
-
if (entityType.description) {
|
|
20460
|
-
descScore = scoreLexicalSignals(preparedQuery, [
|
|
20461
|
-
{ text: entityType.description, weight: 1, strategy: "tokenOverlap" }
|
|
20462
|
-
]);
|
|
20463
|
-
}
|
|
20464
|
-
let subtypeBonus = 0;
|
|
20465
|
-
if (entityType.subtypes && entityType.subtypes.length > 0) {
|
|
20466
|
-
for (const subtype of entityType.subtypes) {
|
|
20467
|
-
const subtypeScore = scoreLexicalSignals(preparedQuery, [
|
|
20468
|
-
{
|
|
20469
|
-
text: `${subtype.label} ${subtype.value} ${subtype.description || ""}`,
|
|
20470
|
-
weight: 1,
|
|
20471
|
-
strategy: "tokenOverlap"
|
|
20472
|
-
}
|
|
20473
|
-
]);
|
|
20474
|
-
subtypeBonus = Math.max(subtypeBonus, subtypeScore * 0.3);
|
|
20475
|
-
}
|
|
20476
|
-
}
|
|
20477
|
-
const score = Math.min(
|
|
20478
|
-
1,
|
|
20479
|
-
tokenScore * MATCH_WEIGHTS.tokenOverlap + bigramScore * MATCH_WEIGHTS.bigramSimilarity + wordScore * MATCH_WEIGHTS.wordOverlap + descScore * MATCH_WEIGHTS.descriptionBonus + subtypeBonus
|
|
20480
|
-
);
|
|
20481
|
-
const reasons = [];
|
|
20482
|
-
if (tokenScore > 0.3) {
|
|
20483
|
-
reasons.push(`stem match: ${(tokenScore * 100).toFixed(0)}%`);
|
|
20484
|
-
}
|
|
20485
|
-
if (bigramScore > 0.3) {
|
|
20486
|
-
reasons.push(`text similarity: ${(bigramScore * 100).toFixed(0)}%`);
|
|
20487
|
-
}
|
|
20488
|
-
if (wordScore > 0.3) {
|
|
20489
|
-
reasons.push(`word match: ${(wordScore * 100).toFixed(0)}%`);
|
|
20490
|
-
}
|
|
20491
|
-
if (descScore > 0.2) {
|
|
20492
|
-
reasons.push("description match");
|
|
20493
|
-
}
|
|
20494
|
-
if (subtypeBonus > 0.05) {
|
|
20495
|
-
reasons.push("subtype match");
|
|
20496
|
-
}
|
|
20497
|
-
const reason = reasons.length > 0 ? reasons.join(", ") : "low similarity";
|
|
20498
|
-
return {
|
|
20499
|
-
entityType: entityType.value,
|
|
20500
|
-
label: entityType.label,
|
|
20501
|
-
score,
|
|
20502
|
-
reason
|
|
20503
|
-
};
|
|
20504
|
-
}
|
|
20505
|
-
function rankEntityTypeMatches(inputText, entityTypes, options) {
|
|
20506
|
-
const minScore = options?.minScore ?? 0.05;
|
|
20507
|
-
const limit = options?.limit ?? 10;
|
|
20508
|
-
const matches = entityTypes.map((et) => scoreEntityTypeMatch(inputText, et)).filter((m) => m.score >= minScore).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
20509
|
-
return matches;
|
|
20510
|
-
}
|
|
20511
|
-
function scoreEntityConnection(nodeText, candidate, options) {
|
|
20512
|
-
const preparedQuery = prepareLexicalQuery(nodeText);
|
|
20513
|
-
const connectivityWeight = options?.connectivityWeight ?? 0.3;
|
|
20514
|
-
const textWeight = 1 - connectivityWeight;
|
|
20515
|
-
const candidateText = `${candidate.title} ${candidate.canonicalText}`;
|
|
20516
|
-
const tokenScore = scoreLexicalSignals(preparedQuery, [
|
|
20517
|
-
{ text: candidateText, weight: 1, strategy: "tokenOverlap" }
|
|
20518
|
-
]);
|
|
20519
|
-
const textScore = scoreLexicalSignals(preparedQuery, [
|
|
20520
|
-
{ text: candidateText, weight: 1, strategy: "bigramJaccard" }
|
|
20521
|
-
]);
|
|
20522
|
-
const wordScore = scoreLexicalSignals(preparedQuery, [
|
|
20523
|
-
{ text: candidateText, weight: 1, strategy: "wordOverlap" }
|
|
20524
|
-
]);
|
|
20525
|
-
const maxConnections = Math.max(
|
|
20526
|
-
1,
|
|
20527
|
-
candidate.connectedBeliefCount + candidate.connectedEvidenceCount
|
|
20528
|
-
);
|
|
20529
|
-
const connectivityScore = Math.min(1, maxConnections / 10);
|
|
20530
|
-
const combinedTextScore = tokenScore * 0.45 + textScore * 0.35 + wordScore * 0.2;
|
|
20531
|
-
const score = combinedTextScore * textWeight + connectivityScore * connectivityWeight;
|
|
20532
|
-
const suggestedEdgeType = suggestEdgeType(candidate.entityType);
|
|
20533
|
-
const reason = tokenScore > 0.3 ? `stem match: ${(tokenScore * 100).toFixed(0)}%` : textScore > 0.2 ? `name similarity: ${(textScore * 100).toFixed(0)}%` : wordScore > 0.2 ? `keyword match: ${(wordScore * 100).toFixed(0)}%` : `connectivity: ${candidate.connectedBeliefCount} beliefs`;
|
|
20534
|
-
return {
|
|
20535
|
-
entityNodeId: candidate.nodeId,
|
|
20536
|
-
entityType: candidate.entityType,
|
|
20537
|
-
title: candidate.title,
|
|
20538
|
-
score,
|
|
20539
|
-
suggestedEdgeType,
|
|
20540
|
-
reason
|
|
20541
|
-
};
|
|
20542
|
-
}
|
|
20543
|
-
function suggestEdgeType(entityType) {
|
|
20544
|
-
switch (entityType) {
|
|
20545
|
-
case "company":
|
|
20546
|
-
case "person":
|
|
20547
|
-
case "investor":
|
|
20548
|
-
return "contains";
|
|
20549
|
-
case "function":
|
|
20550
|
-
case "value_chain":
|
|
20551
|
-
return "impacts";
|
|
20552
|
-
default:
|
|
20553
|
-
return "contains";
|
|
20554
|
-
}
|
|
20555
|
-
}
|
|
20556
|
-
function rankEntityConnections(nodeText, candidates, options) {
|
|
20557
|
-
const minScore = options?.minScore ?? 0.05;
|
|
20558
|
-
const limit = options?.limit ?? 10;
|
|
20559
|
-
return candidates.map(
|
|
20560
|
-
(c) => scoreEntityConnection(nodeText, c, {
|
|
20561
|
-
connectivityWeight: options?.connectivityWeight
|
|
20562
|
-
})
|
|
20563
|
-
).filter((m) => m.score >= minScore).sort((a, b) => b.score - a.score).slice(0, limit);
|
|
20564
|
-
}
|
|
20565
|
-
|
|
20566
18004
|
// src/ontologyRegistry.ts
|
|
20567
18005
|
var ontologyRegistry_exports = {};
|
|
20568
18006
|
__export(ontologyRegistry_exports, {
|