@lucern/graph-primitives 1.0.0 → 1.0.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/README.md +13 -12
- package/dist/beliefDecay.js +24 -17
- package/dist/beliefDecay.js.map +1 -1
- package/dist/beliefEvidenceLinks.js +32 -8
- package/dist/beliefEvidenceLinks.js.map +1 -1
- package/dist/confidencePropagationDispatch.js.map +1 -1
- package/dist/contradictions.js +32 -9
- package/dist/contradictions.js.map +1 -1
- package/dist/convex.d.ts +55 -12
- package/dist/convex.js.map +1 -1
- package/dist/edgeValidation.d.ts +25 -2
- package/dist/edges/index.d.ts +9 -2
- package/dist/edges/index.js.map +1 -1
- package/dist/edges/propagationTypes.d.ts +2 -3
- package/dist/edges/propagationTypes.js.map +1 -1
- package/dist/entityBridge.js +10 -3
- package/dist/entityBridge.js.map +1 -1
- package/dist/entityLifecycle.js +15 -3
- package/dist/entityLifecycle.js.map +1 -1
- package/dist/epistemicAnswers.js.map +1 -1
- package/dist/epistemicBeliefs.admin.d.ts +36 -0
- package/dist/epistemicBeliefs.admin.js +745 -0
- package/dist/epistemicBeliefs.admin.js.map +1 -0
- package/dist/epistemicBeliefs.backfills.d.ts +62 -0
- package/dist/epistemicBeliefs.backfills.js +1004 -0
- package/dist/epistemicBeliefs.backfills.js.map +1 -0
- package/dist/epistemicBeliefs.confidence.d.ts +45 -0
- package/dist/epistemicBeliefs.confidence.js +1285 -0
- package/dist/epistemicBeliefs.confidence.js.map +1 -0
- package/dist/epistemicBeliefs.core.d.ts +35 -0
- package/dist/epistemicBeliefs.core.js +1508 -0
- package/dist/epistemicBeliefs.core.js.map +1 -0
- package/dist/epistemicBeliefs.d.ts +12 -3
- package/dist/epistemicBeliefs.helpers.d.ts +168 -0
- package/dist/epistemicBeliefs.helpers.js +1060 -0
- package/dist/epistemicBeliefs.helpers.js.map +1 -0
- package/dist/epistemicBeliefs.internal.d.ts +30 -0
- package/dist/epistemicBeliefs.internal.js +1329 -0
- package/dist/epistemicBeliefs.internal.js.map +1 -0
- package/dist/epistemicBeliefs.js +1196 -1184
- package/dist/epistemicBeliefs.js.map +1 -1
- package/dist/epistemicBeliefs.lifecycle.d.ts +19 -0
- package/dist/epistemicBeliefs.lifecycle.js +1608 -0
- package/dist/epistemicBeliefs.lifecycle.js.map +1 -0
- package/dist/epistemicBeliefs.links.d.ts +30 -0
- package/dist/epistemicBeliefs.links.js +761 -0
- package/dist/epistemicBeliefs.links.js.map +1 -0
- package/dist/epistemicBeliefs.queries.d.ts +16 -0
- package/dist/epistemicBeliefs.queries.js +90 -0
- package/dist/epistemicBeliefs.queries.js.map +1 -0
- package/dist/epistemicContractHelpers.d.ts +1 -1
- package/dist/epistemicContractHelpers.js +1 -1
- package/dist/epistemicContracts.d.ts +5 -76
- package/dist/epistemicContracts.evaluators.d.ts +36 -0
- package/dist/epistemicContracts.evaluators.js +2506 -0
- package/dist/epistemicContracts.evaluators.js.map +1 -0
- package/dist/epistemicContracts.handlers.d.ts +40 -0
- package/dist/epistemicContracts.handlers.js +3029 -0
- package/dist/epistemicContracts.handlers.js.map +1 -0
- package/dist/epistemicContracts.js +2006 -5281
- package/dist/epistemicContracts.js.map +1 -1
- package/dist/epistemicContracts.metrics.d.ts +26 -0
- package/dist/epistemicContracts.metrics.js +427 -0
- package/dist/epistemicContracts.metrics.js.map +1 -0
- package/dist/epistemicContracts.types.d.ts +159 -0
- package/dist/epistemicContracts.types.js +3 -0
- package/dist/epistemicContracts.types.js.map +1 -0
- package/dist/epistemicEdgeCreation.d.ts +73 -0
- package/dist/epistemicEdgeCreation.js +450 -0
- package/dist/epistemicEdgeCreation.js.map +1 -0
- package/dist/epistemicEdges-BF-cn4i3.d.ts +43 -0
- package/dist/epistemicEdges.d.ts +8 -1
- package/dist/epistemicEdges.handlers.d.ts +20 -0
- package/dist/epistemicEdges.handlers.js +289 -0
- package/dist/epistemicEdges.handlers.js.map +1 -0
- package/dist/epistemicEdges.helpers.d.ts +27 -0
- package/dist/epistemicEdges.helpers.js +162 -0
- package/dist/epistemicEdges.helpers.js.map +1 -0
- package/dist/epistemicEdges.js +797 -875
- package/dist/epistemicEdges.js.map +1 -1
- package/dist/epistemicEdges.mutations.d.ts +39 -0
- package/dist/epistemicEdges.mutations.js +1365 -0
- package/dist/epistemicEdges.mutations.js.map +1 -0
- package/dist/epistemicEdges.queries.d.ts +95 -0
- package/dist/epistemicEdges.queries.js +851 -0
- package/dist/epistemicEdges.queries.js.map +1 -0
- package/dist/epistemicEdges.types.d.ts +32 -0
- package/dist/epistemicEdges.types.js +3 -0
- package/dist/epistemicEdges.types.js.map +1 -0
- package/dist/epistemicEvidence-DvfchNt7.d.ts +46 -0
- package/dist/epistemicEvidence.d.ts +5 -2
- package/dist/epistemicEvidence.js +801 -807
- package/dist/epistemicEvidence.js.map +1 -1
- package/dist/epistemicEvidenceHelpers.d.ts +71 -0
- package/dist/epistemicEvidenceHelpers.js +769 -0
- package/dist/epistemicEvidenceHelpers.js.map +1 -0
- package/dist/epistemicEvidenceMutations.d.ts +10 -0
- package/dist/epistemicEvidenceMutations.js +1421 -0
- package/dist/epistemicEvidenceMutations.js.map +1 -0
- package/dist/epistemicEvidenceQueries.d.ts +10 -0
- package/dist/epistemicEvidenceQueries.js +1049 -0
- package/dist/epistemicEvidenceQueries.js.map +1 -0
- package/dist/epistemicHelpers.d.ts +4 -2
- package/dist/epistemicHelpers.js +132 -127
- package/dist/epistemicHelpers.js.map +1 -1
- package/dist/epistemicLayerRules.d.ts +138 -0
- package/dist/epistemicLayerRules.js +481 -0
- package/dist/epistemicLayerRules.js.map +1 -0
- package/dist/epistemicLinking.js +1 -1
- package/dist/epistemicLinking.js.map +1 -1
- package/dist/epistemicNodeCreation.d.ts +101 -0
- package/dist/epistemicNodeCreation.js +709 -0
- package/dist/epistemicNodeCreation.js.map +1 -0
- package/dist/epistemicNodes-BCQxpYx_.d.ts +54 -0
- package/dist/epistemicNodes.d.ts +5 -1
- package/dist/epistemicNodes.helpers.d.ts +51 -0
- package/dist/epistemicNodes.helpers.js +73 -0
- package/dist/epistemicNodes.helpers.js.map +1 -0
- package/dist/epistemicNodes.internal.d.ts +34 -0
- package/dist/epistemicNodes.internal.js +658 -0
- package/dist/epistemicNodes.internal.js.map +1 -0
- package/dist/epistemicNodes.js +698 -693
- package/dist/epistemicNodes.js.map +1 -1
- package/dist/epistemicNodes.mutations.d.ts +34 -0
- package/dist/epistemicNodes.mutations.js +1153 -0
- package/dist/epistemicNodes.mutations.js.map +1 -0
- package/dist/epistemicNodes.queries.d.ts +36 -0
- package/dist/epistemicNodes.queries.js +619 -0
- package/dist/epistemicNodes.queries.js.map +1 -0
- package/dist/epistemicNodes.validators.d.ts +23 -0
- package/dist/epistemicNodes.validators.js +105 -0
- package/dist/epistemicNodes.validators.js.map +1 -0
- package/dist/epistemicQuestions-bwHd2FWE.d.ts +68 -0
- package/dist/epistemicQuestions.conviction.d.ts +52 -0
- package/dist/epistemicQuestions.conviction.js +1389 -0
- package/dist/epistemicQuestions.conviction.js.map +1 -0
- package/dist/epistemicQuestions.create.d.ts +29 -0
- package/dist/epistemicQuestions.create.js +1300 -0
- package/dist/epistemicQuestions.create.js.map +1 -0
- package/dist/epistemicQuestions.d.ts +10 -2
- package/dist/epistemicQuestions.evidence.d.ts +22 -0
- package/dist/epistemicQuestions.evidence.js +929 -0
- package/dist/epistemicQuestions.evidence.js.map +1 -0
- package/dist/epistemicQuestions.helpers.d.ts +69 -0
- package/dist/epistemicQuestions.helpers.js +824 -0
- package/dist/epistemicQuestions.helpers.js.map +1 -0
- package/dist/epistemicQuestions.js +2435 -2430
- package/dist/epistemicQuestions.js.map +1 -1
- package/dist/epistemicQuestions.lifecycle.d.ts +24 -0
- package/dist/epistemicQuestions.lifecycle.js +838 -0
- package/dist/epistemicQuestions.lifecycle.js.map +1 -0
- package/dist/epistemicQuestions.queries.d.ts +41 -0
- package/dist/epistemicQuestions.queries.js +1013 -0
- package/dist/epistemicQuestions.queries.js.map +1 -0
- package/dist/epistemicQuestions.sprint.d.ts +22 -0
- package/dist/epistemicQuestions.sprint.js +757 -0
- package/dist/epistemicQuestions.sprint.js.map +1 -0
- package/dist/epistemicQuestions.tail.d.ts +42 -0
- package/dist/epistemicQuestions.tail.js +1345 -0
- package/dist/epistemicQuestions.tail.js.map +1 -0
- package/dist/epistemicSources.js +6 -2
- package/dist/epistemicSources.js.map +1 -1
- package/dist/evaluators/index.d.ts +2 -2
- package/dist/evaluators/index.js +45 -5320
- package/dist/evaluators/index.js.map +1 -1
- package/dist/evaluators/lintCheckerEvaluator.d.ts +1 -1
- package/dist/evaluators/sentryCheckerEvaluator.d.ts +1 -1
- package/dist/evaluators/testRunnerEvaluator.d.ts +1 -1
- package/dist/evaluators/tscCheckerEvaluator.d.ts +1 -1
- package/dist/{graphTypes-CpgIuCdo.d.ts → graphTypes-B8VaIjnl.d.ts} +1 -1
- package/dist/graphTypes.d.ts +1 -1
- package/dist/{helpers-BYHIk5vU.d.ts → helpers-DNYfg6mo.d.ts} +2 -3
- package/dist/helpers.d.ts +2 -2
- package/dist/helpers.js.map +1 -1
- package/dist/{index-Dq-7R-gi.d.ts → index-C-Kyd7hD.d.ts} +1 -1
- package/dist/index.d.ts +160 -14
- package/dist/index.js +12291 -13001
- package/dist/index.js.map +1 -1
- package/dist/logicalRoleInference.js.map +1 -1
- package/dist/ontologyApproval.js +1 -1
- package/dist/ontologyApproval.js.map +1 -1
- package/dist/ontologyDefinitions.js +25 -7
- package/dist/ontologyDefinitions.js.map +1 -1
- package/dist/ontologyRegistry.js.map +1 -1
- package/dist/projectionReconciliation.js.map +1 -1
- package/dist/questionEvidenceLinks.js +28 -7
- package/dist/questionEvidenceLinks.js.map +1 -1
- package/dist/resolvers.js.map +1 -1
- package/dist/scopeResolverCompat.js.map +1 -1
- package/dist/topicProjectOverlay.js.map +1 -1
- package/dist/topicScope.js.map +1 -1
- package/dist/workflowBridge.js.map +1 -1
- package/dist/workspaceIsolation.js.map +1 -1
- package/package.json +4 -5
- package/dist/edgeValidation-CeI0wc0r.d.ts +0 -35
- package/dist/epistemicBeliefs-DzKjZAeC.d.ts +0 -377
- package/dist/epistemicEdges-CvlKnEyy.d.ts +0 -191
- package/dist/epistemicEvidence-xw6UUrwh.d.ts +0 -128
- package/dist/epistemicHelpers-DevrYgPN.d.ts +0 -329
- package/dist/epistemicNodes-DjSUfvyD.d.ts +0 -167
- package/dist/epistemicQuestions-B_nUclrH.d.ts +0 -214
- package/dist/index-Dct1T70K.d.ts +0 -25
|
@@ -0,0 +1,1329 @@
|
|
|
1
|
+
import { v } from 'convex/values';
|
|
2
|
+
import { canAudienceClassAccess, normalizeAudienceKey, classFromAudienceKey } from '@lucern/access-control/audience';
|
|
3
|
+
import { listAudienceRegistryRows } from '@lucern/access-control/audienceRegistry';
|
|
4
|
+
import { assertSchemaEnumValue } from '@lucern/contracts/schema-helpers/enumValidation';
|
|
5
|
+
import { permissiveReturn } from '@lucern/contracts/schema-helpers/validators';
|
|
6
|
+
import { componentsGeneric, anyApi, internalQueryGeneric, internalMutationGeneric } from 'convex/server';
|
|
7
|
+
import { isNodeType, getLayerForNodeType } from '@lucern/contracts/schema-helpers/spine/tables/epistemicNodes';
|
|
8
|
+
import { normalizeTupleContradictionPolicy, confidenceFromSL } from '@lucern/confidence';
|
|
9
|
+
import '@lucern/access-control/access';
|
|
10
|
+
import '@lucern/access-control/auth';
|
|
11
|
+
|
|
12
|
+
// src/epistemicBeliefs.internal.ts
|
|
13
|
+
var api = anyApi;
|
|
14
|
+
componentsGeneric();
|
|
15
|
+
var internal = anyApi;
|
|
16
|
+
var internalMutation = internalMutationGeneric;
|
|
17
|
+
var internalQuery = internalQueryGeneric;
|
|
18
|
+
|
|
19
|
+
// src/globalId.ts
|
|
20
|
+
function generateGlobalId() {
|
|
21
|
+
const bytes = new Uint8Array(16);
|
|
22
|
+
crypto.getRandomValues(bytes);
|
|
23
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
24
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
25
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join(
|
|
26
|
+
""
|
|
27
|
+
);
|
|
28
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/debug.ts
|
|
32
|
+
function isGraphPrimitiveDebugEnabled() {
|
|
33
|
+
const env = globalThis.process?.env;
|
|
34
|
+
return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_GRAPH_DEBUG === "1";
|
|
35
|
+
}
|
|
36
|
+
function debugGraphPrimitiveFallback(message, context) {
|
|
37
|
+
if (!isGraphPrimitiveDebugEnabled()) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
console.debug(message, context ?? {});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/topicScope.ts
|
|
44
|
+
var LEGACY_SCOPE_FIELD = "graphScopeProjectId";
|
|
45
|
+
function asMappedProjectId(topic) {
|
|
46
|
+
if (!topic) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const directLegacyProjectId = normalizeScopeValue(topic[LEGACY_SCOPE_FIELD]);
|
|
50
|
+
if (directLegacyProjectId) {
|
|
51
|
+
return directLegacyProjectId;
|
|
52
|
+
}
|
|
53
|
+
const metadata = topic.metadata || {};
|
|
54
|
+
const candidate = metadata[LEGACY_SCOPE_FIELD] || metadata.legacyProjectId || metadata.projectId || metadata.scopeProjectId;
|
|
55
|
+
return candidate ? candidate : void 0;
|
|
56
|
+
}
|
|
57
|
+
function normalizeScopeValue(value) {
|
|
58
|
+
if (typeof value !== "string") {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const normalized = value.trim();
|
|
62
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
63
|
+
}
|
|
64
|
+
function pickPrimaryTopic(candidates) {
|
|
65
|
+
return [...candidates].sort((a, b) => {
|
|
66
|
+
const depthA = a.depth ?? 9999;
|
|
67
|
+
const depthB = b.depth ?? 9999;
|
|
68
|
+
if (depthA !== depthB) {
|
|
69
|
+
return depthA - depthB;
|
|
70
|
+
}
|
|
71
|
+
const createdA = a.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
72
|
+
const createdB = b.createdAt ?? Number.MAX_SAFE_INTEGER;
|
|
73
|
+
if (createdA !== createdB) {
|
|
74
|
+
return createdA - createdB;
|
|
75
|
+
}
|
|
76
|
+
return String(a.name || "").localeCompare(String(b.name || ""));
|
|
77
|
+
})[0];
|
|
78
|
+
}
|
|
79
|
+
async function findTopicsByScopeAlias(ctx, scopeId) {
|
|
80
|
+
try {
|
|
81
|
+
return await ctx.db.query("topics").withIndex(
|
|
82
|
+
"by_graph_scope_project",
|
|
83
|
+
(q) => q.eq(LEGACY_SCOPE_FIELD, scopeId)
|
|
84
|
+
).collect();
|
|
85
|
+
} catch (error) {
|
|
86
|
+
debugGraphPrimitiveFallback(
|
|
87
|
+
"[topicScope] Failed to resolve scope alias via index",
|
|
88
|
+
{
|
|
89
|
+
error,
|
|
90
|
+
scopeId
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
const topics = await ctx.db.query("topics").collect();
|
|
94
|
+
return topics.filter((topic) => {
|
|
95
|
+
const normalizedGlobalId = normalizeScopeValue(topic.globalId);
|
|
96
|
+
const mappedProjectId = asMappedProjectId(topic);
|
|
97
|
+
return String(topic._id) === scopeId || normalizedGlobalId === scopeId || mappedProjectId === scopeId;
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function tryResolveHostTopicById(ctx, topicId) {
|
|
102
|
+
if (typeof ctx.runQuery !== "function") {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
return await ctx.runQuery(api.topics.get, {
|
|
107
|
+
id: topicId
|
|
108
|
+
}) ?? null;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
debugGraphPrimitiveFallback(
|
|
111
|
+
"[topicScope] Failed to resolve topic by host query",
|
|
112
|
+
{
|
|
113
|
+
error,
|
|
114
|
+
topicId
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async function tryResolveHostTopicByLegacyScope(ctx, legacyScopeId) {
|
|
121
|
+
if (typeof ctx.runQuery !== "function") {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
return await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
126
|
+
projectId: legacyScopeId
|
|
127
|
+
}) ?? null;
|
|
128
|
+
} catch (error) {
|
|
129
|
+
debugGraphPrimitiveFallback(
|
|
130
|
+
"[topicScope] Failed to resolve topic by legacy scope",
|
|
131
|
+
{
|
|
132
|
+
error,
|
|
133
|
+
legacyScopeId
|
|
134
|
+
}
|
|
135
|
+
);
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async function resolveInheritedWorkspaceScope(ctx, topic) {
|
|
140
|
+
const MAX_DEPTH = 10;
|
|
141
|
+
let tenantId = normalizeScopeValue(topic.tenantId);
|
|
142
|
+
let workspaceId = normalizeScopeValue(topic.workspaceId);
|
|
143
|
+
if (tenantId && workspaceId) {
|
|
144
|
+
return { tenantId, workspaceId };
|
|
145
|
+
}
|
|
146
|
+
let current = topic;
|
|
147
|
+
for (let i = 0; i < MAX_DEPTH && current?.parentTopicId; i++) {
|
|
148
|
+
current = await ctx.db.get(current.parentTopicId);
|
|
149
|
+
if (!current) break;
|
|
150
|
+
if (!tenantId) {
|
|
151
|
+
tenantId = normalizeScopeValue(current.tenantId);
|
|
152
|
+
}
|
|
153
|
+
if (!workspaceId) {
|
|
154
|
+
workspaceId = normalizeScopeValue(current.workspaceId);
|
|
155
|
+
}
|
|
156
|
+
if (tenantId && workspaceId) break;
|
|
157
|
+
}
|
|
158
|
+
return { tenantId, workspaceId };
|
|
159
|
+
}
|
|
160
|
+
async function resolveTopicProjectScope(ctx, args) {
|
|
161
|
+
if (args.topicId) {
|
|
162
|
+
let topic = null;
|
|
163
|
+
try {
|
|
164
|
+
topic = await ctx.db.get(
|
|
165
|
+
args.topicId
|
|
166
|
+
);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
debugGraphPrimitiveFallback(
|
|
169
|
+
"[topicScope] Failed to load topic by direct id",
|
|
170
|
+
{
|
|
171
|
+
error,
|
|
172
|
+
topicId: args.topicId
|
|
173
|
+
}
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
if (!topic) {
|
|
177
|
+
topic = await tryResolveHostTopicById(ctx, String(args.topicId));
|
|
178
|
+
}
|
|
179
|
+
if (!topic) {
|
|
180
|
+
topic = pickPrimaryTopic(
|
|
181
|
+
await findTopicsByScopeAlias(ctx, String(args.topicId))
|
|
182
|
+
) ?? null;
|
|
183
|
+
}
|
|
184
|
+
if (!topic) {
|
|
185
|
+
throw new Error(`Topic not found: ${String(args.topicId)}`);
|
|
186
|
+
}
|
|
187
|
+
const inherited = await resolveInheritedWorkspaceScope(ctx, topic);
|
|
188
|
+
const mapped = asMappedProjectId(topic);
|
|
189
|
+
if (mapped) {
|
|
190
|
+
return {
|
|
191
|
+
topicId: topic._id,
|
|
192
|
+
projectId: mapped,
|
|
193
|
+
tenantId: inherited.tenantId,
|
|
194
|
+
workspaceId: inherited.workspaceId,
|
|
195
|
+
source: "topic"
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
topicId: topic._id,
|
|
200
|
+
tenantId: inherited.tenantId,
|
|
201
|
+
workspaceId: inherited.workspaceId,
|
|
202
|
+
source: "topic"
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
if (args.projectId) {
|
|
206
|
+
let directTopic = null;
|
|
207
|
+
try {
|
|
208
|
+
directTopic = await ctx.db.get(
|
|
209
|
+
args.projectId
|
|
210
|
+
);
|
|
211
|
+
} catch (error) {
|
|
212
|
+
debugGraphPrimitiveFallback(
|
|
213
|
+
"[topicScope] Failed to load direct project topic",
|
|
214
|
+
{
|
|
215
|
+
error,
|
|
216
|
+
projectId: args.projectId
|
|
217
|
+
}
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
if (directTopic) {
|
|
221
|
+
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
222
|
+
const mapped = asMappedProjectId(directTopic);
|
|
223
|
+
return {
|
|
224
|
+
topicId: directTopic._id,
|
|
225
|
+
projectId: mapped ?? args.projectId,
|
|
226
|
+
tenantId: inherited.tenantId,
|
|
227
|
+
workspaceId: inherited.workspaceId,
|
|
228
|
+
source: "topic_inferred"
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
directTopic = await tryResolveHostTopicByLegacyScope(ctx, args.projectId);
|
|
232
|
+
if (directTopic) {
|
|
233
|
+
const inherited = await resolveInheritedWorkspaceScope(ctx, directTopic);
|
|
234
|
+
const mapped = asMappedProjectId(directTopic);
|
|
235
|
+
return {
|
|
236
|
+
topicId: directTopic._id,
|
|
237
|
+
projectId: mapped ?? args.projectId,
|
|
238
|
+
tenantId: inherited.tenantId,
|
|
239
|
+
workspaceId: inherited.workspaceId,
|
|
240
|
+
source: "topic_inferred"
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
const topics = await findTopicsByScopeAlias(ctx, args.projectId);
|
|
244
|
+
const primary = pickPrimaryTopic(topics);
|
|
245
|
+
if (primary) {
|
|
246
|
+
const inherited = await resolveInheritedWorkspaceScope(ctx, primary);
|
|
247
|
+
return {
|
|
248
|
+
topicId: primary._id,
|
|
249
|
+
projectId: args.projectId,
|
|
250
|
+
tenantId: inherited.tenantId,
|
|
251
|
+
workspaceId: inherited.workspaceId,
|
|
252
|
+
source: "project_mapped_topic"
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
throw new Error(
|
|
256
|
+
`Legacy project scope ${String(args.projectId)} has no mapped topic.`
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
throw new Error(
|
|
260
|
+
"Missing scope: provide topicId (preferred) or legacy projectId alias."
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
var optionalScopeArgs = {
|
|
264
|
+
projectId: v.optional(v.string()),
|
|
265
|
+
topicId: v.optional(v.string())
|
|
266
|
+
};
|
|
267
|
+
function normalizeScopeValue2(value) {
|
|
268
|
+
if (typeof value !== "string") {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
const normalized = value.trim();
|
|
272
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
273
|
+
}
|
|
274
|
+
function throwWorkspaceIsolationError(args) {
|
|
275
|
+
const error = new Error(args.message);
|
|
276
|
+
error.status = 409;
|
|
277
|
+
error.code = "INVARIANT_VIOLATION";
|
|
278
|
+
error.invariantCode = args.invariantCode;
|
|
279
|
+
error.suggestion = args.suggestion;
|
|
280
|
+
error.details = args.details;
|
|
281
|
+
throw error;
|
|
282
|
+
}
|
|
283
|
+
function assertWorkspaceScopedEpistemicNodeScope(args) {
|
|
284
|
+
const layer = isNodeType(args.nodeType) ? getLayerForNodeType(args.nodeType) : void 0;
|
|
285
|
+
if (layer === "ontological") {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const workspaceId = normalizeScopeValue2(args.scope.workspaceId);
|
|
289
|
+
if (workspaceId) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
throwWorkspaceIsolationError({
|
|
293
|
+
message: "Workspace-scoped reasoning isolation requires workspaceId on non-ontological node creation.",
|
|
294
|
+
invariantCode: "workspace.scope_required_for_epistemic_nodes",
|
|
295
|
+
suggestion: "Resolve the topic/project scope through a workspace-bound topic before creating epistemic nodes.",
|
|
296
|
+
details: {
|
|
297
|
+
mutationName: args.mutationName,
|
|
298
|
+
nodeType: args.nodeType,
|
|
299
|
+
topicId: args.scope.topicId,
|
|
300
|
+
projectId: args.scope.projectId
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
function nodeMatchesWorkspaceReasoningScope(node, scope) {
|
|
305
|
+
if (!node) {
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
const scopeTenantId = normalizeScopeValue2(scope.tenantId);
|
|
309
|
+
const scopeWorkspaceId = normalizeScopeValue2(scope.workspaceId);
|
|
310
|
+
const nodeTenantId = normalizeScopeValue2(node.tenantId);
|
|
311
|
+
const nodeWorkspaceId = normalizeScopeValue2(node.workspaceId);
|
|
312
|
+
const epistemicLayer = typeof node.epistemicLayer === "string" ? node.epistemicLayer : void 0;
|
|
313
|
+
if (scopeTenantId && nodeTenantId && scopeTenantId !== nodeTenantId) {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
if (epistemicLayer === "ontological" && nodeWorkspaceId === void 0) {
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
if (!scopeWorkspaceId && node.publicationStatus === "published") {
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
if (!scopeWorkspaceId) {
|
|
323
|
+
return nodeWorkspaceId === void 0;
|
|
324
|
+
}
|
|
325
|
+
return scopeWorkspaceId === nodeWorkspaceId;
|
|
326
|
+
}
|
|
327
|
+
function resolveRuntimePackMutationContext(args) {
|
|
328
|
+
if (!args.runtimeToolName && !args.runtimePackKey && !args.runtimePackInstallScope) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
toolName: args.runtimeToolName,
|
|
333
|
+
packKey: args.runtimePackKey,
|
|
334
|
+
packInstallScope: args.runtimePackInstallScope
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
function assertTenantPackWorkspaceMutationAllowed(args) {
|
|
338
|
+
if (!args.runtime?.packKey || args.runtime.packInstallScope !== "tenant") {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
const targetWorkspaceId = normalizeScopeValue2(args.target.workspaceId);
|
|
342
|
+
const targetLayer = typeof args.target.epistemicLayer === "string" ? args.target.epistemicLayer : void 0;
|
|
343
|
+
if (!targetWorkspaceId || targetLayer === "ontological") {
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
throwWorkspaceIsolationError({
|
|
347
|
+
message: `Tenant-scoped pack "${args.runtime.packKey}" cannot mutate workspace-scoped reasoning state.`,
|
|
348
|
+
invariantCode: "workspace.tenant_pack_reasoning_write_forbidden",
|
|
349
|
+
suggestion: "Use a workspace-scoped pack for workspace-local graph mutations, or route the change through tenant-global canonical entity flows.",
|
|
350
|
+
details: {
|
|
351
|
+
mutationName: args.mutationName,
|
|
352
|
+
toolName: args.runtime.toolName,
|
|
353
|
+
packKey: args.runtime.packKey,
|
|
354
|
+
targetWorkspaceId,
|
|
355
|
+
targetNodeType: args.target.nodeType,
|
|
356
|
+
targetLayer
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// src/beliefLifecycle.ts
|
|
362
|
+
var BELIEF_STATUS_VALUES = [
|
|
363
|
+
"assumption",
|
|
364
|
+
"hypothesis",
|
|
365
|
+
"belief",
|
|
366
|
+
"fact"
|
|
367
|
+
];
|
|
368
|
+
var RESOLVED_PREDICTION_OUTCOMES = [
|
|
369
|
+
"confirmed",
|
|
370
|
+
"disconfirmed",
|
|
371
|
+
"partial",
|
|
372
|
+
"expired"
|
|
373
|
+
];
|
|
374
|
+
function isBeliefLifecycleStatus(value) {
|
|
375
|
+
return typeof value === "string" && BELIEF_STATUS_VALUES.includes(value);
|
|
376
|
+
}
|
|
377
|
+
function normalizeBeliefConfidence(confidence) {
|
|
378
|
+
if (typeof confidence !== "number" || !Number.isFinite(confidence)) {
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
if (confidence >= 0 && confidence <= 1) {
|
|
382
|
+
return confidence;
|
|
383
|
+
}
|
|
384
|
+
if (confidence > 1 && confidence <= 100) {
|
|
385
|
+
return confidence / 100;
|
|
386
|
+
}
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
function isResolvedByConfidence(confidence) {
|
|
390
|
+
const normalized = normalizeBeliefConfidence(confidence);
|
|
391
|
+
if (normalized === null) {
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
return normalized <= 0 || normalized >= 1;
|
|
395
|
+
}
|
|
396
|
+
function hasResolvedPredictionOutcome(predictionMeta) {
|
|
397
|
+
if (!predictionMeta || typeof predictionMeta !== "object") {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
const outcome = predictionMeta.outcome;
|
|
401
|
+
return typeof outcome === "string" && RESOLVED_PREDICTION_OUTCOMES.includes(outcome);
|
|
402
|
+
}
|
|
403
|
+
function getPredictionMetaFromMetadata(metadata) {
|
|
404
|
+
return metadata?.predictionMeta;
|
|
405
|
+
}
|
|
406
|
+
function shouldTreatBeliefAsFact(opts) {
|
|
407
|
+
if (isResolvedByConfidence(opts.confidence)) {
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
if (hasResolvedPredictionOutcome(opts.predictionMeta)) {
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
413
|
+
if (hasResolvedPredictionOutcome(getPredictionMetaFromMetadata(opts.metadata))) {
|
|
414
|
+
return true;
|
|
415
|
+
}
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
function resolveBeliefLifecycleStatus(opts) {
|
|
419
|
+
if (shouldTreatBeliefAsFact(opts)) {
|
|
420
|
+
return "fact";
|
|
421
|
+
}
|
|
422
|
+
const direct = opts.beliefStatus;
|
|
423
|
+
if (isBeliefLifecycleStatus(direct)) {
|
|
424
|
+
const normalized = normalizeBeliefConfidence(opts.confidence);
|
|
425
|
+
if (normalized !== null && isPreValidationBeliefStatus(direct)) {
|
|
426
|
+
return "belief";
|
|
427
|
+
}
|
|
428
|
+
return direct;
|
|
429
|
+
}
|
|
430
|
+
const metaStatus = opts.metadata?.beliefStatus;
|
|
431
|
+
if (isBeliefLifecycleStatus(metaStatus)) {
|
|
432
|
+
const normalized = normalizeBeliefConfidence(opts.confidence);
|
|
433
|
+
if (normalized !== null && isPreValidationBeliefStatus(metaStatus)) {
|
|
434
|
+
return "belief";
|
|
435
|
+
}
|
|
436
|
+
return metaStatus;
|
|
437
|
+
}
|
|
438
|
+
return "assumption";
|
|
439
|
+
}
|
|
440
|
+
function isPreValidationBeliefStatus(status) {
|
|
441
|
+
return status === "assumption" || status === "hypothesis";
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// src/topicProjectOverlay.ts
|
|
445
|
+
var LEGACY_SCOPE_FIELD2 = "graphScopeProjectId";
|
|
446
|
+
function readNonEmptyString(value) {
|
|
447
|
+
if (typeof value !== "string") {
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
const normalized = value.trim();
|
|
451
|
+
return normalized.length > 0 ? normalized : void 0;
|
|
452
|
+
}
|
|
453
|
+
function readStringArray(value) {
|
|
454
|
+
if (!Array.isArray(value)) {
|
|
455
|
+
return [];
|
|
456
|
+
}
|
|
457
|
+
return value.map((entry) => readNonEmptyString(entry)).filter((entry) => Boolean(entry));
|
|
458
|
+
}
|
|
459
|
+
function readMetadata(topic) {
|
|
460
|
+
return topic.metadata && typeof topic.metadata === "object" ? topic.metadata : {};
|
|
461
|
+
}
|
|
462
|
+
function readLegacyProjectId(value) {
|
|
463
|
+
if (!value) {
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
return readNonEmptyString(value[LEGACY_SCOPE_FIELD2]);
|
|
467
|
+
}
|
|
468
|
+
function coerceVisibility(value) {
|
|
469
|
+
return value === "private" || value === "team" || value === "firm" || value === "external" || value === "public" ? value : void 0;
|
|
470
|
+
}
|
|
471
|
+
function coerceStatus(value) {
|
|
472
|
+
return value === "active" || value === "archived" || value === "watching" ? value : void 0;
|
|
473
|
+
}
|
|
474
|
+
function mapProjectType(topic, metadata) {
|
|
475
|
+
const explicit = readNonEmptyString(metadata.projectType);
|
|
476
|
+
if (explicit) {
|
|
477
|
+
return explicit;
|
|
478
|
+
}
|
|
479
|
+
if (topic.type === "theme") {
|
|
480
|
+
return "thematic";
|
|
481
|
+
}
|
|
482
|
+
return readNonEmptyString(topic.type) || "general";
|
|
483
|
+
}
|
|
484
|
+
function isProjectLikeTopic(topic) {
|
|
485
|
+
const metadata = readMetadata(topic);
|
|
486
|
+
return topic.type === "theme" || topic.type === "thematic" || topic.type === "deal" || topic.type === "monitoring" || readLegacyProjectId(topic) !== void 0 || readNonEmptyString(metadata.projectType) !== void 0;
|
|
487
|
+
}
|
|
488
|
+
function isMissingLucernChildComponentError(error) {
|
|
489
|
+
const message = getErrorMessage(error);
|
|
490
|
+
return message.includes(
|
|
491
|
+
'Child component ComponentName(Identifier("lucern")) not found'
|
|
492
|
+
) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
|
|
493
|
+
}
|
|
494
|
+
function getErrorMessage(error) {
|
|
495
|
+
if (error instanceof Error) {
|
|
496
|
+
return error.message;
|
|
497
|
+
}
|
|
498
|
+
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
|
499
|
+
return error.message;
|
|
500
|
+
}
|
|
501
|
+
return "unknown error";
|
|
502
|
+
}
|
|
503
|
+
async function resolveTopicDoc(ctx, scopeId) {
|
|
504
|
+
if (ctx?.db && typeof ctx.db.get === "function") {
|
|
505
|
+
try {
|
|
506
|
+
const directTopic = await ctx.db.get(
|
|
507
|
+
scopeId
|
|
508
|
+
);
|
|
509
|
+
if (directTopic) {
|
|
510
|
+
return directTopic;
|
|
511
|
+
}
|
|
512
|
+
} catch (error) {
|
|
513
|
+
debugGraphPrimitiveFallback(
|
|
514
|
+
"[topicProjectOverlay] Failed to resolve topic by direct ID",
|
|
515
|
+
{
|
|
516
|
+
error,
|
|
517
|
+
scopeId
|
|
518
|
+
}
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
if (typeof ctx.runQuery !== "function") {
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
try {
|
|
526
|
+
const topic = await ctx.runQuery(api.topics.get, {
|
|
527
|
+
id: String(scopeId)
|
|
528
|
+
});
|
|
529
|
+
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
530
|
+
return topic;
|
|
531
|
+
}
|
|
532
|
+
} catch (error) {
|
|
533
|
+
debugGraphPrimitiveFallback(
|
|
534
|
+
"[topicProjectOverlay] Failed to resolve topic by ID query",
|
|
535
|
+
{
|
|
536
|
+
error,
|
|
537
|
+
scopeId
|
|
538
|
+
}
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
try {
|
|
542
|
+
const topic = await ctx.runQuery(api.topics.getByLegacyScopeId, {
|
|
543
|
+
projectId: String(scopeId)
|
|
544
|
+
});
|
|
545
|
+
if (topic?.name !== void 0 && topic?.type !== void 0) {
|
|
546
|
+
return topic;
|
|
547
|
+
}
|
|
548
|
+
} catch (error) {
|
|
549
|
+
debugGraphPrimitiveFallback(
|
|
550
|
+
"[topicProjectOverlay] Failed to resolve topic by legacy scope ID",
|
|
551
|
+
{ error, scopeId }
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
return null;
|
|
555
|
+
}
|
|
556
|
+
function materializeTopicProjectOverlay(topic, idMode = "legacy") {
|
|
557
|
+
const metadata = readMetadata(topic);
|
|
558
|
+
const topicId = String(topic._id);
|
|
559
|
+
const legacyProjectId = readLegacyProjectId(topic) || readLegacyProjectId(metadata) || readNonEmptyString(metadata.legacyProjectId);
|
|
560
|
+
const storageProjectId = legacyProjectId || topicId;
|
|
561
|
+
const outwardId = idMode === "topic" ? topicId : storageProjectId;
|
|
562
|
+
const visibility = coerceVisibility(topic.visibility) || coerceVisibility(metadata.visibility) || "private";
|
|
563
|
+
const status = coerceStatus(topic.status) || coerceStatus(metadata.status) || "active";
|
|
564
|
+
const createdAt = typeof topic.createdAt === "number" ? topic.createdAt : typeof topic._creationTime === "number" ? topic._creationTime : 0;
|
|
565
|
+
const updatedAt = typeof topic.updatedAt === "number" ? topic.updatedAt : typeof metadata.updatedAt === "number" ? metadata.updatedAt : createdAt;
|
|
566
|
+
return {
|
|
567
|
+
...metadata,
|
|
568
|
+
_id: outwardId,
|
|
569
|
+
projectId: outwardId,
|
|
570
|
+
topicId,
|
|
571
|
+
storageProjectId,
|
|
572
|
+
legacyProjectId,
|
|
573
|
+
name: readNonEmptyString(topic.name) || "Untitled Theme",
|
|
574
|
+
type: mapProjectType(topic, metadata),
|
|
575
|
+
description: readNonEmptyString(topic.description),
|
|
576
|
+
ownerId: readNonEmptyString(metadata.ownerId) || readNonEmptyString(topic.createdBy) || "system",
|
|
577
|
+
sharedWith: readStringArray(metadata.sharedWith),
|
|
578
|
+
visibility,
|
|
579
|
+
tenantId: readNonEmptyString(topic.tenantId) || readNonEmptyString(metadata.tenantId),
|
|
580
|
+
workspaceId: readNonEmptyString(topic.workspaceId) || readNonEmptyString(metadata.workspaceId),
|
|
581
|
+
status,
|
|
582
|
+
tags: readStringArray(metadata.tags),
|
|
583
|
+
chatCount: typeof metadata.chatCount === "number" ? metadata.chatCount : 0,
|
|
584
|
+
artifactCount: typeof metadata.artifactCount === "number" ? metadata.artifactCount : 0,
|
|
585
|
+
lastActivityAt: typeof metadata.lastActivityAt === "number" ? metadata.lastActivityAt : updatedAt,
|
|
586
|
+
_creationTime: typeof topic._creationTime === "number" ? topic._creationTime : createdAt,
|
|
587
|
+
createdAt,
|
|
588
|
+
updatedAt
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
async function resolveTopicProjectOverlay(ctx, scopeId, options = {}) {
|
|
592
|
+
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
593
|
+
if (!topic) {
|
|
594
|
+
return null;
|
|
595
|
+
}
|
|
596
|
+
if (options.projectLikeOnly !== false && !isProjectLikeTopic(topic)) {
|
|
597
|
+
return null;
|
|
598
|
+
}
|
|
599
|
+
return materializeTopicProjectOverlay(topic, options.idMode);
|
|
600
|
+
}
|
|
601
|
+
async function listTopicProjectOverlays(ctx, options = {}) {
|
|
602
|
+
let allTopics = [];
|
|
603
|
+
if (ctx?.db?.query && typeof ctx.db.query === "function") {
|
|
604
|
+
try {
|
|
605
|
+
allTopics = await ctx.db.query("topics").collect();
|
|
606
|
+
} catch (error) {
|
|
607
|
+
debugGraphPrimitiveFallback(
|
|
608
|
+
"[topicProjectOverlay] Failed to read topics table; falling back to API",
|
|
609
|
+
{ error }
|
|
610
|
+
);
|
|
611
|
+
allTopics = [];
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
if (allTopics.length === 0 && typeof ctx.runQuery === "function") {
|
|
615
|
+
allTopics = (await ctx.runQuery(api.topics.list, {}) ?? []) || [];
|
|
616
|
+
}
|
|
617
|
+
return allTopics.filter(
|
|
618
|
+
(topic) => options.projectLikeOnly === false || isProjectLikeTopic(topic)
|
|
619
|
+
).map((topic) => materializeTopicProjectOverlay(topic, options.idMode));
|
|
620
|
+
}
|
|
621
|
+
async function patchTopicProjectOverlay(ctx, scopeId, value) {
|
|
622
|
+
const topic = await resolveTopicDoc(ctx, scopeId);
|
|
623
|
+
if (!topic) {
|
|
624
|
+
return null;
|
|
625
|
+
}
|
|
626
|
+
const nextMetadata = { ...readMetadata(topic) };
|
|
627
|
+
const patch = {};
|
|
628
|
+
const topicUpdateArgs = {
|
|
629
|
+
id: String(topic._id)
|
|
630
|
+
};
|
|
631
|
+
for (const [key, rawValue] of Object.entries(value)) {
|
|
632
|
+
switch (key) {
|
|
633
|
+
case "_id":
|
|
634
|
+
case "projectId":
|
|
635
|
+
case "topicId":
|
|
636
|
+
case "legacyProjectId":
|
|
637
|
+
case "storageProjectId":
|
|
638
|
+
break;
|
|
639
|
+
case "name":
|
|
640
|
+
case "description":
|
|
641
|
+
patch[key] = rawValue;
|
|
642
|
+
topicUpdateArgs[key] = rawValue;
|
|
643
|
+
break;
|
|
644
|
+
case "tenantId":
|
|
645
|
+
case "workspaceId":
|
|
646
|
+
case "ownerId":
|
|
647
|
+
throw new Error(
|
|
648
|
+
`patchTopicProjectOverlay cannot mutate ${key} via component-owned topics`
|
|
649
|
+
);
|
|
650
|
+
case "status": {
|
|
651
|
+
const status = coerceStatus(rawValue);
|
|
652
|
+
if (status) {
|
|
653
|
+
patch.status = status;
|
|
654
|
+
topicUpdateArgs.status = status;
|
|
655
|
+
}
|
|
656
|
+
break;
|
|
657
|
+
}
|
|
658
|
+
case "visibility": {
|
|
659
|
+
const visibility = coerceVisibility(rawValue);
|
|
660
|
+
if (visibility) {
|
|
661
|
+
patch.visibility = visibility;
|
|
662
|
+
topicUpdateArgs.visibility = visibility;
|
|
663
|
+
}
|
|
664
|
+
break;
|
|
665
|
+
}
|
|
666
|
+
case "type": {
|
|
667
|
+
const projectType = readNonEmptyString(rawValue);
|
|
668
|
+
if (projectType) {
|
|
669
|
+
nextMetadata.projectType = projectType;
|
|
670
|
+
} else {
|
|
671
|
+
delete nextMetadata.projectType;
|
|
672
|
+
}
|
|
673
|
+
break;
|
|
674
|
+
}
|
|
675
|
+
case "updatedAt":
|
|
676
|
+
case "createdAt":
|
|
677
|
+
break;
|
|
678
|
+
default:
|
|
679
|
+
if (rawValue === void 0) {
|
|
680
|
+
delete nextMetadata[key];
|
|
681
|
+
} else {
|
|
682
|
+
nextMetadata[key] = rawValue;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
patch.updatedAt = Date.now();
|
|
687
|
+
patch.metadata = nextMetadata;
|
|
688
|
+
topicUpdateArgs.metadata = nextMetadata;
|
|
689
|
+
if (typeof ctx.runMutation === "function") {
|
|
690
|
+
try {
|
|
691
|
+
await ctx.runMutation(api.topics.update, topicUpdateArgs);
|
|
692
|
+
} catch (error) {
|
|
693
|
+
if (!isMissingLucernChildComponentError(error) || !ctx?.db || typeof ctx.db.patch !== "function") {
|
|
694
|
+
throw error;
|
|
695
|
+
}
|
|
696
|
+
await ctx.db.patch(String(topic._id), patch);
|
|
697
|
+
}
|
|
698
|
+
} else if (ctx?.db && typeof ctx.db.patch === "function") {
|
|
699
|
+
await ctx.db.patch(String(topic._id), patch);
|
|
700
|
+
} else {
|
|
701
|
+
throw new Error(
|
|
702
|
+
"Cannot patch topic without component adapter (ctx.runMutation unavailable)"
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
return materializeTopicProjectOverlay({
|
|
706
|
+
...topic,
|
|
707
|
+
...patch,
|
|
708
|
+
metadata: nextMetadata
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// src/resolvers.ts
|
|
713
|
+
function isMissingLucernChildComponentError2(error) {
|
|
714
|
+
const message = getErrorMessage2(error);
|
|
715
|
+
return message.includes(
|
|
716
|
+
'Child component ComponentName(Identifier("lucern")) not found'
|
|
717
|
+
) || message.includes("Child component") && message.includes("lucern") && message.includes("not found");
|
|
718
|
+
}
|
|
719
|
+
function getErrorMessage2(error) {
|
|
720
|
+
if (error instanceof Error) {
|
|
721
|
+
return error.message;
|
|
722
|
+
}
|
|
723
|
+
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
|
724
|
+
return error.message;
|
|
725
|
+
}
|
|
726
|
+
return "unknown error";
|
|
727
|
+
}
|
|
728
|
+
function isAdvisoryTopicPatch(value) {
|
|
729
|
+
const advisoryKeys = /* @__PURE__ */ new Set(["lastActivityAt", "updatedAt"]);
|
|
730
|
+
const keys = Object.keys(value);
|
|
731
|
+
return keys.length > 0 && keys.every((key) => advisoryKeys.has(key));
|
|
732
|
+
}
|
|
733
|
+
async function patchProjectWithTolerance(ctx, projectId, value) {
|
|
734
|
+
try {
|
|
735
|
+
await patchTopicProjectOverlay(ctx, projectId, value);
|
|
736
|
+
} catch (error) {
|
|
737
|
+
if (!isAdvisoryTopicPatch(value) || !isMissingLucernChildComponentError2(error)) {
|
|
738
|
+
throw error;
|
|
739
|
+
}
|
|
740
|
+
console.warn(
|
|
741
|
+
"[lucern graph-primitives] Non-fatal advisory topic patch failure",
|
|
742
|
+
{
|
|
743
|
+
projectId,
|
|
744
|
+
keys: Object.keys(value),
|
|
745
|
+
error: getErrorMessage2(error)
|
|
746
|
+
}
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
function defaultResolvers() {
|
|
751
|
+
return {
|
|
752
|
+
getProject: (ctx, projectId) => resolveTopicProjectOverlay(ctx, projectId, {
|
|
753
|
+
idMode: "legacy",
|
|
754
|
+
projectLikeOnly: false
|
|
755
|
+
}),
|
|
756
|
+
patchProject: (ctx, projectId, value) => patchProjectWithTolerance(ctx, projectId, value),
|
|
757
|
+
listTopics: (ctx) => listTopicProjectOverlays(ctx, {
|
|
758
|
+
idMode: "legacy"
|
|
759
|
+
}),
|
|
760
|
+
getFinalArtifact: (ctx, artifactId) => ctx.db.get(artifactId)
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
var resolverOverrides = {};
|
|
764
|
+
function resolveGraphPrimitivesAppResolvers(_ctx) {
|
|
765
|
+
return {
|
|
766
|
+
...defaultResolvers(),
|
|
767
|
+
...resolverOverrides
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// src/epistemicBeliefs.helpers.ts
|
|
772
|
+
v.id("epistemicNodes");
|
|
773
|
+
var DEFAULT_PROJECT_BELIEF_LIMIT = 250;
|
|
774
|
+
var MAX_PROJECT_BELIEF_LIMIT = 1e3;
|
|
775
|
+
var optionalBeliefScopeArgs = optionalScopeArgs;
|
|
776
|
+
({
|
|
777
|
+
tupleContradiction: normalizeTupleContradictionPolicy()
|
|
778
|
+
});
|
|
779
|
+
function throwStructuredMutationError(args) {
|
|
780
|
+
const error = new Error(args.message);
|
|
781
|
+
error.status = args.status;
|
|
782
|
+
error.code = args.code;
|
|
783
|
+
error.invariantCode = args.invariantCode;
|
|
784
|
+
error.suggestion = args.suggestion;
|
|
785
|
+
error.details = args.details;
|
|
786
|
+
throw error;
|
|
787
|
+
}
|
|
788
|
+
function assertBaseRateInRange(baseRate, field = "baseRate") {
|
|
789
|
+
if (baseRate < 0 || baseRate > 1) {
|
|
790
|
+
throwStructuredMutationError({
|
|
791
|
+
message: `${field} must be within [0, 1].`,
|
|
792
|
+
status: 400,
|
|
793
|
+
code: "INVALID_ARGUMENT",
|
|
794
|
+
invariantCode: "request.valid_shape",
|
|
795
|
+
suggestion: `Clamp ${field} into the inclusive [0, 1] interval.`,
|
|
796
|
+
details: { field, baseRate }
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
return baseRate;
|
|
800
|
+
}
|
|
801
|
+
function buildBeliefConfidenceRow(args) {
|
|
802
|
+
return {
|
|
803
|
+
beliefId: args.beliefId,
|
|
804
|
+
confidence: confidenceFromSL(
|
|
805
|
+
args.belief,
|
|
806
|
+
args.disbelief,
|
|
807
|
+
args.uncertainty,
|
|
808
|
+
args.baseRate
|
|
809
|
+
),
|
|
810
|
+
belief: args.belief,
|
|
811
|
+
disbelief: args.disbelief,
|
|
812
|
+
uncertainty: args.uncertainty,
|
|
813
|
+
baseRate: args.baseRate,
|
|
814
|
+
slOperator: args.slOperator ?? "manual_assessment",
|
|
815
|
+
trigger: args.trigger,
|
|
816
|
+
...args.rationale ? { rationale: args.rationale } : {},
|
|
817
|
+
assessedBy: args.assessedBy,
|
|
818
|
+
assessedAt: args.assessedAt,
|
|
819
|
+
...args.triggeringEvidenceId ? {
|
|
820
|
+
triggeringEvidenceId: args.triggeringEvidenceId,
|
|
821
|
+
triggeringEvidenceIds: [String(args.triggeringEvidenceId)]
|
|
822
|
+
} : {},
|
|
823
|
+
...args.triggeringContradictionId ? {
|
|
824
|
+
triggeringContradictionId: args.triggeringContradictionId
|
|
825
|
+
} : {},
|
|
826
|
+
...args.triggeringWorktreeId ? { triggeringWorktreeId: args.triggeringWorktreeId } : {}
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
function clampBeliefLimit(limit, fallback = DEFAULT_PROJECT_BELIEF_LIMIT) {
|
|
830
|
+
if (!Number.isFinite(limit)) {
|
|
831
|
+
return fallback;
|
|
832
|
+
}
|
|
833
|
+
return Math.max(
|
|
834
|
+
1,
|
|
835
|
+
Math.min(Math.floor(limit), MAX_PROJECT_BELIEF_LIMIT)
|
|
836
|
+
);
|
|
837
|
+
}
|
|
838
|
+
function readTupleContradictedFlag(value) {
|
|
839
|
+
return typeof value === "boolean" ? value : void 0;
|
|
840
|
+
}
|
|
841
|
+
function generateContentHash(text) {
|
|
842
|
+
const content = `belief:${text.trim().toLowerCase().replace(/\s+/g, " ")}`;
|
|
843
|
+
let hash = 5381;
|
|
844
|
+
for (let i = 0; i < content.length; i++) {
|
|
845
|
+
hash = (hash << 5) + hash + content.charCodeAt(i);
|
|
846
|
+
hash &= hash;
|
|
847
|
+
}
|
|
848
|
+
return Math.abs(hash).toString(16).padStart(8, "0");
|
|
849
|
+
}
|
|
850
|
+
function resolveBeliefWorktreeId(metadata) {
|
|
851
|
+
const worktreeId = metadata?.worktreeId;
|
|
852
|
+
if (typeof worktreeId === "string" && worktreeId.trim().length > 0) {
|
|
853
|
+
return worktreeId;
|
|
854
|
+
}
|
|
855
|
+
const sprintId = metadata?.sprintId;
|
|
856
|
+
return typeof sprintId === "string" && sprintId.trim().length > 0 ? sprintId : void 0;
|
|
857
|
+
}
|
|
858
|
+
function normalizePillar(pillar) {
|
|
859
|
+
if (!pillar) {
|
|
860
|
+
return "other";
|
|
861
|
+
}
|
|
862
|
+
const lower = pillar.toLowerCase();
|
|
863
|
+
const validPillars = [
|
|
864
|
+
"market",
|
|
865
|
+
"competition",
|
|
866
|
+
"product",
|
|
867
|
+
"team",
|
|
868
|
+
"financials",
|
|
869
|
+
"regulatory",
|
|
870
|
+
"timing",
|
|
871
|
+
"customer",
|
|
872
|
+
"technology",
|
|
873
|
+
"distribution",
|
|
874
|
+
"deal",
|
|
875
|
+
"risks"
|
|
876
|
+
];
|
|
877
|
+
return validPillars.find((p) => lower.includes(p)) || "other";
|
|
878
|
+
}
|
|
879
|
+
async function markBeliefGraphDirty(ctx, scope) {
|
|
880
|
+
const projectId = typeof scope.projectId === "string" && scope.projectId.trim().length > 0 ? scope.projectId : void 0;
|
|
881
|
+
const topicId = typeof scope.topicId === "string" && scope.topicId.trim().length > 0 ? scope.topicId : void 0;
|
|
882
|
+
if (!projectId && !topicId) {
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
if (projectId) {
|
|
886
|
+
await ctx.scheduler.runAfter(
|
|
887
|
+
0,
|
|
888
|
+
internal.graphAnalysisCache.markCacheStaleInternal,
|
|
889
|
+
{ projectId }
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
if (topicId) {
|
|
893
|
+
await ctx.scheduler.runAfter(
|
|
894
|
+
0,
|
|
895
|
+
internal.graphAnalysisCache.markCacheStaleByTopic,
|
|
896
|
+
{ topicId }
|
|
897
|
+
);
|
|
898
|
+
}
|
|
899
|
+
await resolveGraphPrimitivesAppResolvers().patchProject(
|
|
900
|
+
ctx,
|
|
901
|
+
topicId ?? projectId,
|
|
902
|
+
{
|
|
903
|
+
lastActivityAt: Date.now()
|
|
904
|
+
}
|
|
905
|
+
);
|
|
906
|
+
}
|
|
907
|
+
async function resolveBeliefScopeOrNull(ctx, args) {
|
|
908
|
+
if (!args.projectId && !args.topicId) {
|
|
909
|
+
return null;
|
|
910
|
+
}
|
|
911
|
+
try {
|
|
912
|
+
return await resolveTopicProjectScope(ctx, {
|
|
913
|
+
projectId: args.projectId ?? void 0,
|
|
914
|
+
topicId: args.topicId ?? void 0
|
|
915
|
+
});
|
|
916
|
+
} catch (error) {
|
|
917
|
+
debugGraphPrimitiveFallback(
|
|
918
|
+
"[epistemicBeliefs] Failed to resolve belief scope",
|
|
919
|
+
{
|
|
920
|
+
error,
|
|
921
|
+
projectId: args.projectId,
|
|
922
|
+
topicId: args.topicId
|
|
923
|
+
}
|
|
924
|
+
);
|
|
925
|
+
return null;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
async function getBeliefNodesForScope(ctx, scope, args) {
|
|
929
|
+
const baseQuery = ctx.db.query("epistemicNodes").withIndex(
|
|
930
|
+
"by_topic_type",
|
|
931
|
+
(q) => q.eq("topicId", scope.topicId).eq("nodeType", "belief")
|
|
932
|
+
);
|
|
933
|
+
const nodes = typeof args?.scanLimit === "number" ? await baseQuery.order("desc").take(args.scanLimit) : await baseQuery.collect();
|
|
934
|
+
const scopedNodes = nodes.filter(
|
|
935
|
+
(node) => nodeMatchesWorkspaceReasoningScope(node, scope)
|
|
936
|
+
);
|
|
937
|
+
if (!args?.status) {
|
|
938
|
+
return scopedNodes;
|
|
939
|
+
}
|
|
940
|
+
return scopedNodes.filter((node) => node.status === args.status);
|
|
941
|
+
}
|
|
942
|
+
function createBeliefAudienceResolver(registryRows) {
|
|
943
|
+
const audienceClassByKey = new Map(
|
|
944
|
+
registryRows.map((row) => [
|
|
945
|
+
normalizeAudienceKey(row.audienceKey),
|
|
946
|
+
row.audienceClass
|
|
947
|
+
])
|
|
948
|
+
);
|
|
949
|
+
return (audienceKey, fallback) => {
|
|
950
|
+
const key = normalizeAudienceKey(audienceKey);
|
|
951
|
+
if (!key) {
|
|
952
|
+
return fallback;
|
|
953
|
+
}
|
|
954
|
+
return audienceClassByKey.get(key) ?? classFromAudienceKey(key, fallback);
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
function flattenBeliefNode(node) {
|
|
958
|
+
const meta = node.metadata || {};
|
|
959
|
+
const worktreeId = resolveBeliefWorktreeId(meta);
|
|
960
|
+
const tupleContradicted = readTupleContradictedFlag(
|
|
961
|
+
node.tupleContradicted
|
|
962
|
+
) ?? readTupleContradictedFlag(meta.tupleContradicted) ?? false;
|
|
963
|
+
return {
|
|
964
|
+
_id: node._id,
|
|
965
|
+
_epistemicNodeId: node._id,
|
|
966
|
+
_creationTime: node._creationTime,
|
|
967
|
+
belief: node.canonicalText,
|
|
968
|
+
formulation: node.canonicalText,
|
|
969
|
+
projectId: node.projectId,
|
|
970
|
+
topicId: node.topicId,
|
|
971
|
+
userId: node.userId || node.createdBy || "",
|
|
972
|
+
confidence: meta.confidence || "untested",
|
|
973
|
+
status: node.status,
|
|
974
|
+
beliefStatus: resolveBeliefStatus(node, meta),
|
|
975
|
+
topic: meta.topic || meta.pillar || "other",
|
|
976
|
+
pillar: meta.pillar || meta.topic || "",
|
|
977
|
+
category: meta.category || "",
|
|
978
|
+
subcategory: meta.subcategory || "",
|
|
979
|
+
categoryIcon: meta.categoryIcon || "",
|
|
980
|
+
supportingEvidence: meta.supportingEvidenceIds || [],
|
|
981
|
+
contradictingEvidence: meta.contradictingEvidenceIds || [],
|
|
982
|
+
testingQuestions: meta.testingQuestionIds || [],
|
|
983
|
+
linkedInsights: meta.linkedInsightIds || [],
|
|
984
|
+
createdAt: node.createdAt,
|
|
985
|
+
updatedAt: node.updatedAt || node.createdAt,
|
|
986
|
+
tupleContradicted,
|
|
987
|
+
sprintId: meta.sprintId || void 0,
|
|
988
|
+
worktreeId,
|
|
989
|
+
sourceBeliefIds: meta.sourceBeliefIds || void 0,
|
|
990
|
+
criticality: meta.criticality || "unanalyzed",
|
|
991
|
+
rationale: meta.rationale || "",
|
|
992
|
+
audienceLabel: node.audienceLabel,
|
|
993
|
+
policyTags: node.policyTags,
|
|
994
|
+
sensitivityTier: node.sensitivityTier,
|
|
995
|
+
exportClass: node.exportClass,
|
|
996
|
+
anonymizationClass: node.anonymizationClass
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
function resolveBeliefStatus(node, metadata) {
|
|
1000
|
+
return resolveBeliefLifecycleStatus({
|
|
1001
|
+
beliefStatus: node.beliefStatus,
|
|
1002
|
+
confidence: node.confidence,
|
|
1003
|
+
predictionMeta: node.predictionMeta,
|
|
1004
|
+
metadata
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
// src/epistemicBeliefs.internal.ts
|
|
1009
|
+
var internalGetByProject = internalQuery({
|
|
1010
|
+
args: {
|
|
1011
|
+
...optionalBeliefScopeArgs,
|
|
1012
|
+
status: v.optional(
|
|
1013
|
+
v.union(
|
|
1014
|
+
v.literal("active"),
|
|
1015
|
+
v.literal("validated"),
|
|
1016
|
+
v.literal("invalidated"),
|
|
1017
|
+
v.literal("archived"),
|
|
1018
|
+
v.literal("superseded")
|
|
1019
|
+
)
|
|
1020
|
+
),
|
|
1021
|
+
limit: v.optional(v.number()),
|
|
1022
|
+
audienceMode: v.optional(v.string())
|
|
1023
|
+
},
|
|
1024
|
+
returns: permissiveReturn,
|
|
1025
|
+
handler: async (ctx, args) => {
|
|
1026
|
+
const scope = await resolveBeliefScopeOrNull(ctx, args);
|
|
1027
|
+
if (!scope) {
|
|
1028
|
+
return [];
|
|
1029
|
+
}
|
|
1030
|
+
const pageSize = clampBeliefLimit(args.limit, 500);
|
|
1031
|
+
const scanLimit = Math.min(pageSize * 3, MAX_PROJECT_BELIEF_LIMIT);
|
|
1032
|
+
const audienceMode = args.audienceMode ?? "internal";
|
|
1033
|
+
const registryRows = await listAudienceRegistryRows(ctx, {
|
|
1034
|
+
tenantId: scope.tenantId,
|
|
1035
|
+
workspaceId: scope.workspaceId
|
|
1036
|
+
});
|
|
1037
|
+
const resolveAudienceClass = createBeliefAudienceResolver(registryRows);
|
|
1038
|
+
const viewerClass = resolveAudienceClass(audienceMode, "public");
|
|
1039
|
+
const nodes = await getBeliefNodesForScope(ctx, scope, { scanLimit });
|
|
1040
|
+
let filtered = nodes.filter(
|
|
1041
|
+
(node) => canAudienceClassAccess(
|
|
1042
|
+
viewerClass,
|
|
1043
|
+
resolveAudienceClass(node.audienceLabel, "internal")
|
|
1044
|
+
)
|
|
1045
|
+
);
|
|
1046
|
+
if (args.status) {
|
|
1047
|
+
filtered = filtered.filter((n) => n.status === args.status);
|
|
1048
|
+
}
|
|
1049
|
+
filtered = filtered.slice(0, pageSize);
|
|
1050
|
+
return filtered.map(flattenBeliefNode);
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
1053
|
+
var internalGetByTopic = internalQuery({
|
|
1054
|
+
args: {
|
|
1055
|
+
topicId: v.string(),
|
|
1056
|
+
status: v.optional(
|
|
1057
|
+
v.union(
|
|
1058
|
+
v.literal("active"),
|
|
1059
|
+
v.literal("validated"),
|
|
1060
|
+
v.literal("invalidated"),
|
|
1061
|
+
v.literal("archived"),
|
|
1062
|
+
v.literal("superseded")
|
|
1063
|
+
)
|
|
1064
|
+
),
|
|
1065
|
+
limit: v.optional(v.number()),
|
|
1066
|
+
audienceMode: v.optional(v.string())
|
|
1067
|
+
},
|
|
1068
|
+
returns: permissiveReturn,
|
|
1069
|
+
handler: async (ctx, args) => {
|
|
1070
|
+
const pageSize = clampBeliefLimit(args.limit, 500);
|
|
1071
|
+
const scanLimit = Math.min(pageSize * 3, MAX_PROJECT_BELIEF_LIMIT);
|
|
1072
|
+
const audienceMode = args.audienceMode ?? "internal";
|
|
1073
|
+
const scope = await resolveTopicProjectScope(ctx, {
|
|
1074
|
+
topicId: args.topicId
|
|
1075
|
+
});
|
|
1076
|
+
const registryRows = await listAudienceRegistryRows(ctx, {
|
|
1077
|
+
tenantId: scope.tenantId,
|
|
1078
|
+
workspaceId: scope.workspaceId
|
|
1079
|
+
});
|
|
1080
|
+
const resolveAudienceClass = createBeliefAudienceResolver(registryRows);
|
|
1081
|
+
const viewerClass = resolveAudienceClass(audienceMode, "public");
|
|
1082
|
+
const query3 = ctx.db.query("epistemicNodes").withIndex(
|
|
1083
|
+
"by_topic_type",
|
|
1084
|
+
(q) => q.eq("topicId", args.topicId).eq("nodeType", "belief")
|
|
1085
|
+
);
|
|
1086
|
+
const nodes = await query3.order("desc").take(scanLimit);
|
|
1087
|
+
let filtered = nodes.filter(
|
|
1088
|
+
(node) => canAudienceClassAccess(
|
|
1089
|
+
viewerClass,
|
|
1090
|
+
resolveAudienceClass(node.audienceLabel, "internal")
|
|
1091
|
+
)
|
|
1092
|
+
);
|
|
1093
|
+
if (args.status) {
|
|
1094
|
+
filtered = filtered.filter((n) => n.status === args.status);
|
|
1095
|
+
}
|
|
1096
|
+
filtered = filtered.slice(0, pageSize);
|
|
1097
|
+
return filtered.map(flattenBeliefNode);
|
|
1098
|
+
}
|
|
1099
|
+
});
|
|
1100
|
+
var internalGetActive = internalQuery({
|
|
1101
|
+
args: {
|
|
1102
|
+
...optionalBeliefScopeArgs,
|
|
1103
|
+
limit: v.optional(v.number())
|
|
1104
|
+
},
|
|
1105
|
+
returns: permissiveReturn,
|
|
1106
|
+
handler: async (ctx, args) => {
|
|
1107
|
+
const scope = await resolveBeliefScopeOrNull(ctx, args);
|
|
1108
|
+
if (!scope) {
|
|
1109
|
+
return [];
|
|
1110
|
+
}
|
|
1111
|
+
const nodes = await getBeliefNodesForScope(ctx, scope, {
|
|
1112
|
+
status: "active"
|
|
1113
|
+
});
|
|
1114
|
+
const limited = args.limit ? nodes.slice(0, args.limit) : nodes;
|
|
1115
|
+
return limited.map(flattenBeliefNode);
|
|
1116
|
+
}
|
|
1117
|
+
});
|
|
1118
|
+
var internalGetById = internalQuery({
|
|
1119
|
+
args: {
|
|
1120
|
+
beliefId: v.string()
|
|
1121
|
+
},
|
|
1122
|
+
returns: permissiveReturn,
|
|
1123
|
+
handler: async (ctx, args) => {
|
|
1124
|
+
const node = await ctx.db.get(args.beliefId);
|
|
1125
|
+
if (!node || node.nodeType !== "belief") {
|
|
1126
|
+
return null;
|
|
1127
|
+
}
|
|
1128
|
+
return flattenBeliefNode(node);
|
|
1129
|
+
}
|
|
1130
|
+
});
|
|
1131
|
+
var internalCreate = internalMutation({
|
|
1132
|
+
args: {
|
|
1133
|
+
...optionalBeliefScopeArgs,
|
|
1134
|
+
formulation: v.string(),
|
|
1135
|
+
baseRate: v.optional(v.number()),
|
|
1136
|
+
confidence: v.optional(
|
|
1137
|
+
v.union(v.literal("high"), v.literal("medium"), v.literal("low"))
|
|
1138
|
+
),
|
|
1139
|
+
userId: v.string(),
|
|
1140
|
+
rationale: v.optional(v.string()),
|
|
1141
|
+
topic: v.optional(v.string()),
|
|
1142
|
+
pillar: v.optional(v.string()),
|
|
1143
|
+
category: v.optional(v.string()),
|
|
1144
|
+
subcategory: v.optional(v.string()),
|
|
1145
|
+
categoryIcon: v.optional(v.string()),
|
|
1146
|
+
sprintId: v.optional(v.string()),
|
|
1147
|
+
sourceBeliefIds: v.optional(v.array(v.string())),
|
|
1148
|
+
criticality: v.optional(
|
|
1149
|
+
v.union(
|
|
1150
|
+
v.literal("blocking"),
|
|
1151
|
+
v.literal("critical"),
|
|
1152
|
+
v.literal("important"),
|
|
1153
|
+
v.literal("supporting"),
|
|
1154
|
+
v.literal("nice_to_have"),
|
|
1155
|
+
v.literal("unanalyzed")
|
|
1156
|
+
)
|
|
1157
|
+
),
|
|
1158
|
+
beliefType: v.optional(v.string()),
|
|
1159
|
+
/** Optional extra metadata fields merged into the node's metadata object.
|
|
1160
|
+
* Use for domain overlays like coding intelligence (codeAnchors, failedApproach, etc.) */
|
|
1161
|
+
extraMetadata: v.optional(v.any()),
|
|
1162
|
+
runtimeToolName: v.optional(v.string()),
|
|
1163
|
+
runtimePackKey: v.optional(v.string()),
|
|
1164
|
+
runtimePackInstallScope: v.optional(
|
|
1165
|
+
v.union(v.literal("tenant"), v.literal("workspace"))
|
|
1166
|
+
)
|
|
1167
|
+
},
|
|
1168
|
+
returns: permissiveReturn,
|
|
1169
|
+
handler: async (ctx, args) => {
|
|
1170
|
+
const now = Date.now();
|
|
1171
|
+
const baseRate = assertBaseRateInRange(args.baseRate ?? 0.5);
|
|
1172
|
+
const scope = await resolveTopicProjectScope(ctx, {
|
|
1173
|
+
topicId: args.topicId,
|
|
1174
|
+
projectId: args.projectId
|
|
1175
|
+
});
|
|
1176
|
+
assertWorkspaceScopedEpistemicNodeScope({
|
|
1177
|
+
scope,
|
|
1178
|
+
nodeType: "belief",
|
|
1179
|
+
mutationName: "epistemicBeliefs.internalCreate"
|
|
1180
|
+
});
|
|
1181
|
+
assertTenantPackWorkspaceMutationAllowed({
|
|
1182
|
+
runtime: resolveRuntimePackMutationContext(args),
|
|
1183
|
+
target: {
|
|
1184
|
+
tenantId: scope.tenantId,
|
|
1185
|
+
workspaceId: scope.workspaceId,
|
|
1186
|
+
nodeType: "belief",
|
|
1187
|
+
epistemicLayer: "L3"
|
|
1188
|
+
},
|
|
1189
|
+
mutationName: "epistemicBeliefs.internalCreate"
|
|
1190
|
+
});
|
|
1191
|
+
const topic = await ctx.db.get(scope.topicId);
|
|
1192
|
+
const normalizedBeliefType = await assertSchemaEnumValue(ctx, {
|
|
1193
|
+
category: "belief_type",
|
|
1194
|
+
value: args.beliefType,
|
|
1195
|
+
tenantId: topic?.tenantId,
|
|
1196
|
+
context: "epistemicBeliefs.internalCreate"
|
|
1197
|
+
});
|
|
1198
|
+
const globalId = generateGlobalId();
|
|
1199
|
+
const contentHash = generateContentHash(args.formulation);
|
|
1200
|
+
const requestedConfidence = args.confidence;
|
|
1201
|
+
const seedOpinion = {
|
|
1202
|
+
opinion_b: 0,
|
|
1203
|
+
opinion_d: 0,
|
|
1204
|
+
opinion_u: 1,
|
|
1205
|
+
opinion_a: baseRate
|
|
1206
|
+
};
|
|
1207
|
+
const nodeId = await ctx.db.insert("epistemicNodes", {
|
|
1208
|
+
globalId,
|
|
1209
|
+
topicId: scope.topicId,
|
|
1210
|
+
projectId: scope.projectId,
|
|
1211
|
+
tenantId: scope.tenantId,
|
|
1212
|
+
workspaceId: scope.workspaceId,
|
|
1213
|
+
nodeType: "belief",
|
|
1214
|
+
canonicalText: args.formulation,
|
|
1215
|
+
contentHash,
|
|
1216
|
+
status: "active",
|
|
1217
|
+
epistemicLayer: "L3",
|
|
1218
|
+
// L3: Traversal Anchors
|
|
1219
|
+
sourceType: args.userId.startsWith("agent:") ? "ai_generated" : "human",
|
|
1220
|
+
...normalizedBeliefType ? { beliefType: normalizedBeliefType } : {},
|
|
1221
|
+
createdAt: now,
|
|
1222
|
+
updatedAt: now,
|
|
1223
|
+
createdBy: args.userId,
|
|
1224
|
+
...seedOpinion,
|
|
1225
|
+
tupleContradicted: false,
|
|
1226
|
+
metadata: {
|
|
1227
|
+
...requestedConfidence ? { confidence: requestedConfidence } : {},
|
|
1228
|
+
tupleContradicted: false,
|
|
1229
|
+
rationale: args.rationale || "",
|
|
1230
|
+
topic: args.topic || args.pillar || "",
|
|
1231
|
+
pillar: args.pillar || args.topic || "",
|
|
1232
|
+
category: args.category || "",
|
|
1233
|
+
subcategory: args.subcategory || "",
|
|
1234
|
+
categoryIcon: args.categoryIcon || "",
|
|
1235
|
+
sprintId: args.sprintId,
|
|
1236
|
+
sourceBeliefIds: args.sourceBeliefIds || [],
|
|
1237
|
+
criticality: args.criticality || "unanalyzed",
|
|
1238
|
+
...normalizedBeliefType ? { beliefType: normalizedBeliefType } : {},
|
|
1239
|
+
supportingEvidenceIds: [],
|
|
1240
|
+
contradictingEvidenceIds: [],
|
|
1241
|
+
testingQuestionIds: [],
|
|
1242
|
+
linkedInsightIds: [],
|
|
1243
|
+
// Merge caller-provided metadata (e.g. codeAnchors for coding intelligence)
|
|
1244
|
+
...args.extraMetadata && typeof args.extraMetadata === "object" ? args.extraMetadata : {}
|
|
1245
|
+
}
|
|
1246
|
+
});
|
|
1247
|
+
await ctx.db.insert(
|
|
1248
|
+
"beliefConfidence",
|
|
1249
|
+
buildBeliefConfidenceRow({
|
|
1250
|
+
beliefId: nodeId,
|
|
1251
|
+
belief: seedOpinion.opinion_b,
|
|
1252
|
+
disbelief: seedOpinion.opinion_d,
|
|
1253
|
+
uncertainty: seedOpinion.opinion_u,
|
|
1254
|
+
baseRate,
|
|
1255
|
+
trigger: "initial",
|
|
1256
|
+
rationale: "LKC-2 mandatory prior: seeded vacuous opinion at belief creation.",
|
|
1257
|
+
assessedBy: args.userId,
|
|
1258
|
+
assessedAt: now,
|
|
1259
|
+
slOperator: "manual_assessment"
|
|
1260
|
+
})
|
|
1261
|
+
);
|
|
1262
|
+
await ctx.scheduler.runAfter(0, internal.neo4jSync.syncNodeToNeo4j, {
|
|
1263
|
+
nodeId,
|
|
1264
|
+
operation: "upsert"
|
|
1265
|
+
});
|
|
1266
|
+
await ctx.db.insert("epistemicAudit", {
|
|
1267
|
+
entityType: "belief",
|
|
1268
|
+
entityId: nodeId,
|
|
1269
|
+
changeType: "created",
|
|
1270
|
+
changedAt: now,
|
|
1271
|
+
changedBy: args.userId,
|
|
1272
|
+
isAgent: false,
|
|
1273
|
+
newState: {
|
|
1274
|
+
formulation: args.formulation,
|
|
1275
|
+
baseRate,
|
|
1276
|
+
confidence: requestedConfidence,
|
|
1277
|
+
opinion: {
|
|
1278
|
+
b: seedOpinion.opinion_b,
|
|
1279
|
+
d: seedOpinion.opinion_d,
|
|
1280
|
+
u: seedOpinion.opinion_u,
|
|
1281
|
+
a: seedOpinion.opinion_a
|
|
1282
|
+
},
|
|
1283
|
+
tupleContradicted: false,
|
|
1284
|
+
source: "internal"
|
|
1285
|
+
},
|
|
1286
|
+
projectId: scope.projectId,
|
|
1287
|
+
topicId: String(scope.topicId)
|
|
1288
|
+
});
|
|
1289
|
+
if (scope.projectId || scope.topicId) {
|
|
1290
|
+
await ctx.scheduler.runAfter(
|
|
1291
|
+
0,
|
|
1292
|
+
"embeddingActions:generateEpistemicNodeEmbedding",
|
|
1293
|
+
{
|
|
1294
|
+
nodeId,
|
|
1295
|
+
projectId: scope.projectId,
|
|
1296
|
+
topicId: scope.topicId ? String(scope.topicId) : void 0,
|
|
1297
|
+
createdBy: args.userId,
|
|
1298
|
+
nodeType: "belief",
|
|
1299
|
+
text: args.rationale ? `${args.formulation}
|
|
1300
|
+
|
|
1301
|
+
Rationale: ${args.rationale}` : args.formulation,
|
|
1302
|
+
...requestedConfidence ? {
|
|
1303
|
+
confidence: requestedConfidence === "high" ? 0.8 : requestedConfidence === "low" ? 0.3 : 0.5
|
|
1304
|
+
} : {}
|
|
1305
|
+
}
|
|
1306
|
+
);
|
|
1307
|
+
}
|
|
1308
|
+
if (normalizePillar(args.pillar || args.topic) === "other" && (scope.projectId || scope.topicId)) {
|
|
1309
|
+
await ctx.scheduler.runAfter(
|
|
1310
|
+
2500,
|
|
1311
|
+
"beliefCategorization:autoCategorizeBelief",
|
|
1312
|
+
{
|
|
1313
|
+
nodeId,
|
|
1314
|
+
projectId: scope.projectId,
|
|
1315
|
+
topicId: String(scope.topicId)
|
|
1316
|
+
}
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
await markBeliefGraphDirty(ctx, {
|
|
1320
|
+
projectId: scope.projectId,
|
|
1321
|
+
topicId: String(scope.topicId)
|
|
1322
|
+
});
|
|
1323
|
+
return { nodeId };
|
|
1324
|
+
}
|
|
1325
|
+
});
|
|
1326
|
+
|
|
1327
|
+
export { internalCreate, internalGetActive, internalGetById, internalGetByProject, internalGetByTopic };
|
|
1328
|
+
//# sourceMappingURL=epistemicBeliefs.internal.js.map
|
|
1329
|
+
//# sourceMappingURL=epistemicBeliefs.internal.js.map
|