@productbrain/mcp 0.0.1-beta.76 → 0.0.1-beta.78

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.
@@ -2136,7 +2136,17 @@ var captureSuccessOutputSchema = z2.object({
2136
2136
  qualityScore: z2.number(),
2137
2137
  qualityVerdict: z2.record(z2.unknown()).optional(),
2138
2138
  classifier: captureClassifierSchema.optional(),
2139
- studioUrl: z2.string().optional()
2139
+ studioUrl: z2.string().optional(),
2140
+ warnings: z2.array(z2.string()).optional(),
2141
+ normalization: z2.object({
2142
+ remapped: z2.record(z2.string()).optional(),
2143
+ rejected: z2.array(z2.string()).optional()
2144
+ }).optional(),
2145
+ expectedFields: z2.array(z2.object({
2146
+ key: z2.string(),
2147
+ type: z2.string(),
2148
+ required: z2.boolean().optional()
2149
+ })).optional()
2140
2150
  }).strict();
2141
2151
  var captureClassifierOnlyOutputSchema = z2.object({
2142
2152
  classifier: captureClassifierSchema
@@ -2153,7 +2163,12 @@ var batchCaptureOutputSchema = z2.object({
2153
2163
  status: z2.enum(["draft", "committed", "proposed"]),
2154
2164
  classifiedBy: z2.enum(["llm", "heuristic", "explicit"]).optional(),
2155
2165
  confidence: z2.number().optional(),
2156
- confidenceTier: z2.enum(["high", "medium", "low"]).optional()
2166
+ confidenceTier: z2.enum(["high", "medium", "low"]).optional(),
2167
+ warnings: z2.array(z2.string()).optional(),
2168
+ normalization: z2.object({
2169
+ remapped: z2.record(z2.string()).optional(),
2170
+ rejected: z2.array(z2.string()).optional()
2171
+ }).optional()
2157
2172
  })),
2158
2173
  total: z2.number(),
2159
2174
  failed: z2.number(),
@@ -2300,11 +2315,43 @@ Or use \`collections action=list\` to see available collections.`
2300
2315
  }
2301
2316
  data.links = betLinks;
2302
2317
  }
2318
+ const E2_LLM_REMAP_ENABLED = false;
2319
+ const payloadForPreview = E2_LLM_REMAP_ENABLED && userData && typeof userData === "object" ? { ...data } : null;
2320
+ if (payloadForPreview) {
2321
+ try {
2322
+ const preview = await mcpCall(
2323
+ "chain.normalizeEntryDataPreview",
2324
+ { collectionSlug: resolvedCollection, data: payloadForPreview }
2325
+ );
2326
+ const rejected = (preview?.rejected ?? []).filter((k) => k !== "name");
2327
+ if (rejected.length > 0) {
2328
+ const llmResult = await mcpCall(
2329
+ "chain.normalizeEntryDataLLM",
2330
+ {
2331
+ collectionSlug: resolvedCollection,
2332
+ unknownKeys: rejected,
2333
+ data: payloadForPreview
2334
+ }
2335
+ );
2336
+ if (llmResult && Object.keys(llmResult.remapped).length > 0) {
2337
+ const fieldKeySet = new Set((col.fields ?? []).map((f) => f.key));
2338
+ for (const [oldKey, newKey] of Object.entries(llmResult.remapped)) {
2339
+ if (data[oldKey] !== void 0 && !fieldKeySet.has(oldKey)) {
2340
+ data[newKey] = data[oldKey];
2341
+ delete data[oldKey];
2342
+ }
2343
+ }
2344
+ }
2345
+ }
2346
+ } catch {
2347
+ }
2348
+ }
2303
2349
  const status = "draft";
2304
2350
  const agentId = getAgentSessionId();
2305
2351
  let finalEntryId;
2306
2352
  let internalId;
2307
2353
  let entryWarnings = [];
