@lucern/mcp 0.3.0-alpha.6 → 0.3.0-alpha.7
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/cli.js +877 -883
- package/dist/cli.js.map +1 -1
- package/dist/gateway.js +751 -317
- package/dist/gateway.js.map +1 -1
- package/dist/hosted-route.js +808 -890
- package/dist/hosted-route.js.map +1 -1
- package/dist/index.js +914 -888
- package/dist/index.js.map +1 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +505 -161
- package/dist/runtime.js.map +1 -1
- package/package.json +7 -7
package/dist/runtime.js
CHANGED
|
@@ -6,6 +6,50 @@ import { v } from 'convex/values';
|
|
|
6
6
|
import 'crypto';
|
|
7
7
|
import * as os from 'os';
|
|
8
8
|
|
|
9
|
+
// ../../apps/mcp-server/src/logging.ts
|
|
10
|
+
function isMcpDebugEnabled() {
|
|
11
|
+
const debug = process.env.LUCERN_MCP_DEBUG?.trim().toLowerCase();
|
|
12
|
+
return debug === "1" || debug === "true" || debug === "yes";
|
|
13
|
+
}
|
|
14
|
+
function formatUnknownError(error) {
|
|
15
|
+
if (error instanceof Error) {
|
|
16
|
+
const cause = error.cause;
|
|
17
|
+
const message = `${error.name}: ${error.message}`;
|
|
18
|
+
if (cause === void 0) {
|
|
19
|
+
return message;
|
|
20
|
+
}
|
|
21
|
+
return `${message}; cause=${formatUnknownError(cause)}`;
|
|
22
|
+
}
|
|
23
|
+
if (typeof error === "string") {
|
|
24
|
+
return error;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
return JSON.stringify(error);
|
|
28
|
+
} catch {
|
|
29
|
+
return Object.prototype.toString.call(error);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function stringifyForDebug(value) {
|
|
33
|
+
if (typeof value === "string") {
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
return JSON.stringify(value);
|
|
38
|
+
} catch {
|
|
39
|
+
return Object.prototype.toString.call(value);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function debugMcp(message, context) {
|
|
43
|
+
if (!isMcpDebugEnabled()) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const suffix = context ? ` ${stringifyForDebug(context)}` : "";
|
|
47
|
+
console.error(`[lucern-debug] ${message}${suffix}`);
|
|
48
|
+
}
|
|
49
|
+
function debugMcpError(message, error) {
|
|
50
|
+
debugMcp(message, { error: formatUnknownError(error) });
|
|
51
|
+
}
|
|
52
|
+
|
|
9
53
|
// ../../apps/mcp-server/src/handlers/sdk.ts
|
|
10
54
|
var handlerSdkClientCache = /* @__PURE__ */ new WeakMap();
|
|
11
55
|
async function resolveOntologyFromLookup(baseClient, input) {
|
|
@@ -129,7 +173,11 @@ function readStringArray(value) {
|
|
|
129
173
|
if (Array.isArray(parsed)) {
|
|
130
174
|
return readStringArray(parsed);
|
|
131
175
|
}
|
|
132
|
-
} catch {
|
|
176
|
+
} catch (error) {
|
|
177
|
+
debugMcpError("Failed to parse string array payload", {
|
|
178
|
+
value: normalized,
|
|
179
|
+
error
|
|
180
|
+
});
|
|
133
181
|
}
|
|
134
182
|
}
|
|
135
183
|
}
|
|
@@ -5025,6 +5073,11 @@ var TENANT_CLIENT_INSTALLABLE_PACKAGES = [
|
|
|
5025
5073
|
role: "sdk_dependency",
|
|
5026
5074
|
directTenantImport: false
|
|
5027
5075
|
},
|
|
5076
|
+
{
|
|
5077
|
+
packageName: "@lucern/graph-sync",
|
|
5078
|
+
role: "host_addon_runtime",
|
|
5079
|
+
directTenantImport: true
|
|
5080
|
+
},
|
|
5028
5081
|
{
|
|
5029
5082
|
packageName: "@lucern/identity",
|
|
5030
5083
|
role: "component_runtime",
|
|
@@ -5144,8 +5197,11 @@ function compactRecord(input) {
|
|
|
5144
5197
|
Object.entries(input).filter(([, value]) => value !== void 0)
|
|
5145
5198
|
);
|
|
5146
5199
|
}
|
|
5200
|
+
function isRecord(value) {
|
|
5201
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
5202
|
+
}
|
|
5147
5203
|
function recordValue(value) {
|
|
5148
|
-
return
|
|
5204
|
+
return isRecord(value) ? value : {};
|
|
5149
5205
|
}
|
|
5150
5206
|
var createEvidenceProjection = defineProjection({
|
|
5151
5207
|
contractName: "create_evidence",
|
|
@@ -5427,9 +5483,7 @@ function hasProvenance(input) {
|
|
|
5427
5483
|
|
|
5428
5484
|
// ../contracts/src/lens-filter.contract.ts
|
|
5429
5485
|
function isLensFilterCriteria(value) {
|
|
5430
|
-
|
|
5431
|
-
const obj = value;
|
|
5432
|
-
return typeof obj.version === "number" && typeof obj.kind === "string";
|
|
5486
|
+
return isRecord2(value) && typeof value.version === "number" && typeof value.kind === "string";
|
|
5433
5487
|
}
|
|
5434
5488
|
function isTaxonomyFilterCriteriaV1(value) {
|
|
5435
5489
|
if (!isLensFilterCriteria(value)) return false;
|
|
@@ -5458,6 +5512,9 @@ function validateFilterCriteria(value) {
|
|
|
5458
5512
|
]
|
|
5459
5513
|
};
|
|
5460
5514
|
}
|
|
5515
|
+
function isRecord2(value) {
|
|
5516
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
5517
|
+
}
|
|
5461
5518
|
function validateTaxonomyFilterV1(criteria) {
|
|
5462
5519
|
const errors = [];
|
|
5463
5520
|
if (!Array.isArray(criteria.entityTypeFilters)) {
|
|
@@ -5919,9 +5976,16 @@ var ADD_WORKTREE = {
|
|
|
5919
5976
|
},
|
|
5920
5977
|
projectId: {
|
|
5921
5978
|
type: "string",
|
|
5922
|
-
description: "Legacy topicId alias"
|
|
5979
|
+
description: "Legacy topicId alias or resolver hint"
|
|
5980
|
+
},
|
|
5981
|
+
topicId: {
|
|
5982
|
+
type: "string",
|
|
5983
|
+
description: "Optional topic scope hint for resolver validation"
|
|
5984
|
+
},
|
|
5985
|
+
topicHint: {
|
|
5986
|
+
type: "string",
|
|
5987
|
+
description: "Natural-language topic hint for automatic topic resolution"
|
|
5923
5988
|
},
|
|
5924
|
-
topicId: { type: "string", description: "Optional topic scope hint" },
|
|
5925
5989
|
branchId: {
|
|
5926
5990
|
type: "string",
|
|
5927
5991
|
description: "The branch this worktree investigates"
|
|
@@ -6019,6 +6083,22 @@ var ADD_WORKTREE = {
|
|
|
6019
6083
|
type: "string",
|
|
6020
6084
|
description: "Optional domain pack whose shaping hooks should influence generated questions and tasks"
|
|
6021
6085
|
},
|
|
6086
|
+
tags: {
|
|
6087
|
+
type: "array",
|
|
6088
|
+
description: "Additional topic-resolution tags for the worktree"
|
|
6089
|
+
},
|
|
6090
|
+
touchedPaths: {
|
|
6091
|
+
type: "array",
|
|
6092
|
+
description: "File paths used as topic-resolution signals"
|
|
6093
|
+
},
|
|
6094
|
+
sourceRef: {
|
|
6095
|
+
type: "string",
|
|
6096
|
+
description: "Source reference used as a topic-resolution signal"
|
|
6097
|
+
},
|
|
6098
|
+
sourceKind: {
|
|
6099
|
+
type: "string",
|
|
6100
|
+
description: "Source kind used as a topic-resolution signal"
|
|
6101
|
+
},
|
|
6022
6102
|
campaign: {
|
|
6023
6103
|
type: "number",
|
|
6024
6104
|
description: "Top-level pipeline campaign number. Campaigns define the outer execution slice."
|
|
@@ -6056,7 +6136,7 @@ var ADD_WORKTREE = {
|
|
|
6056
6136
|
description: "Timestamp when worktree metadata was last reconciled"
|
|
6057
6137
|
}
|
|
6058
6138
|
},
|
|
6059
|
-
required: ["title"
|
|
6139
|
+
required: ["title"],
|
|
6060
6140
|
response: {
|
|
6061
6141
|
description: "The created worktree",
|
|
6062
6142
|
fields: {
|
|
@@ -7759,18 +7839,60 @@ var CREATE_TASK = {
|
|
|
7759
7839
|
name: "create_task",
|
|
7760
7840
|
description: "Create an execution task tied to the reasoning state. Like `git task` \u2014 tracks concrete work items (calls to make, data to gather, analyses to run) linked to questions, beliefs, or worktrees.",
|
|
7761
7841
|
parameters: {
|
|
7762
|
-
title: { type: "string", description: "Task
|
|
7842
|
+
title: { type: "string", description: "Task title" },
|
|
7763
7843
|
topicId: { type: "string", description: "Topic scope" },
|
|
7844
|
+
description: {
|
|
7845
|
+
type: "string",
|
|
7846
|
+
description: "Long-form task description"
|
|
7847
|
+
},
|
|
7764
7848
|
taskType: {
|
|
7765
7849
|
type: "string",
|
|
7766
|
-
description: "
|
|
7767
|
-
enum: [
|
|
7850
|
+
description: "Task taxonomy",
|
|
7851
|
+
enum: [
|
|
7852
|
+
"general",
|
|
7853
|
+
"find_evidence",
|
|
7854
|
+
"verify_claim",
|
|
7855
|
+
"research",
|
|
7856
|
+
"review",
|
|
7857
|
+
"interview",
|
|
7858
|
+
"analysis",
|
|
7859
|
+
"track_metrics"
|
|
7860
|
+
]
|
|
7861
|
+
},
|
|
7862
|
+
priority: {
|
|
7863
|
+
type: "string",
|
|
7864
|
+
description: "Priority",
|
|
7865
|
+
enum: ["urgent", "high", "medium", "low"]
|
|
7866
|
+
},
|
|
7867
|
+
status: {
|
|
7868
|
+
type: "string",
|
|
7869
|
+
description: "Initial status (defaults to todo)",
|
|
7870
|
+
enum: ["todo", "in_progress", "blocked", "done"]
|
|
7871
|
+
},
|
|
7872
|
+
linkedWorktreeId: {
|
|
7873
|
+
type: "string",
|
|
7874
|
+
description: "Worktree this task belongs to"
|
|
7875
|
+
},
|
|
7876
|
+
linkedBeliefId: {
|
|
7877
|
+
type: "string",
|
|
7878
|
+
description: "Belief this task supports"
|
|
7768
7879
|
},
|
|
7769
7880
|
linkedQuestionId: {
|
|
7770
7881
|
type: "string",
|
|
7771
7882
|
description: "Question this task addresses"
|
|
7772
7883
|
},
|
|
7773
|
-
|
|
7884
|
+
assigneeId: {
|
|
7885
|
+
type: "string",
|
|
7886
|
+
description: "Principal assigned to the task"
|
|
7887
|
+
},
|
|
7888
|
+
dueDate: {
|
|
7889
|
+
type: "number",
|
|
7890
|
+
description: "Due date as epoch milliseconds"
|
|
7891
|
+
},
|
|
7892
|
+
tags: {
|
|
7893
|
+
type: "array",
|
|
7894
|
+
description: "Free-form string tags"
|
|
7895
|
+
}
|
|
7774
7896
|
},
|
|
7775
7897
|
required: ["title"],
|
|
7776
7898
|
response: {
|
|
@@ -9853,9 +9975,7 @@ function mcpContractFromArgsSchema(base, args, contractName) {
|
|
|
9853
9975
|
required: converted.filter(([, field]) => field.required).map(([fieldName]) => fieldName)
|
|
9854
9976
|
};
|
|
9855
9977
|
}
|
|
9856
|
-
|
|
9857
|
-
return contract;
|
|
9858
|
-
}
|
|
9978
|
+
var defineFunctionContract = (contract) => contract;
|
|
9859
9979
|
function authUserId(context) {
|
|
9860
9980
|
return context.userId ?? context.principalId ?? "lucern-agent";
|
|
9861
9981
|
}
|
|
@@ -10009,6 +10129,9 @@ var observationContextArgs = z.object({
|
|
|
10009
10129
|
limit: z.number().optional().describe("Maximum observations to return."),
|
|
10010
10130
|
status: z.string().optional().describe("Observation status filter.")
|
|
10011
10131
|
});
|
|
10132
|
+
function isRecord3(value) {
|
|
10133
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
10134
|
+
}
|
|
10012
10135
|
var observationInput = (input, context) => withUserId(
|
|
10013
10136
|
compactRecord4({
|
|
10014
10137
|
projectId: input.projectId,
|
|
@@ -10063,8 +10186,8 @@ var contextContracts = [
|
|
|
10063
10186
|
kind: "mutation",
|
|
10064
10187
|
inputProjection: observationInput,
|
|
10065
10188
|
outputProjection: (output, input) => ({
|
|
10066
|
-
...output
|
|
10067
|
-
observationId: output
|
|
10189
|
+
...isRecord3(output) ? output : {},
|
|
10190
|
+
observationId: isRecord3(output) ? output.nodeId : void 0,
|
|
10068
10191
|
observationType: input.observationType
|
|
10069
10192
|
})
|
|
10070
10193
|
},
|
|
@@ -11545,10 +11668,11 @@ var worktreeDecisionGateInputSchema = z.object({
|
|
|
11545
11668
|
decidedBy: z.string().optional().describe("Actor that decided the gate verdict.")
|
|
11546
11669
|
}).passthrough().describe("Decision gate contract for worktree activation or exit.");
|
|
11547
11670
|
var addWorktreeArgs = z.object({
|
|
11548
|
-
title: z.string().
|
|
11671
|
+
title: z.string().describe("Human-readable worktree name or objective."),
|
|
11549
11672
|
name: z.string().optional().describe("Storage-name alias for callers that already use backend naming."),
|
|
11550
|
-
topicId: z.string().describe("
|
|
11551
|
-
projectId: z.string().optional().describe("Legacy topicId alias."),
|
|
11673
|
+
topicId: z.string().optional().describe("Optional primary topic scope hint for resolver validation."),
|
|
11674
|
+
projectId: z.string().optional().describe("Legacy topicId alias/hint."),
|
|
11675
|
+
topicHint: z.string().optional().describe("Natural-language topic hint for automatic topic resolution."),
|
|
11552
11676
|
branchId: z.string().optional().describe("Legacy branch identifier for compatibility with workflow callers."),
|
|
11553
11677
|
objective: z.string().optional().describe("Reasoning objective this worktree is intended to resolve."),
|
|
11554
11678
|
hypothesis: z.string().optional().describe("Testable claim this worktree investigates."),
|
|
@@ -11573,6 +11697,10 @@ var addWorktreeArgs = z.object({
|
|
|
11573
11697
|
autoShape: z.boolean().optional().describe("Whether to invoke inquiry auto-shaping during creation."),
|
|
11574
11698
|
autoFixPolicy: autoFixPolicyInputSchema.optional(),
|
|
11575
11699
|
domainPackId: z.string().optional().describe("Domain pack whose shaping hooks should influence the worktree."),
|
|
11700
|
+
tags: z.array(z.string()).optional().describe("Additional topic-resolution tags for the worktree."),
|
|
11701
|
+
touchedPaths: z.array(z.string()).optional().describe("File paths used as topic-resolution signals."),
|
|
11702
|
+
sourceRef: z.string().optional().describe("Source reference used as a topic-resolution signal."),
|
|
11703
|
+
sourceKind: z.string().optional().describe("Source kind used as a topic-resolution signal."),
|
|
11576
11704
|
campaign: z.number().optional().describe("Top-level pipeline campaign number."),
|
|
11577
11705
|
lane: z.string().optional().describe("Campaign lane for the worktree."),
|
|
11578
11706
|
laneOrderInCampaign: z.number().optional().describe("Ordering for this lane within its campaign."),
|
|
@@ -11902,8 +12030,46 @@ var worktreesContracts = [
|
|
|
11902
12030
|
args: openPullRequestArgs
|
|
11903
12031
|
})
|
|
11904
12032
|
];
|
|
11905
|
-
|
|
11906
|
-
|
|
12033
|
+
var taskPrioritySchema = z.enum(["urgent", "high", "medium", "low"]);
|
|
12034
|
+
var taskStatusSchema2 = z.enum(["todo", "in_progress", "blocked", "done"]);
|
|
12035
|
+
var taskTypeSchema = z.enum([
|
|
12036
|
+
"general",
|
|
12037
|
+
"find_evidence",
|
|
12038
|
+
"verify_claim",
|
|
12039
|
+
"research",
|
|
12040
|
+
"review",
|
|
12041
|
+
"interview",
|
|
12042
|
+
"analysis",
|
|
12043
|
+
"track_metrics"
|
|
12044
|
+
]);
|
|
12045
|
+
var createTaskArgs = z.object({
|
|
12046
|
+
title: z.string().describe("Task title."),
|
|
12047
|
+
topicId: z.string().optional().describe("Topic scope."),
|
|
12048
|
+
description: z.string().optional().describe("Long-form task description."),
|
|
12049
|
+
taskType: taskTypeSchema.optional().describe("Task taxonomy."),
|
|
12050
|
+
priority: taskPrioritySchema.optional().describe("Priority. Defaults to medium when omitted by the server."),
|
|
12051
|
+
status: taskStatusSchema2.optional().describe("Initial status. Defaults to todo."),
|
|
12052
|
+
linkedWorktreeId: z.string().optional().describe("Worktree this task belongs to."),
|
|
12053
|
+
linkedBeliefId: z.string().optional().describe("Belief this task supports."),
|
|
12054
|
+
linkedQuestionId: z.string().optional().describe("Question this task addresses."),
|
|
12055
|
+
assigneeId: z.string().optional().describe("Principal assigned to the task."),
|
|
12056
|
+
dueDate: z.number().optional().describe("Due date as epoch milliseconds."),
|
|
12057
|
+
tags: z.array(z.string()).optional().describe("Free-form tags.")
|
|
12058
|
+
});
|
|
12059
|
+
var createTaskInput = (input) => compactRecord4({
|
|
12060
|
+
title: input.title,
|
|
12061
|
+
topicId: input.topicId,
|
|
12062
|
+
description: input.description,
|
|
12063
|
+
taskType: input.taskType,
|
|
12064
|
+
priority: input.priority ?? "medium",
|
|
12065
|
+
status: input.status ?? "todo",
|
|
12066
|
+
linkedWorktreeId: input.linkedWorktreeId,
|
|
12067
|
+
linkedBeliefId: input.linkedBeliefId,
|
|
12068
|
+
linkedQuestionId: input.linkedQuestionId,
|
|
12069
|
+
assigneeId: input.assigneeId,
|
|
12070
|
+
dueDate: input.dueDate,
|
|
12071
|
+
tags: input.tags
|
|
12072
|
+
});
|
|
11907
12073
|
var taskInput = (input) => compactRecord4({
|
|
11908
12074
|
...input,
|
|
11909
12075
|
taskId: input.taskId ?? input.id
|
|
@@ -11935,8 +12101,10 @@ var tasksContracts = [
|
|
|
11935
12101
|
convex: {
|
|
11936
12102
|
module: "tasks",
|
|
11937
12103
|
functionName: "create",
|
|
11938
|
-
kind: "mutation"
|
|
11939
|
-
|
|
12104
|
+
kind: "mutation",
|
|
12105
|
+
inputProjection: createTaskInput
|
|
12106
|
+
},
|
|
12107
|
+
args: createTaskArgs
|
|
11940
12108
|
}),
|
|
11941
12109
|
surfaceContract({
|
|
11942
12110
|
name: "list_tasks",
|
|
@@ -13054,9 +13222,12 @@ var ALL_FUNCTION_CONTRACTS = [
|
|
|
13054
13222
|
...legacyContracts
|
|
13055
13223
|
];
|
|
13056
13224
|
assertSurfaceCoverage(ALL_FUNCTION_CONTRACTS);
|
|
13057
|
-
new Map(
|
|
13225
|
+
var FUNCTION_CONTRACTS_BY_NAME = new Map(
|
|
13058
13226
|
ALL_FUNCTION_CONTRACTS.map((contract) => [contract.name, contract])
|
|
13059
13227
|
);
|
|
13228
|
+
FUNCTION_CONTRACTS_BY_NAME.get.bind(
|
|
13229
|
+
FUNCTION_CONTRACTS_BY_NAME
|
|
13230
|
+
);
|
|
13060
13231
|
|
|
13061
13232
|
// ../contracts/src/tenant-bootstrap-seed.contract.ts
|
|
13062
13233
|
function isCopyableSeedRequirement(entry) {
|
|
@@ -13697,7 +13868,8 @@ function readLucernJson() {
|
|
|
13697
13868
|
topicId: parsed.topicId,
|
|
13698
13869
|
topicName: parsed.topicName
|
|
13699
13870
|
};
|
|
13700
|
-
} catch {
|
|
13871
|
+
} catch (error) {
|
|
13872
|
+
debugMcpError("Failed to read .lucern.json ambient context", error);
|
|
13701
13873
|
return null;
|
|
13702
13874
|
}
|
|
13703
13875
|
}
|
|
@@ -13719,7 +13891,8 @@ function writeLucernJson(context) {
|
|
|
13719
13891
|
`,
|
|
13720
13892
|
"utf-8"
|
|
13721
13893
|
);
|
|
13722
|
-
} catch {
|
|
13894
|
+
} catch (error) {
|
|
13895
|
+
debugMcpError("Failed to write .lucern.json ambient context", error);
|
|
13723
13896
|
}
|
|
13724
13897
|
}
|
|
13725
13898
|
function isNonEmptyString(value) {
|
|
@@ -13761,15 +13934,25 @@ async function tryGetTopicById(topicId) {
|
|
|
13761
13934
|
return null;
|
|
13762
13935
|
}
|
|
13763
13936
|
try {
|
|
13764
|
-
return unwrapGatewayData(
|
|
13765
|
-
|
|
13937
|
+
return unwrapGatewayData(
|
|
13938
|
+
await getLucernClient().topics.get(normalizedTopicId)
|
|
13939
|
+
);
|
|
13940
|
+
} catch (error) {
|
|
13941
|
+
debugMcp("Failed to load topic by id", {
|
|
13942
|
+
topicId,
|
|
13943
|
+
error: formatUnknownError(error)
|
|
13944
|
+
});
|
|
13766
13945
|
return null;
|
|
13767
13946
|
}
|
|
13768
13947
|
}
|
|
13769
13948
|
async function tryGetNodeById(nodeId) {
|
|
13770
13949
|
try {
|
|
13771
13950
|
return unwrapGatewayData(await getLucernClient().nodes.get({ nodeId }));
|
|
13772
|
-
} catch {
|
|
13951
|
+
} catch (error) {
|
|
13952
|
+
debugMcp("Failed to load node by id", {
|
|
13953
|
+
nodeId,
|
|
13954
|
+
error: formatUnknownError(error)
|
|
13955
|
+
});
|
|
13773
13956
|
return null;
|
|
13774
13957
|
}
|
|
13775
13958
|
}
|
|
@@ -13778,8 +13961,14 @@ async function findTopicByMappedProjectId(legacyScopeId) {
|
|
|
13778
13961
|
const response = await getLucernClient().topics.list({});
|
|
13779
13962
|
const data = unwrapGatewayData(response);
|
|
13780
13963
|
const topics2 = Array.isArray(data?.topics) ? data.topics : Array.isArray(data?.items) ? data.items : [];
|
|
13781
|
-
return topics2.find(
|
|
13782
|
-
|
|
13964
|
+
return topics2.find(
|
|
13965
|
+
(topic) => readTopicMappedProjectId(topic) === legacyScopeId
|
|
13966
|
+
) || null;
|
|
13967
|
+
} catch (error) {
|
|
13968
|
+
debugMcp("Failed to resolve topic by mapped project id", {
|
|
13969
|
+
legacyScopeId,
|
|
13970
|
+
error: formatUnknownError(error)
|
|
13971
|
+
});
|
|
13783
13972
|
return null;
|
|
13784
13973
|
}
|
|
13785
13974
|
}
|
|
@@ -13789,17 +13978,9 @@ async function resolveTopicScopeId(scopeId, toolName) {
|
|
|
13789
13978
|
if (!isNonEmptyString(scopeId)) {
|
|
13790
13979
|
if (isNonEmptyString(defaultTopicId)) {
|
|
13791
13980
|
resolved = defaultTopicId;
|
|
13792
|
-
} else {
|
|
13793
|
-
const lucernContext = readLucernJson();
|
|
13794
|
-
const ambientTopicId = decodePublicTopicId(lucernContext?.topicId);
|
|
13795
|
-
if (isNonEmptyString(ambientTopicId)) {
|
|
13796
|
-
resolved = ambientTopicId;
|
|
13797
|
-
}
|
|
13798
13981
|
}
|
|
13799
13982
|
if (!resolved) {
|
|
13800
|
-
throw new Error(
|
|
13801
|
-
`[${toolName}] Missing topic scope. Provide topicId.`
|
|
13802
|
-
);
|
|
13983
|
+
throw new Error(`[${toolName}] Missing topic scope. Provide topicId.`);
|
|
13803
13984
|
}
|
|
13804
13985
|
}
|
|
13805
13986
|
if (!resolved) {
|
|
@@ -14017,28 +14198,30 @@ function readString2(value) {
|
|
|
14017
14198
|
function readNullableNumber(value) {
|
|
14018
14199
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
14019
14200
|
}
|
|
14201
|
+
function isRecord4(value) {
|
|
14202
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
14203
|
+
}
|
|
14020
14204
|
function refreshLucernContextFromBuildSession(payload, args) {
|
|
14021
|
-
if (!
|
|
14205
|
+
if (!isRecord4(payload)) {
|
|
14022
14206
|
return;
|
|
14023
14207
|
}
|
|
14024
|
-
const
|
|
14025
|
-
const
|
|
14026
|
-
const worktreeId = readString2(record.worktreeId) ?? readString2(args.worktreeId);
|
|
14208
|
+
const topicId = readString2(payload.topicId);
|
|
14209
|
+
const worktreeId = readString2(payload.worktreeId) ?? readString2(args.worktreeId);
|
|
14027
14210
|
if (!topicId || !worktreeId) {
|
|
14028
14211
|
return;
|
|
14029
14212
|
}
|
|
14030
14213
|
writeLucernJson({
|
|
14031
14214
|
topicId,
|
|
14032
|
-
topicName: readString2(
|
|
14215
|
+
topicName: readString2(payload.topicName),
|
|
14033
14216
|
activeWorktree: {
|
|
14034
14217
|
worktreeId,
|
|
14035
|
-
title: readString2(
|
|
14036
|
-
status: readString2(
|
|
14037
|
-
campaign: readNullableNumber(
|
|
14038
|
-
lane: readString2(
|
|
14039
|
-
orderInLane: readNullableNumber(
|
|
14040
|
-
gate: readString2(
|
|
14041
|
-
branch: readString2(
|
|
14218
|
+
title: readString2(payload.worktreeName) ?? readString2(payload.title) ?? readString2(payload.name) ?? worktreeId,
|
|
14219
|
+
status: readString2(payload.status) ?? "unknown",
|
|
14220
|
+
campaign: readNullableNumber(payload.campaign),
|
|
14221
|
+
lane: readString2(payload.lane) ?? null,
|
|
14222
|
+
orderInLane: readNullableNumber(payload.orderInLane),
|
|
14223
|
+
gate: readString2(payload.gate) ?? null,
|
|
14224
|
+
branch: readString2(payload.branch) ?? readString2(args.branch) ?? null
|
|
14042
14225
|
}
|
|
14043
14226
|
});
|
|
14044
14227
|
}
|
|
@@ -14252,9 +14435,8 @@ var edgeHandlers = {
|
|
|
14252
14435
|
|
|
14253
14436
|
// ../../apps/mcp-server/src/handlers/graph.ts
|
|
14254
14437
|
var graphHandlers = {
|
|
14255
|
-
|
|
14256
|
-
|
|
14257
|
-
},
|
|
14438
|
+
// Backward-compatible alias to keep the graph transport API stable.
|
|
14439
|
+
traverse_graph: edgeHandlers.traverse_graph,
|
|
14258
14440
|
async search_beliefs(args, ctx) {
|
|
14259
14441
|
const topicId = await resolveTopicScopeId(
|
|
14260
14442
|
readTopicIdArg(args),
|
|
@@ -14270,17 +14452,19 @@ var graphHandlers = {
|
|
|
14270
14452
|
})
|
|
14271
14453
|
);
|
|
14272
14454
|
},
|
|
14273
|
-
|
|
14274
|
-
|
|
14275
|
-
|
|
14276
|
-
|
|
14277
|
-
return beliefHandlers.bisect_confidence(args, ctx);
|
|
14278
|
-
},
|
|
14455
|
+
// Backward-compatible alias to keep the graph transport API stable.
|
|
14456
|
+
find_contradictions: contradictionHandlers.find_contradictions,
|
|
14457
|
+
// Backward-compatible alias to keep the graph transport API stable.
|
|
14458
|
+
bisect_confidence: beliefHandlers.bisect_confidence,
|
|
14279
14459
|
async trace_entity_impact(args, ctx) {
|
|
14280
|
-
|
|
14281
|
-
|
|
14282
|
-
|
|
14283
|
-
|
|
14460
|
+
let topicId;
|
|
14461
|
+
try {
|
|
14462
|
+
topicId = await resolveTopicScopeId(
|
|
14463
|
+
readTopicIdArg(args),
|
|
14464
|
+
"trace_entity_impact"
|
|
14465
|
+
);
|
|
14466
|
+
} catch (error) {
|
|
14467
|
+
}
|
|
14284
14468
|
return formatSdkResult(
|
|
14285
14469
|
await getSdkClient(ctx).graph.traceEntityImpact({
|
|
14286
14470
|
nodeId: typeof args.nodeId === "string" ? args.nodeId : "",
|
|
@@ -15437,6 +15621,42 @@ var api = makeProxy("api");
|
|
|
15437
15621
|
makeProxy("components");
|
|
15438
15622
|
makeProxy("internal");
|
|
15439
15623
|
|
|
15624
|
+
// ../server-core/src/debug.ts
|
|
15625
|
+
function formatUnknownError2(error) {
|
|
15626
|
+
if (error instanceof Error) {
|
|
15627
|
+
const cause = error.cause;
|
|
15628
|
+
const message = `${error.name}: ${error.message}`;
|
|
15629
|
+
if (cause === void 0) {
|
|
15630
|
+
return message;
|
|
15631
|
+
}
|
|
15632
|
+
return `${message}; cause=${formatUnknownError2(cause)}`;
|
|
15633
|
+
}
|
|
15634
|
+
if (typeof error === "string") {
|
|
15635
|
+
return error;
|
|
15636
|
+
}
|
|
15637
|
+
if (error === null) {
|
|
15638
|
+
return "null";
|
|
15639
|
+
}
|
|
15640
|
+
if (error === void 0) {
|
|
15641
|
+
return "undefined";
|
|
15642
|
+
}
|
|
15643
|
+
try {
|
|
15644
|
+
return JSON.stringify(error);
|
|
15645
|
+
} catch {
|
|
15646
|
+
return Object.prototype.toString.call(error);
|
|
15647
|
+
}
|
|
15648
|
+
}
|
|
15649
|
+
function isServerCoreFallbackDebugEnabled() {
|
|
15650
|
+
const env = globalThis.process?.env;
|
|
15651
|
+
return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_SERVER_CORE_DEBUG === "1";
|
|
15652
|
+
}
|
|
15653
|
+
function debugServerCoreFallback(scope, message, context) {
|
|
15654
|
+
if (!isServerCoreFallbackDebugEnabled()) {
|
|
15655
|
+
return;
|
|
15656
|
+
}
|
|
15657
|
+
console.debug(`[${scope}] ${message}`, context ?? {});
|
|
15658
|
+
}
|
|
15659
|
+
|
|
15440
15660
|
// ../server-core/src/mcp-context-tools.ts
|
|
15441
15661
|
function requireMcpConvex(authContext, label) {
|
|
15442
15662
|
const convex = authContext.convex;
|
|
@@ -15449,6 +15669,9 @@ function requireMcpConvex(authContext, label) {
|
|
|
15449
15669
|
// ../server-core/src/topic-resolver.ts
|
|
15450
15670
|
var DEFAULT_THRESHOLD = 0.35;
|
|
15451
15671
|
var TREE_MAX_DEPTH = 20;
|
|
15672
|
+
var BM25_K1 = 1.2;
|
|
15673
|
+
var BM25_B = 0.75;
|
|
15674
|
+
var BM25_NORMALIZATION = 8;
|
|
15452
15675
|
var METADATA_TOKEN_KEYS = [
|
|
15453
15676
|
"aliases",
|
|
15454
15677
|
"alias",
|
|
@@ -15501,35 +15724,44 @@ function readStringArray2(value) {
|
|
|
15501
15724
|
}
|
|
15502
15725
|
return value.map((entry) => readString3(entry)).filter((entry) => Boolean(entry));
|
|
15503
15726
|
}
|
|
15727
|
+
function isRecord5(value) {
|
|
15728
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
15729
|
+
}
|
|
15730
|
+
function decodePrefixedIdOrNull(value) {
|
|
15731
|
+
const normalized = value.trim();
|
|
15732
|
+
const match = /^([a-z][a-z0-9]*)_(.+)$/.exec(normalized);
|
|
15733
|
+
if (!match) {
|
|
15734
|
+
return null;
|
|
15735
|
+
}
|
|
15736
|
+
return {
|
|
15737
|
+
prefix: match[1],
|
|
15738
|
+
value: match[2]
|
|
15739
|
+
};
|
|
15740
|
+
}
|
|
15504
15741
|
function asRecord2(value) {
|
|
15505
|
-
return
|
|
15742
|
+
return isRecord5(value) ? value : {};
|
|
15506
15743
|
}
|
|
15507
15744
|
function normalizeTopicId(value) {
|
|
15508
15745
|
const normalized = readString3(value);
|
|
15509
15746
|
if (!normalized) {
|
|
15510
15747
|
return void 0;
|
|
15511
15748
|
}
|
|
15512
|
-
|
|
15513
|
-
|
|
15514
|
-
return decoded.prefix === "top" ? decoded.value : normalized;
|
|
15515
|
-
} catch {
|
|
15516
|
-
return normalized;
|
|
15517
|
-
}
|
|
15749
|
+
const decoded = decodePrefixedIdOrNull(normalized);
|
|
15750
|
+
return decoded?.prefix === "top" ? decoded.value : normalized;
|
|
15518
15751
|
}
|
|
15519
15752
|
function normalizeExternalId(value, prefix) {
|
|
15520
15753
|
const normalized = readString3(value);
|
|
15521
15754
|
if (!normalized) {
|
|
15522
15755
|
return void 0;
|
|
15523
15756
|
}
|
|
15524
|
-
|
|
15525
|
-
|
|
15526
|
-
|
|
15527
|
-
|
|
15528
|
-
|
|
15529
|
-
return decoded.value;
|
|
15530
|
-
} catch {
|
|
15757
|
+
const decoded = decodePrefixedIdOrNull(normalized);
|
|
15758
|
+
if (!decoded) {
|
|
15759
|
+
return normalized;
|
|
15760
|
+
}
|
|
15761
|
+
if (decoded.prefix !== prefix) {
|
|
15531
15762
|
return normalized;
|
|
15532
15763
|
}
|
|
15764
|
+
return decoded.value;
|
|
15533
15765
|
}
|
|
15534
15766
|
function normalizeToken(value) {
|
|
15535
15767
|
const normalized = value.toLowerCase().trim();
|
|
@@ -15608,15 +15840,83 @@ function collectTopicMetadataTokens(metadata) {
|
|
|
15608
15840
|
function topicNameSlug(name) {
|
|
15609
15841
|
return tokenize(name).join(" ");
|
|
15610
15842
|
}
|
|
15843
|
+
function topicTokenList(topic) {
|
|
15844
|
+
return [
|
|
15845
|
+
...tokenize(topic.name),
|
|
15846
|
+
...tokenize(topic.description),
|
|
15847
|
+
...tokenize(topic.type),
|
|
15848
|
+
...collectTopicMetadataTokens(topic.metadata ?? {}).flatMap(
|
|
15849
|
+
(value) => tokenize(value)
|
|
15850
|
+
)
|
|
15851
|
+
];
|
|
15852
|
+
}
|
|
15611
15853
|
function topicTokenSet(topic) {
|
|
15612
15854
|
const tokens = /* @__PURE__ */ new Set();
|
|
15613
|
-
addTokens(tokens,
|
|
15614
|
-
addTokens(tokens, tokenize(topic.description));
|
|
15615
|
-
addTokens(tokens, tokenize(topic.type));
|
|
15616
|
-
addTokens(tokens, collectTopicMetadataTokens(topic.metadata ?? {}));
|
|
15855
|
+
addTokens(tokens, topicTokenList(topic));
|
|
15617
15856
|
return tokens;
|
|
15618
15857
|
}
|
|
15858
|
+
function buildTopicScoreCorpus(topics2) {
|
|
15859
|
+
const documentFrequencyByToken = /* @__PURE__ */ new Map();
|
|
15860
|
+
let documentCount = 0;
|
|
15861
|
+
let totalLength = 0;
|
|
15862
|
+
for (const topic of topics2) {
|
|
15863
|
+
const tokens = topicTokenList(topic);
|
|
15864
|
+
const uniqueTokens = new Set(tokens);
|
|
15865
|
+
documentCount += 1;
|
|
15866
|
+
totalLength += tokens.length;
|
|
15867
|
+
for (const token of uniqueTokens) {
|
|
15868
|
+
documentFrequencyByToken.set(
|
|
15869
|
+
token,
|
|
15870
|
+
(documentFrequencyByToken.get(token) ?? 0) + 1
|
|
15871
|
+
);
|
|
15872
|
+
}
|
|
15873
|
+
}
|
|
15874
|
+
return {
|
|
15875
|
+
documentCount,
|
|
15876
|
+
averageDocumentLength: documentCount > 0 ? Math.max(1, totalLength / documentCount) : 1,
|
|
15877
|
+
documentFrequencyByToken
|
|
15878
|
+
};
|
|
15879
|
+
}
|
|
15880
|
+
function countTokens(tokens) {
|
|
15881
|
+
const counts = /* @__PURE__ */ new Map();
|
|
15882
|
+
for (const token of tokens) {
|
|
15883
|
+
counts.set(token, (counts.get(token) ?? 0) + 1);
|
|
15884
|
+
}
|
|
15885
|
+
return counts;
|
|
15886
|
+
}
|
|
15887
|
+
function bm25Score(topic, signals, corpus) {
|
|
15888
|
+
if (signals.allTokens.size === 0 || corpus.documentCount === 0) {
|
|
15889
|
+
return 0;
|
|
15890
|
+
}
|
|
15891
|
+
const documentTokens = topicTokenList(topic);
|
|
15892
|
+
if (documentTokens.length === 0) {
|
|
15893
|
+
return 0;
|
|
15894
|
+
}
|
|
15895
|
+
const counts = countTokens(documentTokens);
|
|
15896
|
+
const docLength = documentTokens.length;
|
|
15897
|
+
let score = 0;
|
|
15898
|
+
for (const token of signals.allTokens) {
|
|
15899
|
+
const termFrequency = counts.get(token) ?? 0;
|
|
15900
|
+
if (termFrequency === 0) {
|
|
15901
|
+
continue;
|
|
15902
|
+
}
|
|
15903
|
+
const documentFrequency = corpus.documentFrequencyByToken.get(token) ?? 0;
|
|
15904
|
+
const idf = Math.log(
|
|
15905
|
+
1 + (corpus.documentCount - documentFrequency + 0.5) / (documentFrequency + 0.5)
|
|
15906
|
+
);
|
|
15907
|
+
const denominator = termFrequency + BM25_K1 * (1 - BM25_B + BM25_B * (docLength / corpus.averageDocumentLength));
|
|
15908
|
+
score += idf * (termFrequency * (BM25_K1 + 1) / denominator);
|
|
15909
|
+
}
|
|
15910
|
+
return score / (score + BM25_NORMALIZATION);
|
|
15911
|
+
}
|
|
15619
15912
|
function scoreTopicMatch(topic, input) {
|
|
15913
|
+
return scoreTopicMatchWithCorpus(
|
|
15914
|
+
topic,
|
|
15915
|
+
input,
|
|
15916
|
+
buildTopicScoreCorpus([topic])
|
|
15917
|
+
);
|
|
15918
|
+
}
|
|
15919
|
+
function scoreTopicMatchWithCorpus(topic, input, corpus) {
|
|
15620
15920
|
const signals = buildSignalBag(input);
|
|
15621
15921
|
const topicTokens = topicTokenSet(topic);
|
|
15622
15922
|
if (signals.allTokens.size === 0 || topicTokens.size === 0) {
|
|
@@ -15630,6 +15930,7 @@ function scoreTopicMatch(topic, input) {
|
|
|
15630
15930
|
}
|
|
15631
15931
|
const union = (/* @__PURE__ */ new Set([...signals.allTokens, ...topicTokens])).size;
|
|
15632
15932
|
const jaccard = union > 0 ? intersection / union : 0;
|
|
15933
|
+
const bm25 = bm25Score(topic, signals, corpus);
|
|
15633
15934
|
const topicSlug = topicNameSlug(topic.name);
|
|
15634
15935
|
let boost = 0;
|
|
15635
15936
|
for (const token of signals.tagTokens) {
|
|
@@ -15650,7 +15951,7 @@ function scoreTopicMatch(topic, input) {
|
|
|
15650
15951
|
break;
|
|
15651
15952
|
}
|
|
15652
15953
|
}
|
|
15653
|
-
return Math.max(0, Math.min(1, jaccard + boost));
|
|
15954
|
+
return Math.max(0, Math.min(1, bm25 * 0.55 + jaccard * 0.45 + boost));
|
|
15654
15955
|
}
|
|
15655
15956
|
function normalizeTopicCandidate(value) {
|
|
15656
15957
|
const record = asRecord2(value);
|
|
@@ -15672,26 +15973,21 @@ function normalizeTopicCandidate(value) {
|
|
|
15672
15973
|
globalId: readString3(record.globalId)
|
|
15673
15974
|
};
|
|
15674
15975
|
}
|
|
15675
|
-
function topicMatchesScope(topic, tenantId, workspaceId) {
|
|
15676
|
-
if (topic.status === "archived") {
|
|
15677
|
-
return false;
|
|
15678
|
-
}
|
|
15679
|
-
if (tenantId && topic.tenantId && topic.tenantId !== tenantId) {
|
|
15680
|
-
return false;
|
|
15681
|
-
}
|
|
15682
|
-
if (workspaceId && topic.workspaceId && topic.workspaceId !== workspaceId) {
|
|
15683
|
-
return false;
|
|
15684
|
-
}
|
|
15685
|
-
return true;
|
|
15686
|
-
}
|
|
15687
15976
|
async function fetchTopicById(ctx, topicId) {
|
|
15688
15977
|
const normalizedTopicId = normalizeTopicId(topicId);
|
|
15689
15978
|
if (!normalizedTopicId) {
|
|
15690
15979
|
return null;
|
|
15691
15980
|
}
|
|
15692
|
-
|
|
15693
|
-
|
|
15694
|
-
|
|
15981
|
+
let topic = null;
|
|
15982
|
+
try {
|
|
15983
|
+
topic = await getConvex(ctx).query(api.topics.get, {
|
|
15984
|
+
id: normalizedTopicId
|
|
15985
|
+
});
|
|
15986
|
+
} catch (error) {
|
|
15987
|
+
debugServerCoreFallback("topic-resolver", `fetchTopicById: topics.get(${normalizedTopicId})`, {
|
|
15988
|
+
error: formatUnknownError2(error)
|
|
15989
|
+
});
|
|
15990
|
+
}
|
|
15695
15991
|
return normalizeTopicCandidate(topic);
|
|
15696
15992
|
}
|
|
15697
15993
|
function readRecordTopicId(value) {
|
|
@@ -15701,7 +15997,14 @@ function readRecordTopicId(value) {
|
|
|
15701
15997
|
);
|
|
15702
15998
|
}
|
|
15703
15999
|
async function fetchRecordTopicId(ctx, reference, args) {
|
|
15704
|
-
|
|
16000
|
+
let result = null;
|
|
16001
|
+
try {
|
|
16002
|
+
result = await getConvex(ctx).query(reference, args);
|
|
16003
|
+
} catch (error) {
|
|
16004
|
+
debugServerCoreFallback("topic-resolver", "fetchRecordTopicId", {
|
|
16005
|
+
error: formatUnknownError2(error)
|
|
16006
|
+
});
|
|
16007
|
+
}
|
|
15705
16008
|
return readRecordTopicId(result);
|
|
15706
16009
|
}
|
|
15707
16010
|
async function resolveBeliefTopicId(ctx, beliefId) {
|
|
@@ -15722,7 +16025,9 @@ async function resolveCommonBeliefTopicId(ctx, beliefIds) {
|
|
|
15722
16025
|
return void 0;
|
|
15723
16026
|
}
|
|
15724
16027
|
const resolvedTopicIds = (await Promise.all(
|
|
15725
|
-
normalizedBeliefIds.map(
|
|
16028
|
+
normalizedBeliefIds.map(
|
|
16029
|
+
(beliefId) => resolveBeliefTopicId(ctx, beliefId)
|
|
16030
|
+
)
|
|
15726
16031
|
)).filter((topicId) => Boolean(topicId));
|
|
15727
16032
|
if (resolvedTopicIds.length !== normalizedBeliefIds.length) {
|
|
15728
16033
|
return void 0;
|
|
@@ -15731,17 +16036,31 @@ async function resolveCommonBeliefTopicId(ctx, beliefIds) {
|
|
|
15731
16036
|
}
|
|
15732
16037
|
async function fetchTopicTree(ctx) {
|
|
15733
16038
|
const convex = getConvex(ctx);
|
|
15734
|
-
|
|
15735
|
-
|
|
15736
|
-
|
|
15737
|
-
|
|
16039
|
+
let tree = null;
|
|
16040
|
+
try {
|
|
16041
|
+
tree = await convex.query(api.topics.getTree, {
|
|
16042
|
+
rootId: ROOT_TOPIC_ID,
|
|
16043
|
+
maxDepth: TREE_MAX_DEPTH
|
|
16044
|
+
});
|
|
16045
|
+
} catch (error) {
|
|
16046
|
+
debugServerCoreFallback("topic-resolver", "fetchTopicTree: topics.getTree", {
|
|
16047
|
+
error: formatUnknownError2(error)
|
|
16048
|
+
});
|
|
16049
|
+
}
|
|
15738
16050
|
const normalizedTree = Array.isArray(tree) ? tree.map((topic) => normalizeTopicCandidate(topic)).filter((topic) => Boolean(topic)) : [];
|
|
15739
16051
|
if (normalizedTree.length > 0) {
|
|
15740
16052
|
return normalizedTree;
|
|
15741
16053
|
}
|
|
15742
|
-
|
|
15743
|
-
|
|
15744
|
-
|
|
16054
|
+
let listed = [];
|
|
16055
|
+
try {
|
|
16056
|
+
listed = await convex.query(api.topics.list, {
|
|
16057
|
+
status: "active"
|
|
16058
|
+
});
|
|
16059
|
+
} catch (error) {
|
|
16060
|
+
debugServerCoreFallback("topic-resolver", "fetchTopicTree: topics.list fallback", {
|
|
16061
|
+
error: formatUnknownError2(error)
|
|
16062
|
+
});
|
|
16063
|
+
}
|
|
15745
16064
|
return Array.isArray(listed) ? listed.map((topic) => normalizeTopicCandidate(topic)).filter((topic) => Boolean(topic)) : [];
|
|
15746
16065
|
}
|
|
15747
16066
|
function buildTopicMaps(topics2) {
|
|
@@ -15774,42 +16093,43 @@ function buildPath(topicId, byId) {
|
|
|
15774
16093
|
function titleCase(value) {
|
|
15775
16094
|
return value.replace(/\b[a-z]/g, (match) => match.toUpperCase());
|
|
15776
16095
|
}
|
|
15777
|
-
function compactWhitespace(value) {
|
|
15778
|
-
return value.replace(/\s+/g, " ").trim();
|
|
15779
|
-
}
|
|
15780
16096
|
function deriveTopicName(input, parentTopic) {
|
|
15781
16097
|
const textualHint = readString3(input.topicHint);
|
|
15782
16098
|
if (textualHint && tokenize(textualHint).length > 0) {
|
|
15783
|
-
return titleCase(
|
|
16099
|
+
return titleCase(textualHint.replace(/\s+/g, " ").trim()).slice(0, 60);
|
|
15784
16100
|
}
|
|
15785
16101
|
const firstTag = (input.tags ?? []).find((tag) => tokenize(tag).length > 0);
|
|
15786
16102
|
if (firstTag) {
|
|
15787
|
-
return titleCase(
|
|
16103
|
+
return titleCase(firstTag.replace(/\s+/g, " ").trim()).slice(0, 60);
|
|
15788
16104
|
}
|
|
15789
16105
|
const firstPathStem = (input.touchedPaths ?? []).map((filePath) => basenameStem(filePath)).find((stem) => stem && tokenize(stem).length > 0);
|
|
15790
16106
|
if (firstPathStem) {
|
|
15791
|
-
return titleCase(
|
|
16107
|
+
return titleCase(
|
|
16108
|
+
firstPathStem.replace(/[-_]+/g, " ").replace(/\s+/g, " ").trim()
|
|
16109
|
+
).slice(0, 60);
|
|
15792
16110
|
}
|
|
15793
|
-
const summary =
|
|
16111
|
+
const summary = input.summary.replace(/\s+/g, " ").trim();
|
|
15794
16112
|
if (summary.length > 0) {
|
|
15795
16113
|
return titleCase(summary.slice(0, 60));
|
|
15796
16114
|
}
|
|
15797
16115
|
const sourceRef = readString3(input.sourceRef);
|
|
15798
16116
|
if (sourceRef) {
|
|
15799
|
-
return titleCase(
|
|
16117
|
+
return titleCase(
|
|
16118
|
+
sourceRef.replace(/[-_:/]+/g, " ").replace(/\s+/g, " ").trim()
|
|
16119
|
+
).slice(0, 60);
|
|
15800
16120
|
}
|
|
15801
|
-
return `${titleCase(parentTopic.name)} Subtopic
|
|
16121
|
+
return parentTopic ? `${titleCase(parentTopic.name)} Subtopic` : "Lucern";
|
|
15802
16122
|
}
|
|
15803
|
-
async function
|
|
16123
|
+
async function createTopic(ctx, input, parentTopic) {
|
|
15804
16124
|
const convex = getConvex(ctx);
|
|
15805
16125
|
const desiredTenantId = ctx.tenantId;
|
|
15806
16126
|
const desiredWorkspaceId = readString3(input.workspaceId) ?? ctx.workspaceId;
|
|
15807
16127
|
const createdBy = ctx.principalId ?? ctx.userId;
|
|
15808
16128
|
const created = await convex.mutation(api.topics.create, {
|
|
15809
16129
|
name: deriveTopicName(input, parentTopic),
|
|
15810
|
-
description:
|
|
16130
|
+
description: input.summary.replace(/\s+/g, " ").trim().slice(0, 280) || void 0,
|
|
15811
16131
|
type: "theme",
|
|
15812
|
-
parentTopicId: parentTopic.rawId,
|
|
16132
|
+
...parentTopic ? { parentTopicId: parentTopic.rawId } : {},
|
|
15813
16133
|
tenantId: desiredTenantId,
|
|
15814
16134
|
workspaceId: desiredWorkspaceId,
|
|
15815
16135
|
visibility: "team",
|
|
@@ -15826,10 +16146,18 @@ async function createChildTopic(ctx, parentTopic, input) {
|
|
|
15826
16146
|
const createdId = readString3(createdRecord.id) ?? readString3(createdRecord.topicId) ?? readString3(createdRecord._id);
|
|
15827
16147
|
const hydrated = await fetchTopicById(ctx, createdId);
|
|
15828
16148
|
if (!hydrated) {
|
|
15829
|
-
throw new Error(
|
|
16149
|
+
throw new Error(
|
|
16150
|
+
"[topic-resolver] Auto-created topic could not be reloaded."
|
|
16151
|
+
);
|
|
15830
16152
|
}
|
|
15831
16153
|
return hydrated;
|
|
15832
16154
|
}
|
|
16155
|
+
function createChildTopic(ctx, parentTopic, input) {
|
|
16156
|
+
return createTopic(ctx, input, parentTopic);
|
|
16157
|
+
}
|
|
16158
|
+
function createRootTopic(ctx, input) {
|
|
16159
|
+
return createTopic(ctx, input);
|
|
16160
|
+
}
|
|
15833
16161
|
function pushTrace(trace, topic, score) {
|
|
15834
16162
|
const existing = trace[trace.length - 1];
|
|
15835
16163
|
if (existing?.topicId === topic.rawId) {
|
|
@@ -15844,9 +16172,10 @@ function pushTrace(trace, topic, score) {
|
|
|
15844
16172
|
});
|
|
15845
16173
|
}
|
|
15846
16174
|
function pickBestTopic(topics2, input) {
|
|
16175
|
+
const corpus = buildTopicScoreCorpus(topics2);
|
|
15847
16176
|
const ranked = topics2.filter((topic) => topic.status !== "archived").map((topic) => ({
|
|
15848
16177
|
topic,
|
|
15849
|
-
score:
|
|
16178
|
+
score: scoreTopicMatchWithCorpus(topic, input, corpus)
|
|
15850
16179
|
})).sort((left, right) => {
|
|
15851
16180
|
if (right.score !== left.score) {
|
|
15852
16181
|
return right.score - left.score;
|
|
@@ -15862,27 +16191,31 @@ function pickFallbackRootTopic(topics2, input) {
|
|
|
15862
16191
|
return pickBestTopic(parentless, input) ?? pickBestTopic(topics2, input);
|
|
15863
16192
|
}
|
|
15864
16193
|
async function loadTopicUniverse(ctx, input) {
|
|
15865
|
-
const desiredTenantId = ctx.tenantId;
|
|
15866
|
-
const desiredWorkspaceId = readString3(input.workspaceId) ?? ctx.workspaceId;
|
|
15867
16194
|
const rawTopics = await fetchTopicTree(ctx);
|
|
15868
|
-
|
|
15869
|
-
|
|
15870
|
-
|
|
15871
|
-
const root = rawTopics.find((topic) => topic.rawId === ROOT_TOPIC_ID) ?? await fetchTopicById(ctx, ROOT_TOPIC_ID) ?? pickFallbackRootTopic(scopedTopics, input) ?? null;
|
|
16195
|
+
let candidatePool = rawTopics;
|
|
16196
|
+
let root = pickFallbackRootTopic(candidatePool, input) ?? candidatePool.find((topic) => !topic.parentTopicId) ?? candidatePool[0] ?? null;
|
|
16197
|
+
let rootCreated = false;
|
|
15872
16198
|
if (!root) {
|
|
15873
|
-
|
|
16199
|
+
if (input.autoCreate === false) {
|
|
16200
|
+
throw new Error(
|
|
16201
|
+
"[topic-resolver] No topics available for resolution."
|
|
16202
|
+
);
|
|
16203
|
+
}
|
|
16204
|
+
root = await createRootTopic(ctx, input);
|
|
16205
|
+
rootCreated = true;
|
|
16206
|
+
candidatePool = [root];
|
|
15874
16207
|
}
|
|
15875
|
-
const topics2 = rawTopics.filter(
|
|
15876
|
-
(topic) => topic.rawId === root.rawId || topicMatchesScope(topic, desiredTenantId, desiredWorkspaceId)
|
|
15877
|
-
);
|
|
15878
16208
|
const dedupedTopics = /* @__PURE__ */ new Map();
|
|
15879
16209
|
dedupedTopics.set(root.rawId, root);
|
|
15880
|
-
for (const topic of
|
|
16210
|
+
for (const topic of candidatePool) {
|
|
15881
16211
|
dedupedTopics.set(topic.rawId, topic);
|
|
15882
16212
|
}
|
|
15883
|
-
const { byId, childrenByParent } = buildTopicMaps([
|
|
16213
|
+
const { byId, childrenByParent } = buildTopicMaps([
|
|
16214
|
+
...dedupedTopics.values()
|
|
16215
|
+
]);
|
|
15884
16216
|
return {
|
|
15885
16217
|
root,
|
|
16218
|
+
rootCreated,
|
|
15886
16219
|
byId,
|
|
15887
16220
|
childrenByParent
|
|
15888
16221
|
};
|
|
@@ -15893,7 +16226,9 @@ async function validateTopicOrNull(ctx, topicId) {
|
|
|
15893
16226
|
return null;
|
|
15894
16227
|
}
|
|
15895
16228
|
const { byId } = await loadTopicUniverse(ctx, {
|
|
15896
|
-
summary: topic.name
|
|
16229
|
+
summary: topic.name,
|
|
16230
|
+
autoCreate: false
|
|
16231
|
+
});
|
|
15897
16232
|
if (!byId.has(topic.rawId)) {
|
|
15898
16233
|
byId.set(topic.rawId, topic);
|
|
15899
16234
|
}
|
|
@@ -15915,8 +16250,10 @@ async function resolveTopicForWrite(ctx, input) {
|
|
|
15915
16250
|
const threshold = typeof input.threshold === "number" && Number.isFinite(input.threshold) ? Math.max(0, Math.min(1, input.threshold)) : DEFAULT_THRESHOLD;
|
|
15916
16251
|
const createThreshold = Math.max(0.5, Math.min(1, threshold + 0.15));
|
|
15917
16252
|
const autoCreate = input.autoCreate !== false;
|
|
15918
|
-
const { root, byId, childrenByParent } = await loadTopicUniverse(ctx, input);
|
|
16253
|
+
const { root, rootCreated, byId, childrenByParent } = await loadTopicUniverse(ctx, input);
|
|
15919
16254
|
const trace = [];
|
|
16255
|
+
const scoreCorpus = buildTopicScoreCorpus(byId.values());
|
|
16256
|
+
const scoreTopic = (topic) => scoreTopicMatchWithCorpus(topic, input, scoreCorpus);
|
|
15920
16257
|
const ownScore = /* @__PURE__ */ new Map();
|
|
15921
16258
|
const subtreeScore = /* @__PURE__ */ new Map();
|
|
15922
16259
|
const computeSubtreeScore = (topicId, visited) => {
|
|
@@ -15933,7 +16270,7 @@ async function resolveTopicForWrite(ctx, input) {
|
|
|
15933
16270
|
return 0;
|
|
15934
16271
|
}
|
|
15935
16272
|
if (!ownScore.has(topicId)) {
|
|
15936
|
-
ownScore.set(topicId,
|
|
16273
|
+
ownScore.set(topicId, scoreTopic(topic));
|
|
15937
16274
|
}
|
|
15938
16275
|
let best = ownScore.get(topicId);
|
|
15939
16276
|
for (const child of childrenByParent.get(topicId) ?? []) {
|
|
@@ -15945,7 +16282,7 @@ async function resolveTopicForWrite(ctx, input) {
|
|
|
15945
16282
|
};
|
|
15946
16283
|
computeSubtreeScore(root.rawId, /* @__PURE__ */ new Set());
|
|
15947
16284
|
let current = root;
|
|
15948
|
-
let currentScore = ownScore.get(root.rawId) ??
|
|
16285
|
+
let currentScore = ownScore.get(root.rawId) ?? scoreTopic(root);
|
|
15949
16286
|
pushTrace(trace, current, currentScore);
|
|
15950
16287
|
while (true) {
|
|
15951
16288
|
const children = (childrenByParent.get(current.rawId) ?? []).filter(
|
|
@@ -15956,7 +16293,7 @@ async function resolveTopicForWrite(ctx, input) {
|
|
|
15956
16293
|
}
|
|
15957
16294
|
const rankedChildren = children.map((topic) => {
|
|
15958
16295
|
if (!ownScore.has(topic.rawId)) {
|
|
15959
|
-
ownScore.set(topic.rawId,
|
|
16296
|
+
ownScore.set(topic.rawId, scoreTopic(topic));
|
|
15960
16297
|
}
|
|
15961
16298
|
return {
|
|
15962
16299
|
topic,
|
|
@@ -15968,13 +16305,13 @@ async function resolveTopicForWrite(ctx, input) {
|
|
|
15968
16305
|
break;
|
|
15969
16306
|
}
|
|
15970
16307
|
current = bestChild.topic;
|
|
15971
|
-
currentScore = ownScore.get(current.rawId) ??
|
|
16308
|
+
currentScore = ownScore.get(current.rawId) ?? scoreTopic(current);
|
|
15972
16309
|
pushTrace(trace, current, currentScore);
|
|
15973
16310
|
}
|
|
15974
|
-
let created =
|
|
16311
|
+
let created = rootCreated;
|
|
15975
16312
|
let resolvedTopic = current;
|
|
15976
16313
|
let finalScore = currentScore;
|
|
15977
|
-
if (autoCreate && finalScore < createThreshold) {
|
|
16314
|
+
if (autoCreate && !rootCreated && finalScore < createThreshold) {
|
|
15978
16315
|
const siblings = childrenByParent.get(current.rawId) ?? [];
|
|
15979
16316
|
const derivedName = deriveTopicName(input, current).toLowerCase();
|
|
15980
16317
|
const existingSibling = siblings.find(
|
|
@@ -15982,7 +16319,7 @@ async function resolveTopicForWrite(ctx, input) {
|
|
|
15982
16319
|
);
|
|
15983
16320
|
if (existingSibling) {
|
|
15984
16321
|
resolvedTopic = existingSibling;
|
|
15985
|
-
finalScore =
|
|
16322
|
+
finalScore = scoreTopic(existingSibling);
|
|
15986
16323
|
pushTrace(trace, resolvedTopic, finalScore);
|
|
15987
16324
|
} else {
|
|
15988
16325
|
resolvedTopic = await createChildTopic(ctx, current, input);
|
|
@@ -16360,9 +16697,7 @@ var ontologyHandlers = {
|
|
|
16360
16697
|
|
|
16361
16698
|
// ../../apps/mcp-server/src/handlers/ontology-matching.ts
|
|
16362
16699
|
var ontologyMatchingHandlers = {
|
|
16363
|
-
|
|
16364
|
-
return ontologyHandlers.match_entity_type(args, ctx);
|
|
16365
|
-
},
|
|
16700
|
+
match_entity_type: ontologyHandlers.match_entity_type,
|
|
16366
16701
|
async discover_entity_connections(args, ctx) {
|
|
16367
16702
|
return formatSdkResult(
|
|
16368
16703
|
await getSdkClient(ctx).context.discoverEntityConnections({
|
|
@@ -16539,20 +16874,30 @@ var researchVerificationHandlers = {
|
|
|
16539
16874
|
function cleanString(value) {
|
|
16540
16875
|
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
16541
16876
|
}
|
|
16877
|
+
function isRecord6(value) {
|
|
16878
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
16879
|
+
}
|
|
16542
16880
|
function prefixId(prefix, value) {
|
|
16543
16881
|
const cleaned = cleanString(value);
|
|
16544
16882
|
if (!cleaned) {
|
|
16545
16883
|
return `${prefix}_unknown`;
|
|
16546
16884
|
}
|
|
16547
|
-
|
|
16548
|
-
|
|
16549
|
-
|
|
16550
|
-
return cleaned;
|
|
16551
|
-
}
|
|
16552
|
-
} catch {
|
|
16885
|
+
const decoded = tryDecodePrefixedId(cleaned);
|
|
16886
|
+
if (decoded?.prefix === prefix) {
|
|
16887
|
+
return cleaned;
|
|
16553
16888
|
}
|
|
16554
16889
|
return encodePrefixedId(prefix, cleaned);
|
|
16555
16890
|
}
|
|
16891
|
+
function tryDecodePrefixedId(value) {
|
|
16892
|
+
try {
|
|
16893
|
+
return decodePrefixedId(value);
|
|
16894
|
+
} catch (error) {
|
|
16895
|
+
return ignorePrefixedIdDecodeFailure();
|
|
16896
|
+
}
|
|
16897
|
+
}
|
|
16898
|
+
function ignorePrefixedIdDecodeFailure(_error) {
|
|
16899
|
+
return void 0;
|
|
16900
|
+
}
|
|
16556
16901
|
function mapSelectedIds(value) {
|
|
16557
16902
|
return {
|
|
16558
16903
|
invariants: (value.invariants ?? []).map((id) => prefixId("bel", id)),
|
|
@@ -16585,7 +16930,7 @@ function toPublicCompiledContext(pack) {
|
|
|
16585
16930
|
scopedTopicIds: (pack.scopedTopicIds ?? []).map((id) => prefixId("top", id)),
|
|
16586
16931
|
generatedAt: pack.generatedAt,
|
|
16587
16932
|
ranking: pack.rankingProfile,
|
|
16588
|
-
summary: pack.summary,
|
|
16933
|
+
summary: isRecord6(pack.summary) ? pack.summary : {},
|
|
16589
16934
|
invariants: (pack.invariants ?? []).map((belief) => ({
|
|
16590
16935
|
beliefId: prefixId("bel", belief.nodeId),
|
|
16591
16936
|
text: belief.canonicalText,
|
|
@@ -16635,7 +16980,7 @@ function toPublicCompiledContext(pack) {
|
|
|
16635
16980
|
entityId: cleanString(entity.nodeId) ?? "",
|
|
16636
16981
|
entityType: entity.entityType,
|
|
16637
16982
|
title: entity.title,
|
|
16638
|
-
text: cleanString(entity.
|
|
16983
|
+
text: cleanString(entity.title),
|
|
16639
16984
|
connectedBeliefCount: entity.connectedBeliefCount,
|
|
16640
16985
|
connectedEvidenceCount: entity.connectedEvidenceCount,
|
|
16641
16986
|
score: entity.score,
|
|
@@ -16656,7 +17001,7 @@ function toPublicCompiledContext(pack) {
|
|
|
16656
17001
|
}))
|
|
16657
17002
|
} : {}
|
|
16658
17003
|
},
|
|
16659
|
-
diagnostics: pack.diagnostics,
|
|
17004
|
+
diagnostics: isRecord6(pack.diagnostics) ? pack.diagnostics : {},
|
|
16660
17005
|
...pack.compilationMode ? { compilationMode: pack.compilationMode } : {},
|
|
16661
17006
|
...pack.failureContext ? {
|
|
16662
17007
|
failureContext: {
|
|
@@ -17082,9 +17427,10 @@ var MUTATION_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
17082
17427
|
"deprecate_ontology_version",
|
|
17083
17428
|
"manage_write_policy"
|
|
17084
17429
|
]);
|
|
17085
|
-
|
|
17086
|
-
|
|
17087
|
-
|
|
17430
|
+
var isMutationTool = (toolName) => {
|
|
17431
|
+
const normalized = toolName.trim();
|
|
17432
|
+
return normalized.length > 0 && MUTATION_TOOL_NAMES.has(normalized);
|
|
17433
|
+
};
|
|
17088
17434
|
var sessionWritesBySession = /* @__PURE__ */ new Map();
|
|
17089
17435
|
function recordWrite(toolName, topicId, sessionId = "default") {
|
|
17090
17436
|
const record = {
|
|
@@ -17216,8 +17562,6 @@ async function checkWritePolicy(toolName, topicId, authCtx) {
|
|
|
17216
17562
|
};
|
|
17217
17563
|
}
|
|
17218
17564
|
}
|
|
17219
|
-
|
|
17220
|
-
// ../../apps/mcp-server/src/credentials.ts
|
|
17221
17565
|
var LUCERN_HOME = path2.join(os.homedir(), ".lucern");
|
|
17222
17566
|
path2.join(LUCERN_HOME, "credentials");
|
|
17223
17567
|
|