@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/gateway.js CHANGED
@@ -4865,6 +4865,11 @@ var TENANT_CLIENT_INSTALLABLE_PACKAGES = [
4865
4865
  role: "sdk_dependency",
4866
4866
  directTenantImport: false
4867
4867
  },
4868
+ {
4869
+ packageName: "@lucern/graph-sync",
4870
+ role: "host_addon_runtime",
4871
+ directTenantImport: true
4872
+ },
4868
4873
  {
4869
4874
  packageName: "@lucern/identity",
4870
4875
  role: "component_runtime",
@@ -4984,8 +4989,11 @@ function compactRecord(input) {
4984
4989
  Object.entries(input).filter(([, value]) => value !== void 0)
4985
4990
  );
4986
4991
  }
4992
+ function isRecord(value) {
4993
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4994
+ }
4987
4995
  function recordValue(value) {
4988
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
4996
+ return isRecord(value) ? value : {};
4989
4997
  }
4990
4998
  var createEvidenceProjection = defineProjection({
4991
4999
  contractName: "create_evidence",
@@ -5691,9 +5699,16 @@ var ADD_WORKTREE = {
5691
5699
  },
5692
5700
  projectId: {
5693
5701
  type: "string",
5694
- description: "Legacy topicId alias"
5702
+ description: "Legacy topicId alias or resolver hint"
5703
+ },
5704
+ topicId: {
5705
+ type: "string",
5706
+ description: "Optional topic scope hint for resolver validation"
5707
+ },
5708
+ topicHint: {
5709
+ type: "string",
5710
+ description: "Natural-language topic hint for automatic topic resolution"
5695
5711
  },
5696
- topicId: { type: "string", description: "Optional topic scope hint" },
5697
5712
  branchId: {
5698
5713
  type: "string",
5699
5714
  description: "The branch this worktree investigates"
@@ -5791,6 +5806,22 @@ var ADD_WORKTREE = {
5791
5806
  type: "string",
5792
5807
  description: "Optional domain pack whose shaping hooks should influence generated questions and tasks"
5793
5808
  },
5809
+ tags: {
5810
+ type: "array",
5811
+ description: "Additional topic-resolution tags for the worktree"
5812
+ },
5813
+ touchedPaths: {
5814
+ type: "array",
5815
+ description: "File paths used as topic-resolution signals"
5816
+ },
5817
+ sourceRef: {
5818
+ type: "string",
5819
+ description: "Source reference used as a topic-resolution signal"
5820
+ },
5821
+ sourceKind: {
5822
+ type: "string",
5823
+ description: "Source kind used as a topic-resolution signal"
5824
+ },
5794
5825
  campaign: {
5795
5826
  type: "number",
5796
5827
  description: "Top-level pipeline campaign number. Campaigns define the outer execution slice."
@@ -5828,7 +5859,7 @@ var ADD_WORKTREE = {
5828
5859
  description: "Timestamp when worktree metadata was last reconciled"
5829
5860
  }
5830
5861
  },
5831
- required: ["title", "topicId"],
5862
+ required: ["title"],
5832
5863
  response: {
5833
5864
  description: "The created worktree",
5834
5865
  fields: {
@@ -7531,18 +7562,60 @@ var CREATE_TASK = {
7531
7562
  name: "create_task",
7532
7563
  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.",
7533
7564
  parameters: {
7534
- title: { type: "string", description: "Task description" },
7565
+ title: { type: "string", description: "Task title" },
7535
7566
  topicId: { type: "string", description: "Topic scope" },
7567
+ description: {
7568
+ type: "string",
7569
+ description: "Long-form task description"
7570
+ },
7536
7571
  taskType: {
7537
7572
  type: "string",
7538
- description: "Type: research, interview, analysis, data_collection",
7539
- enum: ["research", "interview", "analysis", "data_collection"]
7573
+ description: "Task taxonomy",
7574
+ enum: [
7575
+ "general",
7576
+ "find_evidence",
7577
+ "verify_claim",
7578
+ "research",
7579
+ "review",
7580
+ "interview",
7581
+ "analysis",
7582
+ "track_metrics"
7583
+ ]
7584
+ },
7585
+ priority: {
7586
+ type: "string",
7587
+ description: "Priority",
7588
+ enum: ["urgent", "high", "medium", "low"]
7589
+ },
7590
+ status: {
7591
+ type: "string",
7592
+ description: "Initial status (defaults to todo)",
7593
+ enum: ["todo", "in_progress", "blocked", "done"]
7594
+ },
7595
+ linkedWorktreeId: {
7596
+ type: "string",
7597
+ description: "Worktree this task belongs to"
7598
+ },
7599
+ linkedBeliefId: {
7600
+ type: "string",
7601
+ description: "Belief this task supports"
7540
7602
  },
7541
7603
  linkedQuestionId: {
7542
7604
  type: "string",
7543
7605
  description: "Question this task addresses"
7544
7606
  },
7545
- linkedWorktreeId: { type: "string", description: "Worktree scope" }
7607
+ assigneeId: {
7608
+ type: "string",
7609
+ description: "Principal assigned to the task"
7610
+ },
7611
+ dueDate: {
7612
+ type: "number",
7613
+ description: "Due date as epoch milliseconds"
7614
+ },
7615
+ tags: {
7616
+ type: "array",
7617
+ description: "Free-form string tags"
7618
+ }
7546
7619
  },
7547
7620
  required: ["title"],
7548
7621
  response: {
@@ -9625,9 +9698,7 @@ function mcpContractFromArgsSchema(base, args, contractName) {
9625
9698
  required: converted.filter(([, field]) => field.required).map(([fieldName]) => fieldName)
9626
9699
  };
9627
9700
  }
9628
- function defineFunctionContract(contract) {
9629
- return contract;
9630
- }
9701
+ var defineFunctionContract = (contract) => contract;
9631
9702
  function authUserId(context) {
9632
9703
  return context.userId ?? context.principalId ?? "lucern-agent";
9633
9704
  }
@@ -9781,6 +9852,9 @@ var observationContextArgs = z.object({
9781
9852
  limit: z.number().optional().describe("Maximum observations to return."),
9782
9853
  status: z.string().optional().describe("Observation status filter.")
9783
9854
  });
9855
+ function isRecord2(value) {
9856
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
9857
+ }
9784
9858
  var observationInput = (input, context) => withUserId(
9785
9859
  compactRecord4({
9786
9860
  projectId: input.projectId,
@@ -9835,8 +9909,8 @@ var contextContracts = [
9835
9909
  kind: "mutation",
9836
9910
  inputProjection: observationInput,
9837
9911
  outputProjection: (output, input) => ({
9838
- ...output && typeof output === "object" ? output : {},
9839
- observationId: output && typeof output === "object" ? output.nodeId : void 0,
9912
+ ...isRecord2(output) ? output : {},
9913
+ observationId: isRecord2(output) ? output.nodeId : void 0,
9840
9914
  observationType: input.observationType
9841
9915
  })
9842
9916
  },
@@ -11317,10 +11391,11 @@ var worktreeDecisionGateInputSchema = z.object({
11317
11391
  decidedBy: z.string().optional().describe("Actor that decided the gate verdict.")
11318
11392
  }).passthrough().describe("Decision gate contract for worktree activation or exit.");
11319
11393
  var addWorktreeArgs = z.object({
11320
- title: z.string().optional().describe("Human-readable worktree name or objective."),
11394
+ title: z.string().describe("Human-readable worktree name or objective."),
11321
11395
  name: z.string().optional().describe("Storage-name alias for callers that already use backend naming."),
11322
- topicId: z.string().describe("Primary topic scope for the worktree."),
11323
- projectId: z.string().optional().describe("Legacy topicId alias."),
11396
+ topicId: z.string().optional().describe("Optional primary topic scope hint for resolver validation."),
11397
+ projectId: z.string().optional().describe("Legacy topicId alias/hint."),
11398
+ topicHint: z.string().optional().describe("Natural-language topic hint for automatic topic resolution."),
11324
11399
  branchId: z.string().optional().describe("Legacy branch identifier for compatibility with workflow callers."),
11325
11400
  objective: z.string().optional().describe("Reasoning objective this worktree is intended to resolve."),
11326
11401
  hypothesis: z.string().optional().describe("Testable claim this worktree investigates."),
@@ -11345,6 +11420,10 @@ var addWorktreeArgs = z.object({
11345
11420
  autoShape: z.boolean().optional().describe("Whether to invoke inquiry auto-shaping during creation."),
11346
11421
  autoFixPolicy: autoFixPolicyInputSchema.optional(),
11347
11422
  domainPackId: z.string().optional().describe("Domain pack whose shaping hooks should influence the worktree."),
11423
+ tags: z.array(z.string()).optional().describe("Additional topic-resolution tags for the worktree."),
11424
+ touchedPaths: z.array(z.string()).optional().describe("File paths used as topic-resolution signals."),
11425
+ sourceRef: z.string().optional().describe("Source reference used as a topic-resolution signal."),
11426
+ sourceKind: z.string().optional().describe("Source kind used as a topic-resolution signal."),
11348
11427
  campaign: z.number().optional().describe("Top-level pipeline campaign number."),
11349
11428
  lane: z.string().optional().describe("Campaign lane for the worktree."),
11350
11429
  laneOrderInCampaign: z.number().optional().describe("Ordering for this lane within its campaign."),
@@ -11674,8 +11753,46 @@ var worktreesContracts = [
11674
11753
  args: openPullRequestArgs
11675
11754
  })
11676
11755
  ];
11677
-
11678
- // ../contracts/src/function-registry/tasks.ts
11756
+ var taskPrioritySchema = z.enum(["urgent", "high", "medium", "low"]);
11757
+ var taskStatusSchema2 = z.enum(["todo", "in_progress", "blocked", "done"]);
11758
+ var taskTypeSchema = z.enum([
11759
+ "general",
11760
+ "find_evidence",
11761
+ "verify_claim",
11762
+ "research",
11763
+ "review",
11764
+ "interview",
11765
+ "analysis",
11766
+ "track_metrics"
11767
+ ]);
11768
+ var createTaskArgs = z.object({
11769
+ title: z.string().describe("Task title."),
11770
+ topicId: z.string().optional().describe("Topic scope."),
11771
+ description: z.string().optional().describe("Long-form task description."),
11772
+ taskType: taskTypeSchema.optional().describe("Task taxonomy."),
11773
+ priority: taskPrioritySchema.optional().describe("Priority. Defaults to medium when omitted by the server."),
11774
+ status: taskStatusSchema2.optional().describe("Initial status. Defaults to todo."),
11775
+ linkedWorktreeId: z.string().optional().describe("Worktree this task belongs to."),
11776
+ linkedBeliefId: z.string().optional().describe("Belief this task supports."),
11777
+ linkedQuestionId: z.string().optional().describe("Question this task addresses."),
11778
+ assigneeId: z.string().optional().describe("Principal assigned to the task."),
11779
+ dueDate: z.number().optional().describe("Due date as epoch milliseconds."),
11780
+ tags: z.array(z.string()).optional().describe("Free-form tags.")
11781
+ });
11782
+ var createTaskInput = (input) => compactRecord4({
11783
+ title: input.title,
11784
+ topicId: input.topicId,
11785
+ description: input.description,
11786
+ taskType: input.taskType,
11787
+ priority: input.priority ?? "medium",
11788
+ status: input.status ?? "todo",
11789
+ linkedWorktreeId: input.linkedWorktreeId,
11790
+ linkedBeliefId: input.linkedBeliefId,
11791
+ linkedQuestionId: input.linkedQuestionId,
11792
+ assigneeId: input.assigneeId,
11793
+ dueDate: input.dueDate,
11794
+ tags: input.tags
11795
+ });
11679
11796
  var taskInput = (input) => compactRecord4({
11680
11797
  ...input,
11681
11798
  taskId: input.taskId ?? input.id
@@ -11707,8 +11824,10 @@ var tasksContracts = [
11707
11824
  convex: {
11708
11825
  module: "tasks",
11709
11826
  functionName: "create",
11710
- kind: "mutation"
11711
- }
11827
+ kind: "mutation",
11828
+ inputProjection: createTaskInput
11829
+ },
11830
+ args: createTaskArgs
11712
11831
  }),
11713
11832
  surfaceContract({
11714
11833
  name: "list_tasks",
@@ -12826,9 +12945,12 @@ var ALL_FUNCTION_CONTRACTS = [
12826
12945
  ...legacyContracts
12827
12946
  ];
12828
12947
  assertSurfaceCoverage(ALL_FUNCTION_CONTRACTS);
12829
- new Map(
12948
+ var FUNCTION_CONTRACTS_BY_NAME = new Map(
12830
12949
  ALL_FUNCTION_CONTRACTS.map((contract) => [contract.name, contract])
12831
12950
  );
12951
+ FUNCTION_CONTRACTS_BY_NAME.get.bind(
12952
+ FUNCTION_CONTRACTS_BY_NAME
12953
+ );
12832
12954
 
12833
12955
  // ../contracts/src/text-matching.contract.ts
12834
12956
  var TOKEN_SPLIT_REGEX = /[^a-z0-9]+/;
@@ -13873,15 +13995,31 @@ function defaultSuggestionForCode(code) {
13873
13995
  return void 0;
13874
13996
  }
13875
13997
  }
13998
+ function createGatewaySuccessBody(payload, args) {
13999
+ const body = {};
14000
+ body.success = true;
14001
+ body.data = payload;
14002
+ body.correlationId = args.correlationId;
14003
+ body.policyTraceId = args.policyTraceId ?? null;
14004
+ body.idempotentReplay = args.idempotentReplay ?? false;
14005
+ return body;
14006
+ }
14007
+ function createGatewayErrorBody(args, safeMessage) {
14008
+ const body = {};
14009
+ body.success = false;
14010
+ body.error = safeMessage;
14011
+ body.code = args.code;
14012
+ body.invariant = args.invariant ?? defaultInvariantForCode(args.code) ?? null;
14013
+ body.suggestion = args.suggestion ?? defaultSuggestionForCode(args.code) ?? null;
14014
+ body.details = args.details;
14015
+ body.correlationId = args.correlationId;
14016
+ body.policyTraceId = args.policyTraceId ?? null;
14017
+ return body;
14018
+ }
13876
14019
  function successResponse(payload, args) {
14020
+ const body = createGatewaySuccessBody(payload, args);
13877
14021
  return Response.json(
13878
- {
13879
- success: true,
13880
- data: payload,
13881
- correlationId: args.correlationId,
13882
- policyTraceId: args.policyTraceId ?? null,
13883
- idempotentReplay: args.idempotentReplay ?? false
13884
- },
14022
+ body,
13885
14023
  {
13886
14024
  status: args.status ?? 200,
13887
14025
  headers: buildHeaders({
@@ -13908,17 +14046,9 @@ function errorResponse(args) {
13908
14046
  status: args.status,
13909
14047
  code: args.code
13910
14048
  });
14049
+ const body = createGatewayErrorBody(args, safeMessage);
13911
14050
  return Response.json(
13912
- {
13913
- success: false,
13914
- error: safeMessage,
13915
- code: args.code,
13916
- invariant: args.invariant ?? defaultInvariantForCode(args.code) ?? null,
13917
- suggestion: args.suggestion ?? defaultSuggestionForCode(args.code) ?? null,
13918
- details: args.details,
13919
- correlationId: args.correlationId,
13920
- policyTraceId: args.policyTraceId ?? null
13921
- },
14051
+ body,
13922
14052
  {
13923
14053
  status: args.status,
13924
14054
  headers
@@ -13967,6 +14097,42 @@ var api = makeProxy("api");
13967
14097
  makeProxy("components");
13968
14098
  makeProxy("internal");
13969
14099
 
14100
+ // ../server-core/src/debug.ts
14101
+ function formatUnknownError(error) {
14102
+ if (error instanceof Error) {
14103
+ const cause = error.cause;
14104
+ const message = `${error.name}: ${error.message}`;
14105
+ if (cause === void 0) {
14106
+ return message;
14107
+ }
14108
+ return `${message}; cause=${formatUnknownError(cause)}`;
14109
+ }
14110
+ if (typeof error === "string") {
14111
+ return error;
14112
+ }
14113
+ if (error === null) {
14114
+ return "null";
14115
+ }
14116
+ if (error === void 0) {
14117
+ return "undefined";
14118
+ }
14119
+ try {
14120
+ return JSON.stringify(error);
14121
+ } catch {
14122
+ return Object.prototype.toString.call(error);
14123
+ }
14124
+ }
14125
+ function isServerCoreFallbackDebugEnabled() {
14126
+ const env = globalThis.process?.env;
14127
+ return env?.LUCERN_COMPAT_FALLBACK_DEBUG === "1" || env?.LUCERN_SERVER_CORE_DEBUG === "1";
14128
+ }
14129
+ function debugServerCoreFallback(scope, message, context) {
14130
+ if (!isServerCoreFallbackDebugEnabled()) {
14131
+ return;
14132
+ }
14133
+ console.debug(`[${scope}] ${message}`, context ?? {});
14134
+ }
14135
+
13970
14136
  // ../server-core/src/mcp-context-tools.ts
13971
14137
  function requireMcpConvex(authContext, label) {
13972
14138
  const convex = authContext.convex;
@@ -14890,6 +15056,9 @@ async function bisectBeliefConfidence(port, input) {
14890
15056
  }
14891
15057
  var DEFAULT_THRESHOLD = 0.35;
14892
15058
  var TREE_MAX_DEPTH = 20;
15059
+ var BM25_K1 = 1.2;
15060
+ var BM25_B = 0.75;
15061
+ var BM25_NORMALIZATION = 8;
14893
15062
  var METADATA_TOKEN_KEYS = [
14894
15063
  "aliases",
14895
15064
  "alias",
@@ -14942,35 +15111,44 @@ function readStringArray2(value) {
14942
15111
  }
14943
15112
  return value.map((entry) => readString2(entry)).filter((entry) => Boolean(entry));
14944
15113
  }
15114
+ function isRecord3(value) {
15115
+ return value !== null && typeof value === "object" && !Array.isArray(value);
15116
+ }
15117
+ function decodePrefixedIdOrNull(value) {
15118
+ const normalized = value.trim();
15119
+ const match = /^([a-z][a-z0-9]*)_(.+)$/.exec(normalized);
15120
+ if (!match) {
15121
+ return null;
15122
+ }
15123
+ return {
15124
+ prefix: match[1],
15125
+ value: match[2]
15126
+ };
15127
+ }
14945
15128
  function asRecord2(value) {
14946
- return value && typeof value === "object" && !Array.isArray(value) ? value : {};
15129
+ return isRecord3(value) ? value : {};
14947
15130
  }
14948
15131
  function normalizeTopicId(value) {
14949
15132
  const normalized = readString2(value);
14950
15133
  if (!normalized) {
14951
15134
  return void 0;
14952
15135
  }
14953
- try {
14954
- const decoded = decodePrefixedId(normalized);
14955
- return decoded.prefix === "top" ? decoded.value : normalized;
14956
- } catch {
14957
- return normalized;
14958
- }
15136
+ const decoded = decodePrefixedIdOrNull(normalized);
15137
+ return decoded?.prefix === "top" ? decoded.value : normalized;
14959
15138
  }
14960
15139
  function normalizeExternalId(value, prefix) {
14961
15140
  const normalized = readString2(value);
14962
15141
  if (!normalized) {
14963
15142
  return void 0;
14964
15143
  }
14965
- try {
14966
- const decoded = decodePrefixedId(normalized);
14967
- if (prefix && decoded.prefix !== prefix) {
14968
- return normalized;
14969
- }
14970
- return decoded.value;
14971
- } catch {
15144
+ const decoded = decodePrefixedIdOrNull(normalized);
15145
+ if (!decoded) {
14972
15146
  return normalized;
14973
15147
  }
15148
+ if (prefix && decoded.prefix !== prefix) {
15149
+ return normalized;
15150
+ }
15151
+ return decoded.value;
14974
15152
  }
14975
15153
  function normalizeToken(value) {
14976
15154
  const normalized = value.toLowerCase().trim();
@@ -15049,15 +15227,83 @@ function collectTopicMetadataTokens(metadata) {
15049
15227
  function topicNameSlug(name) {
15050
15228
  return tokenize(name).join(" ");
15051
15229
  }
15230
+ function topicTokenList(topic) {
15231
+ return [
15232
+ ...tokenize(topic.name),
15233
+ ...tokenize(topic.description),
15234
+ ...tokenize(topic.type),
15235
+ ...collectTopicMetadataTokens(topic.metadata ?? {}).flatMap(
15236
+ (value) => tokenize(value)
15237
+ )
15238
+ ];
15239
+ }
15052
15240
  function topicTokenSet(topic) {
15053
15241
  const tokens = /* @__PURE__ */ new Set();
15054
- addTokens(tokens, tokenize(topic.name));
15055
- addTokens(tokens, tokenize(topic.description));
15056
- addTokens(tokens, tokenize(topic.type));
15057
- addTokens(tokens, collectTopicMetadataTokens(topic.metadata ?? {}));
15242
+ addTokens(tokens, topicTokenList(topic));
15058
15243
  return tokens;
15059
15244
  }
15245
+ function buildTopicScoreCorpus(topics2) {
15246
+ const documentFrequencyByToken = /* @__PURE__ */ new Map();
15247
+ let documentCount = 0;
15248
+ let totalLength = 0;
15249
+ for (const topic of topics2) {
15250
+ const tokens = topicTokenList(topic);
15251
+ const uniqueTokens = new Set(tokens);
15252
+ documentCount += 1;
15253
+ totalLength += tokens.length;
15254
+ for (const token of uniqueTokens) {
15255
+ documentFrequencyByToken.set(
15256
+ token,
15257
+ (documentFrequencyByToken.get(token) ?? 0) + 1
15258
+ );
15259
+ }
15260
+ }
15261
+ return {
15262
+ documentCount,
15263
+ averageDocumentLength: documentCount > 0 ? Math.max(1, totalLength / documentCount) : 1,
15264
+ documentFrequencyByToken
15265
+ };
15266
+ }
15267
+ function countTokens(tokens) {
15268
+ const counts = /* @__PURE__ */ new Map();
15269
+ for (const token of tokens) {
15270
+ counts.set(token, (counts.get(token) ?? 0) + 1);
15271
+ }
15272
+ return counts;
15273
+ }
15274
+ function bm25Score(topic, signals, corpus) {
15275
+ if (signals.allTokens.size === 0 || corpus.documentCount === 0) {
15276
+ return 0;
15277
+ }
15278
+ const documentTokens = topicTokenList(topic);
15279
+ if (documentTokens.length === 0) {
15280
+ return 0;
15281
+ }
15282
+ const counts = countTokens(documentTokens);
15283
+ const docLength = documentTokens.length;
15284
+ let score = 0;
15285
+ for (const token of signals.allTokens) {
15286
+ const termFrequency = counts.get(token) ?? 0;
15287
+ if (termFrequency === 0) {
15288
+ continue;
15289
+ }
15290
+ const documentFrequency = corpus.documentFrequencyByToken.get(token) ?? 0;
15291
+ const idf = Math.log(
15292
+ 1 + (corpus.documentCount - documentFrequency + 0.5) / (documentFrequency + 0.5)
15293
+ );
15294
+ const denominator = termFrequency + BM25_K1 * (1 - BM25_B + BM25_B * (docLength / corpus.averageDocumentLength));
15295
+ score += idf * (termFrequency * (BM25_K1 + 1) / denominator);
15296
+ }
15297
+ return score / (score + BM25_NORMALIZATION);
15298
+ }
15060
15299
  function scoreTopicMatch(topic, input) {
15300
+ return scoreTopicMatchWithCorpus(
15301
+ topic,
15302
+ input,
15303
+ buildTopicScoreCorpus([topic])
15304
+ );
15305
+ }
15306
+ function scoreTopicMatchWithCorpus(topic, input, corpus) {
15061
15307
  const signals = buildSignalBag(input);
15062
15308
  const topicTokens = topicTokenSet(topic);
15063
15309
  if (signals.allTokens.size === 0 || topicTokens.size === 0) {
@@ -15071,6 +15317,7 @@ function scoreTopicMatch(topic, input) {
15071
15317
  }
15072
15318
  const union = (/* @__PURE__ */ new Set([...signals.allTokens, ...topicTokens])).size;
15073
15319
  const jaccard = union > 0 ? intersection / union : 0;
15320
+ const bm25 = bm25Score(topic, signals, corpus);
15074
15321
  const topicSlug = topicNameSlug(topic.name);
15075
15322
  let boost = 0;
15076
15323
  for (const token of signals.tagTokens) {
@@ -15091,7 +15338,7 @@ function scoreTopicMatch(topic, input) {
15091
15338
  break;
15092
15339
  }
15093
15340
  }
15094
- return Math.max(0, Math.min(1, jaccard + boost));
15341
+ return Math.max(0, Math.min(1, bm25 * 0.55 + jaccard * 0.45 + boost));
15095
15342
  }
15096
15343
  function normalizeTopicCandidate(value) {
15097
15344
  const record = asRecord2(value);
@@ -15113,26 +15360,21 @@ function normalizeTopicCandidate(value) {
15113
15360
  globalId: readString2(record.globalId)
15114
15361
  };
15115
15362
  }
15116
- function topicMatchesScope(topic, tenantId, workspaceId) {
15117
- if (topic.status === "archived") {
15118
- return false;
15119
- }
15120
- if (tenantId && topic.tenantId && topic.tenantId !== tenantId) {
15121
- return false;
15122
- }
15123
- if (workspaceId && topic.workspaceId && topic.workspaceId !== workspaceId) {
15124
- return false;
15125
- }
15126
- return true;
15127
- }
15128
15363
  async function fetchTopicById(ctx, topicId) {
15129
15364
  const normalizedTopicId = normalizeTopicId(topicId);
15130
15365
  if (!normalizedTopicId) {
15131
15366
  return null;
15132
15367
  }
15133
- const topic = await getConvex(ctx).query(api.topics.get, {
15134
- id: normalizedTopicId
15135
- }).catch(() => null);
15368
+ let topic = null;
15369
+ try {
15370
+ topic = await getConvex(ctx).query(api.topics.get, {
15371
+ id: normalizedTopicId
15372
+ });
15373
+ } catch (error) {
15374
+ debugServerCoreFallback("topic-resolver", `fetchTopicById: topics.get(${normalizedTopicId})`, {
15375
+ error: formatUnknownError(error)
15376
+ });
15377
+ }
15136
15378
  return normalizeTopicCandidate(topic);
15137
15379
  }
15138
15380
  function readRecordTopicId(value) {
@@ -15142,7 +15384,14 @@ function readRecordTopicId(value) {
15142
15384
  );
15143
15385
  }
15144
15386
  async function fetchRecordTopicId(ctx, reference, args) {
15145
- const result = await getConvex(ctx).query(reference, args).catch(() => null);
15387
+ let result = null;
15388
+ try {
15389
+ result = await getConvex(ctx).query(reference, args);
15390
+ } catch (error) {
15391
+ debugServerCoreFallback("topic-resolver", "fetchRecordTopicId", {
15392
+ error: formatUnknownError(error)
15393
+ });
15394
+ }
15146
15395
  return readRecordTopicId(result);
15147
15396
  }
15148
15397
  async function resolveBeliefTopicId(ctx, beliefId) {
@@ -15185,12 +15434,9 @@ async function resolveTopicIdFromEvidenceTarget(ctx, targetId, targetType) {
15185
15434
  if (targetType === "belief") {
15186
15435
  return resolveBeliefTopicId(ctx, normalizedTargetId);
15187
15436
  }
15188
- try {
15189
- const decoded = decodePrefixedId(normalizedTargetId);
15190
- if (decoded.prefix === "que") {
15191
- return resolveQuestionTopicId(ctx, normalizedTargetId);
15192
- }
15193
- } catch {
15437
+ const decoded = decodePrefixedIdOrNull(normalizedTargetId);
15438
+ if (decoded?.prefix === "que") {
15439
+ return resolveQuestionTopicId(ctx, normalizedTargetId);
15194
15440
  }
15195
15441
  return resolveBeliefTopicId(ctx, normalizedTargetId);
15196
15442
  }
@@ -15202,7 +15448,9 @@ async function resolveCommonBeliefTopicId(ctx, beliefIds) {
15202
15448
  return void 0;
15203
15449
  }
15204
15450
  const resolvedTopicIds = (await Promise.all(
15205
- normalizedBeliefIds.map((beliefId) => resolveBeliefTopicId(ctx, beliefId))
15451
+ normalizedBeliefIds.map(
15452
+ (beliefId) => resolveBeliefTopicId(ctx, beliefId)
15453
+ )
15206
15454
  )).filter((topicId) => Boolean(topicId));
15207
15455
  if (resolvedTopicIds.length !== normalizedBeliefIds.length) {
15208
15456
  return void 0;
@@ -15211,17 +15459,31 @@ async function resolveCommonBeliefTopicId(ctx, beliefIds) {
15211
15459
  }
15212
15460
  async function fetchTopicTree(ctx) {
15213
15461
  const convex = getConvex(ctx);
15214
- const tree = await convex.query(api.topics.getTree, {
15215
- rootId: ROOT_TOPIC_ID,
15216
- maxDepth: TREE_MAX_DEPTH
15217
- }).catch(() => null);
15462
+ let tree = null;
15463
+ try {
15464
+ tree = await convex.query(api.topics.getTree, {
15465
+ rootId: ROOT_TOPIC_ID,
15466
+ maxDepth: TREE_MAX_DEPTH
15467
+ });
15468
+ } catch (error) {
15469
+ debugServerCoreFallback("topic-resolver", "fetchTopicTree: topics.getTree", {
15470
+ error: formatUnknownError(error)
15471
+ });
15472
+ }
15218
15473
  const normalizedTree = Array.isArray(tree) ? tree.map((topic) => normalizeTopicCandidate(topic)).filter((topic) => Boolean(topic)) : [];
15219
15474
  if (normalizedTree.length > 0) {
15220
15475
  return normalizedTree;
15221
15476
  }
15222
- const listed = await convex.query(api.topics.list, {
15223
- status: "active"
15224
- }).catch(() => []);
15477
+ let listed = [];
15478
+ try {
15479
+ listed = await convex.query(api.topics.list, {
15480
+ status: "active"
15481
+ });
15482
+ } catch (error) {
15483
+ debugServerCoreFallback("topic-resolver", "fetchTopicTree: topics.list fallback", {
15484
+ error: formatUnknownError(error)
15485
+ });
15486
+ }
15225
15487
  return Array.isArray(listed) ? listed.map((topic) => normalizeTopicCandidate(topic)).filter((topic) => Boolean(topic)) : [];
15226
15488
  }
15227
15489
  function buildTopicMaps(topics2) {
@@ -15254,42 +15516,43 @@ function buildPath(topicId, byId) {
15254
15516
  function titleCase(value) {
15255
15517
  return value.replace(/\b[a-z]/g, (match) => match.toUpperCase());
15256
15518
  }
15257
- function compactWhitespace(value) {
15258
- return value.replace(/\s+/g, " ").trim();
15259
- }
15260
15519
  function deriveTopicName(input, parentTopic) {
15261
15520
  const textualHint = readString2(input.topicHint);
15262
15521
  if (textualHint && tokenize(textualHint).length > 0) {
15263
- return titleCase(compactWhitespace(textualHint)).slice(0, 60);
15522
+ return titleCase(textualHint.replace(/\s+/g, " ").trim()).slice(0, 60);
15264
15523
  }
15265
15524
  const firstTag = (input.tags ?? []).find((tag) => tokenize(tag).length > 0);
15266
15525
  if (firstTag) {
15267
- return titleCase(compactWhitespace(firstTag)).slice(0, 60);
15526
+ return titleCase(firstTag.replace(/\s+/g, " ").trim()).slice(0, 60);
15268
15527
  }
15269
15528
  const firstPathStem = (input.touchedPaths ?? []).map((filePath) => basenameStem(filePath)).find((stem) => stem && tokenize(stem).length > 0);
15270
15529
  if (firstPathStem) {
15271
- return titleCase(compactWhitespace(firstPathStem.replace(/[-_]+/g, " "))).slice(0, 60);
15530
+ return titleCase(
15531
+ firstPathStem.replace(/[-_]+/g, " ").replace(/\s+/g, " ").trim()
15532
+ ).slice(0, 60);
15272
15533
  }
15273
- const summary = compactWhitespace(input.summary);
15534
+ const summary = input.summary.replace(/\s+/g, " ").trim();
15274
15535
  if (summary.length > 0) {
15275
15536
  return titleCase(summary.slice(0, 60));
15276
15537
  }
15277
15538
  const sourceRef = readString2(input.sourceRef);
15278
15539
  if (sourceRef) {
15279
- return titleCase(compactWhitespace(sourceRef.replace(/[-_:/]+/g, " "))).slice(0, 60);
15540
+ return titleCase(
15541
+ sourceRef.replace(/[-_:/]+/g, " ").replace(/\s+/g, " ").trim()
15542
+ ).slice(0, 60);
15280
15543
  }
15281
- return `${titleCase(parentTopic.name)} Subtopic`;
15544
+ return parentTopic ? `${titleCase(parentTopic.name)} Subtopic` : "Lucern";
15282
15545
  }
15283
- async function createChildTopic(ctx, parentTopic, input) {
15546
+ async function createTopic(ctx, input, parentTopic) {
15284
15547
  const convex = getConvex(ctx);
15285
15548
  const desiredTenantId = ctx.tenantId;
15286
15549
  const desiredWorkspaceId = readString2(input.workspaceId) ?? ctx.workspaceId;
15287
15550
  const createdBy = ctx.principalId ?? ctx.userId;
15288
15551
  const created = await convex.mutation(api.topics.create, {
15289
15552
  name: deriveTopicName(input, parentTopic),
15290
- description: compactWhitespace(input.summary).slice(0, 280) || void 0,
15553
+ description: input.summary.replace(/\s+/g, " ").trim().slice(0, 280) || void 0,
15291
15554
  type: "theme",
15292
- parentTopicId: parentTopic.rawId,
15555
+ ...parentTopic ? { parentTopicId: parentTopic.rawId } : {},
15293
15556
  tenantId: desiredTenantId,
15294
15557
  workspaceId: desiredWorkspaceId,
15295
15558
  visibility: "team",
@@ -15306,10 +15569,18 @@ async function createChildTopic(ctx, parentTopic, input) {
15306
15569
  const createdId = readString2(createdRecord.id) ?? readString2(createdRecord.topicId) ?? readString2(createdRecord._id);
15307
15570
  const hydrated = await fetchTopicById(ctx, createdId);
15308
15571
  if (!hydrated) {
15309
- throw new Error("[topic-resolver] Auto-created topic could not be reloaded.");
15572
+ throw new Error(
15573
+ "[topic-resolver] Auto-created topic could not be reloaded."
15574
+ );
15310
15575
  }
15311
15576
  return hydrated;
15312
15577
  }
15578
+ function createChildTopic(ctx, parentTopic, input) {
15579
+ return createTopic(ctx, input, parentTopic);
15580
+ }
15581
+ function createRootTopic(ctx, input) {
15582
+ return createTopic(ctx, input);
15583
+ }
15313
15584
  function pushTrace(trace, topic, score) {
15314
15585
  const existing = trace[trace.length - 1];
15315
15586
  if (existing?.topicId === topic.rawId) {
@@ -15324,9 +15595,10 @@ function pushTrace(trace, topic, score) {
15324
15595
  });
15325
15596
  }
15326
15597
  function pickBestTopic(topics2, input) {
15598
+ const corpus = buildTopicScoreCorpus(topics2);
15327
15599
  const ranked = topics2.filter((topic) => topic.status !== "archived").map((topic) => ({
15328
15600
  topic,
15329
- score: scoreTopicMatch(topic, input)
15601
+ score: scoreTopicMatchWithCorpus(topic, input, corpus)
15330
15602
  })).sort((left, right) => {
15331
15603
  if (right.score !== left.score) {
15332
15604
  return right.score - left.score;
@@ -15342,27 +15614,31 @@ function pickFallbackRootTopic(topics2, input) {
15342
15614
  return pickBestTopic(parentless, input) ?? pickBestTopic(topics2, input);
15343
15615
  }
15344
15616
  async function loadTopicUniverse(ctx, input) {
15345
- const desiredTenantId = ctx.tenantId;
15346
- const desiredWorkspaceId = readString2(input.workspaceId) ?? ctx.workspaceId;
15347
15617
  const rawTopics = await fetchTopicTree(ctx);
15348
- const scopedTopics = rawTopics.filter(
15349
- (topic) => topicMatchesScope(topic, desiredTenantId, desiredWorkspaceId)
15350
- );
15351
- const root = rawTopics.find((topic) => topic.rawId === ROOT_TOPIC_ID) ?? await fetchTopicById(ctx, ROOT_TOPIC_ID) ?? pickFallbackRootTopic(scopedTopics, input) ?? null;
15618
+ let candidatePool = rawTopics;
15619
+ let root = pickFallbackRootTopic(candidatePool, input) ?? candidatePool.find((topic) => !topic.parentTopicId) ?? candidatePool[0] ?? null;
15620
+ let rootCreated = false;
15352
15621
  if (!root) {
15353
- throw new Error("[topic-resolver] Root topic not found.");
15622
+ if (input.autoCreate === false) {
15623
+ throw new Error(
15624
+ "[topic-resolver] No topics available for resolution."
15625
+ );
15626
+ }
15627
+ root = await createRootTopic(ctx, input);
15628
+ rootCreated = true;
15629
+ candidatePool = [root];
15354
15630
  }
15355
- const topics2 = rawTopics.filter(
15356
- (topic) => topic.rawId === root.rawId || topicMatchesScope(topic, desiredTenantId, desiredWorkspaceId)
15357
- );
15358
15631
  const dedupedTopics = /* @__PURE__ */ new Map();
15359
15632
  dedupedTopics.set(root.rawId, root);
15360
- for (const topic of topics2) {
15633
+ for (const topic of candidatePool) {
15361
15634
  dedupedTopics.set(topic.rawId, topic);
15362
15635
  }
15363
- const { byId, childrenByParent } = buildTopicMaps([...dedupedTopics.values()]);
15636
+ const { byId, childrenByParent } = buildTopicMaps([
15637
+ ...dedupedTopics.values()
15638
+ ]);
15364
15639
  return {
15365
15640
  root,
15641
+ rootCreated,
15366
15642
  byId,
15367
15643
  childrenByParent
15368
15644
  };
@@ -15373,7 +15649,9 @@ async function validateTopicOrNull(ctx, topicId) {
15373
15649
  return null;
15374
15650
  }
15375
15651
  const { byId } = await loadTopicUniverse(ctx, {
15376
- summary: topic.name});
15652
+ summary: topic.name,
15653
+ autoCreate: false
15654
+ });
15377
15655
  if (!byId.has(topic.rawId)) {
15378
15656
  byId.set(topic.rawId, topic);
15379
15657
  }
@@ -15395,8 +15673,10 @@ async function resolveTopicForWrite(ctx, input) {
15395
15673
  const threshold = typeof input.threshold === "number" && Number.isFinite(input.threshold) ? Math.max(0, Math.min(1, input.threshold)) : DEFAULT_THRESHOLD;
15396
15674
  const createThreshold = Math.max(0.5, Math.min(1, threshold + 0.15));
15397
15675
  const autoCreate = input.autoCreate !== false;
15398
- const { root, byId, childrenByParent } = await loadTopicUniverse(ctx, input);
15676
+ const { root, rootCreated, byId, childrenByParent } = await loadTopicUniverse(ctx, input);
15399
15677
  const trace = [];
15678
+ const scoreCorpus = buildTopicScoreCorpus(byId.values());
15679
+ const scoreTopic = (topic) => scoreTopicMatchWithCorpus(topic, input, scoreCorpus);
15400
15680
  const ownScore = /* @__PURE__ */ new Map();
15401
15681
  const subtreeScore = /* @__PURE__ */ new Map();
15402
15682
  const computeSubtreeScore = (topicId, visited) => {
@@ -15413,7 +15693,7 @@ async function resolveTopicForWrite(ctx, input) {
15413
15693
  return 0;
15414
15694
  }
15415
15695
  if (!ownScore.has(topicId)) {
15416
- ownScore.set(topicId, scoreTopicMatch(topic, input));
15696
+ ownScore.set(topicId, scoreTopic(topic));
15417
15697
  }
15418
15698
  let best = ownScore.get(topicId);
15419
15699
  for (const child of childrenByParent.get(topicId) ?? []) {
@@ -15425,7 +15705,7 @@ async function resolveTopicForWrite(ctx, input) {
15425
15705
  };
15426
15706
  computeSubtreeScore(root.rawId, /* @__PURE__ */ new Set());
15427
15707
  let current = root;
15428
- let currentScore = ownScore.get(root.rawId) ?? scoreTopicMatch(root, input);
15708
+ let currentScore = ownScore.get(root.rawId) ?? scoreTopic(root);
15429
15709
  pushTrace(trace, current, currentScore);
15430
15710
  while (true) {
15431
15711
  const children = (childrenByParent.get(current.rawId) ?? []).filter(
@@ -15436,7 +15716,7 @@ async function resolveTopicForWrite(ctx, input) {
15436
15716
  }
15437
15717
  const rankedChildren = children.map((topic) => {
15438
15718
  if (!ownScore.has(topic.rawId)) {
15439
- ownScore.set(topic.rawId, scoreTopicMatch(topic, input));
15719
+ ownScore.set(topic.rawId, scoreTopic(topic));
15440
15720
  }
15441
15721
  return {
15442
15722
  topic,
@@ -15448,13 +15728,13 @@ async function resolveTopicForWrite(ctx, input) {
15448
15728
  break;
15449
15729
  }
15450
15730
  current = bestChild.topic;
15451
- currentScore = ownScore.get(current.rawId) ?? scoreTopicMatch(current, input);
15731
+ currentScore = ownScore.get(current.rawId) ?? scoreTopic(current);
15452
15732
  pushTrace(trace, current, currentScore);
15453
15733
  }
15454
- let created = false;
15734
+ let created = rootCreated;
15455
15735
  let resolvedTopic = current;
15456
15736
  let finalScore = currentScore;
15457
- if (autoCreate && finalScore < createThreshold) {
15737
+ if (autoCreate && !rootCreated && finalScore < createThreshold) {
15458
15738
  const siblings = childrenByParent.get(current.rawId) ?? [];
15459
15739
  const derivedName = deriveTopicName(input, current).toLowerCase();
15460
15740
  const existingSibling = siblings.find(
@@ -15462,7 +15742,7 @@ async function resolveTopicForWrite(ctx, input) {
15462
15742
  );
15463
15743
  if (existingSibling) {
15464
15744
  resolvedTopic = existingSibling;
15465
- finalScore = scoreTopicMatch(existingSibling, input);
15745
+ finalScore = scoreTopic(existingSibling);
15466
15746
  pushTrace(trace, resolvedTopic, finalScore);
15467
15747
  } else {
15468
15748
  resolvedTopic = await createChildTopic(ctx, current, input);
@@ -15486,6 +15766,23 @@ async function resolveTopicForWrite(ctx, input) {
15486
15766
  trace
15487
15767
  };
15488
15768
  }
15769
+ async function resolveTopicIdForWrite(ctx, input) {
15770
+ const explicitTopicId = normalizeTopicId(input.topicId);
15771
+ const explicit = await validateTopicOrNull(ctx, explicitTopicId);
15772
+ if (explicit) {
15773
+ return explicit;
15774
+ }
15775
+ if (explicitTopicId) {
15776
+ console.warn(
15777
+ `[topic-resolver] Explicit topicId "${explicitTopicId}" is invalid or stale. Falling back to hop-by-hop topic resolution.`
15778
+ );
15779
+ }
15780
+ return resolveTopicForWrite(ctx, {
15781
+ ...input,
15782
+ topicHint: input.topicHint ?? explicitTopicId,
15783
+ autoCreate: explicitTopicId ? false : input.autoCreate
15784
+ });
15785
+ }
15489
15786
 
15490
15787
  // ../server-core/src/beliefs.ts
15491
15788
  function resolveBeliefNodeId(value) {
@@ -16454,39 +16751,47 @@ function resolveExternalId2(value) {
16454
16751
  if (!value) {
16455
16752
  return value;
16456
16753
  }
16754
+ return resolveNodeId(value);
16755
+ }
16756
+ function resolveNodeId(value) {
16457
16757
  try {
16458
16758
  return decodePrefixedId(value).value;
16459
- } catch {
16759
+ } catch (_error) {
16460
16760
  return value;
16461
16761
  }
16462
16762
  }
16763
+ async function queryWithFallback(label, run, fallback) {
16764
+ try {
16765
+ return await run();
16766
+ } catch (error) {
16767
+ debugServerCoreFallback("edges", label, {
16768
+ error: formatUnknownError(error)
16769
+ });
16770
+ return fallback;
16771
+ }
16772
+ }
16463
16773
  async function fetchNodeWithQuery(runQuery, id) {
16464
16774
  if (!id.trim()) {
16465
16775
  return null;
16466
16776
  }
16467
- const rawId = (() => {
16468
- try {
16469
- return decodePrefixedId(id).value;
16470
- } catch {
16471
- return id;
16472
- }
16473
- })();
16474
- try {
16475
- const byNodeId = await runQuery(api.epistemicNodes.get, {
16777
+ const rawId = resolveNodeId(id);
16778
+ const byNodeId = await queryWithFallback(
16779
+ `fetchNodeWithQuery local id lookup for ${id}`,
16780
+ () => runQuery(api.epistemicNodes.get, {
16476
16781
  nodeId: rawId
16477
- });
16478
- if (byNodeId) {
16479
- return byNodeId;
16480
- }
16481
- } catch {
16782
+ }),
16783
+ null
16784
+ );
16785
+ if (byNodeId) {
16786
+ return byNodeId;
16482
16787
  }
16483
- try {
16484
- return await runQuery(api.epistemicNodes.getByGlobalId, {
16788
+ return await queryWithFallback(
16789
+ `fetchNodeWithQuery global id lookup for ${id}`,
16790
+ () => runQuery(api.epistemicNodes.getByGlobalId, {
16485
16791
  globalId: id
16486
- });
16487
- } catch {
16488
- return null;
16489
- }
16792
+ }),
16793
+ null
16794
+ );
16490
16795
  }
16491
16796
  function resolveTopicIdFromNode(node) {
16492
16797
  const record = asRecord6(node);
@@ -16520,7 +16825,9 @@ async function ensureTopicAccessWithQuery2(runQuery, topicId, userId, contextLab
16520
16825
  userId
16521
16826
  });
16522
16827
  if (allowed !== true) {
16523
- throw new Error(`[edges] ${contextLabel} requires access to topic ${topicId}.`);
16828
+ throw new Error(
16829
+ `[edges] ${contextLabel} requires access to topic ${topicId}.`
16830
+ );
16524
16831
  }
16525
16832
  }
16526
16833
  async function executeTraversal(runQuery, runAction, userId, input) {
@@ -16591,10 +16898,13 @@ function createGatewayEdgesPort(authContext) {
16591
16898
  authContext.userId,
16592
16899
  "list"
16593
16900
  );
16594
- const rows = await authContext.convex.query(api.epistemicEdges.getBySourceNode, {
16595
- sourceNodeId: args.sourceRawId,
16596
- edgeType: args.edgeType
16597
- });
16901
+ const rows = await authContext.convex.query(
16902
+ api.epistemicEdges.getBySourceNode,
16903
+ {
16904
+ sourceNodeId: args.sourceRawId,
16905
+ edgeType: args.edgeType
16906
+ }
16907
+ );
16598
16908
  return Array.isArray(rows) ? rows.slice(0, args.limit ?? rows.length) : [];
16599
16909
  },
16600
16910
  traverseEdges(input) {
@@ -17357,17 +17667,11 @@ function normalizeGlobalIds(input) {
17357
17667
  }
17358
17668
  return [...ids];
17359
17669
  }
17360
- function normalizeGraphNode(value) {
17361
- return asRecord8(value);
17362
- }
17363
- function normalizeGraphEdge(value) {
17364
- return asRecord8(value);
17365
- }
17366
17670
  function normalizeNeighborhoodResult(value, maxDepth) {
17367
17671
  const payload = asRecord8(value);
17368
17672
  return {
17369
- nodes: asRecordArray(payload.nodes).map(normalizeGraphNode),
17370
- edges: asRecordArray(payload.edges).map(normalizeGraphEdge),
17673
+ nodes: asRecordArray(payload.nodes).map(asRecord8),
17674
+ edges: asRecordArray(payload.edges).map(asRecord8),
17371
17675
  depth: normalizeDepth(payload.depth, maxDepth)
17372
17676
  };
17373
17677
  }
@@ -17377,8 +17681,8 @@ function normalizeTraverseResult(value, input) {
17377
17681
  startNode: readString9(payload.startNode) ?? input.startNode,
17378
17682
  direction: normalizeDirection2(payload.direction ?? input.direction),
17379
17683
  maxDepth: normalizeDepth(payload.maxDepth, input.maxDepth),
17380
- nodes: asRecordArray(payload.nodes).map(normalizeGraphNode),
17381
- edges: asRecordArray(payload.edges).map(normalizeGraphEdge),
17684
+ nodes: asRecordArray(payload.nodes).map(asRecord8),
17685
+ edges: asRecordArray(payload.edges).map(asRecord8),
17382
17686
  depth: normalizeDepth(payload.depth, input.maxDepth),
17383
17687
  ...readString9(payload.topicId) ?? readString9(input.topicId) ? { topicId: readString9(payload.topicId) ?? readString9(input.topicId) } : {}
17384
17688
  };
@@ -17835,6 +18139,20 @@ async function answerQuestion(deps, input) {
17835
18139
  }
17836
18140
 
17837
18141
  // ../server-core/src/questions.ts
18142
+ function normalizePrefixedId(value, prefix) {
18143
+ if (!value) {
18144
+ return value;
18145
+ }
18146
+ try {
18147
+ const decoded = decodePrefixedId(value);
18148
+ if (prefix && decoded.prefix !== prefix) {
18149
+ return value;
18150
+ }
18151
+ return decoded.value;
18152
+ } catch (_error) {
18153
+ return value;
18154
+ }
18155
+ }
17838
18156
  function normalizeAnswerConfidence2(value) {
17839
18157
  if (value === "medium") {
17840
18158
  return "moderate";
@@ -17855,26 +18173,18 @@ function readStringArray5(value) {
17855
18173
  const items = value.map((entry) => readString11(entry)).filter((entry) => Boolean(entry));
17856
18174
  return items.length > 0 ? items : void 0;
17857
18175
  }
17858
- function resolveExternalId4(value, prefix) {
17859
- if (!value) {
17860
- return value;
17861
- }
17862
- try {
17863
- const decoded = decodePrefixedId(value);
17864
- if (prefix && decoded.prefix !== prefix) {
17865
- return value;
17866
- }
17867
- return decoded.value;
17868
- } catch {
17869
- return value;
17870
- }
18176
+ function isRecord4(value) {
18177
+ return value !== null && typeof value === "object" && !Array.isArray(value);
17871
18178
  }
17872
18179
  async function resolveQuestionTopicIdWithGatewayAuth(authContext, questionId) {
17873
- const question = await authContext.convex.query(api.epistemicQuestions.getById, {
17874
- questionId,
17875
- nodeId: questionId
17876
- });
17877
- return readString11(question?.topicId);
18180
+ const question = await authContext.convex.query(
18181
+ api.epistemicQuestions.getById,
18182
+ {
18183
+ questionId,
18184
+ nodeId: questionId
18185
+ }
18186
+ );
18187
+ return isRecord4(question) ? readString11(question.topicId) : void 0;
17878
18188
  }
17879
18189
  async function emitQuestionEventFromGatewayAuth(authContext, args) {
17880
18190
  return emitDomainEvent(
@@ -17906,8 +18216,8 @@ function normalizeLatestAnswerRecord(questionId, answer) {
17906
18216
  questionNodeId: questionId
17907
18217
  };
17908
18218
  }
17909
- const record = answer;
17910
- const metadata = record.metadata && typeof record.metadata === "object" && !Array.isArray(record.metadata) ? record.metadata : {};
18219
+ const record = isRecord4(answer) ? answer : {};
18220
+ const metadata = isRecord4(record.metadata) ? record.metadata : {};
17911
18221
  return {
17912
18222
  nodeId: typeof record._id === "string" ? record._id : null,
17913
18223
  globalId: typeof record.globalId === "string" ? record.globalId : null,
@@ -17946,10 +18256,13 @@ function createGatewayQuestionsPort(authContext) {
17946
18256
  });
17947
18257
  },
17948
18258
  refineQuestion(input) {
17949
- return authContext.convex.mutation(api.epistemicQuestions.updateQuestion, {
17950
- questionId: input.questionRawId,
17951
- question: input.text
17952
- });
18259
+ return authContext.convex.mutation(
18260
+ api.epistemicQuestions.updateQuestion,
18261
+ {
18262
+ questionId: input.questionRawId,
18263
+ question: input.text
18264
+ }
18265
+ );
17953
18266
  },
17954
18267
  updateQuestionStatus(input) {
17955
18268
  return authContext.convex.mutation(api.epistemicQuestions.updateStatus, {
@@ -17992,10 +18305,13 @@ async function createQuestionFromGatewayAuth(authContext, input) {
17992
18305
  sourceKind: "question",
17993
18306
  autoCreate: input.topicId ? false : true
17994
18307
  });
17995
- const payload = await createQuestion(createGatewayQuestionsPort(authContext), {
17996
- ...input,
17997
- topicId: resolvedTopic.topicId
17998
- });
18308
+ const payload = await createQuestion(
18309
+ createGatewayQuestionsPort(authContext),
18310
+ {
18311
+ ...input,
18312
+ topicId: resolvedTopic.topicId
18313
+ }
18314
+ );
17999
18315
  await emitQuestionEventFromGatewayAuth(authContext, {
18000
18316
  topicId: payload.topicId ?? resolvedTopic.topicId,
18001
18317
  type: "question.created",
@@ -18017,9 +18333,15 @@ function listQuestionsFromGatewayAuth(authContext, query) {
18017
18333
  return listQuestions(createGatewayQuestionsPort(authContext), query);
18018
18334
  }
18019
18335
  async function answerQuestionFromGatewayAuth(authContext, input) {
18020
- const payload = await answerQuestion(createGatewayQuestionsPort(authContext), input);
18336
+ const payload = await answerQuestion(
18337
+ createGatewayQuestionsPort(authContext),
18338
+ input
18339
+ );
18021
18340
  await emitQuestionEventFromGatewayAuth(authContext, {
18022
- topicId: await resolveQuestionTopicIdWithGatewayAuth(authContext, payload.questionId) ?? "",
18341
+ topicId: await resolveQuestionTopicIdWithGatewayAuth(
18342
+ authContext,
18343
+ payload.questionId
18344
+ ) ?? "",
18023
18345
  type: "question.answered",
18024
18346
  resourceId: payload.questionId,
18025
18347
  data: {
@@ -18043,13 +18365,16 @@ async function getQuestionAnswerFromGatewayAuth(authContext, questionId) {
18043
18365
  return normalizeLatestAnswerRecord(questionId, answer);
18044
18366
  }
18045
18367
  async function refineQuestionFromGatewayAuth(authContext, input) {
18046
- const payload = await refineQuestion(createGatewayQuestionsPort(authContext), input);
18368
+ const payload = await refineQuestion(
18369
+ createGatewayQuestionsPort(authContext),
18370
+ input
18371
+ );
18047
18372
  await emitQuestionEventFromGatewayAuth(authContext, {
18048
18373
  topicId: payload.topicId ?? "",
18049
18374
  type: "question.refined",
18050
18375
  resourceId: payload.id,
18051
18376
  data: {
18052
- previousText: typeof payload.previousText === "string" ? payload.previousText : void 0,
18377
+ previousText: isRecord4(payload) && typeof payload.previousText === "string" ? payload.previousText : void 0,
18053
18378
  text: payload.text,
18054
18379
  rationale: input.rationale
18055
18380
  }
@@ -18057,7 +18382,10 @@ async function refineQuestionFromGatewayAuth(authContext, input) {
18057
18382
  return payload;
18058
18383
  }
18059
18384
  async function archiveQuestionFromGatewayAuth(authContext, input) {
18060
- const payload = await archiveQuestion(createGatewayQuestionsPort(authContext), input);
18385
+ const payload = await archiveQuestion(
18386
+ createGatewayQuestionsPort(authContext),
18387
+ input
18388
+ );
18061
18389
  await emitQuestionEventFromGatewayAuth(authContext, {
18062
18390
  topicId: payload.topicId ?? "",
18063
18391
  type: "question.archived",
@@ -18080,15 +18408,17 @@ async function updateQuestionStatusFromGatewayAuth(authContext, input) {
18080
18408
  type: "question.status_updated",
18081
18409
  resourceId: payload.id,
18082
18410
  data: {
18083
- previousStatus: typeof payload.previousStatus === "string" ? payload.previousStatus : void 0,
18084
- newStatus: typeof payload.newStatus === "string" ? payload.newStatus : payload.status,
18411
+ previousStatus: isRecord4(payload) && typeof payload.previousStatus === "string" ? payload.previousStatus : void 0,
18412
+ newStatus: isRecord4(payload) && typeof payload.newStatus === "string" ? payload.newStatus : payload.status,
18085
18413
  rationale: input.rationale
18086
18414
  }
18087
18415
  });
18088
18416
  return payload;
18089
18417
  }
18090
18418
  function createQuestionsBatchFromGatewayAuth(authContext, input) {
18091
- const linkedBeliefIds = input.questions.map((question) => question.linkedBeliefNodeId).filter((questionId) => Boolean(readString11(questionId)));
18419
+ const linkedBeliefIds = input.questions.map((question) => question.linkedBeliefNodeId).filter(
18420
+ (questionId) => Boolean(readString11(questionId))
18421
+ );
18092
18422
  const linkedWorktreeIds = [
18093
18423
  ...new Set(
18094
18424
  input.questions.map((question) => question.linkedWorktreeId).filter(
@@ -18120,22 +18450,19 @@ function createQuestionsBatchFromGatewayAuth(authContext, input) {
18120
18450
  sourceKind: "question_batch",
18121
18451
  autoCreate: input.topicId ? false : true
18122
18452
  })).topicId;
18123
- return authContext.convex.mutation(
18124
- api.epistemicQuestions.createBatch,
18125
- {
18126
- topicId: resolvedTopicId,
18127
- questions: input.questions.map((question) => ({
18128
- ...question,
18129
- linkedBeliefNodeId: resolveExternalId4(
18130
- question.linkedBeliefNodeId,
18131
- "bel"
18132
- ),
18133
- linkedWorktreeId: resolveExternalId4(question.linkedWorktreeId, "wt")
18134
- })),
18135
- source: input.source,
18136
- userId: authContext.userId
18137
- }
18138
- );
18453
+ return authContext.convex.mutation(api.epistemicQuestions.createBatch, {
18454
+ topicId: resolvedTopicId,
18455
+ questions: input.questions.map((question) => ({
18456
+ ...question,
18457
+ linkedBeliefNodeId: normalizePrefixedId(
18458
+ question.linkedBeliefNodeId,
18459
+ "bel"
18460
+ ),
18461
+ linkedWorktreeId: normalizePrefixedId(question.linkedWorktreeId, "wt")
18462
+ })),
18463
+ source: input.source,
18464
+ userId: authContext.userId
18465
+ });
18139
18466
  })();
18140
18467
  }
18141
18468
  async function addQuestionFromGatewayAuth(authContext, input) {
@@ -18159,26 +18486,35 @@ async function addQuestionFromGatewayAuth(authContext, input) {
18159
18486
  sourceKind: input.questionType ?? "question",
18160
18487
  autoCreate: input.topicId ? false : true
18161
18488
  })).topicId;
18162
- const questionId = await authContext.convex.mutation(api.epistemicQuestions.addQuestion, {
18163
- topicId: resolvedTopicId,
18164
- question: input.question ?? input.text ?? "",
18165
- category: input.category,
18166
- priority: input.priority,
18167
- source: input.source,
18168
- beliefId: resolveExternalId4(input.beliefId ?? input.linkedBeliefId, "bel"),
18169
- linkedWorktreeId: input.linkedWorktreeId,
18170
- chatId: input.chatId,
18171
- importance: input.importance,
18172
- epistemicUnlock: input.epistemicUnlock,
18173
- metadata: input.metadata,
18174
- questionType: input.questionType,
18175
- userId: authContext.userId
18176
- });
18489
+ const questionId = await authContext.convex.mutation(
18490
+ api.epistemicQuestions.addQuestion,
18491
+ {
18492
+ topicId: resolvedTopicId,
18493
+ question: input.question ?? input.text ?? "",
18494
+ category: input.category,
18495
+ priority: input.priority,
18496
+ source: input.source,
18497
+ beliefId: normalizePrefixedId(
18498
+ input.beliefId ?? input.linkedBeliefId,
18499
+ "bel"
18500
+ ),
18501
+ linkedWorktreeId: input.linkedWorktreeId,
18502
+ chatId: input.chatId,
18503
+ importance: input.importance,
18504
+ epistemicUnlock: input.epistemicUnlock,
18505
+ metadata: input.metadata,
18506
+ questionType: input.questionType,
18507
+ userId: authContext.userId
18508
+ }
18509
+ );
18177
18510
  return getQuestionFromGatewayAuth(authContext, String(questionId));
18178
18511
  }
18179
18512
  async function updateQuestionPriorityFromGatewayAuth(authContext, input) {
18180
18513
  await authContext.convex.mutation(api.epistemicQuestions.updatePriority, {
18181
- nodeId: resolveExternalId4(input.id ?? input.nodeId ?? input.questionId, "que") ?? "",
18514
+ nodeId: normalizePrefixedId(
18515
+ input.id ?? input.nodeId ?? input.questionId,
18516
+ "que"
18517
+ ) ?? "",
18182
18518
  priority: input.priority,
18183
18519
  userId: authContext.userId
18184
18520
  });
@@ -18188,15 +18524,24 @@ async function updateQuestionPriorityFromGatewayAuth(authContext, input) {
18188
18524
  );
18189
18525
  }
18190
18526
  function advanceQuestionToConvictionFromGatewayAuth(authContext, input) {
18191
- return authContext.convex.mutation(api.epistemicQuestions.advanceToConviction, {
18192
- questionId: resolveExternalId4(input.questionId ?? input.id ?? input.nodeId, "que") ?? "",
18193
- worktreeId: input.worktreeId,
18194
- userId: authContext.userId
18195
- });
18527
+ return authContext.convex.mutation(
18528
+ api.epistemicQuestions.advanceToConviction,
18529
+ {
18530
+ questionId: normalizePrefixedId(
18531
+ input.questionId ?? input.id ?? input.nodeId,
18532
+ "que"
18533
+ ) ?? "",
18534
+ worktreeId: input.worktreeId,
18535
+ userId: authContext.userId
18536
+ }
18537
+ );
18196
18538
  }
18197
18539
  function updateQuestionConvictionFromGatewayAuth(authContext, input) {
18198
18540
  return authContext.convex.mutation(api.epistemicQuestions.updateConviction, {
18199
- questionId: resolveExternalId4(input.questionId ?? input.id ?? input.nodeId, "que") ?? "",
18541
+ questionId: normalizePrefixedId(
18542
+ input.questionId ?? input.id ?? input.nodeId,
18543
+ "que"
18544
+ ) ?? "",
18200
18545
  conviction: input.conviction,
18201
18546
  answerCompleteness: input.answerCompleteness,
18202
18547
  convictionRationale: input.convictionRationale,
@@ -18204,18 +18549,24 @@ function updateQuestionConvictionFromGatewayAuth(authContext, input) {
18204
18549
  });
18205
18550
  }
18206
18551
  function finalizeQuestionConvictionFromGatewayAuth(authContext, input) {
18207
- return authContext.convex.mutation(api.epistemicQuestions.finalizeConviction, {
18208
- questionId: resolveExternalId4(input.questionId ?? input.id ?? input.nodeId, "que") ?? "",
18209
- conviction: input.conviction,
18210
- answer: input.answer,
18211
- convictionRationale: input.convictionRationale,
18212
- answerCompleteness: input.answerCompleteness,
18213
- whatWeNeed: input.whatWeNeed,
18214
- userId: authContext.userId
18215
- });
18552
+ return authContext.convex.mutation(
18553
+ api.epistemicQuestions.finalizeConviction,
18554
+ {
18555
+ questionId: normalizePrefixedId(
18556
+ input.questionId ?? input.id ?? input.nodeId,
18557
+ "que"
18558
+ ) ?? "",
18559
+ conviction: input.conviction,
18560
+ answer: input.answer,
18561
+ convictionRationale: input.convictionRationale,
18562
+ answerCompleteness: input.answerCompleteness,
18563
+ whatWeNeed: input.whatWeNeed,
18564
+ userId: authContext.userId
18565
+ }
18566
+ );
18216
18567
  }
18217
18568
  async function updateQuestionFromGatewayAuth(authContext, input) {
18218
- const questionId = resolveExternalId4(input.questionId ?? input.id ?? input.nodeId, "que") ?? "";
18569
+ const questionId = normalizePrefixedId(input.questionId ?? input.id ?? input.nodeId, "que") ?? "";
18219
18570
  await authContext.convex.mutation(api.epistemicQuestions.updateQuestion, {
18220
18571
  questionId,
18221
18572
  question: input.question ?? input.text,
@@ -18226,7 +18577,10 @@ async function updateQuestionFromGatewayAuth(authContext, input) {
18226
18577
  }
18227
18578
  function deleteQuestionFromGatewayAuth(authContext, input) {
18228
18579
  return authContext.convex.mutation(api.epistemicQuestions.deleteQuestion, {
18229
- questionId: resolveExternalId4(input.questionId ?? input.id ?? input.nodeId, "que") ?? "",
18580
+ questionId: normalizePrefixedId(
18581
+ input.questionId ?? input.id ?? input.nodeId,
18582
+ "que"
18583
+ ) ?? "",
18230
18584
  userId: authContext.userId
18231
18585
  });
18232
18586
  }
@@ -18267,7 +18621,7 @@ function matchesOptionalString(actual, expected) {
18267
18621
  }
18268
18622
  return readString12(actual) === expected;
18269
18623
  }
18270
- function normalizeGraphNode2(row) {
18624
+ function normalizeGraphNode(row) {
18271
18625
  const record = asRecord10(row);
18272
18626
  const nodeId = readString12(record.nodeId) ?? readString12(record._id);
18273
18627
  const globalId = readString12(record.globalId);
@@ -18279,7 +18633,7 @@ function normalizeGraphNode2(row) {
18279
18633
  ...text ? { text, canonicalText: text } : {}
18280
18634
  };
18281
18635
  }
18282
- function normalizeGraphEdge2(row) {
18636
+ function normalizeGraphEdge(row) {
18283
18637
  const record = asRecord10(row);
18284
18638
  const edgeId = readString12(record.edgeId) ?? readString12(record._id);
18285
18639
  return {
@@ -18294,13 +18648,13 @@ async function fetchGraphNodeByIdentifier(authContext, input) {
18294
18648
  const node = await authContext.convex.query(api.epistemicNodes.get, {
18295
18649
  nodeId: input.nodeId
18296
18650
  });
18297
- return node ? normalizeGraphNode2(node) : null;
18651
+ return node ? normalizeGraphNode(node) : null;
18298
18652
  }
18299
18653
  if (input.globalId) {
18300
18654
  const node = await authContext.convex.query(api.epistemicNodes.getByGlobalId, {
18301
18655
  globalId: input.globalId
18302
18656
  });
18303
- return node ? normalizeGraphNode2(node) : null;
18657
+ return node ? normalizeGraphNode(node) : null;
18304
18658
  }
18305
18659
  return null;
18306
18660
  }
@@ -18538,7 +18892,7 @@ async function listGraphNodesFromGatewayAuth(authContext, input) {
18538
18892
  userId: authContext.userId,
18539
18893
  limit: normalizeLimit(input.limit, 250, 1e3)
18540
18894
  });
18541
- const nodes = asRecordArray2(rows).map(normalizeGraphNode2).filter((node) => matchesOptionalString(node.nodeType, input.nodeType));
18895
+ const nodes = asRecordArray2(rows).map(normalizeGraphNode).filter((node) => matchesOptionalString(node.nodeType, input.nodeType));
18542
18896
  return { items: nodes, nodes, total: nodes.length };
18543
18897
  }
18544
18898
  async function listGraphEdgesFromGatewayAuth(authContext, input) {
@@ -18551,7 +18905,7 @@ async function listGraphEdgesFromGatewayAuth(authContext, input) {
18551
18905
  userId: authContext.userId,
18552
18906
  limit: normalizeLimit(input.limit, 500, 2e3)
18553
18907
  });
18554
- const edges = asRecordArray2(rows).map(normalizeGraphEdge2).filter((edge) => matchesOptionalString(edge.edgeId, input.edgeId)).filter((edge) => matchesOptionalString(edge.fromNodeId, input.fromNodeId)).filter((edge) => matchesOptionalString(edge.toNodeId, input.toNodeId)).filter((edge) => matchesOptionalString(edge.edgeType, input.edgeType));
18908
+ const edges = asRecordArray2(rows).map(normalizeGraphEdge).filter((edge) => matchesOptionalString(edge.edgeId, input.edgeId)).filter((edge) => matchesOptionalString(edge.fromNodeId, input.fromNodeId)).filter((edge) => matchesOptionalString(edge.toNodeId, input.toNodeId)).filter((edge) => matchesOptionalString(edge.edgeType, input.edgeType));
18555
18909
  return { items: edges, edges, total: edges.length };
18556
18910
  }
18557
18911
 
@@ -20008,7 +20362,7 @@ function classifyCoverage(beliefs, questions, evidence) {
20008
20362
  }
20009
20363
  return "partial";
20010
20364
  }
20011
- async function createTopic(deps, input) {
20365
+ async function createTopic2(deps, input) {
20012
20366
  const created = await deps.createTopic({
20013
20367
  name: requireString2(input.name, "name"),
20014
20368
  description: readString18(input.description),
@@ -20195,24 +20549,29 @@ async function emitTopicEventFromGatewayAuth(authContext, args) {
20195
20549
  }
20196
20550
  );
20197
20551
  }
20552
+ function normalizePrefixedId2(value) {
20553
+ try {
20554
+ const decoded = decodePrefixedId(value);
20555
+ return decoded.prefix === "top" ? decoded.value : value;
20556
+ } catch (_error) {
20557
+ return value;
20558
+ }
20559
+ }
20560
+ function isRecord5(value) {
20561
+ return value !== null && typeof value === "object" && !Array.isArray(value);
20562
+ }
20198
20563
  function normalizeTopicEventId(topicId) {
20199
20564
  const normalized = topicId.trim();
20200
20565
  if (!normalized) {
20201
20566
  return normalized;
20202
20567
  }
20203
- try {
20204
- const decoded = decodePrefixedId(normalized);
20205
- return decoded.prefix === "top" ? decoded.value : normalized;
20206
- } catch {
20207
- return normalized;
20208
- }
20568
+ return normalizePrefixedId2(normalized);
20209
20569
  }
20210
20570
  function readCanonicalTopicRawId(value) {
20211
- if (!value || typeof value !== "object" || Array.isArray(value)) {
20571
+ if (!isRecord5(value)) {
20212
20572
  return void 0;
20213
20573
  }
20214
- const record = value;
20215
- for (const candidate of [record._id, record.id, record.topicId, record.projectId]) {
20574
+ for (const candidate of [value._id, value.id, value.topicId, value.projectId]) {
20216
20575
  if (typeof candidate === "string" && candidate.trim().length > 0) {
20217
20576
  return normalizeTopicEventId(candidate);
20218
20577
  }
@@ -20291,7 +20650,10 @@ function createGatewayTopicsPort(authContext) {
20291
20650
  };
20292
20651
  }
20293
20652
  async function createTopicFromGatewayAuth(authContext, input) {
20294
- const payload = await createTopic(createGatewayTopicsPort(authContext), input);
20653
+ const payload = await createTopic2(
20654
+ createGatewayTopicsPort(authContext),
20655
+ input
20656
+ );
20295
20657
  await emitTopicEventFromGatewayAuth(authContext, {
20296
20658
  topicId: normalizeTopicEventId(payload.topicId),
20297
20659
  type: "topic.created",
@@ -20308,7 +20670,10 @@ async function createTopicFromGatewayAuth(authContext, input) {
20308
20670
  return payload;
20309
20671
  }
20310
20672
  async function updateTopicFromGatewayAuth(authContext, input) {
20311
- const payload = await updateTopic(createGatewayTopicsPort(authContext), input);
20673
+ const payload = await updateTopic(
20674
+ createGatewayTopicsPort(authContext),
20675
+ input
20676
+ );
20312
20677
  await emitTopicEventFromGatewayAuth(authContext, {
20313
20678
  topicId: normalizeTopicEventId(payload.topicId),
20314
20679
  type: payload.status === "archived" ? "topic.archived" : "topic.updated",
@@ -20345,7 +20710,7 @@ async function removeTopicFromGatewayAuth(authContext, input) {
20345
20710
  id: normalizedId
20346
20711
  });
20347
20712
  return {
20348
- ...payload,
20713
+ ...isRecord5(payload) ? payload : {},
20349
20714
  id: input.id
20350
20715
  };
20351
20716
  }
@@ -20979,7 +21344,7 @@ function toSubjectiveLogicOpinion(confidence) {
20979
21344
  baseRate: 0.5
20980
21345
  };
20981
21346
  }
20982
- function resolveExternalId5(value, prefix) {
21347
+ function normalizePrefixedId3(value, prefix) {
20983
21348
  if (!value) {
20984
21349
  return value;
20985
21350
  }
@@ -20989,26 +21354,28 @@ function resolveExternalId5(value, prefix) {
20989
21354
  return value;
20990
21355
  }
20991
21356
  return decoded.value;
20992
- } catch {
21357
+ } catch (_error) {
20993
21358
  return value;
20994
21359
  }
20995
21360
  }
21361
+ function isRecord6(value) {
21362
+ return value !== null && typeof value === "object" && !Array.isArray(value);
21363
+ }
20996
21364
  function summarizeWorktreeCollection(rows) {
20997
21365
  const worktrees2 = Array.isArray(rows) ? rows : [];
20998
21366
  const lanes = {};
20999
21367
  const campaigns = {};
21000
21368
  for (const row of worktrees2) {
21001
- if (!row || typeof row !== "object" || Array.isArray(row)) {
21369
+ if (!isRecord6(row)) {
21002
21370
  continue;
21003
21371
  }
21004
- const record = row;
21005
- const lane = typeof record.lane === "string" && record.lane.trim().length > 0 ? record.lane.trim() : "unlaned";
21372
+ const lane = typeof row.lane === "string" && row.lane.trim().length > 0 ? row.lane.trim() : "unlaned";
21006
21373
  lanes[lane] = (lanes[lane] ?? 0) + 1;
21007
- const campaignValue = typeof record.campaign === "number" ? String(record.campaign) : "uncampaigned";
21374
+ const campaignValue = typeof row.campaign === "number" ? String(row.campaign) : "uncampaigned";
21008
21375
  campaigns[campaignValue] = (campaigns[campaignValue] ?? 0) + 1;
21009
21376
  }
21010
21377
  return {
21011
- worktrees: worktrees2,
21378
+ worktrees: worktrees2.filter(isRecord6),
21012
21379
  total: worktrees2.length,
21013
21380
  lanes,
21014
21381
  campaigns
@@ -21068,7 +21435,7 @@ function readString20(value) {
21068
21435
  return normalized.length > 0 ? normalized : void 0;
21069
21436
  }
21070
21437
  function withRequestedWorktreeId(payload, worktreeId) {
21071
- if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
21438
+ if (!isRecord6(payload)) {
21072
21439
  return payload;
21073
21440
  }
21074
21441
  return {
@@ -21080,7 +21447,7 @@ async function resolveWorktreeTopicIdWithGatewayAuth(authContext, worktreeId) {
21080
21447
  const worktree = await authContext.convex.query(api.worktrees.get, {
21081
21448
  worktreeId
21082
21449
  });
21083
- return readString20(worktree?.topicId);
21450
+ return isRecord6(worktree) ? readString20(worktree.topicId) : void 0;
21084
21451
  }
21085
21452
  async function emitWorktreeEventFromGatewayAuth(authContext, args) {
21086
21453
  return emitDomainEvent(
@@ -21124,8 +21491,30 @@ function createGatewayWorktreesPort(authContext) {
21124
21491
  },
21125
21492
  async createWorktree(input) {
21126
21493
  const shouldAutoShape = input.autoShape === true || Boolean(input.domainPackId?.trim());
21127
- const created = shouldAutoShape ? await authContext.convex.mutation(api.worktrees.createShaped, {
21128
- ...buildCreateMutationArgs(
21494
+ const created = shouldAutoShape ? await authContext.convex.mutation(
21495
+ api.worktrees.createShaped,
21496
+ {
21497
+ ...buildCreateMutationArgs(
21498
+ {
21499
+ ...input,
21500
+ topicId: input.topicRawId,
21501
+ title: input.title,
21502
+ beliefIds: input.beliefRawIds,
21503
+ targetBeliefIds: input.targetBeliefRawIds,
21504
+ targetQuestionIds: input.targetQuestionRawIds
21505
+ },
21506
+ authContext.userId
21507
+ ),
21508
+ ...input.domainPackId?.trim() ? { domainPackId: input.domainPackId.trim() } : {},
21509
+ ...typeof input.autoShape === "boolean" ? { autoShape: input.autoShape } : {},
21510
+ ...Array.isArray(input.proofArtifacts) ? { proofArtifacts: input.proofArtifacts } : {},
21511
+ ...input.staffingHint?.trim() ? { staffingHint: input.staffingHint.trim() } : {},
21512
+ ...typeof input.lastReconciledAt === "number" ? { lastReconciledAt: input.lastReconciledAt } : {},
21513
+ ...input.autoFixPolicy ? { autoFixPolicy: input.autoFixPolicy } : {}
21514
+ }
21515
+ ) : await authContext.convex.mutation(
21516
+ api.worktrees.create,
21517
+ buildCreateMutationArgs(
21129
21518
  {
21130
21519
  ...input,
21131
21520
  topicId: input.topicRawId,
@@ -21135,26 +21524,10 @@ function createGatewayWorktreesPort(authContext) {
21135
21524
  targetQuestionIds: input.targetQuestionRawIds
21136
21525
  },
21137
21526
  authContext.userId
21138
- ),
21139
- ...input.domainPackId?.trim() ? { domainPackId: input.domainPackId.trim() } : {},
21140
- ...typeof input.autoShape === "boolean" ? { autoShape: input.autoShape } : {},
21141
- ...Array.isArray(input.proofArtifacts) ? { proofArtifacts: input.proofArtifacts } : {},
21142
- ...input.staffingHint?.trim() ? { staffingHint: input.staffingHint.trim() } : {},
21143
- ...typeof input.lastReconciledAt === "number" ? { lastReconciledAt: input.lastReconciledAt } : {},
21144
- ...input.autoFixPolicy ? { autoFixPolicy: input.autoFixPolicy } : {}
21145
- }) : await authContext.convex.mutation(api.worktrees.create, buildCreateMutationArgs(
21146
- {
21147
- ...input,
21148
- topicId: input.topicRawId,
21149
- title: input.title,
21150
- beliefIds: input.beliefRawIds,
21151
- targetBeliefIds: input.targetBeliefRawIds,
21152
- targetQuestionIds: input.targetQuestionRawIds
21153
- },
21154
- authContext.userId
21155
- ));
21527
+ )
21528
+ );
21156
21529
  if (!shouldAutoShape && (Array.isArray(input.proofArtifacts) || input.staffingHint || typeof input.lastReconciledAt === "number" || input.autoFixPolicy)) {
21157
- const createdRecord = created && typeof created === "object" ? created : {};
21530
+ const createdRecord = isRecord6(created) ? created : {};
21158
21531
  const worktreeId = typeof created === "string" ? created : typeof createdRecord.worktreeId === "string" ? createdRecord.worktreeId : "";
21159
21532
  if (worktreeId) {
21160
21533
  await authContext.convex.mutation(api.worktrees.updateMetadata, {
@@ -21185,14 +21558,17 @@ function createGatewayWorktreesPort(authContext) {
21185
21558
  },
21186
21559
  scoreBeliefOutcome(args) {
21187
21560
  const opinion = toSubjectiveLogicOpinion(args.confidence);
21188
- return authContext.convex.mutation(api.epistemicBeliefs.modulateConfidence, {
21189
- nodeId: args.beliefRawId,
21190
- trigger: "worktree_outcome",
21191
- rationale: args.rationale,
21192
- userId: authContext.userId,
21193
- triggeringWorktreeId: args.worktreeRawId,
21194
- ...opinion
21195
- });
21561
+ return authContext.convex.mutation(
21562
+ api.epistemicBeliefs.modulateConfidence,
21563
+ {
21564
+ nodeId: args.beliefRawId,
21565
+ trigger: "worktree_outcome",
21566
+ rationale: args.rationale,
21567
+ userId: authContext.userId,
21568
+ triggeringWorktreeId: args.worktreeRawId,
21569
+ ...opinion
21570
+ }
21571
+ );
21196
21572
  },
21197
21573
  updateWorktreeMetadata(input) {
21198
21574
  return authContext.convex.mutation(api.worktrees.updateMetadata, {
@@ -21226,16 +21602,44 @@ function createGatewayWorktreesPort(authContext) {
21226
21602
  removeBeliefIds: input.removeBeliefRawIds.map((id) => id)
21227
21603
  } : {},
21228
21604
  ...input.removeQuestionRawIds?.length ? {
21229
- removeQuestionIds: input.removeQuestionRawIds.map((id) => id)
21605
+ removeQuestionIds: input.removeQuestionRawIds.map(
21606
+ (id) => id
21607
+ )
21230
21608
  } : {}
21231
21609
  });
21232
21610
  }
21233
21611
  };
21234
21612
  }
21235
21613
  async function createWorktreeFromGatewayAuth(authContext, input) {
21236
- const payload = await createWorktree(createGatewayWorktreesPort(authContext), input);
21614
+ const explicitTopicId = readString20(input.topicId);
21615
+ const resolvedTopic = await resolveTopicIdForWrite(authContext, {
21616
+ topicId: explicitTopicId,
21617
+ topicHint: readString20(input.topicHint) ?? explicitTopicId,
21618
+ workspaceId: authContext.workspaceId,
21619
+ summary: readString20(input.objective) ?? readString20(input.hypothesis) ?? readString20(input.rationale) ?? readString20(input.title) ?? "",
21620
+ tags: [
21621
+ ...input.tags ?? [],
21622
+ input.lane,
21623
+ input.gate,
21624
+ input.worktreeType,
21625
+ input.domainPackId,
21626
+ input.confidenceImpact
21627
+ ].filter((value) => Boolean(readString20(value))),
21628
+ touchedPaths: input.touchedPaths,
21629
+ sourceRef: readString20(input.sourceRef) ?? readString20(input.title),
21630
+ sourceKind: readString20(input.sourceKind) ?? "worktree",
21631
+ autoCreate: !explicitTopicId
21632
+ });
21633
+ const resolvedInput = {
21634
+ ...input,
21635
+ topicId: resolvedTopic.topicId
21636
+ };
21637
+ const payload = await createWorktree(
21638
+ createGatewayWorktreesPort(authContext),
21639
+ resolvedInput
21640
+ );
21237
21641
  await emitWorktreeEventFromGatewayAuth(authContext, {
21238
- topicId: payload.topicId ?? input.topicId,
21642
+ topicId: payload.topicId ?? resolvedInput.topicId,
21239
21643
  type: "worktree.created",
21240
21644
  resourceId: payload.worktreeId,
21241
21645
  data: {
@@ -21269,15 +21673,21 @@ async function listAllWorktreesFromGatewayAuth(authContext, query = {}) {
21269
21673
  ...typeof query.campaign === "number" ? { campaign: query.campaign } : {},
21270
21674
  ...typeof query.limit === "number" ? { limit: query.limit } : {}
21271
21675
  });
21272
- return summarizeWorktreeCollection(rows);
21676
+ return summarizeWorktreeCollection(Array.isArray(rows) ? rows : []);
21273
21677
  }
21274
21678
  async function listCampaignsFromGatewayAuth(authContext, query = {}) {
21275
21679
  return listCampaigns(createGatewayWorktreesPort(authContext), query);
21276
21680
  }
21277
21681
  async function activateWorktreeFromGatewayAuth(authContext, input) {
21278
- const payload = await activateWorktree(createGatewayWorktreesPort(authContext), input);
21682
+ const payload = await activateWorktree(
21683
+ createGatewayWorktreesPort(authContext),
21684
+ input
21685
+ );
21279
21686
  await emitWorktreeEventFromGatewayAuth(authContext, {
21280
- topicId: payload.topicId ?? await resolveWorktreeTopicIdWithGatewayAuth(authContext, payload.worktreeId) ?? "",
21687
+ topicId: payload.topicId ?? await resolveWorktreeTopicIdWithGatewayAuth(
21688
+ authContext,
21689
+ payload.worktreeId
21690
+ ) ?? "",
21281
21691
  type: "worktree.activated",
21282
21692
  resourceId: payload.worktreeId,
21283
21693
  data: {
@@ -21289,9 +21699,15 @@ async function activateWorktreeFromGatewayAuth(authContext, input) {
21289
21699
  return payload;
21290
21700
  }
21291
21701
  async function updateWorktreeFromGatewayAuth(authContext, input) {
21292
- const payload = await updateWorktree(createGatewayWorktreesPort(authContext), input);
21702
+ const payload = await updateWorktree(
21703
+ createGatewayWorktreesPort(authContext),
21704
+ input
21705
+ );
21293
21706
  await emitWorktreeEventFromGatewayAuth(authContext, {
21294
- topicId: payload.topicId ?? await resolveWorktreeTopicIdWithGatewayAuth(authContext, payload.worktreeId) ?? "",
21707
+ topicId: payload.topicId ?? await resolveWorktreeTopicIdWithGatewayAuth(
21708
+ authContext,
21709
+ payload.worktreeId
21710
+ ) ?? "",
21295
21711
  type: "worktree.metadata_updated",
21296
21712
  resourceId: payload.worktreeId,
21297
21713
  data: {
@@ -21308,9 +21724,15 @@ async function updateWorktreeFromGatewayAuth(authContext, input) {
21308
21724
  return payload;
21309
21725
  }
21310
21726
  async function mergeWorktreeFromGatewayAuth(authContext, input) {
21311
- const payload = await mergeWorktree(createGatewayWorktreesPort(authContext), input);
21727
+ const payload = await mergeWorktree(
21728
+ createGatewayWorktreesPort(authContext),
21729
+ input
21730
+ );
21312
21731
  await emitWorktreeEventFromGatewayAuth(authContext, {
21313
- topicId: await resolveWorktreeTopicIdWithGatewayAuth(authContext, payload.worktreeId) ?? "",
21732
+ topicId: await resolveWorktreeTopicIdWithGatewayAuth(
21733
+ authContext,
21734
+ payload.worktreeId
21735
+ ) ?? "",
21314
21736
  type: "worktree.merged",
21315
21737
  resourceId: payload.worktreeId,
21316
21738
  data: {
@@ -21330,7 +21752,10 @@ async function updateWorktreeTargetsFromGatewayAuth(authContext, input) {
21330
21752
  input
21331
21753
  );
21332
21754
  await emitWorktreeEventFromGatewayAuth(authContext, {
21333
- topicId: payload.topicId ?? await resolveWorktreeTopicIdWithGatewayAuth(authContext, payload.worktreeId) ?? "",
21755
+ topicId: payload.topicId ?? await resolveWorktreeTopicIdWithGatewayAuth(
21756
+ authContext,
21757
+ payload.worktreeId
21758
+ ) ?? "",
21334
21759
  type: "worktree.targets_updated",
21335
21760
  resourceId: payload.worktreeId,
21336
21761
  data: {
@@ -21346,32 +21771,36 @@ async function updateWorktreeTargetsFromGatewayAuth(authContext, input) {
21346
21771
  }
21347
21772
  function completeWorktreeRecordFromGatewayAuth(authContext, input) {
21348
21773
  return authContext.convex.mutation(api.worktrees.complete, {
21349
- worktreeId: resolveExternalId5(input.worktreeId, "wt") ?? input.worktreeId,
21774
+ worktreeId: normalizePrefixedId3(input.worktreeId, "wt") ?? input.worktreeId,
21350
21775
  keyFindings: input.keyFindings ?? [],
21351
21776
  decisionsReached: input.decisionsReached ?? [],
21352
21777
  nextSteps: input.nextSteps ?? [],
21353
21778
  userId: authContext.userId
21354
- }).then((payload) => withRequestedWorktreeId(payload, input.worktreeId));
21779
+ }).then(
21780
+ (payload) => withRequestedWorktreeId(payload, input.worktreeId)
21781
+ );
21355
21782
  }
21356
21783
  function advanceWorktreePhaseFromGatewayAuth(authContext, input) {
21357
21784
  return authContext.convex.mutation(api.worktrees.advancePhase, {
21358
- worktreeId: resolveExternalId5(input.worktreeId, "wt") ?? input.worktreeId,
21785
+ worktreeId: normalizePrefixedId3(input.worktreeId, "wt") ?? input.worktreeId,
21359
21786
  userId: authContext.userId
21360
21787
  });
21361
21788
  }
21362
21789
  function setWorktreePhaseFromGatewayAuth(authContext, input) {
21363
21790
  return authContext.convex.mutation(api.worktrees.setPhase, {
21364
- worktreeId: resolveExternalId5(input.worktreeId, "wt") ?? input.worktreeId,
21791
+ worktreeId: normalizePrefixedId3(input.worktreeId, "wt") ?? input.worktreeId,
21365
21792
  phase: input.phase,
21366
21793
  userId: authContext.userId
21367
21794
  });
21368
21795
  }
21369
21796
  function patchWorktreeStateFromGatewayAuth(authContext, input) {
21370
21797
  return authContext.convex.mutation(api.worktrees.patchState, {
21371
- worktreeId: resolveExternalId5(input.worktreeId, "wt") ?? input.worktreeId,
21798
+ worktreeId: normalizePrefixedId3(input.worktreeId, "wt") ?? input.worktreeId,
21372
21799
  patch: input.patch,
21373
21800
  userId: authContext.userId
21374
- }).then((payload) => withRequestedWorktreeId(payload, input.worktreeId));
21801
+ }).then(
21802
+ (payload) => withRequestedWorktreeId(payload, input.worktreeId)
21803
+ );
21375
21804
  }
21376
21805
  function bulkCreateWorktreesFromGatewayAuth(authContext, input) {
21377
21806
  return authContext.convex.mutation(api.worktrees.bulkCreate, {
@@ -24815,6 +25244,7 @@ async function handleWorktreeCreate(args) {
24815
25244
  const payload = await createWorktreeFromGatewayAuth(args.authContext, {
24816
25245
  title: readString33(body.title) ?? readString33(body.name) ?? "",
24817
25246
  topicId: readString33(body.topicId ?? body.projectId) ?? "",
25247
+ topicHint: readString33(body.topicHint),
24818
25248
  objective: readString33(body.objective),
24819
25249
  hypothesis: readString33(body.hypothesis),
24820
25250
  rationale: readString33(body.rationale),
@@ -24835,6 +25265,10 @@ async function handleWorktreeCreate(args) {
24835
25265
  } : void 0,
24836
25266
  autoShape: typeof body.autoShape === "boolean" ? body.autoShape : void 0,
24837
25267
  domainPackId: readString33(body.domainPackId),
25268
+ tags: readStringArray17(body.tags),
25269
+ touchedPaths: readStringArray17(body.touchedPaths),
25270
+ sourceRef: readString33(body.sourceRef),
25271
+ sourceKind: readString33(body.sourceKind),
24838
25272
  campaign: readNumber22(body.campaign),
24839
25273
  lane: readString33(body.lane),
24840
25274
  laneOrderInCampaign: readNumber22(body.laneOrderInCampaign),