2354
+ let normalizationMeta;
2308
2355
  try {
2309
2356
  const result = await mcpMutation("chain.createEntry", {
2310
2357
  collectionSlug: resolvedCollection,
@@ -2319,6 +2366,7 @@ Or use \`collections action=list\` to see available collections.`
2319
2366
  internalId = result.docId;
2320
2367
  finalEntryId = result.entryId;
2321
2368
  entryWarnings = result.warnings ?? [];
2369
+ normalizationMeta = result.normalization;
2322
2370
  resolveGapsForEntry(name, result.entryId);
2323
2371
  } catch (error) {
2324
2372
  const msg = error instanceof Error ? error.message : String(error);
@@ -2348,6 +2396,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
2348
2396
  const linksCreated = [];
2349
2397
  const linksSuggested = [];
2350
2398
  const userLinkResults = [];
2399
+ const pendingRelations = [];
2351
2400
  const skipAutoDiscovery = links && links.length > 0;
2352
2401
  const searchQuery = extractSearchTerms(name, description);
2353
2402
  if (searchQuery && !skipAutoDiscovery) {
@@ -2366,63 +2415,95 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
2366
2415
  confidenceReason: conf.reason
2367
2416
  };
2368
2417
  }).sort((a, b) => b.confidence - a.confidence);
2418
+ let autoCount = 0;
2369
2419
  for (const c of candidates) {
2370
- if (linksCreated.length >= MAX_AUTO_LINKS) break;
2420
+ if (autoCount >= MAX_AUTO_LINKS) break;
2371
2421
  if (c.confidence < AUTO_LINK_CONFIDENCE_THRESHOLD) break;
2372
2422
  if (!c.entryId || !finalEntryId) continue;
2373
2423
  const { type: relationType, reason: relationReason } = inferRelationType(resolvedCollection, c.collSlug, profile);
2374
- try {
2375
- await mcpMutation("chain.createEntryRelation", {
2376
- fromEntryId: finalEntryId,
2377
- toEntryId: c.entryId,
2378
- type: relationType,
2379
- sessionId: agentId ?? void 0
2380
- });
2381
- linksCreated.push({
2382
- targetEntryId: c.entryId,
2424
+ pendingRelations.push({
2425
+ fromEntryId: finalEntryId,
2426
+ toEntryId: c.entryId,
2427
+ type: relationType,
2428
+ meta: {
2429
+ source: "auto",
2383
2430
  targetName: c.name,
2384
2431
  targetCollection: c.collSlug,
2385
- relationType,
2386
2432
  linkReason: `confidence ${c.confidence} (${c.confidenceReason}) + ${relationReason}`
2387
- });
2388
- } catch {
2389
- }
2433
+ }
2434
+ });
2435
+ autoCount++;
2390
2436
  }
2391
- const linkedIds = new Set(linksCreated.map((l) => l.targetEntryId));
2437
+ const autoTargetIds = new Set(pendingRelations.map((r) => r.toEntryId));
2392
2438
  for (const c of candidates) {
2393
2439
  if (linksSuggested.length >= MAX_SUGGESTIONS) break;
2394
- if (linkedIds.has(c.entryId)) continue;
2440
+ if (autoTargetIds.has(c.entryId)) continue;
2395
2441
  if (c.confidence < 10) continue;
2396
2442
  const preview = extractPreview(c.data, 80);
2397
2443
  const reason = c.confidence >= AUTO_LINK_CONFIDENCE_THRESHOLD ? "high relevance (already linked)" : `"${c.name.toLowerCase().split(/\s+/).filter((w) => `${name} ${description}`.toLowerCase().includes(w) && w.length > 3).slice(0, 2).join('", "')}" appears in content`;
2398
- linksSuggested.push({
2399
- entryId: c.entryId,
2400
- name: c.name,
2401
- collection: c.collSlug,
2402
- reason,
2403
- preview
2404
- });
2444
+ linksSuggested.push({ entryId: c.entryId, name: c.name, collection: c.collSlug, reason, preview });
2405
2445
  }
2406
2446
  }
