@clien-ai/mcp 0.13.0 → 0.13.2
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/hypothesis-semantics.js +334 -11
- package/dist/hypothesis-semantics.js.map +1 -1
- package/dist/lib/excerpt-suitability.js +46 -0
- package/dist/lib/excerpt-suitability.js.map +1 -0
- package/dist/tools/collections.js +7 -3
- package/dist/tools/collections.js.map +1 -1
- package/dist/tools/content-type-display.js +11 -3
- package/dist/tools/content-type-display.js.map +1 -1
- package/dist/tools/failure-guidance.js +53 -5
- package/dist/tools/failure-guidance.js.map +1 -1
- package/dist/tools/market-sizing-proof.js +3 -1
- package/dist/tools/market-sizing-proof.js.map +1 -1
- package/dist/tools/receipt-children.js +4 -1
- package/dist/tools/receipt-children.js.map +1 -1
- package/dist/tools/report-digest.js +145 -47
- package/dist/tools/report-digest.js.map +1 -1
- package/dist/tools/research.js +4 -0
- package/dist/tools/research.js.map +1 -1
- package/dist/tools/scoped-research.js +23 -4
- package/dist/tools/scoped-research.js.map +1 -1
- package/dist/tools/status.js +4 -0
- package/dist/tools/status.js.map +1 -1
- package/dist/types/report.js +372 -17
- package/dist/types/report.js.map +1 -1
- package/package.json +1 -1
|
@@ -47,13 +47,14 @@
|
|
|
47
47
|
* rule, which a reader applies positionally.
|
|
48
48
|
*/
|
|
49
49
|
import { collapseWhitespace, safeInline, safeId, clipReceiptQuote, containsSpan, hasVisibleContent, RECEIPT_QUOTE_MAX, } from './render-safety.js';
|
|
50
|
-
import { canonicalContentType, contentTypeDeviationLine, contentTypeProvenanceLine, } from './content-type-display.js';
|
|
50
|
+
import { canonicalContentType, contentTypeDeviationLine, contentTypeProvenanceLine, hypothesisContentTypeProvenanceLine, } from './content-type-display.js';
|
|
51
51
|
import { readFinalHypothesisState } from './hypothesis-final.js';
|
|
52
|
-
import { readRobustnessCounts } from '../hypothesis-semantics.js';
|
|
52
|
+
import { finalClauseCoverageMatchesIntegrity, readRobustnessCounts, reportClaimNeedsIndependentMarketAuthority, sourceAuthenticatesMarketEconomics, } from '../hypothesis-semantics.js';
|
|
53
53
|
import { parseReceiptId, resolveSourceReceipt } from './receipt-children.js';
|
|
54
54
|
import { resolveMarketSignals } from './market-signals.js';
|
|
55
55
|
import { MCP_OVERVIEW_REF_CAP, MCP_OVERVIEW_SPAN_CAP, REPORT_PORTRAIT_PATHS, sanitizeMcpMarkdownTitle, sanitizeMcpReportTitle, } from '../types/report.js';
|
|
56
56
|
import { readSemanticTheme } from '../types/semantic-theme.js';
|
|
57
|
+
import { isDisplaySuitableExcerpt } from '../lib/excerpt-suitability.js';
|
|
57
58
|
/**
|
|
58
59
|
* Max claims rendered per spine. Above this the digest states how many were
|
|
59
60
|
* dropped and where the full list lives — never a silent cut. Sized so a typical
|
|
@@ -352,7 +353,10 @@ function resolveOverviewRef(ref, claimSpan, reportEvidence, forumThreads) {
|
|
|
352
353
|
const source = asRecord(reportEvidence[Number(match[1])]);
|
|
353
354
|
const href = safeReceiptUrl(source?.url);
|
|
354
355
|
const quote = str(source?.quote);
|
|
355
|
-
if (!source || !href || !quote || !overviewReceiptSupportsClaim(claimSpan, quote))
|
|
356
|
+
if (!source || !href || !quote || !isDisplaySuitableExcerpt(quote) || !overviewReceiptSupportsClaim(claimSpan, quote))
|
|
357
|
+
return null;
|
|
358
|
+
if (reportClaimNeedsIndependentMarketAuthority('summary', claimSpan) &&
|
|
359
|
+
!sourceAuthenticatesMarketEconomics(source))
|
|
356
360
|
return null;
|
|
357
361
|
return `${sourceId} -> ${href}`;
|
|
358
362
|
}
|
|
@@ -368,6 +372,7 @@ function resolveOverviewRef(ref, claimSpan, reportEvidence, forumThreads) {
|
|
|
368
372
|
if (str(receipt?.receiptId) !== receiptId ||
|
|
369
373
|
!excerpt ||
|
|
370
374
|
!hasVisibleContent(excerpt) ||
|
|
375
|
+
!isDisplaySuitableExcerpt(excerpt) ||
|
|
371
376
|
!overviewReceiptSupportsClaim(claimSpan, excerpt))
|
|
372
377
|
return [];
|
|
373
378
|
const href = safeReceiptUrl(thread?.url);
|
|
@@ -531,6 +536,13 @@ function resolvePersonaReceipt(rawClaim, personas) {
|
|
|
531
536
|
// additive field would start changing what a legacy reader sees.
|
|
532
537
|
return null;
|
|
533
538
|
}
|
|
539
|
+
else if (Object.prototype.hasOwnProperty.call(source, 'quote') &&
|
|
540
|
+
!isDisplaySuitableExcerpt(source.quote)) {
|
|
541
|
+
// A legacy parent has no child identity to fall back to. Its exact cached quote is the only
|
|
542
|
+
// evidence the positional receipt authenticates, so an unsuitable quote withdraws the
|
|
543
|
+
// grounding rather than leaving a bare URL carrying a GROUNDED label.
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
534
546
|
return {
|
|
535
547
|
sourceId: `RCP-p${personaIndex}-s${sourceIndex}`,
|
|
536
548
|
href,
|
|
@@ -662,7 +674,7 @@ function resolveReportReceipt(rawClaim, reportEvidence) {
|
|
|
662
674
|
// non-empty string, clears the guard, and the claim earns `cited → RRCP-s0` plus an
|
|
663
675
|
// `Attested window: ""` line. A citation marker over a window nobody can read is the
|
|
664
676
|
// presentation this resolver exists to withhold.
|
|
665
|
-
if (!hasVisibleContent(quote))
|
|
677
|
+
if (!hasVisibleContent(quote) || !isDisplaySuitableExcerpt(quote))
|
|
666
678
|
return null;
|
|
667
679
|
}
|
|
668
680
|
else {
|
|
@@ -677,7 +689,12 @@ function resolveReportReceipt(rawClaim, reportEvidence) {
|
|
|
677
689
|
if (!Number.isSafeInteger(sourceIndex))
|
|
678
690
|
return null;
|
|
679
691
|
const source = asRecord(reportEvidence[sourceIndex]);
|
|
680
|
-
if (!source ||
|
|
692
|
+
if (!source ||
|
|
693
|
+
!safeReceiptUrl(source.url) ||
|
|
694
|
+
(kind === 'grounded' && !isDisplaySuitableExcerpt(source.quote)))
|
|
695
|
+
return null;
|
|
696
|
+
if (reportClaimNeedsIndependentMarketAuthority(claim?.section, claim?.text) &&
|
|
697
|
+
!sourceAuthenticatesMarketEconomics(source))
|
|
681
698
|
return null;
|
|
682
699
|
return { kind, sourceId, quote };
|
|
683
700
|
}
|
|
@@ -1062,7 +1079,15 @@ function renderReceiptPools(reportData, channel) {
|
|
|
1062
1079
|
// No `safeInline` and no FUL-253 concern: `canonicalContentType` admits three literal tokens
|
|
1063
1080
|
// and nothing else, which is strictly stronger than flattening — a newline cannot survive a
|
|
1064
1081
|
// set-membership test, so this field cannot forge a row.
|
|
1065
|
-
|
|
1082
|
+
const sourceTypeLine = section === 'hypothesis'
|
|
1083
|
+
? hypothesisContentTypeProvenanceLine(source?.contentType, source?.publisherSubjectRelationship)
|
|
1084
|
+
: contentTypeProvenanceLine(source?.contentType);
|
|
1085
|
+
const marketAuthority = source?.marketEconomicsAuthority === 'independent' ||
|
|
1086
|
+
source?.marketEconomicsAuthority === 'commercial_report_seller'
|
|
1087
|
+
? source.marketEconomicsAuthority
|
|
1088
|
+
: 'unknown';
|
|
1089
|
+
const marketAuthorityLine = `\n market economics authority: ${marketAuthority}`;
|
|
1090
|
+
return `- RRCP-s${n} (${section}) — ${platform} — ${published} — ${url}${sourceTypeLine}${marketAuthorityLine}`;
|
|
1066
1091
|
});
|
|
1067
1092
|
if (personaReceipts.length === 0 && evidenceReceipts.length === 0) {
|
|
1068
1093
|
return ('### Receipt pools — EMPTY\n' +
|
|
@@ -1198,30 +1223,21 @@ function renderCommunityPatterns(reportData, channel) {
|
|
|
1198
1223
|
capNote(shown.length, threads.length, channel, 'report_data.forumResearch.threads'));
|
|
1199
1224
|
}
|
|
1200
1225
|
/**
|
|
1201
|
-
*
|
|
1202
|
-
*
|
|
1203
|
-
* Legacy Markdown lacks this grammar and still receives the compatibility projection above.
|
|
1226
|
+
* Persisted Markdown predates the display projection and may contain source excerpts verbatim.
|
|
1227
|
+
* Remove its entire discussions subsection before rebuilding the safe structured projection.
|
|
1204
1228
|
*/
|
|
1205
|
-
function
|
|
1206
|
-
const
|
|
1207
|
-
|
|
1208
|
-
return false;
|
|
1209
|
-
const lines = reportMarkdown.split('\n');
|
|
1210
|
-
const heading = lines.indexOf('### Relevant Discussions');
|
|
1229
|
+
function removeRelevantDiscussionsOutput(markdown) {
|
|
1230
|
+
const lines = markdown.split('\n');
|
|
1231
|
+
const heading = lines.findIndex((line) => /^###\s+Relevant Discussions\s*$/iu.test(line.trim()));
|
|
1211
1232
|
if (heading < 0)
|
|
1212
|
-
return
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
foundRows += 1;
|
|
1221
|
-
index += 2;
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
|
-
return foundRows === expectedRows;
|
|
1233
|
+
return markdown;
|
|
1234
|
+
let end = heading + 1;
|
|
1235
|
+
while (end < lines.length && !/^#{1,3}\s+/u.test(lines[end] ?? ''))
|
|
1236
|
+
end += 1;
|
|
1237
|
+
return [...lines.slice(0, heading), ...lines.slice(end)]
|
|
1238
|
+
.join('\n')
|
|
1239
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
1240
|
+
.trimEnd();
|
|
1225
1241
|
}
|
|
1226
1242
|
/**
|
|
1227
1243
|
* The anti-sycophancy readout — deterministically computed from the interviews,
|
|
@@ -1752,7 +1768,9 @@ function renderInsightCrossReferences(reportData) {
|
|
|
1752
1768
|
const match = sourceId ? /^RRCP-s(0|[1-9]\d*)$/.exec(sourceId) : null;
|
|
1753
1769
|
const index = match ? Number(match[1]) : Number.NaN;
|
|
1754
1770
|
const source = Number.isSafeInteger(index) ? asRecord(evidence[index]) : null;
|
|
1755
|
-
const
|
|
1771
|
+
const authorityEligible = !reportClaimNeedsIndependentMarketAuthority('summary', statement) ||
|
|
1772
|
+
sourceAuthenticatesMarketEconomics(source);
|
|
1773
|
+
const href = source && authorityEligible ? safeReceiptUrl(source.url) : null;
|
|
1756
1774
|
return sourceId && href
|
|
1757
1775
|
? `${label} → ${sourceId} — ${href}`
|
|
1758
1776
|
: `${label} (no resolvable receipt)`;
|
|
@@ -1765,16 +1783,24 @@ function renderInsightCrossReferences(reportData) {
|
|
|
1765
1783
|
return ('## Insight source cross-references — model-authored findings, code-resolved destinations\n\n' +
|
|
1766
1784
|
rows.join('\n'));
|
|
1767
1785
|
}
|
|
1768
|
-
function resolveCrossCuttingEvidence(reportData, ref) {
|
|
1786
|
+
function resolveCrossCuttingEvidence(reportData, ref, claim) {
|
|
1769
1787
|
if (ref.kind === 'report_evidence') {
|
|
1770
1788
|
const sourceId = safeId(ref.sourceId);
|
|
1771
1789
|
const match = sourceId ? /^RRCP-s(0|[1-9]\d*)$/.exec(sourceId) : null;
|
|
1772
1790
|
const source = match ? asRecord(asArray(reportData.reportEvidence)[Number(match[1])]) : null;
|
|
1773
1791
|
if (!source)
|
|
1774
1792
|
return null;
|
|
1793
|
+
if (reportClaimNeedsIndependentMarketAuthority('summary', claim) &&
|
|
1794
|
+
!sourceAuthenticatesMarketEconomics(source))
|
|
1795
|
+
return null;
|
|
1775
1796
|
const label = safeInline(source.platform, MODEL_FRAMING_TEXT_MAX) || 'Web source';
|
|
1776
1797
|
const href = safeReceiptUrl(source.url);
|
|
1777
|
-
const
|
|
1798
|
+
const hasQuote = Object.prototype.hasOwnProperty.call(source, 'quote');
|
|
1799
|
+
if (hasQuote && !isDisplaySuitableExcerpt(source.quote))
|
|
1800
|
+
return null;
|
|
1801
|
+
const quote = hasQuote
|
|
1802
|
+
? safeInline(source.quote, RECEIPT_QUOTE_MAX)
|
|
1803
|
+
: null;
|
|
1778
1804
|
if (!href)
|
|
1779
1805
|
return null;
|
|
1780
1806
|
return `${label}${href ? ` — ${href}` : ''}${quote ? `\n > ${quote}` : ''}`;
|
|
@@ -1795,7 +1821,9 @@ function resolveCrossCuttingEvidence(reportData, ref) {
|
|
|
1795
1821
|
const match = matches[0];
|
|
1796
1822
|
const label = safeInline(match.thread?.platform, MODEL_FRAMING_TEXT_MAX) || 'Community source';
|
|
1797
1823
|
const href = safeReceiptUrl(match.thread?.url);
|
|
1798
|
-
const quote =
|
|
1824
|
+
const quote = isDisplaySuitableExcerpt(match.receipt?.excerpt)
|
|
1825
|
+
? safeInline(match.receipt?.excerpt, RECEIPT_QUOTE_MAX)
|
|
1826
|
+
: null;
|
|
1799
1827
|
if (!quote)
|
|
1800
1828
|
return null;
|
|
1801
1829
|
return `${label}${href ? ` — ${href}` : ''}${quote ? `\n > ${quote}` : ''}`;
|
|
@@ -1837,7 +1865,7 @@ function renderCrossCuttingFindings(reportData) {
|
|
|
1837
1865
|
const rawRefs = asArray(finding?.evidenceRefs).slice(0, 8);
|
|
1838
1866
|
const resolvedRefs = rawRefs.flatMap((rawRef) => {
|
|
1839
1867
|
const ref = asRecord(rawRef);
|
|
1840
|
-
const rendered = ref ? resolveCrossCuttingEvidence(reportData, ref) : null;
|
|
1868
|
+
const rendered = ref ? resolveCrossCuttingEvidence(reportData, ref, claim) : null;
|
|
1841
1869
|
return ref && rendered ? [{ ref, rendered }] : [];
|
|
1842
1870
|
});
|
|
1843
1871
|
const methods = new Set(resolvedRefs.flatMap(({ ref }) => ref.kind === 'report_evidence' ? ['web']
|
|
@@ -1861,10 +1889,47 @@ function renderCrossCuttingFindings(reportData) {
|
|
|
1861
1889
|
function removeLegacyInsightOutput(markdown) {
|
|
1862
1890
|
return markdown
|
|
1863
1891
|
.replace(/^## (?:Key )?Insights(?: \(legacy\))?\s*\n[\s\S]*?(?=^## |$(?![\s\S]))/gm, '')
|
|
1892
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
1893
|
+
.trimEnd();
|
|
1894
|
+
}
|
|
1895
|
+
/** Persisted cross-cutting Markdown may contain raw receipt excerpts; rebuild it from projection. */
|
|
1896
|
+
function removeCrossCuttingFindingsOutput(markdown) {
|
|
1897
|
+
return markdown
|
|
1864
1898
|
.replace(/^## Cross-cutting findings\s*\n[\s\S]*?(?=^## |$(?![\s\S]))/gm, '')
|
|
1865
1899
|
.replace(/\n{3,}/g, '\n\n')
|
|
1866
1900
|
.trimEnd();
|
|
1867
1901
|
}
|
|
1902
|
+
/** Historical agent Markdown copied cached quotes into these sections; the digest replaces them. */
|
|
1903
|
+
function removeHistoricalSourceBearingOutput(markdown) {
|
|
1904
|
+
return markdown
|
|
1905
|
+
.replace(/^## (?:Claim Provenance|Report Sources)\s*\n[\s\S]*?(?=^## |$(?![\s\S]))/gm, '')
|
|
1906
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
1907
|
+
.trimEnd();
|
|
1908
|
+
}
|
|
1909
|
+
/** Rebuild historical source sections from the projected claim registries, never persisted text. */
|
|
1910
|
+
function renderProjectedSourceBearingOutput(reportData, channel) {
|
|
1911
|
+
const sections = [];
|
|
1912
|
+
if (asArray(reportData.claims).length > 0) {
|
|
1913
|
+
sections.push(`## Claim Provenance\n\n${renderPersonaSpine(reportData, channel)}`);
|
|
1914
|
+
}
|
|
1915
|
+
if (asArray(reportData.reportClaims).length > 0) {
|
|
1916
|
+
sections.push(`## Report Sources\n\n${renderReportSpine(reportData, channel)}`);
|
|
1917
|
+
}
|
|
1918
|
+
else {
|
|
1919
|
+
const evidenceRows = asArray(reportData.reportEvidence).slice(0, LIST_RENDER_CAP).flatMap((raw, index) => {
|
|
1920
|
+
const source = asRecord(raw);
|
|
1921
|
+
const href = safeReceiptUrl(source?.url);
|
|
1922
|
+
if (!source || !href)
|
|
1923
|
+
return [];
|
|
1924
|
+
const section = safeInline(source.section, 40);
|
|
1925
|
+
const platform = safeInline(source.platform, 40) ?? 'Source';
|
|
1926
|
+
return [`- RRCP-s${index}${section ? ` (${section})` : ''} — ${platform} — ${href}`];
|
|
1927
|
+
});
|
|
1928
|
+
if (evidenceRows.length > 0)
|
|
1929
|
+
sections.push(`## Report Sources\n\n${evidenceRows.join('\n')}`);
|
|
1930
|
+
}
|
|
1931
|
+
return sections.join('\n\n');
|
|
1932
|
+
}
|
|
1868
1933
|
function labelLegacyInsightOutput(markdown) {
|
|
1869
1934
|
return markdown.replace(/^## (?:Key )?Insights$/gm, '## Insights (legacy)');
|
|
1870
1935
|
}
|
|
@@ -2258,7 +2323,19 @@ function renderRedditRetrieval(reportData) {
|
|
|
2258
2323
|
* digest and a clean one must not look the same.
|
|
2259
2324
|
*/
|
|
2260
2325
|
export function buildTrustDigest(reportData, channel) {
|
|
2261
|
-
const
|
|
2326
|
+
const rawData = asRecord(reportData);
|
|
2327
|
+
const outerVersion = rawData?.hypothesisSemanticsVersion;
|
|
2328
|
+
const outerVersionPresent = rawData !== null && Object.prototype.hasOwnProperty.call(rawData, 'hypothesisSemanticsVersion');
|
|
2329
|
+
const data = outerVersionPresent && outerVersion !== 1
|
|
2330
|
+
? {
|
|
2331
|
+
...rawData,
|
|
2332
|
+
hypothesisResults: asArray(rawData.hypothesisResults).map((raw) => {
|
|
2333
|
+
const row = asRecord(raw);
|
|
2334
|
+
const final = asRecord(row?.final);
|
|
2335
|
+
return row && (outerVersion !== 2 || final?.version !== 2) ? { ...row, final: { version: 2 } } : raw;
|
|
2336
|
+
}),
|
|
2337
|
+
}
|
|
2338
|
+
: rawData;
|
|
2262
2339
|
if (!data)
|
|
2263
2340
|
return NO_TRUST_DATA;
|
|
2264
2341
|
const sections = [
|
|
@@ -2354,34 +2431,55 @@ function renderReportGaps(reportData) {
|
|
|
2354
2431
|
* that may resemble it.
|
|
2355
2432
|
*/
|
|
2356
2433
|
export function composeReportText(reportMarkdown, reportData, channel) {
|
|
2357
|
-
const rationale = renderVerdictRationale(reportData);
|
|
2358
|
-
const framing = renderModelFraming(reportData);
|
|
2359
2434
|
const data = asRecord(reportData);
|
|
2360
|
-
const
|
|
2361
|
-
|
|
2435
|
+
const rows = asArray(data?.hypothesisResults);
|
|
2436
|
+
const semanticsVersionPresent = data !== null &&
|
|
2437
|
+
Object.prototype.hasOwnProperty.call(data, 'hypothesisSemanticsVersion');
|
|
2438
|
+
const rowClaimsV2 = rows.some((raw) => {
|
|
2439
|
+
const row = asRecord(raw);
|
|
2440
|
+
return asRecord(row?.final)?.version === 2 ||
|
|
2441
|
+
(row !== null && Object.prototype.hasOwnProperty.call(row, 'clauseIntegrity'));
|
|
2442
|
+
});
|
|
2443
|
+
const requiresClauseIntegrity = rowClaimsV2 ||
|
|
2444
|
+
(semanticsVersionPresent && data?.hypothesisSemanticsVersion !== 1);
|
|
2445
|
+
const clauseFailure = requiresClauseIntegrity && (rows.length === 0 ||
|
|
2446
|
+
rows.some((raw) => {
|
|
2447
|
+
const final = asRecord(asRecord(raw)?.final);
|
|
2448
|
+
return final?.version !== 2 ||
|
|
2449
|
+
!finalClauseCoverageMatchesIntegrity(raw, data) ||
|
|
2450
|
+
final.clauseCoverage === 'invalid' ||
|
|
2451
|
+
final.clauseCoverage !== 'complete' && final.originalVerdict !== final.verdict;
|
|
2452
|
+
}));
|
|
2453
|
+
const rationale = clauseFailure ? '' : renderVerdictRationale(reportData);
|
|
2454
|
+
const framing = clauseFailure ? '' : renderModelFraming(reportData);
|
|
2455
|
+
const sourceMarkdown = clauseFailure
|
|
2456
|
+
? '# Report\n\nThis run did not establish every material part of at least one hypothesis. Treat the affected hypotheses as unresolved.'
|
|
2457
|
+
: reportMarkdown;
|
|
2458
|
+
const markdownTitle = sourceMarkdown.startsWith('# ')
|
|
2459
|
+
? sourceMarkdown.slice(2, sourceMarkdown.indexOf('\n') >= 0 ? sourceMarkdown.indexOf('\n') : undefined)
|
|
2362
2460
|
: undefined;
|
|
2363
2461
|
const safeTitle = data?.reportTitle === undefined
|
|
2364
2462
|
? normalizeLegacyMcpHeading(markdownTitle)
|
|
2365
2463
|
: escapeMcpMarkdownHeading(sanitizeMcpReportTitle(data.reportTitle));
|
|
2366
|
-
const titledReportMarkdown =
|
|
2367
|
-
? `# ${safeTitle}${
|
|
2368
|
-
:
|
|
2464
|
+
const titledReportMarkdown = sourceMarkdown.startsWith('# ')
|
|
2465
|
+
? `# ${safeTitle}${sourceMarkdown.includes('\n') ? sourceMarkdown.slice(sourceMarkdown.indexOf('\n')) : ''}`
|
|
2466
|
+
: sourceMarkdown;
|
|
2467
|
+
const excerptFreeMarkdown = removeHistoricalSourceBearingOutput(removeCrossCuttingFindingsOutput(removeRelevantDiscussionsOutput(titledReportMarkdown)));
|
|
2369
2468
|
const hasCrossCutting = Boolean(data && Object.prototype.hasOwnProperty.call(data, 'crossCuttingFindings'));
|
|
2370
2469
|
const safeReportMarkdown = hasCrossCutting
|
|
2371
|
-
? removeLegacyInsightOutput(
|
|
2372
|
-
: labelLegacyInsightOutput(
|
|
2470
|
+
? removeLegacyInsightOutput(excerptFreeMarkdown)
|
|
2471
|
+
: labelLegacyInsightOutput(excerptFreeMarkdown);
|
|
2373
2472
|
const insightReferences = data && !hasCrossCutting ? renderInsightCrossReferences(data) : '';
|
|
2374
2473
|
const crossCutting = data && hasCrossCutting ? renderCrossCuttingFindings(data) : '';
|
|
2474
|
+
const sourceBearing = data ? renderProjectedSourceBearingOutput(data, channel) : '';
|
|
2375
2475
|
// Community themes, sentiment, and source titles are producer/source-authored framing, not
|
|
2376
2476
|
// machine-checked claims. Keep them above the authority separator while preserving the literal
|
|
2377
2477
|
// Theme → Sentiment → Discussion contract for every full-report caller.
|
|
2378
|
-
const communityPatterns = data
|
|
2379
|
-
? renderCommunityPatterns(data, channel)
|
|
2380
|
-
: '';
|
|
2478
|
+
const communityPatterns = data ? renderCommunityPatterns(data, channel) : '';
|
|
2381
2479
|
// FUL-586: the completeness banner goes FIRST — before the prose it is a caveat about.
|
|
2382
2480
|
const gaps = renderReportGaps(reportData);
|
|
2383
2481
|
return (`${gaps ? `${gaps}\n\n---\n\n` : ''}` +
|
|
2384
|
-
`${safeReportMarkdown}${insightReferences ? `\n\n${insightReferences}` : ''}${crossCutting ? `\n\n${crossCutting}` : ''}${communityPatterns ? `\n\n${communityPatterns}` : ''}${framing ? `\n\n${framing}` : ''}${rationale ? `\n\n${rationale}` : ''}` +
|
|
2482
|
+
`${safeReportMarkdown}${insightReferences ? `\n\n${insightReferences}` : ''}${crossCutting ? `\n\n${crossCutting}` : ''}${sourceBearing ? `\n\n${sourceBearing}` : ''}${communityPatterns ? `\n\n${communityPatterns}` : ''}${framing ? `\n\n${framing}` : ''}${rationale ? `\n\n${rationale}` : ''}` +
|
|
2385
2483
|
`\n\n---\n\n${buildTrustDigest(reportData, channel)}`);
|
|
2386
2484
|
}
|
|
2387
2485
|
/** Keep a model-authored structured title inert when replacing the report Markdown H1. */
|