@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
|
@@ -1,1722 +1,22 @@
|
|
|
1
1
|
import { v } from 'convex/values';
|
|
2
|
+
import { checkScopeAccess, checkProjectAccess } from '@lucern/access-control/access';
|
|
3
|
+
import { getCurrentUserId } from '@lucern/access-control/auth';
|
|
4
|
+
import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
|
|
2
5
|
import { componentsGeneric, internalMutationGeneric, mutationGeneric, anyApi, queryGeneric, internalQueryGeneric } from 'convex/server';
|
|
6
|
+
import { 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 } from '@lucern/confidence';
|
|
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';
|
|
3
11
|
|
|
4
12
|
// src/epistemicContracts.ts
|
|
5
13
|
var api = anyApi;
|
|
6
14
|
componentsGeneric();
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
var
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
const normalized = value.trim();
|
|
15
|
-
return normalized.length > 0 ? normalized : void 0;
|
|
16
|
-
}
|
|
17
|
-
function readStringArray(value) {
|
|
18
|
-
if (!Array.isArray(value)) {
|
|
19
|
-
return [];
|
|
20
|
-
}
|
|
21
|
-
return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
|
|
22
|
-
}
|
|
23
|
-
function readMetadata(topic) {
|
|
24
|
-
return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
|
|
25
|
-
}
|
|
26
|
-
function readLegacyProjectId(value) {
|
|
27
|
-
if (!value) {
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
return readNonEmptyString(value[LEGACY_SCOPE_FIELD]);
|
|
31
|
-
}
|
|
32
|
-
function coerceVisibility(value) {
|
|
33
|
-
return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
|
|
34
|
-
}
|
|
35
|
-
function coerceStatus(value) {
|
|
36
|
-
return value === "active" || value === "archived" || value === "watching" ? value : void 0;
|
|
37
|
-
}
|
|
38
|
-
function mapProjectType(topic, metadata) {
|
|
39
|
-
const explicit = readNonEmptyString(metadata.projectType);
|
|
40
|
-
if (explicit) {
|
|
41
|
-
return explicit;
|
|
42
|
-
}
|
|
43
|
-
if (topic.type === "theme") {
|
|
44
|
-
return "thematic";
|
|
45
|
-
}
|
|
46
|
-
return readNonEmptyString(topic.type) || "general";
|
|
47
|
-
}
|
|
48
|
-
function isProjectLikeTopic(topic) {
|
|
49
|
-
const metadata = readMetadata(topic);
|
|
50
|
-
return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
|
|
51
|
-
}
|
|
52
|
-
async function resolveTopicDoc(ctx, scopeId) {
|
|
53
|
-
if (ctx?.db && typeof ctx.db.get === "function") {
|
|
54
|
-
try {
|
|
55
|
-
const directTopic = await ctx.db.get(scopeId);
|
|
56
|
-
if (directTopic) {
|
|
57
|
-
return directTopic;
|
|
58
|
-
}
|
|
59
|
-
} catch {
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
if (typeof ctx.runQuery !== "function") {
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
try {
|
|
66
|
-
const topic = await ctx.runQuery(api.topics.get, {
|
|
67
|
-
id: String(scopeId)
|
|
68
|
-
});
|
|
69
|
-
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
70
|
-
return topic;
|
|
71
|
-
}
|
|
72
|
-
} catch {
|
|
73
|
-
}
|
|
74
|
-
try {
|
|
75
|
-
const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
76
|
-
projectId: String(scopeId)
|
|
77
|
-
});
|
|
78
|
-
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
79
|
-
return topic;
|
|
80
|
-
}
|
|
81
|
-
} catch {
|
|
82
|
-
}
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
function materializeTopicProjectOverlay(topic, idMode = "legacy") {
|
|
86
|
-
const metadata = readMetadata(topic);
|
|
87
|
-
const topicId = String(topic._id);
|
|
88
|
-
const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
|
|
89
|
-
const storageProjectId = legacyProjectId || topicId;
|
|
90
|
-
const outwardId = idMode === "topic" ? topicId : storageProjectId;
|
|
91
|
-
const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
|
|
92
|
-
const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
|
|
93
|
-
const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
|
|
94
|
-
const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
|
|
95
|
-
return {
|
|
96
|
-
...metadata,
|
|
97
|
-
_id: outwardId,
|
|
98
|
-
projectId: outwardId,
|
|
99
|
-
topicId,
|
|
100
|
-
storageProjectId,
|
|
101
|
-
legacyProjectId,
|
|
102
|
-
name: readNonEmptyString(topic.name) || "Untitled Theme",
|
|
103
|
-
type: mapProjectType(topic, metadata),
|
|
104
|
-
description: readNonEmptyString(topic.description),
|
|
105
|
-
ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
|
|
106
|
-
sharedWith: readStringArray(metadata.sharedWith),
|
|
107
|
-
visibility,
|
|
108
|
-
tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
|
|
109
|
-
workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
|
|
110
|
-
status,
|
|
111
|
-
tags: readStringArray(metadata.tags),
|
|
112
|
-
chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
|
|
113
|
-
artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
|
|
114
|
-
lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
|
|
115
|
-
_creationTime: typeof topic._creationTime === "number" ? topic._creationTime : createdAt,
|
|
116
|
-
createdAt,
|
|
117
|
-
updatedAt
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
|
|
121
|
-
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
122
|
-
if (!topic) {
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
|
|
126
|
-
return null;
|
|
127
|
-
}
|
|
128
|
-
return materializeTopicProjectOverlay(topic, options.idMode);
|
|
129
|
-
}
|
|
130
|
-
async function listTopicProjectOverlays(ctx, options = {}) {
|
|
131
|
-
let allTopics = [];
|
|
132
|
-
if (ctx?.db?.query && typeof ctx.db.query === "function") {
|
|
133
|
-
try {
|
|
134
|
-
allTopics = await ctx.db.query("topics").collect();
|
|
135
|
-
} catch {
|
|
136
|
-
allTopics = [];
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
|
|
140
|
-
allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
|
|
141
|
-
}
|
|
142
|
-
return allTopics.filter(
|
|
143
|
-
(topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
|
|
144
|
-
).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// ../access-control/src/projectGrantsBridge.ts
|
|
148
|
-
var PROJECT_GRANT_STATUSES = ["active", "revoked", "expired"];
|
|
149
|
-
function normalizeString(value) {
|
|
150
|
-
if (typeof value !== "string") {
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
const trimmed = value.trim();
|
|
154
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
155
|
-
}
|
|
156
|
-
async function resolveGrantScopeIds(ctx, args) {
|
|
157
|
-
const topicId = normalizeString(args.topicId);
|
|
158
|
-
const projectId = normalizeString(args.projectId);
|
|
159
|
-
for (const scopeId of [topicId, projectId]) {
|
|
160
|
-
if (!scopeId) {
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
try {
|
|
164
|
-
const overlay = await resolveTopicProjectOverlay(ctx, scopeId, {
|
|
165
|
-
idMode: "legacy",
|
|
166
|
-
projectLikeOnly: false
|
|
167
|
-
});
|
|
168
|
-
if (overlay) {
|
|
169
|
-
return {
|
|
170
|
-
topicId: normalizeString(overlay.topicId) ?? topicId,
|
|
171
|
-
projectId: normalizeString(overlay.projectId) ?? projectId ?? scopeId
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
} catch {
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return { topicId, projectId };
|
|
178
|
-
}
|
|
179
|
-
async function normalizeProjectGrantRow(ctx, row) {
|
|
180
|
-
const scope = await resolveGrantScopeIds(ctx, {
|
|
181
|
-
topicId: row.topicId,
|
|
182
|
-
projectId: row.projectId
|
|
183
|
-
});
|
|
184
|
-
return {
|
|
185
|
-
...row,
|
|
186
|
-
...scope.topicId ? { topicId: scope.topicId } : {},
|
|
187
|
-
...scope.projectId ?? scope.topicId ? { projectId: scope.projectId ?? scope.topicId } : {}
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
async function normalizeProjectGrantRows(ctx, rows) {
|
|
191
|
-
return await Promise.all(rows.map((row) => normalizeProjectGrantRow(ctx, row)));
|
|
192
|
-
}
|
|
193
|
-
async function listProjectGrantsByPrincipal(ctx, principalId) {
|
|
194
|
-
const rows = await Promise.all(
|
|
195
|
-
PROJECT_GRANT_STATUSES.map(
|
|
196
|
-
(status) => ctx.db.query("projectGrants").withIndex(
|
|
197
|
-
"by_principal_status",
|
|
198
|
-
(q) => q.eq("principalId", principalId).eq("status", status)
|
|
199
|
-
).collect()
|
|
200
|
-
)
|
|
201
|
-
);
|
|
202
|
-
return await normalizeProjectGrantRows(ctx, rows.flat());
|
|
203
|
-
}
|
|
204
|
-
async function listProjectGrantsByGroup(ctx, groupId) {
|
|
205
|
-
const rows = await Promise.all(
|
|
206
|
-
PROJECT_GRANT_STATUSES.map(
|
|
207
|
-
(status) => ctx.db.query("projectGrants").withIndex(
|
|
208
|
-
"by_group_status",
|
|
209
|
-
(q) => q.eq("groupId", groupId).eq("status", status)
|
|
210
|
-
).collect()
|
|
211
|
-
)
|
|
212
|
-
);
|
|
213
|
-
return await normalizeProjectGrantRows(ctx, rows.flat());
|
|
214
|
-
}
|
|
215
|
-
function buildScopeMatchers(inputScopeId, resolved) {
|
|
216
|
-
return new Set(
|
|
217
|
-
[inputScopeId, resolved.topicId, resolved.projectId].map((value) => normalizeString(value)).filter((value) => Boolean(value))
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
function matchesResolvedScope(row, scopeIds) {
|
|
221
|
-
const rowTopicId = normalizeString(row.topicId);
|
|
222
|
-
const rowProjectId = normalizeString(row.projectId);
|
|
223
|
-
return rowTopicId !== void 0 && scopeIds.has(rowTopicId) || rowProjectId !== void 0 && scopeIds.has(rowProjectId);
|
|
224
|
-
}
|
|
225
|
-
async function bridgeListProjectGrantsByTopicAndPrincipal(ctx, topicId, principalId) {
|
|
226
|
-
const resolved = await resolveGrantScopeIds(ctx, { topicId });
|
|
227
|
-
const scopeIds = buildScopeMatchers(topicId, resolved);
|
|
228
|
-
const rows = await listProjectGrantsByPrincipal(ctx, principalId);
|
|
229
|
-
return rows.filter((row) => matchesResolvedScope(row, scopeIds));
|
|
230
|
-
}
|
|
231
|
-
async function bridgeListProjectGrantsByTopicAndGroup(ctx, topicId, groupId) {
|
|
232
|
-
const resolved = await resolveGrantScopeIds(ctx, { topicId });
|
|
233
|
-
const scopeIds = buildScopeMatchers(topicId, resolved);
|
|
234
|
-
const rows = await listProjectGrantsByGroup(ctx, groupId);
|
|
235
|
-
return rows.filter((row) => matchesResolvedScope(row, scopeIds));
|
|
236
|
-
}
|
|
237
|
-
async function bridgeListProjectGrantsByPrincipalStatus(ctx, principalId, status) {
|
|
238
|
-
const rows = await listProjectGrantsByPrincipal(ctx, principalId);
|
|
239
|
-
return rows.filter((row) => row.status === status);
|
|
240
|
-
}
|
|
241
|
-
async function bridgeListProjectGrantsByGroupStatus(ctx, groupId, status) {
|
|
242
|
-
const rows = await listProjectGrantsByGroup(ctx, groupId);
|
|
243
|
-
return rows.filter((row) => row.status === status);
|
|
244
|
-
}
|
|
245
|
-
async function bridgeInsertProjectGrant(ctx, value) {
|
|
246
|
-
const resolved = await resolveGrantScopeIds(ctx, value);
|
|
247
|
-
return await ctx.db.insert("projectGrants", {
|
|
248
|
-
...value,
|
|
249
|
-
...resolved.topicId ? { topicId: resolved.topicId } : {},
|
|
250
|
-
...resolved.projectId ?? resolved.topicId ? { projectId: resolved.projectId ?? resolved.topicId } : {}
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// ../access-control/src/resolvers.ts
|
|
255
|
-
async function findUserByClerkId(ctx, clerkId) {
|
|
256
|
-
const normalizedClerkId = clerkId.trim();
|
|
257
|
-
if (!normalizedClerkId) {
|
|
258
|
-
return null;
|
|
259
|
-
}
|
|
260
|
-
if (typeof ctx.runQuery === "function") {
|
|
261
|
-
try {
|
|
262
|
-
const bridgedUser = await ctx.runQuery(api.users.getUserByClerkId, {
|
|
263
|
-
clerkId: normalizedClerkId
|
|
264
|
-
});
|
|
265
|
-
if (bridgedUser) {
|
|
266
|
-
return bridgedUser;
|
|
267
|
-
}
|
|
268
|
-
} catch {
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
try {
|
|
272
|
-
const users = await ctx.db.query("users").collect();
|
|
273
|
-
return users.find((user) => String(user.clerkId ?? "") === normalizedClerkId) ?? null;
|
|
274
|
-
} catch {
|
|
275
|
-
return null;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
async function findUserByPrincipalId(ctx, principalId) {
|
|
279
|
-
const normalizedPrincipalId = principalId.trim();
|
|
280
|
-
if (!normalizedPrincipalId) {
|
|
281
|
-
return null;
|
|
282
|
-
}
|
|
283
|
-
try {
|
|
284
|
-
const users = await ctx.db.query("users").collect();
|
|
285
|
-
return users.find(
|
|
286
|
-
(user) => String(user.defaultPrincipalId ?? "") === normalizedPrincipalId
|
|
287
|
-
) ?? null;
|
|
288
|
-
} catch {
|
|
289
|
-
return null;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
async function findAgentByPrincipalId(ctx, principalId) {
|
|
293
|
-
const normalizedPrincipalId = principalId.trim();
|
|
294
|
-
if (!normalizedPrincipalId) {
|
|
295
|
-
return null;
|
|
296
|
-
}
|
|
297
|
-
if (typeof ctx.runQuery === "function") {
|
|
298
|
-
try {
|
|
299
|
-
const bridgedAgent = await ctx.runQuery(
|
|
300
|
-
api.agents.getAgentByPrincipalId,
|
|
301
|
-
{
|
|
302
|
-
principalId: normalizedPrincipalId
|
|
303
|
-
}
|
|
304
|
-
);
|
|
305
|
-
if (bridgedAgent) {
|
|
306
|
-
return bridgedAgent;
|
|
307
|
-
}
|
|
308
|
-
} catch {
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
try {
|
|
312
|
-
const agents = await ctx.db.query("agents").collect();
|
|
313
|
-
return agents.find(
|
|
314
|
-
(agent) => String(agent.principalId ?? "") === normalizedPrincipalId
|
|
315
|
-
) ?? null;
|
|
316
|
-
} catch {
|
|
317
|
-
return null;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
function defaultResolvers() {
|
|
321
|
-
return {
|
|
322
|
-
async getProject(ctx, topicId) {
|
|
323
|
-
return await resolveTopicProjectOverlay(ctx, topicId, {
|
|
324
|
-
idMode: "legacy",
|
|
325
|
-
projectLikeOnly: false
|
|
326
|
-
});
|
|
327
|
-
},
|
|
328
|
-
async listTopics(ctx) {
|
|
329
|
-
return await listTopicProjectOverlays(ctx, { idMode: "legacy" });
|
|
330
|
-
},
|
|
331
|
-
async listTopicsByOwner(ctx, ownerId) {
|
|
332
|
-
const topics = await listTopicProjectOverlays(ctx, { idMode: "legacy" });
|
|
333
|
-
return topics.filter((topic) => topic.ownerId === ownerId);
|
|
334
|
-
},
|
|
335
|
-
async listTopicsByVisibility(ctx, visibility) {
|
|
336
|
-
const topics = await listTopicProjectOverlays(ctx, { idMode: "legacy" });
|
|
337
|
-
return topics.filter((topic) => topic.visibility === visibility);
|
|
338
|
-
},
|
|
339
|
-
async listProjectGrantsByProjectAndPrincipal(ctx, topicId, principalId) {
|
|
340
|
-
return await bridgeListProjectGrantsByTopicAndPrincipal(
|
|
341
|
-
ctx,
|
|
342
|
-
topicId,
|
|
343
|
-
principalId
|
|
344
|
-
);
|
|
345
|
-
},
|
|
346
|
-
async listProjectGrantsByProjectAndGroup(ctx, topicId, groupId) {
|
|
347
|
-
return await bridgeListProjectGrantsByTopicAndGroup(ctx, topicId, groupId);
|
|
348
|
-
},
|
|
349
|
-
async listProjectGrantsByPrincipalStatus(ctx, principalId, status) {
|
|
350
|
-
return await bridgeListProjectGrantsByPrincipalStatus(
|
|
351
|
-
ctx,
|
|
352
|
-
principalId,
|
|
353
|
-
status
|
|
354
|
-
);
|
|
355
|
-
},
|
|
356
|
-
async listProjectGrantsByGroupStatus(ctx, groupId, status) {
|
|
357
|
-
return await bridgeListProjectGrantsByGroupStatus(ctx, groupId, status);
|
|
358
|
-
},
|
|
359
|
-
async insertProjectGrant(ctx, value) {
|
|
360
|
-
return await bridgeInsertProjectGrant(ctx, value);
|
|
361
|
-
},
|
|
362
|
-
async getAgentByPrincipalId(ctx, principalId) {
|
|
363
|
-
return await findAgentByPrincipalId(ctx, principalId);
|
|
364
|
-
},
|
|
365
|
-
async getUserByClerkId(ctx, clerkId) {
|
|
366
|
-
return await findUserByClerkId(ctx, clerkId);
|
|
367
|
-
},
|
|
368
|
-
async getUserByPrincipalId(ctx, principalId) {
|
|
369
|
-
return await findUserByPrincipalId(ctx, principalId);
|
|
370
|
-
}
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
var resolverOverrides = {};
|
|
374
|
-
function resolveAccessControlAppResolvers(_ctx) {
|
|
375
|
-
return {
|
|
376
|
-
...defaultResolvers(),
|
|
377
|
-
...resolverOverrides
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// ../access-control/src/principalContext.ts
|
|
382
|
-
function requireCanonicalResolvedUser(user, clerkId) {
|
|
383
|
-
const resolved = user;
|
|
384
|
-
if (!resolved) {
|
|
385
|
-
throw new Error(
|
|
386
|
-
`[AccessControl] Canonical user identity required for ${clerkId}. Sync users.upsertUser before user-bound access checks.`
|
|
387
|
-
);
|
|
388
|
-
}
|
|
389
|
-
const { mcRole, defaultTenantId, defaultWorkspaceId, defaultPrincipalId } = resolved;
|
|
390
|
-
if (mcRole !== "platform_admin" && mcRole !== "tenant_admin" && mcRole !== "workspace_admin" && mcRole !== "editor" && mcRole !== "viewer" && mcRole !== "auditor" && mcRole !== "service_agent") {
|
|
391
|
-
throw new Error(
|
|
392
|
-
`[AccessControl] Canonical MC role required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
|
|
393
|
-
);
|
|
394
|
-
}
|
|
395
|
-
if (typeof defaultTenantId !== "string" || defaultTenantId.trim().length === 0) {
|
|
396
|
-
throw new Error(
|
|
397
|
-
`[AccessControl] Canonical home tenant required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
|
|
398
|
-
);
|
|
399
|
-
}
|
|
400
|
-
if (typeof defaultWorkspaceId !== "string" || defaultWorkspaceId.trim().length === 0) {
|
|
401
|
-
throw new Error(
|
|
402
|
-
`[AccessControl] Canonical home workspace required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
if (typeof defaultPrincipalId !== "string" || defaultPrincipalId.trim().length === 0) {
|
|
406
|
-
throw new Error(
|
|
407
|
-
`[AccessControl] Canonical federated principal required for ${clerkId}. Re-sync Master Control identity before user-bound access checks.`
|
|
408
|
-
);
|
|
409
|
-
}
|
|
410
|
-
return {
|
|
411
|
-
mcRole,
|
|
412
|
-
defaultTenantId: defaultTenantId.trim(),
|
|
413
|
-
defaultWorkspaceId: defaultWorkspaceId.trim(),
|
|
414
|
-
defaultPrincipalId: defaultPrincipalId.trim()
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
function isPrincipalIdInput(value) {
|
|
418
|
-
return value.startsWith("user:") || value.startsWith("group:") || value.startsWith("service:") || value.startsWith("agent:") || value.startsWith("external_viewer:");
|
|
419
|
-
}
|
|
420
|
-
async function resolveCanonicalUserRecord(ctx, actorId) {
|
|
421
|
-
const normalizedActorId = actorId.trim();
|
|
422
|
-
const clerkId = isPrincipalIdInput(normalizedActorId) && normalizedActorId.startsWith("user:") ? normalizedActorId.slice("user:".length) : normalizedActorId;
|
|
423
|
-
const resolvers = resolveAccessControlAppResolvers();
|
|
424
|
-
const resolvedByClerkId = await resolvers.getUserByClerkId(ctx, clerkId);
|
|
425
|
-
if (resolvedByClerkId) {
|
|
426
|
-
return {
|
|
427
|
-
resolvedUser: resolvedByClerkId,
|
|
428
|
-
clerkId,
|
|
429
|
-
contextClerkId: clerkId
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
const resolvedByPrincipalId = await resolvers.getUserByPrincipalId(
|
|
433
|
-
ctx,
|
|
434
|
-
normalizedActorId
|
|
435
|
-
);
|
|
436
|
-
return {
|
|
437
|
-
resolvedUser: resolvedByPrincipalId ?? null,
|
|
438
|
-
clerkId,
|
|
439
|
-
contextClerkId: normalizedActorId.startsWith("user:") && clerkId.length > 0 ? clerkId : normalizedActorId
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
function uniqRoles(roles) {
|
|
443
|
-
const roleSet = /* @__PURE__ */ new Set();
|
|
444
|
-
for (const role of roles) {
|
|
445
|
-
if (role === "platform_admin" || role === "tenant_admin" || role === "workspace_admin" || role === "editor" || role === "viewer" || role === "auditor" || role === "service_agent") {
|
|
446
|
-
roleSet.add(role);
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
return [...roleSet];
|
|
450
|
-
}
|
|
451
|
-
function normalizeGroupIds(value) {
|
|
452
|
-
if (!Array.isArray(value)) {
|
|
453
|
-
return [];
|
|
454
|
-
}
|
|
455
|
-
return [...new Set(
|
|
456
|
-
value.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter(Boolean)
|
|
457
|
-
)];
|
|
458
|
-
}
|
|
459
|
-
function requireServiceAgentUser(user, actorId) {
|
|
460
|
-
const canonicalUser = requireCanonicalResolvedUser(user, actorId);
|
|
461
|
-
if (canonicalUser.mcRole !== "service_agent") {
|
|
462
|
-
throw new Error(
|
|
463
|
-
`[AccessControl] Canonical service_agent identity required for ${actorId}. Sync users.upsertUser before agent-bound access checks.`
|
|
464
|
-
);
|
|
465
|
-
}
|
|
466
|
-
return canonicalUser;
|
|
467
|
-
}
|
|
468
|
-
function requireCanonicalResolvedAgent(agent, actorId) {
|
|
469
|
-
const resolved = agent;
|
|
470
|
-
if (!resolved) {
|
|
471
|
-
throw new Error(
|
|
472
|
-
`[AccessControl] Agent "${actorId}" not found in agents or users table.`
|
|
473
|
-
);
|
|
474
|
-
}
|
|
475
|
-
if (typeof resolved.principalId !== "string" || resolved.principalId.trim().length === 0) {
|
|
476
|
-
throw new Error(
|
|
477
|
-
`[AccessControl] Canonical agent principalId required for ${actorId}.`
|
|
478
|
-
);
|
|
479
|
-
}
|
|
480
|
-
if (typeof resolved.tenantId !== "string" || resolved.tenantId.trim().length === 0) {
|
|
481
|
-
throw new Error(
|
|
482
|
-
`[AccessControl] Canonical home tenant required for ${actorId}.`
|
|
483
|
-
);
|
|
484
|
-
}
|
|
485
|
-
if (typeof resolved.workspaceId !== "string" || resolved.workspaceId.trim().length === 0) {
|
|
486
|
-
throw new Error(
|
|
487
|
-
`[AccessControl] Canonical home workspace required for ${actorId}.`
|
|
488
|
-
);
|
|
489
|
-
}
|
|
490
|
-
return {
|
|
491
|
-
principalId: resolved.principalId.trim(),
|
|
492
|
-
tenantId: resolved.tenantId.trim(),
|
|
493
|
-
workspaceId: resolved.workspaceId.trim(),
|
|
494
|
-
roles: uniqRoles(Array.isArray(resolved.roles) ? resolved.roles : []) ?? ["service_agent"],
|
|
495
|
-
groupIds: normalizeGroupIds(resolved.groupIds)
|
|
496
|
-
};
|
|
497
|
-
}
|
|
498
|
-
async function resolvePrincipalContext(ctx, actorId) {
|
|
499
|
-
if (actorId.startsWith("agent:")) {
|
|
500
|
-
const resolvers = resolveAccessControlAppResolvers();
|
|
501
|
-
const resolvedAgent = await resolvers.getAgentByPrincipalId(ctx, actorId);
|
|
502
|
-
if (resolvedAgent) {
|
|
503
|
-
const agent = requireCanonicalResolvedAgent(
|
|
504
|
-
resolvedAgent,
|
|
505
|
-
actorId
|
|
506
|
-
);
|
|
507
|
-
return {
|
|
508
|
-
principalId: agent.principalId,
|
|
509
|
-
principalType: "service",
|
|
510
|
-
clerkId: actorId,
|
|
511
|
-
tenantId: agent.tenantId,
|
|
512
|
-
workspaceId: agent.workspaceId,
|
|
513
|
-
roles: agent.roles.length > 0 ? agent.roles : ["service_agent"],
|
|
514
|
-
groupIds: agent.groupIds,
|
|
515
|
-
isPlatformAdmin: false,
|
|
516
|
-
isTenantAdmin: false,
|
|
517
|
-
isWorkspaceAdmin: false,
|
|
518
|
-
isSystemFallback: false
|
|
519
|
-
};
|
|
520
|
-
}
|
|
521
|
-
const resolvedUser2 = await resolvers.getUserByClerkId(
|
|
522
|
-
ctx,
|
|
523
|
-
actorId
|
|
524
|
-
);
|
|
525
|
-
if (!resolvedUser2) {
|
|
526
|
-
throw new Error(
|
|
527
|
-
`[AccessControl] Agent "${actorId}" not found in agents or users table.`
|
|
528
|
-
);
|
|
529
|
-
}
|
|
530
|
-
const user2 = requireServiceAgentUser(
|
|
531
|
-
resolvedUser2,
|
|
532
|
-
actorId
|
|
533
|
-
);
|
|
534
|
-
console.warn(
|
|
535
|
-
`[AccessControl] Deprecated legacy service-agent fallback for ${actorId}; migrate this principal into identity.agents.`
|
|
536
|
-
);
|
|
537
|
-
return {
|
|
538
|
-
principalId: user2.defaultPrincipalId,
|
|
539
|
-
principalType: "service",
|
|
540
|
-
clerkId: actorId,
|
|
541
|
-
tenantId: user2.defaultTenantId,
|
|
542
|
-
workspaceId: user2.defaultWorkspaceId,
|
|
543
|
-
roles: ["service_agent"],
|
|
544
|
-
groupIds: normalizeGroupIds(resolvedUser2?.principalGroupIds),
|
|
545
|
-
isPlatformAdmin: false,
|
|
546
|
-
isTenantAdmin: false,
|
|
547
|
-
isWorkspaceAdmin: false,
|
|
548
|
-
isSystemFallback: false
|
|
549
|
-
};
|
|
550
|
-
}
|
|
551
|
-
const {
|
|
552
|
-
resolvedUser,
|
|
553
|
-
contextClerkId
|
|
554
|
-
} = await resolveCanonicalUserRecord(ctx, actorId);
|
|
555
|
-
const user = requireCanonicalResolvedUser(
|
|
556
|
-
resolvedUser,
|
|
557
|
-
contextClerkId
|
|
558
|
-
);
|
|
559
|
-
if (!user.defaultPrincipalId) {
|
|
560
|
-
throw new Error(
|
|
561
|
-
`[AccessControl] Canonical federated principal required for ${contextClerkId}. Re-sync Master Control identity before user-bound access checks.`
|
|
562
|
-
);
|
|
563
|
-
}
|
|
564
|
-
if (user.mcRole === "service_agent") {
|
|
565
|
-
return {
|
|
566
|
-
principalId: user.defaultPrincipalId,
|
|
567
|
-
principalType: "service",
|
|
568
|
-
clerkId: contextClerkId,
|
|
569
|
-
tenantId: user.defaultTenantId,
|
|
570
|
-
workspaceId: user.defaultWorkspaceId,
|
|
571
|
-
roles: ["service_agent"],
|
|
572
|
-
groupIds: normalizeGroupIds(resolvedUser?.principalGroupIds),
|
|
573
|
-
isPlatformAdmin: false,
|
|
574
|
-
isTenantAdmin: false,
|
|
575
|
-
isWorkspaceAdmin: false,
|
|
576
|
-
isSystemFallback: false
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
const principalId = user.defaultPrincipalId;
|
|
580
|
-
const effectiveRole = user.mcRole;
|
|
581
|
-
const roles = effectiveRole === "platform_admin" ? ["platform_admin", "tenant_admin"] : effectiveRole === "tenant_admin" ? ["tenant_admin"] : [effectiveRole];
|
|
582
|
-
const tenantId = user.defaultTenantId;
|
|
583
|
-
const workspaceId = user.defaultWorkspaceId;
|
|
584
|
-
const isPlatformAdmin = effectiveRole === "platform_admin";
|
|
585
|
-
return {
|
|
586
|
-
principalId,
|
|
587
|
-
principalType: "user",
|
|
588
|
-
clerkId: contextClerkId,
|
|
589
|
-
tenantId,
|
|
590
|
-
workspaceId,
|
|
591
|
-
roles: uniqRoles(roles),
|
|
592
|
-
groupIds: normalizeGroupIds(resolvedUser?.principalGroupIds),
|
|
593
|
-
isPlatformAdmin,
|
|
594
|
-
isTenantAdmin: isPlatformAdmin || effectiveRole === "tenant_admin",
|
|
595
|
-
isWorkspaceAdmin: isPlatformAdmin || effectiveRole === "tenant_admin" || effectiveRole === "workspace_admin",
|
|
596
|
-
isSystemFallback: false
|
|
597
|
-
};
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
// ../access-control/src/access.ts
|
|
601
|
-
function isTopicInPrincipalTenant(topic, principalTenantId) {
|
|
602
|
-
if (!topic.tenantId) {
|
|
603
|
-
return false;
|
|
604
|
-
}
|
|
605
|
-
if (!principalTenantId) {
|
|
606
|
-
return false;
|
|
607
|
-
}
|
|
608
|
-
return String(topic.tenantId) === String(principalTenantId);
|
|
609
|
-
}
|
|
610
|
-
function isTopicInPrincipalWorkspace(topic, principalWorkspaceId) {
|
|
611
|
-
if (!topic.workspaceId) {
|
|
612
|
-
return false;
|
|
613
|
-
}
|
|
614
|
-
if (!principalWorkspaceId) {
|
|
615
|
-
return false;
|
|
616
|
-
}
|
|
617
|
-
return String(topic.workspaceId) === String(principalWorkspaceId);
|
|
618
|
-
}
|
|
619
|
-
function isLegacyUnscopedTopic(topic) {
|
|
620
|
-
return !topic.tenantId || !topic.workspaceId;
|
|
621
|
-
}
|
|
622
|
-
function isGrantScopeAlignedToTopic(topic, grant) {
|
|
623
|
-
if (topic.tenantId && grant.tenantId && String(topic.tenantId) !== String(grant.tenantId)) {
|
|
624
|
-
return false;
|
|
625
|
-
}
|
|
626
|
-
if (topic.workspaceId && grant.workspaceId && String(topic.workspaceId) !== String(grant.workspaceId)) {
|
|
627
|
-
return false;
|
|
628
|
-
}
|
|
629
|
-
return true;
|
|
630
|
-
}
|
|
631
|
-
function isGrantSourceAllowedForVisibility(visibility, source) {
|
|
632
|
-
if (source !== "external_share") {
|
|
633
|
-
return true;
|
|
634
|
-
}
|
|
635
|
-
return visibility === "external" || visibility === "public";
|
|
636
|
-
}
|
|
637
|
-
function isGrantActive(grant) {
|
|
638
|
-
if (grant.status !== "active") {
|
|
639
|
-
return false;
|
|
640
|
-
}
|
|
641
|
-
if (grant.expiresAt !== void 0 && grant.expiresAt <= Date.now()) {
|
|
642
|
-
return false;
|
|
643
|
-
}
|
|
644
|
-
return true;
|
|
645
|
-
}
|
|
646
|
-
async function hasPrincipalGrant(ctx, args) {
|
|
647
|
-
const grants = await resolveAccessControlAppResolvers().listProjectGrantsByProjectAndPrincipal(
|
|
648
|
-
ctx,
|
|
649
|
-
args.topic._id,
|
|
650
|
-
args.principalId
|
|
651
|
-
);
|
|
652
|
-
if (grants.some(
|
|
653
|
-
(grant) => isGrantActive(grant) && isGrantScopeAlignedToTopic(args.topic, grant) && isGrantSourceAllowedForVisibility(
|
|
654
|
-
args.topic.visibility,
|
|
655
|
-
grant.source
|
|
656
|
-
) && (!args.principalIsExternal || args.topic.visibility === "public" || grant.source === "external_share")
|
|
657
|
-
)) {
|
|
658
|
-
return true;
|
|
659
|
-
}
|
|
660
|
-
return false;
|
|
661
|
-
}
|
|
662
|
-
async function hasGroupGrant(ctx, args) {
|
|
663
|
-
if (args.groupIds.length === 0) {
|
|
664
|
-
return false;
|
|
665
|
-
}
|
|
666
|
-
for (const groupId of args.groupIds) {
|
|
667
|
-
const grants = await resolveAccessControlAppResolvers().listProjectGrantsByProjectAndGroup(ctx, args.topic._id, groupId);
|
|
668
|
-
if (grants.some(
|
|
669
|
-
(grant) => isGrantActive(grant) && isGrantScopeAlignedToTopic(args.topic, grant) && isGrantSourceAllowedForVisibility(
|
|
670
|
-
args.topic.visibility,
|
|
671
|
-
grant.source
|
|
672
|
-
)
|
|
673
|
-
)) {
|
|
674
|
-
return true;
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
return false;
|
|
678
|
-
}
|
|
679
|
-
function isExternalPrincipal(_ctx, _args) {
|
|
680
|
-
return false;
|
|
681
|
-
}
|
|
682
|
-
async function evaluateTopicAccessDetailed(ctx, args) {
|
|
683
|
-
if (args.legacyUserId) {
|
|
684
|
-
return {
|
|
685
|
-
hasAccess: true,
|
|
686
|
-
isAdmin: false,
|
|
687
|
-
isOwner: false,
|
|
688
|
-
isShared: false,
|
|
689
|
-
hasGrant: true,
|
|
690
|
-
isFirmVisible: true,
|
|
691
|
-
isExternalVisible: false,
|
|
692
|
-
isPublicVisible: false,
|
|
693
|
-
isTenantScopeMatch: true,
|
|
694
|
-
isWorkspaceScopeMatch: true,
|
|
695
|
-
isPrincipalExternal: false
|
|
696
|
-
};
|
|
697
|
-
}
|
|
698
|
-
const topic = await resolveAccessControlAppResolvers().getProject(
|
|
699
|
-
ctx,
|
|
700
|
-
args.topicId
|
|
701
|
-
);
|
|
702
|
-
if (!topic) {
|
|
703
|
-
return {
|
|
704
|
-
hasAccess: false,
|
|
705
|
-
isAdmin: false,
|
|
706
|
-
isOwner: false,
|
|
707
|
-
isShared: false,
|
|
708
|
-
hasGrant: false,
|
|
709
|
-
isFirmVisible: false,
|
|
710
|
-
isExternalVisible: false,
|
|
711
|
-
isPublicVisible: false,
|
|
712
|
-
isTenantScopeMatch: false,
|
|
713
|
-
isWorkspaceScopeMatch: false,
|
|
714
|
-
isPrincipalExternal: false
|
|
715
|
-
};
|
|
716
|
-
}
|
|
717
|
-
const { principalContext, legacyUserId } = args;
|
|
718
|
-
const userIsAdmin = principalContext.isPlatformAdmin;
|
|
719
|
-
const isOwner = topic.ownerId === legacyUserId;
|
|
720
|
-
const isShared = (topic.sharedWith ?? []).includes(legacyUserId);
|
|
721
|
-
const principalIsExternal = await isExternalPrincipal(ctx, {
|
|
722
|
-
groupIds: principalContext.groupIds,
|
|
723
|
-
topicTenantId: topic.tenantId,
|
|
724
|
-
topicWorkspaceId: topic.workspaceId
|
|
725
|
-
});
|
|
726
|
-
const hasPrincipalGrantResult = await hasPrincipalGrant(ctx, {
|
|
727
|
-
topic,
|
|
728
|
-
principalId: principalContext.principalId,
|
|
729
|
-
principalIsExternal
|
|
730
|
-
});
|
|
731
|
-
const hasGroupGrantResult = await hasGroupGrant(ctx, {
|
|
732
|
-
topic,
|
|
733
|
-
groupIds: principalContext.groupIds
|
|
734
|
-
});
|
|
735
|
-
const hasGrant = isShared || hasPrincipalGrantResult || hasGroupGrantResult;
|
|
736
|
-
const legacyUnscoped = isLegacyUnscopedTopic(topic);
|
|
737
|
-
const tenantScopeMatch = isTopicInPrincipalTenant(
|
|
738
|
-
topic,
|
|
739
|
-
principalContext.tenantId
|
|
740
|
-
);
|
|
741
|
-
const workspaceScopeMatch = isTopicInPrincipalWorkspace(
|
|
742
|
-
topic,
|
|
743
|
-
principalContext.workspaceId
|
|
744
|
-
);
|
|
745
|
-
const isPublicVisible = topic.visibility === "public";
|
|
746
|
-
const isFirmVisible = topic.visibility === "firm" && !legacyUnscoped && tenantScopeMatch && workspaceScopeMatch && !principalIsExternal;
|
|
747
|
-
const hasScopedGrant = hasGrant && (legacyUnscoped || tenantScopeMatch && workspaceScopeMatch);
|
|
748
|
-
const isExternalVisible = topic.visibility === "external" && hasScopedGrant;
|
|
749
|
-
const hasAccess = userIsAdmin || isOwner || hasScopedGrant || isPublicVisible || isFirmVisible;
|
|
750
|
-
return {
|
|
751
|
-
hasAccess,
|
|
752
|
-
isAdmin: userIsAdmin,
|
|
753
|
-
isOwner,
|
|
754
|
-
isShared,
|
|
755
|
-
hasGrant,
|
|
756
|
-
isFirmVisible,
|
|
757
|
-
isExternalVisible,
|
|
758
|
-
isPublicVisible,
|
|
759
|
-
isTenantScopeMatch: tenantScopeMatch,
|
|
760
|
-
isWorkspaceScopeMatch: workspaceScopeMatch,
|
|
761
|
-
isPrincipalExternal: principalIsExternal
|
|
762
|
-
};
|
|
763
|
-
}
|
|
764
|
-
async function checkTopicAccessDetailed(ctx, topicId, userId) {
|
|
765
|
-
const principalContext = await resolvePrincipalContext(ctx, userId);
|
|
766
|
-
return evaluateTopicAccessDetailed(ctx, {
|
|
767
|
-
topicId,
|
|
768
|
-
legacyUserId: userId,
|
|
769
|
-
principalContext
|
|
770
|
-
});
|
|
771
|
-
}
|
|
772
|
-
async function checkTopicAccess(ctx, topicId, userId) {
|
|
773
|
-
const result = await checkTopicAccessDetailed(ctx, topicId, userId);
|
|
774
|
-
return result.hasAccess;
|
|
775
|
-
}
|
|
776
|
-
async function checkScopeAccess(ctx, scopeId, userId) {
|
|
777
|
-
try {
|
|
778
|
-
const topic = await ctx.db.get(scopeId);
|
|
779
|
-
if (topic && topic.name !== void 0 && topic.type !== void 0) {
|
|
780
|
-
return true;
|
|
781
|
-
}
|
|
782
|
-
} catch {
|
|
783
|
-
}
|
|
784
|
-
try {
|
|
785
|
-
return await checkTopicAccess(ctx, scopeId, userId);
|
|
786
|
-
} catch {
|
|
787
|
-
return false;
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
var checkProjectAccess = checkTopicAccess;
|
|
791
|
-
|
|
792
|
-
// ../access-control/src/auth.ts
|
|
793
|
-
async function getCurrentUserId(ctx) {
|
|
794
|
-
const identity = await ctx.auth.getUserIdentity();
|
|
795
|
-
return identity?.subject ?? null;
|
|
796
|
-
}
|
|
797
|
-
var permissiveReturn = v.optional(v.any());
|
|
798
|
-
var looseJsonObject = v.record(v.string(), v.any());
|
|
799
|
-
var looseJsonArray = v.array(v.any());
|
|
800
|
-
v.union(
|
|
801
|
-
v.string(),
|
|
802
|
-
v.number(),
|
|
803
|
-
v.boolean(),
|
|
804
|
-
v.null(),
|
|
805
|
-
looseJsonObject,
|
|
806
|
-
looseJsonArray
|
|
807
|
-
);
|
|
808
|
-
var api2 = anyApi;
|
|
809
|
-
componentsGeneric();
|
|
810
|
-
var internal = anyApi;
|
|
811
|
-
var internalMutation = internalMutationGeneric;
|
|
812
|
-
var internalQuery = internalQueryGeneric;
|
|
813
|
-
var mutation = mutationGeneric;
|
|
814
|
-
var query = queryGeneric;
|
|
815
|
-
|
|
816
|
-
// ../confidence/src/v1/operations/subjectiveLogic/index.ts
|
|
817
|
-
function opinion(belief, disbelief, uncertainty, baseRate = 0.5) {
|
|
818
|
-
const b = Math.max(0, Math.min(1, belief));
|
|
819
|
-
const d = Math.max(0, Math.min(1, disbelief));
|
|
820
|
-
const u = Math.max(0, Math.min(1, uncertainty));
|
|
821
|
-
const a = Math.max(0, Math.min(1, baseRate));
|
|
822
|
-
const sum = b + d + u;
|
|
823
|
-
if (sum === 0) {
|
|
824
|
-
return { b: 0, d: 0, u: 1, a };
|
|
825
|
-
}
|
|
826
|
-
return {
|
|
827
|
-
b: b / sum,
|
|
828
|
-
d: d / sum,
|
|
829
|
-
u: u / sum,
|
|
830
|
-
a
|
|
831
|
-
};
|
|
832
|
-
}
|
|
833
|
-
function vacuous(baseRate = 0.5) {
|
|
834
|
-
return { b: 0, d: 0, u: 1, a: baseRate };
|
|
835
|
-
}
|
|
836
|
-
function project(o) {
|
|
837
|
-
return o.b + o.a * o.u;
|
|
838
|
-
}
|
|
839
|
-
function cumulativeFusion(left, right) {
|
|
840
|
-
if (left.u === 0 && right.u === 0) {
|
|
841
|
-
return opinion(
|
|
842
|
-
(left.b + right.b) / 2,
|
|
843
|
-
(left.d + right.d) / 2,
|
|
844
|
-
0,
|
|
845
|
-
(left.a + right.a) / 2
|
|
846
|
-
);
|
|
847
|
-
}
|
|
848
|
-
const k = left.u + right.u - left.u * right.u;
|
|
849
|
-
if (k === 0) {
|
|
850
|
-
return vacuous((left.a + right.a) / 2);
|
|
851
|
-
}
|
|
852
|
-
return opinion(
|
|
853
|
-
(left.b * right.u + right.b * left.u) / k,
|
|
854
|
-
(left.d * right.u + right.d * left.u) / k,
|
|
855
|
-
left.u * right.u / k,
|
|
856
|
-
(left.a + right.a) / 2
|
|
857
|
-
);
|
|
858
|
-
}
|
|
859
|
-
function trustDiscount(sourceOpinion, trust) {
|
|
860
|
-
const weight = Math.max(0, Math.min(1, Math.abs(trust)));
|
|
861
|
-
return opinion(
|
|
862
|
-
weight * sourceOpinion.b,
|
|
863
|
-
weight * sourceOpinion.d,
|
|
864
|
-
1 - weight * (sourceOpinion.b + sourceOpinion.d),
|
|
865
|
-
sourceOpinion.a
|
|
866
|
-
);
|
|
867
|
-
}
|
|
868
|
-
var EPSILON = 1e-9;
|
|
869
|
-
function childBaseRateFallback(ifTrue, ifFalse, fallbackBaseRate) {
|
|
870
|
-
if (fallbackBaseRate !== void 0) {
|
|
871
|
-
return Math.max(0, Math.min(1, fallbackBaseRate));
|
|
872
|
-
}
|
|
873
|
-
if (Math.abs(ifTrue.a - ifFalse.a) <= EPSILON) {
|
|
874
|
-
return ifTrue.a;
|
|
875
|
-
}
|
|
876
|
-
return (ifTrue.a + ifFalse.a) / 2;
|
|
877
|
-
}
|
|
878
|
-
function computeConditionalDeductionBaseRate(opinionA, ifTrue, ifFalse, fallbackBaseRate) {
|
|
879
|
-
const denominator = 1 - opinionA.a * ifTrue.u - (1 - opinionA.a) * ifFalse.u;
|
|
880
|
-
if (ifTrue.u + ifFalse.u < 2 - EPSILON && Math.abs(denominator) > EPSILON) {
|
|
881
|
-
const baseRate = (opinionA.a * ifTrue.b + (1 - opinionA.a) * ifFalse.b) / denominator;
|
|
882
|
-
if (baseRate >= -EPSILON && baseRate <= 1 + EPSILON) {
|
|
883
|
-
return Math.max(0, Math.min(1, baseRate));
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
return fallbackBaseRate;
|
|
887
|
-
}
|
|
888
|
-
function safeCorrectionTerm(numerator, denominator) {
|
|
889
|
-
if (Math.abs(denominator) <= EPSILON) {
|
|
890
|
-
return void 0;
|
|
891
|
-
}
|
|
892
|
-
const value = numerator / denominator;
|
|
893
|
-
if (!Number.isFinite(value)) {
|
|
894
|
-
return void 0;
|
|
895
|
-
}
|
|
896
|
-
return Math.max(0, value);
|
|
897
|
-
}
|
|
898
|
-
function conditionalDeduction(opinionA, ifTrue, ifFalse, fallbackBaseRate) {
|
|
899
|
-
const fallbackChildBaseRate = childBaseRateFallback(
|
|
900
|
-
ifTrue,
|
|
901
|
-
ifFalse,
|
|
902
|
-
fallbackBaseRate
|
|
903
|
-
);
|
|
904
|
-
const childBaseRate = computeConditionalDeductionBaseRate(
|
|
905
|
-
opinionA,
|
|
906
|
-
ifTrue,
|
|
907
|
-
ifFalse,
|
|
908
|
-
fallbackChildBaseRate
|
|
909
|
-
);
|
|
910
|
-
const projectedAntecedent = project(opinionA);
|
|
911
|
-
const projectedAntecedentComplement = 1 - projectedAntecedent;
|
|
912
|
-
const intermediateBelief = opinionA.b * ifTrue.b + opinionA.d * ifFalse.b + opinionA.u * (ifTrue.b * opinionA.a + ifFalse.b * (1 - opinionA.a));
|
|
913
|
-
const intermediateDisbelief = opinionA.b * ifTrue.d + opinionA.d * ifFalse.d + opinionA.u * (ifTrue.d * opinionA.a + ifFalse.d * (1 - opinionA.a));
|
|
914
|
-
const intermediateUncertainty = opinionA.b * ifTrue.u + opinionA.d * ifFalse.u + opinionA.u * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));
|
|
915
|
-
const projectedVacuousDeduction = ifTrue.b * opinionA.a + ifFalse.b * (1 - opinionA.a) + childBaseRate * (ifTrue.u * opinionA.a + ifFalse.u * (1 - opinionA.a));
|
|
916
|
-
const projectedConditionalA = ifTrue.b + childBaseRate * (1 - ifTrue.b - ifTrue.d);
|
|
917
|
-
let correction = 0;
|
|
918
|
-
if (ifTrue.b > ifFalse.b && ifTrue.d > ifFalse.d || ifTrue.b <= ifFalse.b && ifTrue.d <= ifFalse.d) {
|
|
919
|
-
correction = 0;
|
|
920
|
-
} else if (ifTrue.b > ifFalse.b && ifTrue.d <= ifFalse.d) {
|
|
921
|
-
const beliefGap = ifTrue.b - ifFalse.b;
|
|
922
|
-
const disbeliefGap = ifFalse.d - ifTrue.d;
|
|
923
|
-
if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
924
|
-
correction = safeCorrectionTerm(
|
|
925
|
-
opinionA.a * opinionA.u * (intermediateBelief - ifTrue.b),
|
|
926
|
-
projectedAntecedent * childBaseRate
|
|
927
|
-
) ?? 0;
|
|
928
|
-
} else if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent > opinionA.a) {
|
|
929
|
-
correction = safeCorrectionTerm(
|
|
930
|
-
opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d) * beliefGap,
|
|
931
|
-
projectedAntecedentComplement * childBaseRate * disbeliefGap
|
|
932
|
-
) ?? 0;
|
|
933
|
-
} else if (projectedVacuousDeduction > projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
934
|
-
correction = safeCorrectionTerm(
|
|
935
|
-
(1 - opinionA.a) * opinionA.u * (intermediateBelief - ifTrue.b) * disbeliefGap,
|
|
936
|
-
projectedAntecedent * (1 - childBaseRate) * beliefGap
|
|
937
|
-
) ?? 0;
|
|
938
|
-
} else {
|
|
939
|
-
correction = safeCorrectionTerm(
|
|
940
|
-
(1 - opinionA.a) * opinionA.u * (intermediateDisbelief - ifTrue.d),
|
|
941
|
-
projectedAntecedentComplement * (1 - childBaseRate)
|
|
942
|
-
) ?? 0;
|
|
943
|
-
}
|
|
944
|
-
} else {
|
|
945
|
-
const beliefGap = ifFalse.b - ifTrue.b;
|
|
946
|
-
const disbeliefGap = ifTrue.d - ifFalse.d;
|
|
947
|
-
if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
948
|
-
correction = safeCorrectionTerm(
|
|
949
|
-
(1 - opinionA.a) * opinionA.u * (intermediateDisbelief - ifTrue.d) * beliefGap,
|
|
950
|
-
projectedAntecedent * childBaseRate * disbeliefGap
|
|
951
|
-
) ?? 0;
|
|
952
|
-
} else if (projectedVacuousDeduction <= projectedConditionalA && projectedAntecedent > opinionA.a) {
|
|
953
|
-
correction = safeCorrectionTerm(
|
|
954
|
-
(1 - opinionA.a) * opinionA.u * (intermediateBelief - ifTrue.b),
|
|
955
|
-
projectedAntecedentComplement * childBaseRate
|
|
956
|
-
) ?? 0;
|
|
957
|
-
} else if (projectedVacuousDeduction > projectedConditionalA && projectedAntecedent <= opinionA.a) {
|
|
958
|
-
correction = safeCorrectionTerm(
|
|
959
|
-
opinionA.a * opinionA.u * (intermediateDisbelief - ifTrue.d),
|
|
960
|
-
projectedAntecedent * (1 - childBaseRate)
|
|
961
|
-
) ?? 0;
|
|
962
|
-
} else {
|
|
963
|
-
correction = safeCorrectionTerm(
|
|
964
|
-
opinionA.a * opinionA.u * (intermediateBelief - ifTrue.b) * disbeliefGap,
|
|
965
|
-
projectedAntecedentComplement * (1 - childBaseRate) * beliefGap
|
|
966
|
-
) ?? 0;
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
return opinion(
|
|
970
|
-
intermediateBelief - childBaseRate * correction,
|
|
971
|
-
intermediateDisbelief - (1 - childBaseRate) * correction,
|
|
972
|
-
intermediateUncertainty + correction,
|
|
973
|
-
childBaseRate
|
|
974
|
-
);
|
|
975
|
-
}
|
|
976
|
-
function negate(o) {
|
|
977
|
-
return { b: o.d, d: o.b, u: o.u, a: 1 - o.a };
|
|
978
|
-
}
|
|
979
|
-
function constraintFusion(left, right, mode = "pressure") {
|
|
980
|
-
if (mode === "redistribute") {
|
|
981
|
-
const leftProjected = project(left);
|
|
982
|
-
const rightProjected = project(right);
|
|
983
|
-
const total = leftProjected + rightProjected;
|
|
984
|
-
if (total <= 1) {
|
|
985
|
-
return { o1: left, o2: right };
|
|
986
|
-
}
|
|
987
|
-
const scale = 1 / total;
|
|
988
|
-
return {
|
|
989
|
-
o1: opinion(
|
|
990
|
-
left.b * scale,
|
|
991
|
-
left.d + left.b * (1 - scale),
|
|
992
|
-
left.u,
|
|
993
|
-
left.a
|
|
994
|
-
),
|
|
995
|
-
o2: opinion(
|
|
996
|
-
right.b * scale,
|
|
997
|
-
right.d + right.b * (1 - scale),
|
|
998
|
-
right.u,
|
|
999
|
-
right.a
|
|
1000
|
-
)
|
|
1001
|
-
};
|
|
1002
|
-
}
|
|
1003
|
-
const pressureLeft = right.b * 0.5;
|
|
1004
|
-
const pressureRight = left.b * 0.5;
|
|
1005
|
-
return {
|
|
1006
|
-
o1: opinion(
|
|
1007
|
-
left.b - pressureLeft * 0.3,
|
|
1008
|
-
left.d + pressureLeft * 0.3,
|
|
1009
|
-
left.u,
|
|
1010
|
-
left.a
|
|
1011
|
-
),
|
|
1012
|
-
o2: opinion(
|
|
1013
|
-
right.b - pressureRight * 0.3,
|
|
1014
|
-
right.d + pressureRight * 0.3,
|
|
1015
|
-
right.u,
|
|
1016
|
-
right.a
|
|
1017
|
-
)
|
|
1018
|
-
};
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
// ../confidence/src/v1/operations/scoring.ts
|
|
1022
|
-
function finiteNumber(value) {
|
|
1023
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
1024
|
-
}
|
|
1025
|
-
function clamp01(value) {
|
|
1026
|
-
return Math.max(0, Math.min(1, value));
|
|
1027
|
-
}
|
|
1028
|
-
function confidenceFromOpinion(opinion2) {
|
|
1029
|
-
return clamp01(opinion2.b + opinion2.a * opinion2.u);
|
|
1030
|
-
}
|
|
1031
|
-
function confidenceFromSL(belief, _disbelief, uncertainty, baseRate = 0.5) {
|
|
1032
|
-
return confidenceFromOpinion({
|
|
1033
|
-
b: belief,
|
|
1034
|
-
u: uncertainty,
|
|
1035
|
-
a: baseRate
|
|
1036
|
-
});
|
|
1037
|
-
}
|
|
1038
|
-
function readOpinionFromRecord(source, fallback = {}) {
|
|
1039
|
-
const record = source && typeof source === "object" ? source : {};
|
|
1040
|
-
return {
|
|
1041
|
-
b: finiteNumber(record.b) ?? finiteNumber(record.belief) ?? finiteNumber(record.slBelief) ?? finiteNumber(record.opinion_b) ?? fallback.b ?? 0,
|
|
1042
|
-
d: finiteNumber(record.d) ?? finiteNumber(record.disbelief) ?? finiteNumber(record.slDisbelief) ?? finiteNumber(record.opinion_d) ?? fallback.d ?? 0,
|
|
1043
|
-
u: finiteNumber(record.u) ?? finiteNumber(record.uncertainty) ?? finiteNumber(record.slUncertainty) ?? finiteNumber(record.opinion_u) ?? fallback.u ?? 1,
|
|
1044
|
-
a: finiteNumber(record.a) ?? finiteNumber(record.baseRate) ?? finiteNumber(record.slBaseRate) ?? finiteNumber(record.opinion_a) ?? fallback.a ?? 0.5
|
|
1045
|
-
};
|
|
1046
|
-
}
|
|
1047
|
-
function hasProjectedOpinionChanged(current, next, tolerance = 0.01) {
|
|
1048
|
-
return Math.abs(confidenceFromOpinion(next) - confidenceFromOpinion(current)) >= tolerance;
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
// ../confidence/src/v1/operations/contradiction/detectTupleContradiction.ts
|
|
1052
|
-
var DEFAULT_TUPLE_CONTRADICTION_BELIEF_THRESHOLD = 0.7;
|
|
1053
|
-
var DEFAULT_TUPLE_CONTRADICTION_DISBELIEF_THRESHOLD = 0.7;
|
|
1054
|
-
function normalizeTupleContradictionPolicy(policy = {}) {
|
|
1055
|
-
return {
|
|
1056
|
-
beliefThreshold: clamp01(
|
|
1057
|
-
policy.beliefThreshold ?? DEFAULT_TUPLE_CONTRADICTION_BELIEF_THRESHOLD
|
|
1058
|
-
),
|
|
1059
|
-
disbeliefThreshold: clamp01(
|
|
1060
|
-
policy.disbeliefThreshold ?? DEFAULT_TUPLE_CONTRADICTION_DISBELIEF_THRESHOLD
|
|
1061
|
-
)
|
|
1062
|
-
};
|
|
1063
|
-
}
|
|
1064
|
-
function detectTupleContradiction(opinion2, tauB = DEFAULT_TUPLE_CONTRADICTION_BELIEF_THRESHOLD, tauD = DEFAULT_TUPLE_CONTRADICTION_DISBELIEF_THRESHOLD) {
|
|
1065
|
-
return opinion2.b > tauB && opinion2.d > tauD;
|
|
1066
|
-
}
|
|
1067
|
-
function evaluateTupleContradictionTransition(args) {
|
|
1068
|
-
const policy = normalizeTupleContradictionPolicy(args.policy);
|
|
1069
|
-
const tupleContradicted = detectTupleContradiction(
|
|
1070
|
-
args.opinion,
|
|
1071
|
-
policy.beliefThreshold,
|
|
1072
|
-
policy.disbeliefThreshold
|
|
1073
|
-
);
|
|
1074
|
-
const previousTupleContradicted = Boolean(args.previousTupleContradicted);
|
|
1075
|
-
return {
|
|
1076
|
-
tupleContradicted,
|
|
1077
|
-
crossedIntoTupleContradiction: !previousTupleContradicted && tupleContradicted,
|
|
1078
|
-
crossedOutOfTupleContradiction: previousTupleContradicted && !tupleContradicted,
|
|
1079
|
-
policy
|
|
1080
|
-
};
|
|
1081
|
-
}
|
|
1082
|
-
|
|
1083
|
-
// ../confidence/src/v1/operations/dynamics/cascade.ts
|
|
1084
|
-
function dampedDependencyOpinion(dependencyOpinion, beliefOpinion, mode = "continuous", threshold = 0.3) {
|
|
1085
|
-
const dependencyProjection = project(dependencyOpinion);
|
|
1086
|
-
if (mode === "threshold") {
|
|
1087
|
-
if (dependencyProjection < threshold) {
|
|
1088
|
-
return opinion(
|
|
1089
|
-
0,
|
|
1090
|
-
beliefOpinion.d + beliefOpinion.b * 0.5,
|
|
1091
|
-
0.5,
|
|
1092
|
-
beliefOpinion.a
|
|
1093
|
-
);
|
|
1094
|
-
}
|
|
1095
|
-
return beliefOpinion;
|
|
1096
|
-
}
|
|
1097
|
-
const dampingFactor = Math.pow(dependencyProjection, 0.5);
|
|
1098
|
-
return opinion(
|
|
1099
|
-
beliefOpinion.b * dampingFactor,
|
|
1100
|
-
beliefOpinion.d + beliefOpinion.b * (1 - dampingFactor) * 0.3,
|
|
1101
|
-
beliefOpinion.u + beliefOpinion.b * (1 - dampingFactor) * 0.7,
|
|
1102
|
-
beliefOpinion.a
|
|
1103
|
-
);
|
|
1104
|
-
}
|
|
1105
|
-
function dampedDependencyCascade(dependencyOpinion, beliefOpinion, mode = "continuous") {
|
|
1106
|
-
return {
|
|
1107
|
-
opinion: dampedDependencyOpinion(dependencyOpinion, beliefOpinion, mode),
|
|
1108
|
-
operator: "dependency_cascade",
|
|
1109
|
-
rationale: `Damped dependency cascade (${mode}): prerequisite at ${project(
|
|
1110
|
-
dependencyOpinion
|
|
1111
|
-
).toFixed(2)}`
|
|
1112
|
-
};
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
// ../confidence/src/v1/operations/dynamics/defeat.ts
|
|
1116
|
-
function applyNegativeSupport(source, target, weight, metadata = {}) {
|
|
1117
|
-
if (metadata.constraint === "xor") {
|
|
1118
|
-
const result = constraintFusion(
|
|
1119
|
-
source,
|
|
1120
|
-
target,
|
|
1121
|
-
metadata.normalization ?? "pressure"
|
|
1122
|
-
);
|
|
1123
|
-
return {
|
|
1124
|
-
opinion: result.o2,
|
|
1125
|
-
operator: "constraint_fusion",
|
|
1126
|
-
rationale: `XOR constraint: source belief at ${project(source).toFixed(
|
|
1127
|
-
2
|
|
1128
|
-
)} pressures target`
|
|
1129
|
-
};
|
|
1130
|
-
}
|
|
1131
|
-
const discounted = trustDiscount(negate(source), Math.abs(weight));
|
|
1132
|
-
return {
|
|
1133
|
-
opinion: cumulativeFusion(target, discounted),
|
|
1134
|
-
operator: "cumulative_fusion",
|
|
1135
|
-
rationale: `Contradicting evidence (weight=${weight.toFixed(
|
|
1136
|
-
2
|
|
1137
|
-
)}) from source at ${project(source).toFixed(2)}`
|
|
1138
|
-
};
|
|
1139
|
-
}
|
|
1140
|
-
function applyNegativeEvidence(source, target, weight) {
|
|
1141
|
-
const discounted = trustDiscount(negate(source), Math.abs(weight));
|
|
1142
|
-
return {
|
|
1143
|
-
opinion: cumulativeFusion(target, discounted),
|
|
1144
|
-
operator: "cumulative_fusion",
|
|
1145
|
-
rationale: `Contradicting evidence (weight=${weight.toFixed(2)})`
|
|
1146
|
-
};
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
// ../confidence/src/v1/operations/contracts/epistemicContract.ts
|
|
1150
|
-
var BUILT_IN_METRIC_CHECKER = "metric_checker";
|
|
1151
|
-
var BUILT_IN_REFERENCE_CHECK_COUNTER = "reference_check_counter";
|
|
1152
|
-
var BUILT_IN_MARKET_INDEX_COMPARATOR = "market_index_comparator";
|
|
1153
|
-
function clampConfidence(value) {
|
|
1154
|
-
return Math.max(0, Math.min(1, value));
|
|
1155
|
-
}
|
|
1156
|
-
function generateContractId() {
|
|
1157
|
-
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
1158
|
-
return crypto.randomUUID();
|
|
1159
|
-
}
|
|
1160
|
-
return `contract-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
1161
|
-
}
|
|
1162
|
-
function deriveContractStatus(result, currentStatus) {
|
|
1163
|
-
if (currentStatus === "archived") {
|
|
1164
|
-
return currentStatus;
|
|
1165
|
-
}
|
|
1166
|
-
switch (result) {
|
|
1167
|
-
case "confirmed":
|
|
1168
|
-
return "satisfied";
|
|
1169
|
-
case "disconfirmed":
|
|
1170
|
-
return "violated";
|
|
1171
|
-
case "expired":
|
|
1172
|
-
return "expired";
|
|
1173
|
-
default:
|
|
1174
|
-
return currentStatus === "satisfied" || currentStatus === "violated" || currentStatus === "expired" ? "active" : currentStatus;
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
function deriveVerificationTrigger(result) {
|
|
1178
|
-
switch (result) {
|
|
1179
|
-
case "confirmed":
|
|
1180
|
-
return "verification_confirmed";
|
|
1181
|
-
case "disconfirmed":
|
|
1182
|
-
return "verification_disconfirmed";
|
|
1183
|
-
case "expired":
|
|
1184
|
-
return "verification_expired";
|
|
1185
|
-
case "partial":
|
|
1186
|
-
return "verification_partial";
|
|
1187
|
-
default:
|
|
1188
|
-
return null;
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
function deriveContractModulationPlan(args) {
|
|
1192
|
-
const trigger = deriveVerificationTrigger(args.result);
|
|
1193
|
-
if (!trigger) {
|
|
1194
|
-
return null;
|
|
1195
|
-
}
|
|
1196
|
-
if (args.result === "confirmed") {
|
|
1197
|
-
const rawNext = args.currentConfidence + args.modulation.onConfirmed.delta;
|
|
1198
|
-
const confidenceAfter = clampConfidence(
|
|
1199
|
-
Math.min(
|
|
1200
|
-
args.modulation.onConfirmed.ceiling ?? Number.POSITIVE_INFINITY,
|
|
1201
|
-
rawNext
|
|
1202
|
-
)
|
|
1203
|
-
);
|
|
1204
|
-
return {
|
|
1205
|
-
trigger,
|
|
1206
|
-
confidenceBefore: args.currentConfidence,
|
|
1207
|
-
confidenceAfter,
|
|
1208
|
-
confidenceDelta: confidenceAfter - args.currentConfidence
|
|
1209
|
-
};
|
|
1210
|
-
}
|
|
1211
|
-
if (args.result === "disconfirmed") {
|
|
1212
|
-
const rawNext = args.currentConfidence + args.modulation.onDisconfirmed.delta;
|
|
1213
|
-
const confidenceAfter = clampConfidence(
|
|
1214
|
-
Math.max(args.modulation.onDisconfirmed.floor ?? 0, rawNext)
|
|
1215
|
-
);
|
|
1216
|
-
return {
|
|
1217
|
-
trigger,
|
|
1218
|
-
confidenceBefore: args.currentConfidence,
|
|
1219
|
-
confidenceAfter,
|
|
1220
|
-
confidenceDelta: confidenceAfter - args.currentConfidence
|
|
1221
|
-
};
|
|
1222
|
-
}
|
|
1223
|
-
if (args.result === "expired" && args.modulation.onExpired) {
|
|
1224
|
-
const confidenceAfter = clampConfidence(
|
|
1225
|
-
args.currentConfidence + args.modulation.onExpired.delta
|
|
1226
|
-
);
|
|
1227
|
-
return {
|
|
1228
|
-
trigger,
|
|
1229
|
-
confidenceBefore: args.currentConfidence,
|
|
1230
|
-
confidenceAfter,
|
|
1231
|
-
confidenceDelta: confidenceAfter - args.currentConfidence
|
|
1232
|
-
};
|
|
1233
|
-
}
|
|
1234
|
-
if (args.result === "partial" && args.modulation.onPartial) {
|
|
1235
|
-
if ((args.resultConfidence ?? 0) < args.modulation.onPartial.threshold) {
|
|
1236
|
-
return null;
|
|
1237
|
-
}
|
|
1238
|
-
const confidenceAfter = clampConfidence(
|
|
1239
|
-
args.currentConfidence + args.modulation.onPartial.delta
|
|
1240
|
-
);
|
|
1241
|
-
return {
|
|
1242
|
-
trigger,
|
|
1243
|
-
confidenceBefore: args.currentConfidence,
|
|
1244
|
-
confidenceAfter,
|
|
1245
|
-
confidenceDelta: confidenceAfter - args.currentConfidence
|
|
1246
|
-
};
|
|
1247
|
-
}
|
|
1248
|
-
return null;
|
|
1249
|
-
}
|
|
1250
|
-
function createInheritedContractRecord(contract, args) {
|
|
1251
|
-
return {
|
|
1252
|
-
beliefNodeId: args.beliefNodeId,
|
|
1253
|
-
contractId: generateContractId(),
|
|
1254
|
-
title: contract.title,
|
|
1255
|
-
description: contract.description,
|
|
1256
|
-
conditionType: contract.conditionType,
|
|
1257
|
-
direction: contract.direction,
|
|
1258
|
-
condition: contract.condition,
|
|
1259
|
-
deadline: contract.deadline,
|
|
1260
|
-
compositeOf: contract.compositeOf,
|
|
1261
|
-
compositeOperator: contract.compositeOperator,
|
|
1262
|
-
modulation: contract.modulation,
|
|
1263
|
-
evaluationSchedule: contract.evaluationSchedule,
|
|
1264
|
-
periodicIntervalMs: contract.periodicIntervalMs,
|
|
1265
|
-
status: "active",
|
|
1266
|
-
lineageSource: "inherited",
|
|
1267
|
-
inheritedFromContractId: contract.contractId,
|
|
1268
|
-
inheritedFromBeliefNodeId: contract.beliefNodeId,
|
|
1269
|
-
inheritedAt: args.now,
|
|
1270
|
-
topicId: args.topicId,
|
|
1271
|
-
createdAt: args.now,
|
|
1272
|
-
createdBy: args.createdBy,
|
|
1273
|
-
updatedAt: args.now
|
|
1274
|
-
};
|
|
1275
|
-
}
|
|
1276
|
-
function normalizeEvidentialAction(value) {
|
|
1277
|
-
return value === "modulate_confidence" || value === "flag_review" || value === "archive" ? value : void 0;
|
|
1278
|
-
}
|
|
1279
|
-
function parseComparisonOperator(value, evaluatorName) {
|
|
1280
|
-
if (value === "gte" || value === "lte" || value === "eq" || value === "gt" || value === "lt") {
|
|
1281
|
-
return value;
|
|
1282
|
-
}
|
|
1283
|
-
throw new Error(
|
|
1284
|
-
`${evaluatorName} requires operator to be one of gte, lte, eq, gt, or lt.`
|
|
1285
|
-
);
|
|
1286
|
-
}
|
|
1287
|
-
function parseNumericThreshold(value, evaluatorName) {
|
|
1288
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1289
|
-
return value;
|
|
1290
|
-
}
|
|
1291
|
-
throw new Error(`${evaluatorName} requires a finite numeric threshold.`);
|
|
1292
|
-
}
|
|
1293
|
-
function pickFiniteNumber(config, keys) {
|
|
1294
|
-
for (const key of keys) {
|
|
1295
|
-
const value = config[key];
|
|
1296
|
-
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1297
|
-
return value;
|
|
1298
|
-
}
|
|
1299
|
-
}
|
|
1300
|
-
return void 0;
|
|
1301
|
-
}
|
|
1302
|
-
function getEvaluatorInputRecord(inputData, nestedKey) {
|
|
1303
|
-
if (!inputData || typeof inputData !== "object") {
|
|
1304
|
-
return {};
|
|
1305
|
-
}
|
|
1306
|
-
const root = inputData;
|
|
1307
|
-
const nested = root[nestedKey];
|
|
1308
|
-
if (nested && typeof nested === "object") {
|
|
1309
|
-
return nested;
|
|
1310
|
-
}
|
|
1311
|
-
return root;
|
|
1312
|
-
}
|
|
1313
|
-
function parseEvidentialEvaluatorConfig(value) {
|
|
1314
|
-
if (!value || typeof value !== "object") {
|
|
1315
|
-
throw new Error(
|
|
1316
|
-
"Evidential contracts require condition.evaluatorConfig with metric/operator/threshold."
|
|
1317
|
-
);
|
|
1318
|
-
}
|
|
1319
|
-
const config = value;
|
|
1320
|
-
const metric = config.metric;
|
|
1321
|
-
const operator = config.operator;
|
|
1322
|
-
const threshold = config.threshold;
|
|
1323
|
-
if (metric !== "evidence_count" && metric !== "contradiction_status" && metric !== "edge_freshness" && metric !== "dependent_count") {
|
|
1324
|
-
throw new Error(`Unsupported evidential metric: ${String(metric)}`);
|
|
1325
|
-
}
|
|
1326
|
-
if (operator !== "gte" && operator !== "lte" && operator !== "eq" && operator !== "gt" && operator !== "lt") {
|
|
1327
|
-
throw new Error(`Unsupported evidential operator: ${String(operator)}`);
|
|
1328
|
-
}
|
|
1329
|
-
if (typeof threshold !== "number" || !Number.isFinite(threshold)) {
|
|
1330
|
-
throw new Error("Evidential contracts require a numeric threshold.");
|
|
1331
|
-
}
|
|
1332
|
-
const actionParams = config.actionParams && typeof config.actionParams === "object" && config.actionParams !== null ? config.actionParams : void 0;
|
|
1333
|
-
return {
|
|
1334
|
-
metric,
|
|
1335
|
-
operator,
|
|
1336
|
-
threshold,
|
|
1337
|
-
action: normalizeEvidentialAction(config.action),
|
|
1338
|
-
actionParams: actionParams && (typeof actionParams.targetConfidence === "number" || typeof actionParams.rationale === "string") ? {
|
|
1339
|
-
targetConfidence: typeof actionParams.targetConfidence === "number" ? actionParams.targetConfidence : void 0,
|
|
1340
|
-
rationale: typeof actionParams.rationale === "string" ? actionParams.rationale : void 0
|
|
1341
|
-
} : void 0
|
|
1342
|
-
};
|
|
1343
|
-
}
|
|
1344
|
-
function compareMetricValue(operator, left, right) {
|
|
1345
|
-
switch (operator) {
|
|
1346
|
-
case "gte":
|
|
1347
|
-
return left >= right;
|
|
1348
|
-
case "lte":
|
|
1349
|
-
return left <= right;
|
|
1350
|
-
case "eq":
|
|
1351
|
-
return left === right;
|
|
1352
|
-
case "gt":
|
|
1353
|
-
return left > right;
|
|
1354
|
-
case "lt":
|
|
1355
|
-
return left < right;
|
|
1356
|
-
default:
|
|
1357
|
-
return false;
|
|
1358
|
-
}
|
|
1359
|
-
}
|
|
1360
|
-
function resolveComparisonResult(direction, comparisonSatisfied) {
|
|
1361
|
-
return direction === "falsifies" ? comparisonSatisfied ? "disconfirmed" : "confirmed" : comparisonSatisfied ? "confirmed" : "disconfirmed";
|
|
1362
|
-
}
|
|
1363
|
-
function buildComparisonRationale(args) {
|
|
1364
|
-
const renderedObserved = args.observedValue === null ? "no data" : `${args.observedValue}${args.unit ? ` ${args.unit}` : ""}`;
|
|
1365
|
-
const renderedThreshold = `${args.threshold}${args.unit ? ` ${args.unit}` : ""}`;
|
|
1366
|
-
const clause = `${args.label} observed ${renderedObserved} against ${args.operator} ${renderedThreshold}`;
|
|
1367
|
-
if (args.observedValue === null) {
|
|
1368
|
-
return `${clause}; evaluator returned ${args.result} because no current data was available.`;
|
|
1369
|
-
}
|
|
1370
|
-
return `${clause}; comparison ${args.comparisonSatisfied ? "passed" : "failed"}, resulting in ${args.result}.`;
|
|
1371
|
-
}
|
|
1372
|
-
function buildEvidentialRationale(args) {
|
|
1373
|
-
const observed = args.snapshot.value === null ? "no data" : String(args.snapshot.value);
|
|
1374
|
-
const clause = `${args.snapshot.metric} observed ${observed} against ${args.config.operator} ${args.config.threshold}`;
|
|
1375
|
-
if (args.snapshot.value === null) {
|
|
1376
|
-
return `${clause}; evidential evaluator treated the comparison as unmet, resulting in ${args.result}.`;
|
|
1377
|
-
}
|
|
1378
|
-
return `${clause}; comparison ${args.comparisonSatisfied ? "passed" : "failed"}, resulting in ${args.result}.`;
|
|
1379
|
-
}
|
|
1380
|
-
function parseMetricCheckerConfig(value) {
|
|
1381
|
-
if (!value || typeof value !== "object") {
|
|
1382
|
-
throw new Error(
|
|
1383
|
-
"metric_checker requires condition.evaluatorConfig with observedValue/operator/threshold."
|
|
1384
|
-
);
|
|
1385
|
-
}
|
|
1386
|
-
const config = value;
|
|
1387
|
-
return {
|
|
1388
|
-
metric: typeof config.metric === "string" && config.metric.length > 0 ? config.metric : void 0,
|
|
1389
|
-
operator: parseComparisonOperator(config.operator, BUILT_IN_METRIC_CHECKER),
|
|
1390
|
-
threshold: parseNumericThreshold(config.threshold, BUILT_IN_METRIC_CHECKER),
|
|
1391
|
-
observedValue: pickFiniteNumber(config, ["observedValue", "value"]),
|
|
1392
|
-
currentValue: pickFiniteNumber(config, ["currentValue"]),
|
|
1393
|
-
metricValue: pickFiniteNumber(config, ["metricValue"]),
|
|
1394
|
-
unit: typeof config.unit === "string" && config.unit.length > 0 ? config.unit : void 0
|
|
1395
|
-
};
|
|
1396
|
-
}
|
|
1397
|
-
function parseReferenceCheckCounterConfig(value) {
|
|
1398
|
-
if (!value || typeof value !== "object") {
|
|
1399
|
-
throw new Error(
|
|
1400
|
-
"reference_check_counter requires condition.evaluatorConfig with tag/operator/threshold."
|
|
1401
|
-
);
|
|
1402
|
-
}
|
|
1403
|
-
const config = value;
|
|
1404
|
-
if (typeof config.tag !== "string" || config.tag.trim().length === 0) {
|
|
1405
|
-
throw new Error("reference_check_counter requires a non-empty tag.");
|
|
1406
|
-
}
|
|
1407
|
-
return {
|
|
1408
|
-
tag: config.tag.trim(),
|
|
1409
|
-
operator: parseComparisonOperator(
|
|
1410
|
-
config.operator,
|
|
1411
|
-
BUILT_IN_REFERENCE_CHECK_COUNTER
|
|
1412
|
-
),
|
|
1413
|
-
threshold: parseNumericThreshold(
|
|
1414
|
-
config.threshold,
|
|
1415
|
-
BUILT_IN_REFERENCE_CHECK_COUNTER
|
|
1416
|
-
),
|
|
1417
|
-
caseSensitive: config.caseSensitive === true
|
|
1418
|
-
};
|
|
1419
|
-
}
|
|
1420
|
-
function parseTemporalDeadlineConfig(value) {
|
|
1421
|
-
if (!value || typeof value !== "object") {
|
|
1422
|
-
return {};
|
|
1423
|
-
}
|
|
1424
|
-
const config = value;
|
|
1425
|
-
return {
|
|
1426
|
-
label: typeof config.label === "string" && config.label.length > 0 ? config.label : void 0,
|
|
1427
|
-
completed: config.completed === true,
|
|
1428
|
-
completedAt: pickFiniteNumber(config, ["completedAt"]),
|
|
1429
|
-
observedAt: pickFiniteNumber(config, ["observedAt"]),
|
|
1430
|
-
satisfiedAt: pickFiniteNumber(config, ["satisfiedAt"]),
|
|
1431
|
-
achievedAt: pickFiniteNumber(config, ["achievedAt"])
|
|
1432
|
-
};
|
|
1433
|
-
}
|
|
1434
|
-
function parseMarketIndexComparatorConfig(value) {
|
|
1435
|
-
if (!value || typeof value !== "object") {
|
|
1436
|
-
throw new Error(
|
|
1437
|
-
"market_index_comparator requires condition.evaluatorConfig with subjectValue/benchmarkValue/operator/threshold."
|
|
1438
|
-
);
|
|
1439
|
-
}
|
|
1440
|
-
const config = value;
|
|
1441
|
-
return {
|
|
1442
|
-
subject: typeof config.subject === "string" && config.subject.length > 0 ? config.subject : void 0,
|
|
1443
|
-
subjectValue: pickFiniteNumber(config, ["subjectValue", "leftValue"]),
|
|
1444
|
-
primaryValue: pickFiniteNumber(config, ["primaryValue"]),
|
|
1445
|
-
benchmark: typeof config.benchmark === "string" && config.benchmark.length > 0 ? config.benchmark : void 0,
|
|
1446
|
-
benchmarkValue: pickFiniteNumber(config, ["benchmarkValue", "rightValue"]),
|
|
1447
|
-
comparisonValue: pickFiniteNumber(config, ["comparisonValue"]),
|
|
1448
|
-
operator: parseComparisonOperator(
|
|
1449
|
-
config.operator,
|
|
1450
|
-
BUILT_IN_MARKET_INDEX_COMPARATOR
|
|
1451
|
-
),
|
|
1452
|
-
threshold: parseNumericThreshold(
|
|
1453
|
-
config.threshold,
|
|
1454
|
-
BUILT_IN_MARKET_INDEX_COMPARATOR
|
|
1455
|
-
)
|
|
1456
|
-
};
|
|
1457
|
-
}
|
|
1458
|
-
|
|
1459
|
-
// ../access-control/src/audience.ts
|
|
1460
|
-
var AUDIENCE_CLASS_RANK = {
|
|
1461
|
-
public: 0,
|
|
1462
|
-
restricted_external: 1,
|
|
1463
|
-
internal: 2
|
|
1464
|
-
};
|
|
1465
|
-
function normalizeKey(key) {
|
|
1466
|
-
return (key ?? "").trim().toLowerCase().replace(/[^a-z0-9:_-]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
1467
|
-
}
|
|
1468
|
-
function normalizeAudienceKey(key) {
|
|
1469
|
-
return normalizeKey(key);
|
|
1470
|
-
}
|
|
1471
|
-
function classFromAudienceKey(audienceKey, fallback = "internal") {
|
|
1472
|
-
const key = normalizeKey(audienceKey);
|
|
1473
|
-
if (!key) {
|
|
1474
|
-
return fallback;
|
|
1475
|
-
}
|
|
1476
|
-
if (key === "internal") {
|
|
1477
|
-
return "internal";
|
|
1478
|
-
}
|
|
1479
|
-
if (key === "public") {
|
|
1480
|
-
return "public";
|
|
1481
|
-
}
|
|
1482
|
-
if (key === "lp" || key === "external" || key === "client" || key === "partner" || key === "portfolio" || key === "network" || key === "restricted_external") {
|
|
1483
|
-
return "restricted_external";
|
|
1484
|
-
}
|
|
1485
|
-
return fallback;
|
|
1486
|
-
}
|
|
1487
|
-
function canAudienceClassAccess(viewerClass, resourceClass) {
|
|
1488
|
-
return AUDIENCE_CLASS_RANK[viewerClass] >= AUDIENCE_CLASS_RANK[resourceClass];
|
|
1489
|
-
}
|
|
1490
|
-
|
|
1491
|
-
// ../access-control/src/audienceRegistry.ts
|
|
1492
|
-
var DEFAULT_AUDIENCES = [
|
|
1493
|
-
{
|
|
1494
|
-
audienceKey: "internal",
|
|
1495
|
-
audienceLabel: "Internal",
|
|
1496
|
-
audienceClass: "internal"
|
|
1497
|
-
},
|
|
1498
|
-
{
|
|
1499
|
-
audienceKey: "lp",
|
|
1500
|
-
audienceLabel: "Limited Partners",
|
|
1501
|
-
audienceClass: "restricted_external"
|
|
1502
|
-
},
|
|
1503
|
-
{
|
|
1504
|
-
audienceKey: "public",
|
|
1505
|
-
audienceLabel: "Public",
|
|
1506
|
-
audienceClass: "public"
|
|
1507
|
-
}
|
|
1508
|
-
];
|
|
1509
|
-
var AUDIENCE_CLASS_PRIORITY = {
|
|
1510
|
-
internal: 0,
|
|
1511
|
-
restricted_external: 1,
|
|
1512
|
-
public: 2
|
|
1513
|
-
};
|
|
1514
|
-
function normalizeRegistryRow(row) {
|
|
1515
|
-
return {
|
|
1516
|
-
audienceKey: normalizeAudienceKey(row.audienceKey),
|
|
1517
|
-
audienceLabel: row.audienceLabel,
|
|
1518
|
-
audienceClass: row.audienceClass,
|
|
1519
|
-
workspaceId: row.workspaceId
|
|
1520
|
-
};
|
|
1521
|
-
}
|
|
1522
|
-
function dedupeRegistryRows(rows) {
|
|
1523
|
-
const byKey = /* @__PURE__ */ new Map();
|
|
1524
|
-
for (const row of rows) {
|
|
1525
|
-
const key = normalizeAudienceKey(row.audienceKey);
|
|
1526
|
-
if (!key) {
|
|
1527
|
-
continue;
|
|
1528
|
-
}
|
|
1529
|
-
const existing = byKey.get(key);
|
|
1530
|
-
const isWorkspaceScoped = row.workspaceId !== void 0;
|
|
1531
|
-
const existingWorkspaceScoped = existing?.workspaceId !== void 0;
|
|
1532
|
-
if (!existing || isWorkspaceScoped && !existingWorkspaceScoped) {
|
|
1533
|
-
byKey.set(key, {
|
|
1534
|
-
...row,
|
|
1535
|
-
audienceKey: key
|
|
1536
|
-
});
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
const normalized = [...byKey.values()];
|
|
1540
|
-
normalized.sort((a, b) => {
|
|
1541
|
-
const classDelta = AUDIENCE_CLASS_PRIORITY[a.audienceClass] - AUDIENCE_CLASS_PRIORITY[b.audienceClass];
|
|
1542
|
-
if (classDelta !== 0) {
|
|
1543
|
-
return classDelta;
|
|
1544
|
-
}
|
|
1545
|
-
return a.audienceKey.localeCompare(b.audienceKey);
|
|
1546
|
-
});
|
|
1547
|
-
return normalized;
|
|
1548
|
-
}
|
|
1549
|
-
async function queryRegistryRows(ctx, args) {
|
|
1550
|
-
if (!args.tenantId) {
|
|
1551
|
-
return [...DEFAULT_AUDIENCES];
|
|
1552
|
-
}
|
|
1553
|
-
const rows = await ctx.db.query("platformAudiences").withIndex("by_tenantId", (q) => q.eq("tenantId", args.tenantId)).collect();
|
|
1554
|
-
const workspaceIdString = args.workspaceId ? String(args.workspaceId) : null;
|
|
1555
|
-
const tenantScoped = rows.filter((row) => row.status === "active");
|
|
1556
|
-
const applicable = tenantScoped.filter((row) => {
|
|
1557
|
-
if (!row.workspaceId) {
|
|
1558
|
-
return true;
|
|
1559
|
-
}
|
|
1560
|
-
if (!workspaceIdString) {
|
|
1561
|
-
return false;
|
|
1562
|
-
}
|
|
1563
|
-
return String(row.workspaceId) === workspaceIdString;
|
|
1564
|
-
});
|
|
1565
|
-
return dedupeRegistryRows([
|
|
1566
|
-
...DEFAULT_AUDIENCES,
|
|
1567
|
-
...applicable.map(
|
|
1568
|
-
(row) => normalizeRegistryRow({
|
|
1569
|
-
audienceKey: row.audienceKey,
|
|
1570
|
-
audienceLabel: row.audienceLabel,
|
|
1571
|
-
audienceClass: row.audienceClass,
|
|
1572
|
-
workspaceId: row.workspaceId
|
|
1573
|
-
})
|
|
1574
|
-
)
|
|
1575
|
-
]);
|
|
1576
|
-
}
|
|
1577
|
-
async function listAudienceRegistryRows(ctx, args) {
|
|
1578
|
-
return queryRegistryRows(ctx, args);
|
|
1579
|
-
}
|
|
1580
|
-
|
|
1581
|
-
// ../../packages/contracts/src/schema-helpers/enumValidation.ts
|
|
1582
|
-
var BUILTIN_ENUM_FALLBACK = {
|
|
1583
|
-
topic_type: /* @__PURE__ */ new Set([
|
|
1584
|
-
"domain",
|
|
1585
|
-
"theme",
|
|
1586
|
-
"deal",
|
|
1587
|
-
"strategy",
|
|
1588
|
-
"constitution",
|
|
1589
|
-
"project",
|
|
1590
|
-
"portfolio",
|
|
1591
|
-
"architecture",
|
|
1592
|
-
"capability",
|
|
1593
|
-
"runtime",
|
|
1594
|
-
"interface",
|
|
1595
|
-
"governance",
|
|
1596
|
-
"operations",
|
|
1597
|
-
"security",
|
|
1598
|
-
"data"
|
|
1599
|
-
]),
|
|
1600
|
-
branch_schema: /* @__PURE__ */ new Set(["pillar", "track", "dimension", "axis", "phase"]),
|
|
1601
|
-
lens_perspective_type: /* @__PURE__ */ new Set([
|
|
1602
|
-
"investigation",
|
|
1603
|
-
"monitoring",
|
|
1604
|
-
"analysis",
|
|
1605
|
-
"comparison",
|
|
1606
|
-
"taxonomy"
|
|
1607
|
-
]),
|
|
1608
|
-
belief_type: /* @__PURE__ */ new Set([
|
|
1609
|
-
"belief",
|
|
1610
|
-
"hypothesis",
|
|
1611
|
-
"principle",
|
|
1612
|
-
"invariant",
|
|
1613
|
-
"assumption",
|
|
1614
|
-
"tenet",
|
|
1615
|
-
"prior",
|
|
1616
|
-
"preference",
|
|
1617
|
-
"goal",
|
|
1618
|
-
"forecast",
|
|
1619
|
-
"decision",
|
|
1620
|
-
"constraint",
|
|
1621
|
-
"tradeoff",
|
|
1622
|
-
"policy",
|
|
1623
|
-
"implementation_choice",
|
|
1624
|
-
"implementation_decision",
|
|
1625
|
-
"interface_contract",
|
|
1626
|
-
"migration_state",
|
|
1627
|
-
"code_pattern",
|
|
1628
|
-
"deprecation_notice"
|
|
1629
|
-
]),
|
|
1630
|
-
edge_type: /* @__PURE__ */ new Set([
|
|
1631
|
-
"supports",
|
|
1632
|
-
"informs",
|
|
1633
|
-
"depends_on",
|
|
1634
|
-
"derived_from",
|
|
1635
|
-
"contains",
|
|
1636
|
-
"tests",
|
|
1637
|
-
"supersedes",
|
|
1638
|
-
"responds_to",
|
|
1639
|
-
"belongs_to",
|
|
1640
|
-
"relates_to_thesis",
|
|
1641
|
-
"works_at",
|
|
1642
|
-
"invested_in",
|
|
1643
|
-
"competes_with",
|
|
1644
|
-
"participates_in",
|
|
1645
|
-
"founded_by",
|
|
1646
|
-
"evaluates",
|
|
1647
|
-
"performs",
|
|
1648
|
-
"function_in",
|
|
1649
|
-
"impacts",
|
|
1650
|
-
"raised_from",
|
|
1651
|
-
"mentioned_in",
|
|
1652
|
-
"perspective_on",
|
|
1653
|
-
"plays_theme"
|
|
1654
|
-
]),
|
|
1655
|
-
worktree_type: /* @__PURE__ */ new Set([
|
|
1656
|
-
"belief_test",
|
|
1657
|
-
"existential",
|
|
1658
|
-
"contradiction",
|
|
1659
|
-
"refinement",
|
|
1660
|
-
"coverage",
|
|
1661
|
-
"discovery",
|
|
1662
|
-
"clarification",
|
|
1663
|
-
"confirmation"
|
|
1664
|
-
]),
|
|
1665
|
-
worktree_phase: /* @__PURE__ */ new Set([
|
|
1666
|
-
"cluster_mapping",
|
|
1667
|
-
"hypothesis_formation",
|
|
1668
|
-
"question_generation",
|
|
1669
|
-
"evidence_collection",
|
|
1670
|
-
"synthesis",
|
|
1671
|
-
"decision",
|
|
1672
|
-
"retrospective"
|
|
1673
|
-
]),
|
|
1674
|
-
activity_type: /* @__PURE__ */ new Set([
|
|
1675
|
-
"create",
|
|
1676
|
-
"update",
|
|
1677
|
-
"review",
|
|
1678
|
-
"merge",
|
|
1679
|
-
"archive",
|
|
1680
|
-
"comment",
|
|
1681
|
-
"status_change",
|
|
1682
|
-
"evidence_added",
|
|
1683
|
-
"question_added"
|
|
1684
|
-
])
|
|
1685
|
-
};
|
|
1686
|
-
function normalizeEnumValue(value) {
|
|
1687
|
-
return value.trim().toLowerCase();
|
|
1688
|
-
}
|
|
1689
|
-
async function validateSchemaEnumValue(_ctx, args) {
|
|
1690
|
-
const normalized = normalizeEnumValue(args.value);
|
|
1691
|
-
if (!normalized) {
|
|
1692
|
-
return { valid: false, source: "none" };
|
|
1693
|
-
}
|
|
1694
|
-
if (BUILTIN_ENUM_FALLBACK[args.category].has(normalized)) {
|
|
1695
|
-
return { valid: true, source: "builtin" };
|
|
1696
|
-
}
|
|
1697
|
-
return { valid: false, source: "none" };
|
|
1698
|
-
}
|
|
1699
|
-
async function assertSchemaEnumValue(ctx, args) {
|
|
1700
|
-
if (typeof args.value !== "string") {
|
|
1701
|
-
return;
|
|
1702
|
-
}
|
|
1703
|
-
const normalized = normalizeEnumValue(args.value);
|
|
1704
|
-
if (!normalized) {
|
|
1705
|
-
return;
|
|
1706
|
-
}
|
|
1707
|
-
const validation = await validateSchemaEnumValue(ctx, {
|
|
1708
|
-
category: args.category,
|
|
1709
|
-
value: normalized,
|
|
1710
|
-
tenantId: args.tenantId
|
|
1711
|
-
});
|
|
1712
|
-
if (!validation.valid) {
|
|
1713
|
-
const tenantHint = args.tenantId ? ` for tenant ${args.tenantId}` : "";
|
|
1714
|
-
throw new Error(
|
|
1715
|
-
`[${args.context}] Invalid value "${normalized}" for category "${args.category}"${tenantHint}. Add it to the contracts schema enum manifest before use.`
|
|
1716
|
-
);
|
|
1717
|
-
}
|
|
1718
|
-
return normalized;
|
|
1719
|
-
}
|
|
15
|
+
var internal = anyApi;
|
|
16
|
+
var internalMutation = internalMutationGeneric;
|
|
17
|
+
var internalQuery = internalQueryGeneric;
|
|
18
|
+
var mutation = mutationGeneric;
|
|
19
|
+
var query = queryGeneric;
|
|
1720
20
|
|
|
1721
21
|
// src/beliefLifecycle.ts
|
|
1722
22
|
var BELIEF_STATUS_VALUES = [
|
|
@@ -1820,8 +120,6 @@ var containsPropagationSpec = {
|
|
|
1820
120
|
operator: () => null,
|
|
1821
121
|
description: "Structural containment only. Traversed for explicit semantics, but it never propagates opinions."
|
|
1822
122
|
};
|
|
1823
|
-
|
|
1824
|
-
// src/edges/utils.ts
|
|
1825
123
|
function readEdgeMetadata(edge) {
|
|
1826
124
|
return {
|
|
1827
125
|
constraint: edge.constraint ?? void 0,
|
|
@@ -1898,8 +196,6 @@ var contradictsPropagationSpec = {
|
|
|
1898
196
|
},
|
|
1899
197
|
description: "Legacy contradiction edges move negative pressure in either direction, but never beyond one hop."
|
|
1900
198
|
};
|
|
1901
|
-
|
|
1902
|
-
// src/edges/dependsOn.ts
|
|
1903
199
|
var dependsOnPropagationSpec = {
|
|
1904
200
|
edgeType: "depends_on",
|
|
1905
201
|
direction: "incoming",
|
|
@@ -1915,8 +211,18 @@ var dependsOnPropagationSpec = {
|
|
|
1915
211
|
if (metadata.conditionalA && metadata.conditionalNotA) {
|
|
1916
212
|
const deducedOpinion = conditionalDeduction(
|
|
1917
213
|
dampedSource,
|
|
1918
|
-
|
|
1919
|
-
|
|
214
|
+
mkOpinion(
|
|
215
|
+
metadata.conditionalA.b,
|
|
216
|
+
metadata.conditionalA.d,
|
|
217
|
+
metadata.conditionalA.u,
|
|
218
|
+
metadata.conditionalA.a
|
|
219
|
+
),
|
|
220
|
+
mkOpinion(
|
|
221
|
+
metadata.conditionalNotA.b,
|
|
222
|
+
metadata.conditionalNotA.d,
|
|
223
|
+
metadata.conditionalNotA.u,
|
|
224
|
+
metadata.conditionalNotA.a
|
|
225
|
+
),
|
|
1920
226
|
targetOpinion.a
|
|
1921
227
|
);
|
|
1922
228
|
return annotateRationale(
|
|
@@ -2088,70 +394,17 @@ function getTraversalDirections(direction) {
|
|
|
2088
394
|
}
|
|
2089
395
|
return ["outgoing", "incoming"];
|
|
2090
396
|
}
|
|
2091
|
-
|
|
2092
|
-
// ../../packages/contracts/src/schema-helpers/spine/tables/epistemicNodes.ts
|
|
2093
|
-
var NODE_TYPES = [
|
|
2094
|
-
"decision",
|
|
2095
|
-
"belief",
|
|
2096
|
-
"question",
|
|
2097
|
-
"theme",
|
|
2098
|
-
"deal",
|
|
2099
|
-
"topic",
|
|
2100
|
-
"claim",
|
|
2101
|
-
"evidence",
|
|
2102
|
-
"synthesis",
|
|
2103
|
-
"answer",
|
|
2104
|
-
"atomic_fact",
|
|
2105
|
-
"excerpt",
|
|
2106
|
-
"source",
|
|
2107
|
-
"company",
|
|
2108
|
-
"person",
|
|
2109
|
-
"investor",
|
|
2110
|
-
"function",
|
|
2111
|
-
"value_chain"
|
|
2112
|
-
];
|
|
2113
|
-
function isNodeType(value) {
|
|
2114
|
-
return NODE_TYPES.includes(value);
|
|
2115
|
-
}
|
|
2116
|
-
function getLayerForNodeType(type) {
|
|
2117
|
-
switch (type) {
|
|
2118
|
-
case "decision":
|
|
2119
|
-
return "L4";
|
|
2120
|
-
case "belief":
|
|
2121
|
-
case "question":
|
|
2122
|
-
case "theme":
|
|
2123
|
-
case "deal":
|
|
2124
|
-
return "L3";
|
|
2125
|
-
case "claim":
|
|
2126
|
-
case "evidence":
|
|
2127
|
-
case "synthesis":
|
|
2128
|
-
case "answer":
|
|
2129
|
-
return "L2";
|
|
2130
|
-
case "atomic_fact":
|
|
2131
|
-
case "excerpt":
|
|
2132
|
-
case "source":
|
|
2133
|
-
return "L1";
|
|
2134
|
-
case "topic":
|
|
2135
|
-
return "organizational";
|
|
2136
|
-
case "company":
|
|
2137
|
-
case "person":
|
|
2138
|
-
case "investor":
|
|
2139
|
-
case "function":
|
|
2140
|
-
case "value_chain":
|
|
2141
|
-
return "ontological";
|
|
2142
|
-
}
|
|
2143
|
-
}
|
|
2144
|
-
var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
|
|
397
|
+
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
2145
398
|
function asMappedProjectId(topic) {
|
|
2146
399
|
if (!topic) {
|
|
2147
400
|
return;
|
|
2148
401
|
}
|
|
2149
|
-
const directLegacyProjectId = normalizeScopeValue(topic[
|
|
402
|
+
const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
|
|
2150
403
|
if (directLegacyProjectId) {
|
|
2151
404
|
return directLegacyProjectId;
|
|
2152
405
|
}
|
|
2153
406
|
const metadata = topic.metadata || {};
|
|
2154
|
-
const candidate = metadata[
|
|
407
|
+
const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
2155
408
|
return candidate ? candidate : void 0;
|
|
2156
409
|
}
|
|
2157
410
|
function normalizeScopeValue(value) {
|
|
@@ -2180,7 +433,7 @@ async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
|
2180
433
|
try {
|
|
2181
434
|
return await ctx.db.query("topics").withIndex(
|
|
2182
435
|
"by_graph_scope_project",
|
|
2183
|
-
(q) => q.eq(
|
|
436
|
+
(q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
|
|
2184
437
|
).collect();
|
|
2185
438
|
} catch {
|
|
2186
439
|
const topics = await ctx.db.query("topics").collect();
|
|
@@ -2196,7 +449,7 @@ async function tryResolveHostTopicById(ctx, topicId) {
|
|
|
2196
449
|
return null;
|
|
2197
450
|
}
|
|
2198
451
|
try {
|
|
2199
|
-
return await ctx.runQuery(
|
|
452
|
+
return await ctx.runQuery(api.topics.get, {
|
|
2200
453
|
id: topicId
|
|
2201
454
|
}) ?? null;
|
|
2202
455
|
} catch {
|
|
@@ -2208,7 +461,7 @@ async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
|
|
|
2208
461
|
return null;
|
|
2209
462
|
}
|
|
2210
463
|
try {
|
|
2211
|
-
return await ctx.runQuery(
|
|
464
|
+
return await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
2212
465
|
projectId: legacyScopeId
|
|
2213
466
|
}) ?? null;
|
|
2214
467
|
} catch {
|
|
@@ -2485,16 +738,17 @@ function resolveTraversalTargetNodeId(edge, direction) {
|
|
|
2485
738
|
}
|
|
2486
739
|
function readNodeOpinion(node) {
|
|
2487
740
|
const metadata = node.metadata ?? {};
|
|
2488
|
-
|
|
2489
|
-
{
|
|
741
|
+
try {
|
|
742
|
+
return readOpinionFromRecord({
|
|
2490
743
|
...metadata,
|
|
2491
744
|
opinion_b: node.opinion_b,
|
|
2492
745
|
opinion_d: node.opinion_d,
|
|
2493
746
|
opinion_u: node.opinion_u,
|
|
2494
747
|
opinion_a: node.opinion_a
|
|
2495
|
-
}
|
|
2496
|
-
|
|
2497
|
-
|
|
748
|
+
});
|
|
749
|
+
} catch {
|
|
750
|
+
return mkOpinion(0, 0, 1, 0.5);
|
|
751
|
+
}
|
|
2498
752
|
}
|
|
2499
753
|
async function collectConfidencePropagationDispatches(args) {
|
|
2500
754
|
const dispatchesByTargetId = /* @__PURE__ */ new Map();
|
|
@@ -2563,14 +817,20 @@ async function collectConfidencePropagationDispatches(args) {
|
|
|
2563
817
|
if (!result || !hasProjectedOpinionChanged(targetOpinion, result.opinion)) {
|
|
2564
818
|
continue;
|
|
2565
819
|
}
|
|
2566
|
-
|
|
820
|
+
const projectedOpinion = mkOpinion(
|
|
821
|
+
result.opinion.b,
|
|
822
|
+
result.opinion.d,
|
|
823
|
+
result.opinion.u,
|
|
824
|
+
result.opinion.a
|
|
825
|
+
);
|
|
826
|
+
opinionCache.set(cacheKey, projectedOpinion);
|
|
2567
827
|
const existingDispatch = dispatchesByTargetId.get(cacheKey);
|
|
2568
828
|
dispatchesByTargetId.set(cacheKey, {
|
|
2569
829
|
targetNodeId,
|
|
2570
830
|
edgeType: spec.edgeType,
|
|
2571
831
|
traversedDirection: direction,
|
|
2572
832
|
weight: edge.weight ?? 1,
|
|
2573
|
-
opinion:
|
|
833
|
+
opinion: projectedOpinion,
|
|
2574
834
|
operator: result.operator,
|
|
2575
835
|
rationale: existingDispatch ? `${existingDispatch.rationale}; ${result.rationale}` : result.rationale,
|
|
2576
836
|
hop: nextHop
|
|
@@ -2578,7 +838,7 @@ async function collectConfidencePropagationDispatches(args) {
|
|
|
2578
838
|
if (canContinueTransitively(spec, nextHop)) {
|
|
2579
839
|
queue.push({
|
|
2580
840
|
nodeId: targetNodeId,
|
|
2581
|
-
opinion:
|
|
841
|
+
opinion: projectedOpinion,
|
|
2582
842
|
hop: nextHop,
|
|
2583
843
|
visitedNodeIds: /* @__PURE__ */ new Set([
|
|
2584
844
|
...state.visitedNodeIds,
|
|
@@ -2693,48 +953,48 @@ async function getQuestionsAnsweredByEvidence(ctx, evidenceId) {
|
|
|
2693
953
|
}
|
|
2694
954
|
|
|
2695
955
|
// src/topicProjectOverlay.ts
|
|
2696
|
-
var
|
|
2697
|
-
function
|
|
956
|
+
var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
|
|
957
|
+
function readNonEmptyString(value) {
|
|
2698
958
|
if (typeof value !== "string") {
|
|
2699
959
|
return;
|
|
2700
960
|
}
|
|
2701
961
|
const normalized = value.trim();
|
|
2702
962
|
return normalized.length > 0 ? normalized : void 0;
|
|
2703
963
|
}
|
|
2704
|
-
function
|
|
964
|
+
function readStringArray(value) {
|
|
2705
965
|
if (!Array.isArray(value)) {
|
|
2706
966
|
return [];
|
|
2707
967
|
}
|
|
2708
|
-
return value.map((entry) =>
|
|
968
|
+
return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
|
|
2709
969
|
}
|
|
2710
|
-
function
|
|
970
|
+
function readMetadata(topic) {
|
|
2711
971
|
return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
|
|
2712
972
|
}
|
|
2713
|
-
function
|
|
973
|
+
function readLegacyProjectId(value) {
|
|
2714
974
|
if (!value) {
|
|
2715
975
|
return;
|
|
2716
976
|
}
|
|
2717
|
-
return
|
|
977
|
+
return readNonEmptyString(value[LEGACY_SCOPE_FIELD2]);
|
|
2718
978
|
}
|
|
2719
|
-
function
|
|
979
|
+
function coerceVisibility(value) {
|
|
2720
980
|
return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
|
|
2721
981
|
}
|
|
2722
|
-
function
|
|
982
|
+
function coerceStatus(value) {
|
|
2723
983
|
return value === "active" || value === "archived" || value === "watching" ? value : void 0;
|
|
2724
984
|
}
|
|
2725
|
-
function
|
|
2726
|
-
const explicit =
|
|
985
|
+
function mapProjectType(topic, metadata) {
|
|
986
|
+
const explicit = readNonEmptyString(metadata.projectType);
|
|
2727
987
|
if (explicit) {
|
|
2728
988
|
return explicit;
|
|
2729
989
|
}
|
|
2730
990
|
if (topic.type === "theme") {
|
|
2731
991
|
return "thematic";
|
|
2732
992
|
}
|
|
2733
|
-
return
|
|
993
|
+
return readNonEmptyString(topic.type) || "general";
|
|
2734
994
|
}
|
|
2735
|
-
function
|
|
2736
|
-
const metadata =
|
|
2737
|
-
return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" ||
|
|
995
|
+
function isProjectLikeTopic(topic) {
|
|
996
|
+
const metadata = readMetadata(topic);
|
|
997
|
+
return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
|
|
2738
998
|
}
|
|
2739
999
|
function isMissingLucernChildComponentError(error) {
|
|
2740
1000
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -2742,7 +1002,7 @@ function isMissingLucernChildComponentError(error) {
|
|
|
2742
1002
|
'Child component ComponentName(Identifier("lucern")) not found'
|
|
2743
1003
|
) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
|
|
2744
1004
|
}
|
|
2745
|
-
async function
|
|
1005
|
+
async function resolveTopicDoc(ctx, scopeId) {
|
|
2746
1006
|
if (ctx?.db && typeof ctx.db.get === "function") {
|
|
2747
1007
|
try {
|
|
2748
1008
|
const directTopic = await ctx.db.get(scopeId);
|
|
@@ -2756,7 +1016,7 @@ async function resolveTopicDoc2(ctx, scopeId) {
|
|
|
2756
1016
|
return null;
|
|
2757
1017
|
}
|
|
2758
1018
|
try {
|
|
2759
|
-
const topic = await ctx.runQuery(
|
|
1019
|
+
const topic = await ctx.runQuery(api.topics.get, {
|
|
2760
1020
|
id: String(scopeId)
|
|
2761
1021
|
});
|
|
2762
1022
|
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
@@ -2765,7 +1025,7 @@ async function resolveTopicDoc2(ctx, scopeId) {
|
|
|
2765
1025
|
} catch {
|
|
2766
1026
|
}
|
|
2767
1027
|
try {
|
|
2768
|
-
const topic = await ctx.runQuery(
|
|
1028
|
+
const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
2769
1029
|
projectId: String(scopeId)
|
|
2770
1030
|
});
|
|
2771
1031
|
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
@@ -2775,14 +1035,14 @@ async function resolveTopicDoc2(ctx, scopeId) {
|
|
|
2775
1035
|
}
|
|
2776
1036
|
return null;
|
|
2777
1037
|
}
|
|
2778
|
-
function
|
|
2779
|
-
const metadata =
|
|
1038
|
+
function materializeTopicProjectOverlay(topic, idMode = "legacy") {
|
|
1039
|
+
const metadata = readMetadata(topic);
|
|
2780
1040
|
const topicId = String(topic._id);
|
|
2781
|
-
const legacyProjectId =
|
|
1041
|
+
const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
|
|
2782
1042
|
const storageProjectId = legacyProjectId || topicId;
|
|
2783
1043
|
const outwardId = idMode === "topic" ? topicId : storageProjectId;
|
|
2784
|
-
const visibility =
|
|
2785
|
-
const status =
|
|
1044
|
+
const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
|
|
1045
|
+
const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
|
|
2786
1046
|
const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
|
|
2787
1047
|
const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
|
|
2788
1048
|
return {
|
|
@@ -2792,16 +1052,16 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
|
|
|
2792
1052
|
topicId,
|
|
2793
1053
|
storageProjectId,
|
|
2794
1054
|
legacyProjectId,
|
|
2795
|
-
name:
|
|
2796
|
-
type:
|
|
2797
|
-
description:
|
|
2798
|
-
ownerId:
|
|
2799
|
-
sharedWith:
|
|
1055
|
+
name: readNonEmptyString(topic.name) || "Untitled Theme",
|
|
1056
|
+
type: mapProjectType(topic, metadata),
|
|
1057
|
+
description: readNonEmptyString(topic.description),
|
|
1058
|
+
ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
|
|
1059
|
+
sharedWith: readStringArray(metadata.sharedWith),
|
|
2800
1060
|
visibility,
|
|
2801
|
-
tenantId:
|
|
2802
|
-
workspaceId:
|
|
1061
|
+
tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
|
|
1062
|
+
workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
|
|
2803
1063
|
status,
|
|
2804
|
-
tags:
|
|
1064
|
+
tags: readStringArray(metadata.tags),
|
|
2805
1065
|
chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
|
|
2806
1066
|
artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
|
|
2807
1067
|
lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
|
|
@@ -2810,17 +1070,17 @@ function materializeTopicProjectOverlay2(topic, idMode = "legacy") {
|
|
|
2810
1070
|
updatedAt
|
|
2811
1071
|
};
|
|
2812
1072
|
}
|
|
2813
|
-
async function
|
|
2814
|
-
const topic = await
|
|
1073
|
+
async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
|
|
1074
|
+
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
2815
1075
|
if (!topic) {
|
|
2816
1076
|
return null;
|
|
2817
1077
|
}
|
|
2818
|
-
if (options.projectLikeOnly !== false && !
|
|
1078
|
+
if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
|
|
2819
1079
|
return null;
|
|
2820
1080
|
}
|
|
2821
|
-
return
|
|
1081
|
+
return materializeTopicProjectOverlay(topic, options.idMode);
|
|
2822
1082
|
}
|
|
2823
|
-
async function
|
|
1083
|
+
async function listTopicProjectOverlays(ctx, options = {}) {
|
|
2824
1084
|
let allTopics = [];
|
|
2825
1085
|
if (ctx?.db?.query && typeof ctx.db.query === "function") {
|
|
2826
1086
|
try {
|
|
@@ -2830,18 +1090,18 @@ async function listTopicProjectOverlays2(ctx, options = {}) {
|
|
|
2830
1090
|
}
|
|
2831
1091
|
}
|
|
2832
1092
|
if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
|
|
2833
|
-
allTopics = (await ctx.runQuery(
|
|
1093
|
+
allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
|
|
2834
1094
|
}
|
|
2835
1095
|
return allTopics.filter(
|
|
2836
|
-
(topic) => options.projectLikeOnly === false ||
|
|
2837
|
-
).map((topic) =>
|
|
1096
|
+
(topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
|
|
1097
|
+
).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
|
|
2838
1098
|
}
|
|
2839
1099
|
async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
2840
|
-
const topic = await
|
|
1100
|
+
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
2841
1101
|
if (!topic) {
|
|
2842
1102
|
return null;
|
|
2843
1103
|
}
|
|
2844
|
-
const nextMetadata = { ...
|
|
1104
|
+
const nextMetadata = { ...readMetadata(topic) };
|
|
2845
1105
|
const patch = {};
|
|
2846
1106
|
const topicUpdateArgs = {
|
|
2847
1107
|
id: String(topic._id)
|
|
@@ -2866,7 +1126,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2866
1126
|
`patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
|
|
2867
1127
|
);
|
|
2868
1128
|
case "status": {
|
|
2869
|
-
const status =
|
|
1129
|
+
const status = coerceStatus(rawValue);
|
|
2870
1130
|
if (status) {
|
|
2871
1131
|
patch.status = status;
|
|
2872
1132
|
topicUpdateArgs.status = status;
|
|
@@ -2874,7 +1134,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2874
1134
|
break;
|
|
2875
1135
|
}
|
|
2876
1136
|
case "visibility": {
|
|
2877
|
-
const visibility =
|
|
1137
|
+
const visibility = coerceVisibility(rawValue);
|
|
2878
1138
|
if (visibility) {
|
|
2879
1139
|
patch.visibility = visibility;
|
|
2880
1140
|
topicUpdateArgs.visibility = visibility;
|
|
@@ -2882,7 +1142,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2882
1142
|
break;
|
|
2883
1143
|
}
|
|
2884
1144
|
case "type": {
|
|
2885
|
-
const projectType =
|
|
1145
|
+
const projectType = readNonEmptyString(rawValue);
|
|
2886
1146
|
if (projectType) {
|
|
2887
1147
|
nextMetadata.projectType = projectType;
|
|
2888
1148
|
} else {
|
|
@@ -2906,7 +1166,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2906
1166
|
topicUpdateArgs.metadata = nextMetadata;
|
|
2907
1167
|
if (typeof ctx.runMutation === "function") {
|
|
2908
1168
|
try {
|
|
2909
|
-
await ctx.runMutation(
|
|
1169
|
+
await ctx.runMutation(api.topics.update, topicUpdateArgs);
|
|
2910
1170
|
} catch (error) {
|
|
2911
1171
|
if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
|
|
2912
1172
|
throw error;
|
|
@@ -2920,7 +1180,7 @@ async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
|
2920
1180
|
"Cannot patch topic without component adapter (ctx.runMutation unavailable)"
|
|
2921
1181
|
);
|
|
2922
1182
|
}
|
|
2923
|
-
return
|
|
1183
|
+
return materializeTopicProjectOverlay(
|
|
2924
1184
|
{
|
|
2925
1185
|
...topic,
|
|
2926
1186
|
...patch,
|
|
@@ -2953,10 +1213,10 @@ async function patchProjectWithTolerance(ctx, projectId, value) {
|
|
|
2953
1213
|
});
|
|
2954
1214
|
}
|
|
2955
1215
|
}
|
|
2956
|
-
function
|
|
1216
|
+
function defaultResolvers() {
|
|
2957
1217
|
return {
|
|
2958
1218
|
async getProject(ctx, projectId) {
|
|
2959
|
-
return await
|
|
1219
|
+
return await resolveTopicProjectOverlay(ctx, projectId, {
|
|
2960
1220
|
idMode: "legacy",
|
|
2961
1221
|
projectLikeOnly: false
|
|
2962
1222
|
});
|
|
@@ -2965,7 +1225,7 @@ function defaultResolvers2() {
|
|
|
2965
1225
|
await patchProjectWithTolerance(ctx, projectId, value);
|
|
2966
1226
|
},
|
|
2967
1227
|
async listTopics(ctx) {
|
|
2968
|
-
return await
|
|
1228
|
+
return await listTopicProjectOverlays(ctx, {
|
|
2969
1229
|
idMode: "legacy"
|
|
2970
1230
|
});
|
|
2971
1231
|
},
|
|
@@ -2974,11 +1234,11 @@ function defaultResolvers2() {
|
|
|
2974
1234
|
}
|
|
2975
1235
|
};
|
|
2976
1236
|
}
|
|
2977
|
-
var
|
|
1237
|
+
var resolverOverrides = {};
|
|
2978
1238
|
function resolveGraphPrimitivesAppResolvers(_ctx) {
|
|
2979
1239
|
return {
|
|
2980
|
-
...
|
|
2981
|
-
...
|
|
1240
|
+
...defaultResolvers(),
|
|
1241
|
+
...resolverOverrides
|
|
2982
1242
|
};
|
|
2983
1243
|
}
|
|
2984
1244
|
|
|
@@ -3003,7 +1263,7 @@ function throwStructuredMutationError(args) {
|
|
|
3003
1263
|
function readFiniteNumber(value) {
|
|
3004
1264
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3005
1265
|
}
|
|
3006
|
-
function
|
|
1266
|
+
function clamp01(value) {
|
|
3007
1267
|
return Math.max(0, Math.min(1, value));
|
|
3008
1268
|
}
|
|
3009
1269
|
function assertBaseRateInRange(baseRate, field = "baseRate") {
|
|
@@ -3053,20 +1313,14 @@ function deriveSyntheticBackfillOpinion(source) {
|
|
|
3053
1313
|
const uncertainty = readFiniteNumber(source.opinion_u) ?? readFiniteNumber(source.uncertainty);
|
|
3054
1314
|
const baseRate = readFiniteNumber(source.opinion_a) ?? readFiniteNumber(source.baseRate);
|
|
3055
1315
|
if (belief !== void 0 || disbelief !== void 0 || uncertainty !== void 0 || baseRate !== void 0) {
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
});
|
|
1316
|
+
try {
|
|
1317
|
+
return readOpinionFromRecord(source);
|
|
1318
|
+
} catch {
|
|
1319
|
+
return mkOpinion(0, 0, 1, 0.5);
|
|
1320
|
+
}
|
|
3062
1321
|
}
|
|
3063
|
-
const confidence =
|
|
3064
|
-
return
|
|
3065
|
-
b: confidence,
|
|
3066
|
-
d: 1 - confidence,
|
|
3067
|
-
u: 0,
|
|
3068
|
-
a: 0.5
|
|
3069
|
-
};
|
|
1322
|
+
const confidence = clamp01(readFiniteNumber(source.confidence) ?? 0);
|
|
1323
|
+
return mkOpinion(confidence, 1 - confidence, 0, 0.5);
|
|
3070
1324
|
}
|
|
3071
1325
|
function clampBeliefLimit(limit, fallback = DEFAULT_PROJECT_BELIEF_LIMIT) {
|
|
3072
1326
|
if (!Number.isFinite(limit)) {
|
|
@@ -3081,16 +1335,17 @@ function readTupleContradictedFlag(value) {
|
|
|
3081
1335
|
return typeof value === "boolean" ? value : void 0;
|
|
3082
1336
|
}
|
|
3083
1337
|
function readBeliefOpinionSnapshot(node, metadata) {
|
|
3084
|
-
|
|
3085
|
-
{
|
|
1338
|
+
try {
|
|
1339
|
+
return readOpinionFromRecord({
|
|
3086
1340
|
...metadata,
|
|
3087
1341
|
opinion_b: node.opinion_b,
|
|
3088
1342
|
opinion_d: node.opinion_d,
|
|
3089
1343
|
opinion_u: node.opinion_u,
|
|
3090
1344
|
opinion_a: node.opinion_a
|
|
3091
|
-
}
|
|
3092
|
-
|
|
3093
|
-
|
|
1345
|
+
});
|
|
1346
|
+
} catch {
|
|
1347
|
+
return mkOpinion(0, 0, 1, 0.5);
|
|
1348
|
+
}
|
|
3094
1349
|
}
|
|
3095
1350
|
function deriveTupleContradictionSeverity(node) {
|
|
3096
1351
|
const metadata = node.metadata || {};
|
|
@@ -3538,12 +1793,12 @@ internalMutation({
|
|
|
3538
1793
|
},
|
|
3539
1794
|
returns: permissiveReturn,
|
|
3540
1795
|
handler: async (ctx, args) => {
|
|
3541
|
-
const sourceOpinion =
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
1796
|
+
const sourceOpinion = mkOpinion(
|
|
1797
|
+
args.opinion_b,
|
|
1798
|
+
args.opinion_d,
|
|
1799
|
+
args.opinion_u,
|
|
1800
|
+
args.opinion_a
|
|
1801
|
+
);
|
|
3547
1802
|
const sourceNode = await ctx.db.get(args.nodeId);
|
|
3548
1803
|
const sourceScope = await resolveNodeScopeForWorkspaceIsolation(
|
|
3549
1804
|
ctx,
|
|
@@ -3650,7 +1905,7 @@ mutation({
|
|
|
3650
1905
|
expectedBy: v.optional(v.number())
|
|
3651
1906
|
})
|
|
3652
1907
|
),
|
|
3653
|
-
baseRate: v.number(),
|
|
1908
|
+
baseRate: v.optional(v.number()),
|
|
3654
1909
|
metadata: v.optional(v.any())
|
|
3655
1910
|
// Additional metadata including isConditional
|
|
3656
1911
|
},
|
|
@@ -3681,7 +1936,7 @@ mutation({
|
|
|
3681
1936
|
);
|
|
3682
1937
|
}
|
|
3683
1938
|
const now = Date.now();
|
|
3684
|
-
const baseRate = assertBaseRateInRange(args.baseRate);
|
|
1939
|
+
const baseRate = assertBaseRateInRange(args.baseRate ?? 0.5);
|
|
3685
1940
|
const initialBeliefStatus = args.worktreeId ? "hypothesis" : "assumption";
|
|
3686
1941
|
const initialEpistemicStatus = args.worktreeId ? "hypothesis" : "assumption";
|
|
3687
1942
|
const seedOpinion = {
|
|
@@ -4255,8 +2510,8 @@ mutation({
|
|
|
4255
2510
|
// d: evidence AGAINST [0,1]
|
|
4256
2511
|
uncertainty: v.number(),
|
|
4257
2512
|
// u: lack of evidence [0,1]
|
|
4258
|
-
baseRate: v.
|
|
4259
|
-
// a: prior probability [0,1]
|
|
2513
|
+
baseRate: v.number(),
|
|
2514
|
+
// a: prior probability [0,1]
|
|
4260
2515
|
trigger: v.union(
|
|
4261
2516
|
v.literal("evidence_added"),
|
|
4262
2517
|
v.literal("evidence_removed"),
|
|
@@ -5407,7 +3662,7 @@ internalMutation({
|
|
|
5407
3662
|
args: {
|
|
5408
3663
|
...optionalBeliefScopeArgs,
|
|
5409
3664
|
formulation: v.string(),
|
|
5410
|
-
baseRate: v.number(),
|
|
3665
|
+
baseRate: v.optional(v.number()),
|
|
5411
3666
|
confidence: v.optional(
|
|
5412
3667
|
v.union(v.literal("high"), v.literal("medium"), v.literal("low"))
|
|
5413
3668
|
),
|
|
@@ -5443,7 +3698,7 @@ internalMutation({
|
|
|
5443
3698
|
returns: permissiveReturn,
|
|
5444
3699
|
handler: async (ctx, args) => {
|
|
5445
3700
|
const now = Date.now();
|
|
5446
|
-
const baseRate = assertBaseRateInRange(args.baseRate);
|
|
3701
|
+
const baseRate = assertBaseRateInRange(args.baseRate ?? 0.5);
|
|
5447
3702
|
const scope = await resolveTopicProjectScope(ctx, {
|
|
5448
3703
|
topicId: args.topicId,
|
|
5449
3704
|
projectId: args.projectId
|
|
@@ -5692,15 +3947,15 @@ internalMutation({
|
|
|
5692
3947
|
skippedHasHistory++;
|
|
5693
3948
|
continue;
|
|
5694
3949
|
}
|
|
5695
|
-
const
|
|
3950
|
+
const opinion = deriveSyntheticBackfillOpinion(node);
|
|
5696
3951
|
await ctx.db.insert(
|
|
5697
3952
|
"beliefConfidence",
|
|
5698
3953
|
buildBeliefConfidenceRow({
|
|
5699
3954
|
beliefId: node._id,
|
|
5700
|
-
belief:
|
|
5701
|
-
disbelief:
|
|
5702
|
-
uncertainty:
|
|
5703
|
-
baseRate:
|
|
3955
|
+
belief: opinion.b,
|
|
3956
|
+
disbelief: opinion.d,
|
|
3957
|
+
uncertainty: opinion.u,
|
|
3958
|
+
baseRate: opinion.a,
|
|
5704
3959
|
trigger: "backfill_synthetic",
|
|
5705
3960
|
rationale: "LK-6 backfill: synthesized t0 from node-level opinion fields (no prior beliefConfidence row found).",
|
|
5706
3961
|
assessedAt: readFiniteNumber(node.createdAt) ?? readFiniteNumber(node.updatedAt) ?? Date.now(),
|