2407
2447
  if (links && links.length > 0 && finalEntryId) {
2408
2448
  for (const link of links) {
2409
- try {
2410
- await mcpMutation("chain.createEntryRelation", {
2411
- fromEntryId: finalEntryId,
2412
- toEntryId: link.to,
2413
- type: link.type,
2414
- sessionId: agentId ?? void 0
2415
- });
2416
- userLinkResults.push({ label: `\u2713 ${link.type} \u2192 ${link.to}`, ok: true });
2417
- linksCreated.push({
2418
- targetEntryId: link.to,
2419
- targetName: link.to,
2420
- targetCollection: "unknown",
2421
- relationType: link.type
2422
- });
2423
- } catch (e) {
2424
- const msg = e instanceof Error ? e.message : "failed";
2425
- userLinkResults.push({ label: `\u2717 ${link.type} \u2192 ${link.to}: ${msg}`, ok: false });
2449
+ pendingRelations.push({
2450
+ fromEntryId: finalEntryId,
2451
+ toEntryId: link.to,
2452
+ type: link.type,
2453
+ meta: { source: "user", targetName: link.to, targetCollection: "unknown" }
2454
+ });
2455
+ }
2456
+ }
2457
+ const resolvedCK = canonicalKey;
2458
+ const batchRelationsPromise = pendingRelations.length > 0 ? mcpMutation(
2459
+ "chain.createEntryRelations",
2460
+ {
2461
+ relations: pendingRelations.slice(0, 25).map((r) => ({ fromEntryId: r.fromEntryId, toEntryId: r.toEntryId, type: r.type })),
2462
+ sessionId: agentId ?? void 0
2463
+ }
2464
+ ).catch((err) => {
2465
+ entryWarnings.push(`Auto-linking partially failed: ${err instanceof Error ? err.message : "unknown error"}`);
2466
+ return null;
2467
+ }) : Promise.resolve(null);
2468
+ const cardinalityPromise = resolvedCK ? mcpQuery("chain.checkCardinalityWarning", { canonicalKey: resolvedCK }).catch(() => null) : Promise.resolve(null);
2469
+ const contradictionPromise = runContradictionCheck(name, description);
2470
+ const coachingPromise = mcpMutation("quality.evaluateHeuristicAndSchedule", {
2471
+ entryId: finalEntryId,
2472
+ context: "capture"
2473
+ }).catch(() => null);
2474
+ const [batchResult, cardinalityCheck, contradictionWarnings, verdictResult] = await Promise.all([
2475
+ batchRelationsPromise,
2476
+ cardinalityPromise,
2477
+ contradictionPromise,
2478
+ coachingPromise
2479
+ ]);
2480
+ if (batchResult) {
2481
+ for (let i = 0; i < pendingRelations.length; i++) {
2482
+ const pending = pendingRelations[i];
2483
+ const result = batchResult.results[i];
2484
+ if (!result) continue;
2485
+ if (pending.meta.source === "auto") {
2486
+ if (result.ok) {
2487
+ linksCreated.push({
2488
+ targetEntryId: pending.toEntryId,
2489
+ targetName: pending.meta.targetName,
2490
+ targetCollection: pending.meta.targetCollection,
2491
+ relationType: pending.type,
2492
+ linkReason: pending.meta.linkReason
2493
+ });
2494
+ }
2495
+ } else {
2496
+ if (result.ok) {
2497
+ userLinkResults.push({ label: `\u2713 ${pending.type} \u2192 ${pending.toEntryId}`, ok: true });
2498
+ linksCreated.push({
2499
+ targetEntryId: pending.toEntryId,
2500
+ targetName: pending.meta.targetName,
2501
+ targetCollection: pending.meta.targetCollection,
2502
+ relationType: pending.type
2503
+ });
2504
+ } else {
2505
+ userLinkResults.push({ label: `\u2717 ${pending.type} \u2192 ${pending.toEntryId}: ${result.error ?? "failed"}`, ok: false });
2506
+ }
2426
2507
  }
2427
2508
  }
2428
2509
  }
@@ -2439,34 +2520,13 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
2439
2520
  collectionFields: col.fields ?? []
2440
2521
  };
