@productbrain/mcp 0.0.1-beta.6496 → 0.0.1-beta.6499

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.
@@ -1000,22 +1000,6 @@ async function captureVersionOrPreview(preview, params) {
1000
1000
  return preview ? buildVersionPreviewResult(params.groundingEntry, params.name) : runVersionCapture(params);
1001
1001
  }
1002
1002
 
1003
- // src/lib/fieldTypes.ts
1004
- var FIELD_TYPE_DEFAULTS = {
1005
- "string": "",
1006
- "text": "",
1007
- "rich-text": "",
1008
- "number": null,
1009
- "boolean": false,
1010
- "select": null,
1011
- "multi-select": [],
1012
- "array": [],
1013
- "json": null,
1014
- "date": null,
1015
- "person": null,
1016
- "role": null
1017
- };
1018
-
1019
1003
  // src/lib/formatFieldGuidance.ts
1020
1004
  function formatFieldGuidance(fields) {
1021
1005
  const guidedFields = fields.filter((f) => f.writingGuidance);
@@ -1151,6 +1135,49 @@ function tokenizeText(input) {
1151
1135
  }
1152
1136
 
1153
1137
  // src/lib/entryDataNormalization.ts
1138
+ function buildDataFromFields(fields, descriptionField, descriptionValue) {
1139
+ if (!descriptionField) return null;
1140
+ const data = {};
1141
+ for (const field of fields) {
1142
+ if (field.key === descriptionField) {
1143
+ data[field.key] = descriptionValue;
1144
+ }
1145
+ }
1146
+ return data;
1147
+ }
1148
+ function resolveDescriptionField(fields, descriptionFieldKey) {
1149
+ const verifiedDescriptionFieldKey = descriptionFieldKey && fields.some((field) => field.key === descriptionFieldKey) ? descriptionFieldKey : void 0;
1150
+ const literalDescriptionField = fields.find(
1151
+ (field) => field.key === "description" && (field.type === "text" || field.type === "string")
1152
+ )?.key;
1153
+ const layoutDescriptionField = fields.find(
1154
+ (field) => field.zone === "body" || field.displayHint === "textarea" || field.displayHint === "section"
1155
+ )?.key;
1156
+ const freeTextCandidates = fields.filter((field) => field.zone !== "meta" && field.zone !== "header");
1157
+ const firstTextField = freeTextCandidates.find((field) => field.type === "text")?.key;
1158
+ const firstStringField = freeTextCandidates.find((field) => field.type === "string")?.key;
1159
+ return verifiedDescriptionFieldKey || literalDescriptionField || layoutDescriptionField || firstTextField || firstStringField || null;
1160
+ }
1161
+ function noDescriptionFieldMessage(collectionSlug) {
1162
+ return `\`${collectionSlug}\` has no field for captured free text \u2014 no descriptionFieldKey is declared, no layout body field, and no text/string field outside its meta/header zone. Ask a platform admin to declare a descriptionFieldKey (or a body-zoned text field) on this collection before capturing into it.`;
1163
+ }
1164
+ function resolveProfileDescriptionField(col, fieldsOverride) {
1165
+ const fields = fieldsOverride ?? col.fields ?? [];
1166
+ return resolveDescriptionField(fields, col.descriptionFieldKey);
1167
+ }
1168
+ function resolveGenericDescriptionField(fieldsOverride) {
1169
+ return fieldsOverride ? resolveDescriptionField(fieldsOverride, void 0) : null;
1170
+ }
1171
+ function buildDataFromFieldsOrRefuse(fields, descriptionField, descriptionValue, collectionSlug) {
1172
+ if (!descriptionField) {
1173
+ const msg = noDescriptionFieldMessage(collectionSlug);
1174
+ const recovery = `Pass the text via an existing field in \`data\` (see \`collections action=get\` for ${collectionSlug}'s field list), or ask a platform admin to declare a descriptionFieldKey (or a body-zoned text field) on ${collectionSlug}.`;
1175
+ return {
1176
+ refusal: failureResult(msg, "VALIDATION_FAILED", `Collection '${collectionSlug}' has no field for free text.`, recovery)
1177
+ };
1178
+ }
1179
+ return { data: buildDataFromFields(fields, descriptionField, descriptionValue), descriptionField };
1180
+ }
1154
1181
  function isEmptyValue(v) {
1155
1182
  return v == null || (Array.isArray(v) ? v.length === 0 : String(v).trim() === "");
1156
1183
  }
@@ -1378,18 +1405,15 @@ var READINESS_PROVISIONAL_LINE = "Readiness is provisional: its sample omitted a
1378
1405
  // src/tools/smart-capture.ts
1379
1406
  var GENERIC_PROFILE = {
1380
1407
  governedDraft: false,
1381
- descriptionField: "description",
1408
+ descriptionField: null,
1409
+ // TEN-3704 F4: never a hard-coded "description" — see resolveGenericDescriptionField
1382
1410
  defaults: [],
1383
1411
  recommendedRelationTypes: ["related_to", "references"]
1384
1412
  };
1385
- async function buildRuntimeProfile(slug) {
1413
+ async function buildRuntimeProfile(slug, fieldsOverride) {
1386
1414
  const col = await getCollectionBySlug(slug);
1387
- if (!col) return GENERIC_PROFILE;
1388
- const fields = col.fields ?? [];
1389
- const layoutDescriptionField = fields.find(
1390
- (field) => field.zone === "body" || field.displayHint === "textarea" || field.displayHint === "section"
1391
- )?.key;
1392
- const descriptionField = col.descriptionFieldKey || layoutDescriptionField || "description";
1415
+ if (!col) return { ...GENERIC_PROFILE, descriptionField: resolveGenericDescriptionField(fieldsOverride) };
1416
+ const descriptionField = resolveProfileDescriptionField(col, fieldsOverride);
1393
1417
  return {
1394
1418
  governedDraft: col.governed === true,
1395
1419
  descriptionField,
@@ -1398,7 +1422,7 @@ async function buildRuntimeProfile(slug) {
1398
1422
  };
1399
1423
  }
1400
1424
  var profileCache = new ScopedCache(COLLECTION_CACHE_TTL_MS);
1401
- async function getProfile(slug) {
1425
+ async function getProfile(slug, fieldsOverride) {
1402
1426
  const scope = cacheScope();
1403
1427
  let map = profileCache.get(scope);
1404
1428
  if (!map) {
@@ -1407,7 +1431,7 @@ async function getProfile(slug) {
1407
1431
  }
1408
1432
  const cached = map.get(slug);
1409
1433
  if (cached) return cached;
1410
- const profile = await buildRuntimeProfile(slug);
1434
+ const profile = await buildRuntimeProfile(slug, fieldsOverride);
1411
1435
  map.set(slug, profile);
1412
1436
  return profile;
1413
1437
  }
@@ -1767,17 +1791,6 @@ var captureOutputSchema = z4.union([
1767
1791
  captureSinglePreviewOutputSchema,
1768
1792
  captureClassifierOnlyOutputSchema
1769
1793
  ]);
1770
- function buildDataFromFields(fields, descriptionField, descriptionValue) {
1771
- const data = {};
1772
- for (const field of fields) {
1773
- if (field.key === descriptionField) {
1774
- data[field.key] = descriptionValue;
1775
- } else {
1776
- data[field.key] = FIELD_TYPE_DEFAULTS[field.type] ?? null;
1777
- }
1778
- }
1779
- return data;
1780
- }
1781
1794
  var CAPTURE_ACTIONS = ["capture"];
1782
1795
  var captureCompoundSchema = z4.object({
1783
1796
  action: z4.enum(CAPTURE_ACTIONS).optional().default("capture").describe("Create a single knowledge entry."),
@@ -1905,7 +1918,7 @@ async function handleCapture(server, { collection, name, description, context, e
1905
1918
  )
1906
1919
  };
1907
1920
  }
1908
- const profile = await getProfile(resolvedCollection);
1921
+ const profile = await getProfile(resolvedCollection, col.fields);
1909
1922
  const groundingStart = Date.now();
1910
1923
  const groundingPromise = name || description ? kernelQuery("chain.capture", { name, description: description ?? "", collectionSlug: resolvedCollection }).catch(() => null) : Promise.resolve(null);
1911
1924
  const groundingRaw = await Promise.race([groundingPromise, new Promise((r) => setTimeout(() => r(null), 500))]);
@@ -1980,7 +1993,9 @@ ${groundingReport.duplicates.map((d) => `- **${d.entryId}** ${d.name} [${d.colle
1980
1993
  };
1981
1994
  }
1982
1995
  if (groundingEntry?.kind === "version") return captureVersionOrPreview(preview ?? false, { groundingEntry, name, description, data: userData, sourceRef, sourceExcerpt, links, groundingElapsedMs });
1983
- const data = buildDataFromFields(col.fields ?? [], profile.descriptionField, description);
1996
+ const built = buildDataFromFieldsOrRefuse(col.fields ?? [], profile.descriptionField, description, resolvedCollection);
1997
+ if ("refusal" in built) return built.refusal;
1998
+ const { data, descriptionField } = built;
1984
1999
  for (const def of profile.defaults) {
1985
2000
  if (def.value !== "infer" && def.value !== "today") {
1986
2001
  data[def.key] = def.value;
@@ -2011,7 +2026,7 @@ ${groundingReport.duplicates.map((d) => `- **${d.entryId}** ${d.name} [${d.colle
2011
2026
  }
2012
2027
  }
2013
2028
  if (userData && typeof userData === "object") Object.assign(data, userData);
2014
- data[profile.descriptionField || "description"] = description;
2029
+ data[descriptionField] = description;
2015
2030
  const colMeta = await getCollectionBySlug(resolvedCollection);
2016
2031
  const isWorkPackageCapture = canonicalKey === "work_package" || colMeta?.defaultCanonicalKey === "work_package";
2017
2032
  let entryWarnings = [];
@@ -15630,4 +15645,4 @@ export {
15630
15645
  createProductBrainServer,
15631
15646
  initFeatureFlags
15632
15647
  };
15633
- //# sourceMappingURL=chunk-YSYVDNEK.js.map
15648
+ //# sourceMappingURL=chunk-GBEBTAN6.js.map