2441
2522
  const quality = scoreQuality(captureCtx, profile);
2442
- let cardinalityWarning = null;
2443
- const resolvedCK = canonicalKey ?? captureCtx.canonicalKey;
2444
- if (resolvedCK) {
2445
- try {
2446
- const check = await mcpQuery("chain.checkCardinalityWarning", {
2447
- canonicalKey: resolvedCK
2448
- });
2449
- if (check?.warning) {
2450
- cardinalityWarning = check.warning;
2451
- }
2452
- } catch {
2453
- }
2454
- }
2455
- const contradictionWarnings = await runContradictionCheck(name, description);
2523
+ const cardinalityWarning = cardinalityCheck?.warning ?? null;
2456
2524
  if (contradictionWarnings.length > 0) {
2457
2525
  await recordSessionActivity({ contradictionWarning: true });
2458
2526
  }
2459
2527
  let coachingSection = "";
2460
- let verdictResult = null;
2461
- try {
2462
- verdictResult = await mcpMutation("quality.evaluateHeuristicAndSchedule", {
2463
- entryId: finalEntryId,
2464
- context: "capture"
2465
- });
2466
- if (verdictResult?.verdict && verdictResult.verdict.tier !== "passive" && verdictResult.verdict.criteria.length > 0) {
2467
- coachingSection = formatRubricCoaching(verdictResult);
2468
- }
2469
- } catch {
2528
+ if (verdictResult?.verdict && verdictResult.verdict.tier !== "passive" && verdictResult.verdict.criteria.length > 0) {
2529
+ coachingSection = formatRubricCoaching(verdictResult);
2470
2530
  }
2471
2531
  if (verdictResult?.verdict) {
2472
2532
  try {
@@ -2668,6 +2728,11 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
2668
2728
  next.push({ tool: "commit-entry", description: "Commit to Chain", parameters: { entryId: finalEntryId } });
2669
2729
  }
2670
2730
  const summary = finalStatus === "committed" ? `Captured and committed ${finalEntryId} (${name}) to ${resolvedCollection}. Quality ${quality.score}/10.` : finalStatus === "proposed" ? `Captured ${finalEntryId} (${name}) in ${resolvedCollection} and created a proposal for commit. Quality ${quality.score}/10.` : `Captured ${finalEntryId} (${name}) as draft in ${resolvedCollection}. Quality ${quality.score}/10.`;
2731
+ const expectedFields = (col.fields ?? []).map((f) => ({
2732
+ key: f.key,
2733
+ type: f.type,
2734
+ ...f.required && { required: true }
2735
+ }));
2671
2736
  const toolResult = {
2672
2737
  content: [{ type: "text", text: lines.join("\n") }],
2673
2738
  structuredContent: success(
@@ -2681,7 +2746,14 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
2681
2746
  qualityVerdict: verdictResult?.verdict ? { ...verdictResult.verdict, source: verdictResult.source ?? "heuristic" } : void 0,
2682
2747
  ...classifierMeta && { classifier: classifierMeta },
2683
2748
  ...studioUrl && { studioUrl },
2684
- ...entryWarnings.length > 0 && { warnings: entryWarnings }
2749
+ ...entryWarnings.length > 0 && { warnings: entryWarnings },
2750
+ ...normalizationMeta && (Object.keys(normalizationMeta.remapped).length > 0 || normalizationMeta.rejected.length > 0) && {
2751
+ normalization: {
2752
+ remapped: normalizationMeta.remapped,
2753
+ rejected: normalizationMeta.rejected
2754
+ }
2755
+ },
2756
+ expectedFields
2685
2757
  },
2686
2758
  next
2687
2759
  )
@@ -2829,27 +2901,22 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
2829
2901
  const searchResults = await mcpQuery("chain.searchEntries", { query: searchQuery });
2830
2902
  const candidates = (searchResults ?? []).filter((r) => r.entryId !== finalEntryId).map((r) => {
2831
2903
  const conf = computeLinkConfidence(r, entry.name, entry.description, resolvedSlug, collIdToSlug.get(r.collectionId) ?? "unknown");
2832
- return {
2833
- ...r,
2834
- collSlug: collIdToSlug.get(r.collectionId) ?? "unknown",
2835
- confidence: conf.score
2836
- };
2904
+ return { ...r, collSlug: collIdToSlug.get(r.collectionId) ?? "unknown", confidence: conf.score };
2837
2905
  }).sort((a, b) => b.confidence - a.confidence);
2906
+ const batchAutoLinks = [];
2838
2907
  for (const c of candidates) {
2839
- if (autoLinkCount >= MAX_AUTO_LINKS) break;
2908
+ if (batchAutoLinks.length >= MAX_AUTO_LINKS) break;
2840
2909
  if (c.confidence < AUTO_LINK_CONFIDENCE_THRESHOLD) break;
2841
2910
  if (!c.entryId) continue;
2842
2911
  const { type: relationType } = inferRelationType(resolvedSlug, c.collSlug, profile);
2843
- try {
2844
- await mcpMutation("chain.createEntryRelation", {
2845
- fromEntryId: finalEntryId,
2846
- toEntryId: c.entryId,
2847
- type: relationType,
2848
- sessionId: agentId ?? void 0
2849
- });
2850
- autoLinkCount++;
2851
- } catch {
2852
- }
2912
+ batchAutoLinks.push({ fromEntryId: finalEntryId, toEntryId: c.entryId, type: relationType });
2913
+ }
2914
+ if (batchAutoLinks.length > 0) {
2915
+ const batchRes = await mcpMutation("chain.createEntryRelations", {
2916
+ relations: batchAutoLinks,
2917
+ sessionId: agentId ?? void 0
2918
+ });
2919
+ autoLinkCount = batchRes.created;
2853
2920
  }
2854
2921
  } catch {
2855
2922
  }
@@ -2869,6 +2936,7 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
2869
2936
  commitError = error instanceof Error ? error.message : String(error);
2870
2937
  }
2871
2938
  }
2939
+ const entryNorm = result.normalization;
2872
2940
  results.push({
2873
2941
  name: entry.name,
2874
2942
  collection: resolvedSlug,
@@ -2880,7 +2948,10 @@ Use \`entries action=get\` to inspect the existing entry, or \`update-entry\` to
2880
2948
  confidence,
2881
2949
  confidenceTier,
2882
2950
  ...commitError ? { commitError } : {},
2883
- ...batchEntryWarnings.length > 0 ? { warnings: batchEntryWarnings } : {}
2951
+ ...batchEntryWarnings.length > 0 ? { warnings: batchEntryWarnings } : {},
2952
+ ...entryNorm && (Object.keys(entryNorm.remapped).length > 0 || entryNorm.rejected.length > 0) && {
2953
+ normalization: { remapped: entryNorm.remapped, rejected: entryNorm.rejected }
2954
+ }
2884
2955
  });
2885
2956
  } catch (error) {
2886
2957
  const msg = error instanceof Error ? error.message : String(error);
@@ -3039,7 +3110,8 @@ _Use \`entries action=move\` to correct any misclassified entries._`);
3039
3110
  ...r.classifiedBy ? { classifiedBy: r.classifiedBy } : {},
3040
3111
  ...r.confidence != null ? { confidence: r.confidence } : {},
3041
3112
  ...r.confidenceTier ? { confidenceTier: r.confidenceTier } : {},
3042
- ...r.warnings?.length ? { warnings: r.warnings } : {}
3113
+ ...r.warnings?.length ? { warnings: r.warnings } : {},
3114
+ ...r.normalization ? { normalization: r.normalization } : {}
3043
3115
  })),
3044
3116
  total: created.length,
3045
3117
  failed: failed.length,
@@ -3331,4 +3403,4 @@ export {
3331
3403
  formatRubricCoaching,
3332
3404
  formatRubricVerdictSection
3333
3405
  };
3334
- //# sourceMappingURL=chunk-KWQSXRIH.js.map
3406
+ //# sourceMappingURL=chunk-ZY3ORYC3.js.map