scientify 1.12.2 → 1.13.1
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/README.md +3 -1
- package/dist/src/hooks/research-mode.d.ts.map +1 -1
- package/dist/src/hooks/research-mode.js +4 -3
- package/dist/src/hooks/research-mode.js.map +1 -1
- package/dist/src/knowledge-state/store.d.ts.map +1 -1
- package/dist/src/knowledge-state/store.js +105 -36
- package/dist/src/knowledge-state/store.js.map +1 -1
- package/dist/src/knowledge-state/types.d.ts +28 -0
- package/dist/src/knowledge-state/types.d.ts.map +1 -1
- package/dist/src/literature/subscription-state.d.ts.map +1 -1
- package/dist/src/literature/subscription-state.js +652 -39
- package/dist/src/literature/subscription-state.js.map +1 -1
- package/dist/src/research-subscriptions/prompt.d.ts.map +1 -1
- package/dist/src/research-subscriptions/prompt.js +13 -7
- package/dist/src/research-subscriptions/prompt.js.map +1 -1
- package/dist/src/tools/scientify-literature-state.d.ts +32 -0
- package/dist/src/tools/scientify-literature-state.d.ts.map +1 -1
- package/dist/src/tools/scientify-literature-state.js +94 -1
- package/dist/src/tools/scientify-literature-state.js.map +1 -1
- package/package.json +1 -1
- package/skills/research-subscription/SKILL.md +7 -6
|
@@ -16,6 +16,12 @@ const MAX_STRICT_FULLTEXT_ATTEMPTS = 5;
|
|
|
16
16
|
const ARXIV_API_URL = "https://export.arxiv.org/api/query";
|
|
17
17
|
const STRICT_EMPTY_FALLBACK_MAX_RESULTS = 12;
|
|
18
18
|
const STRICT_EMPTY_FALLBACK_MAX_QUERIES = 4;
|
|
19
|
+
const DEFAULT_STRICT_CANDIDATE_POOL = 24;
|
|
20
|
+
const DEFAULT_STRICT_MIN_CORE_FLOOR = 3;
|
|
21
|
+
const TIER_A_RATIO = 0.5;
|
|
22
|
+
const TIER_B_RATIO = 0.35;
|
|
23
|
+
const TIER_C_RATIO = 0.15;
|
|
24
|
+
const REFLECTION_MAX_ADDED_PAPERS = 2;
|
|
19
25
|
const FEEDBACK_SIGNAL_DELTA = {
|
|
20
26
|
read: 1,
|
|
21
27
|
skip: -1,
|
|
@@ -390,6 +396,98 @@ function tokenizeKeywords(raw) {
|
|
|
390
396
|
}
|
|
391
397
|
return [...seen];
|
|
392
398
|
}
|
|
399
|
+
function inferTopicAliases(tokens) {
|
|
400
|
+
const normalized = tokens
|
|
401
|
+
.map((token) => token.toLowerCase())
|
|
402
|
+
.filter((token) => /^[a-z][a-z0-9_-]*$/.test(token))
|
|
403
|
+
.slice(0, 6);
|
|
404
|
+
if (normalized.length < 3)
|
|
405
|
+
return [];
|
|
406
|
+
const aliases = new Set();
|
|
407
|
+
const [a, b, c] = normalized;
|
|
408
|
+
if (a.length >= 2 && b.length >= 1 && c.length >= 1) {
|
|
409
|
+
aliases.add(`${a.slice(0, 2)}${b[0]}${c[0]}`);
|
|
410
|
+
}
|
|
411
|
+
aliases.add(`${a[0]}${b[0]}${c[0]}`);
|
|
412
|
+
const hasLow = normalized.includes("low");
|
|
413
|
+
const hasRank = normalized.includes("rank");
|
|
414
|
+
const hasAdapt = normalized.some((token) => token.startsWith("adapt"));
|
|
415
|
+
if (hasLow && hasRank && hasAdapt)
|
|
416
|
+
aliases.add("lora");
|
|
417
|
+
return [...aliases].filter((alias) => alias.length >= 3 && alias.length <= 8);
|
|
418
|
+
}
|
|
419
|
+
function buildScoringTokens(topic) {
|
|
420
|
+
const stopwords = new Set([
|
|
421
|
+
"from",
|
|
422
|
+
"with",
|
|
423
|
+
"without",
|
|
424
|
+
"first",
|
|
425
|
+
"basics",
|
|
426
|
+
"basic",
|
|
427
|
+
"foundational",
|
|
428
|
+
"foundation",
|
|
429
|
+
"seminal",
|
|
430
|
+
"classic",
|
|
431
|
+
"avoid",
|
|
432
|
+
"benchmark",
|
|
433
|
+
"only",
|
|
434
|
+
"prefer",
|
|
435
|
+
"authoritative",
|
|
436
|
+
"latest",
|
|
437
|
+
"recent",
|
|
438
|
+
"paper",
|
|
439
|
+
"papers",
|
|
440
|
+
"study",
|
|
441
|
+
"works",
|
|
442
|
+
]);
|
|
443
|
+
const rawTokens = tokenizeKeywords(topic);
|
|
444
|
+
const aliases = inferTopicAliases(rawTokens);
|
|
445
|
+
const base = rawTokens.filter((token) => token.length >= 4 && !stopwords.has(token));
|
|
446
|
+
if (base.length > 0)
|
|
447
|
+
return [...new Set([...base, ...aliases])].slice(0, 10);
|
|
448
|
+
return [...new Set([...rawTokens, ...aliases])].slice(0, 10);
|
|
449
|
+
}
|
|
450
|
+
function buildRetrievalSeedTokens(topic) {
|
|
451
|
+
const directiveWords = new Set([
|
|
452
|
+
"from",
|
|
453
|
+
"with",
|
|
454
|
+
"without",
|
|
455
|
+
"first",
|
|
456
|
+
"basics",
|
|
457
|
+
"basic",
|
|
458
|
+
"foundational",
|
|
459
|
+
"foundation",
|
|
460
|
+
"seminal",
|
|
461
|
+
"classic",
|
|
462
|
+
"avoid",
|
|
463
|
+
"benchmark",
|
|
464
|
+
"only",
|
|
465
|
+
"prefer",
|
|
466
|
+
"authoritative",
|
|
467
|
+
"latest",
|
|
468
|
+
"recent",
|
|
469
|
+
"paper",
|
|
470
|
+
"papers",
|
|
471
|
+
"study",
|
|
472
|
+
"works",
|
|
473
|
+
"strict",
|
|
474
|
+
"fast",
|
|
475
|
+
]);
|
|
476
|
+
const rawTokens = tokenizeKeywords(topic);
|
|
477
|
+
const aliases = inferTopicAliases(rawTokens);
|
|
478
|
+
const tokens = rawTokens
|
|
479
|
+
.map((token) => token.toLowerCase())
|
|
480
|
+
.filter((token) => token.length >= 3 && !directiveWords.has(token));
|
|
481
|
+
return [...new Set([...tokens, ...aliases])].slice(0, 10);
|
|
482
|
+
}
|
|
483
|
+
const FOUNDATIONAL_HINT_RE = /\b(foundational|foundation|seminal|classic|groundwork|original paper|from basics|start from basics|first principles)\b|\u57fa\u7840|\u5950\u57fa|\u7ecf\u5178|\u539f\u59cb/u;
|
|
484
|
+
const AVOID_BENCHMARK_HINT_RE = /\b(avoid benchmark|benchmark-only|no benchmark|less benchmark|not benchmark only)\b|\u5c11\u63a8.*benchmark|\u4e0d\u8981.*benchmark/u;
|
|
485
|
+
const SURVEY_HINT_RE = /\b(survey|review|taxonomy|overview|tutorial)\b|\u7efc\u8ff0|\u8bc4\u8ff0/u;
|
|
486
|
+
const AUTHORITY_HINT_RE = /\b(authoritative|high impact|top-tier|highly cited|landmark|canonical)\b|\u6743\u5a01|\u9ad8\u5f15\u7528/u;
|
|
487
|
+
const RECENT_HINT_RE = /\b(latest|recent|state[- ]of[- ]the[- ]art|newest)\b|\u6700\u65b0|\u8fd1\u671f/u;
|
|
488
|
+
const BENCHMARK_WORD_RE = /\b(benchmark|leaderboard|dataset|evaluation)\b/i;
|
|
489
|
+
const METHOD_WORD_RE = /\b(method|approach|adaptation|training|fine[- ]?tuning|optimization|algorithm|framework|model)\b/i;
|
|
490
|
+
const SURVEY_WORD_RE = /\b(survey|review|taxonomy|overview|tutorial)\b/i;
|
|
393
491
|
function decodeXmlEntities(raw) {
|
|
394
492
|
return raw
|
|
395
493
|
.replace(/</g, "<")
|
|
@@ -426,16 +524,7 @@ function parseArxivAtomCandidates(xml) {
|
|
|
426
524
|
}
|
|
427
525
|
return parsed;
|
|
428
526
|
}
|
|
429
|
-
function
|
|
430
|
-
const normalizedTopic = normalizeText(topic);
|
|
431
|
-
const queries = [normalizedTopic];
|
|
432
|
-
const tokens = tokenizeKeywords(normalizedTopic).filter((token) => token.length >= 3).slice(0, 8);
|
|
433
|
-
if (tokens.length >= 2) {
|
|
434
|
-
queries.push(tokens.join(" "));
|
|
435
|
-
}
|
|
436
|
-
if (tokens.length >= 4) {
|
|
437
|
-
queries.push(tokens.slice(0, 4).join(" "));
|
|
438
|
-
}
|
|
527
|
+
function dedupeQueries(queries, limit) {
|
|
439
528
|
const seen = new Set();
|
|
440
529
|
const deduped = [];
|
|
441
530
|
for (const query of queries) {
|
|
@@ -444,28 +533,139 @@ function buildStrictFallbackQueries(topic) {
|
|
|
444
533
|
continue;
|
|
445
534
|
seen.add(key);
|
|
446
535
|
deduped.push(query);
|
|
536
|
+
if (deduped.length >= limit)
|
|
537
|
+
break;
|
|
447
538
|
}
|
|
448
|
-
return deduped
|
|
539
|
+
return deduped;
|
|
540
|
+
}
|
|
541
|
+
function buildStrictFallbackQueries(topic) {
|
|
542
|
+
const seedTokens = buildRetrievalSeedTokens(topic);
|
|
543
|
+
const normalizedTopic = seedTokens.length > 0 ? seedTokens.join(" ") : normalizeText(topic);
|
|
544
|
+
const tokens = seedTokens.length > 0 ? seedTokens : tokenizeKeywords(normalizedTopic).filter((token) => token.length >= 3).slice(0, 10);
|
|
545
|
+
const queries = [normalizedTopic];
|
|
546
|
+
if (tokens.length >= 2)
|
|
547
|
+
queries.push(tokens.slice(0, 4).join(" "));
|
|
548
|
+
if (tokens.length >= 3)
|
|
549
|
+
queries.push(tokens.slice(0, 3).join(" "));
|
|
550
|
+
return dedupeQueries(queries, STRICT_EMPTY_FALLBACK_MAX_QUERIES);
|
|
551
|
+
}
|
|
552
|
+
function buildTieredFallbackQueries(topic) {
|
|
553
|
+
const seedTokens = buildRetrievalSeedTokens(topic);
|
|
554
|
+
const normalizedTopic = seedTokens.length > 0 ? seedTokens.join(" ") : normalizeText(topic);
|
|
555
|
+
const tokens = seedTokens.length > 0 ? seedTokens : tokenizeKeywords(normalizedTopic).filter((token) => token.length >= 3).slice(0, 10);
|
|
556
|
+
const tierA = buildStrictFallbackQueries(topic);
|
|
557
|
+
const tierB = dedupeQueries([
|
|
558
|
+
...tokens.slice(0, 6).map((token) => `${token} adaptation`),
|
|
559
|
+
...tokens.slice(0, 6).map((token) => `${token} method`),
|
|
560
|
+
...tokens.slice(0, 4).map((token) => `${token} framework`),
|
|
561
|
+
tokens.slice(0, 4).join(" "),
|
|
562
|
+
], STRICT_EMPTY_FALLBACK_MAX_QUERIES);
|
|
563
|
+
const tierC = dedupeQueries([
|
|
564
|
+
...tokens.slice(0, 5).map((token) => `${token} transfer learning`),
|
|
565
|
+
...tokens.slice(0, 5).map((token) => `${token} benchmark`),
|
|
566
|
+
...tokens.slice(0, 5).map((token) => `${token} retrieval`),
|
|
567
|
+
`${normalizedTopic} cross domain`,
|
|
568
|
+
], STRICT_EMPTY_FALLBACK_MAX_QUERIES);
|
|
569
|
+
return {
|
|
570
|
+
tierA: tierA.length > 0 ? tierA : [normalizedTopic],
|
|
571
|
+
tierB,
|
|
572
|
+
tierC,
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
function inferRequirementProfile(raw) {
|
|
576
|
+
const text = normalizeText(raw);
|
|
577
|
+
return {
|
|
578
|
+
foundationalFirst: FOUNDATIONAL_HINT_RE.test(text),
|
|
579
|
+
avoidBenchmarkOnly: AVOID_BENCHMARK_HINT_RE.test(text),
|
|
580
|
+
preferSurvey: SURVEY_HINT_RE.test(text),
|
|
581
|
+
preferAuthority: AUTHORITY_HINT_RE.test(text),
|
|
582
|
+
preferRecent: RECENT_HINT_RE.test(text),
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
function inferCandidateYear(paper) {
|
|
586
|
+
if (paper.published) {
|
|
587
|
+
const ts = Date.parse(paper.published);
|
|
588
|
+
if (Number.isFinite(ts))
|
|
589
|
+
return new Date(ts).getUTCFullYear();
|
|
590
|
+
}
|
|
591
|
+
const modern = paper.id.match(/:(\d{2})(\d{2})\./);
|
|
592
|
+
if (modern?.[1]) {
|
|
593
|
+
const yy = Number.parseInt(modern[1], 10);
|
|
594
|
+
if (Number.isFinite(yy))
|
|
595
|
+
return 2000 + yy;
|
|
596
|
+
}
|
|
597
|
+
return undefined;
|
|
598
|
+
}
|
|
599
|
+
function isBenchmarkOnlyPaper(paper) {
|
|
600
|
+
const text = `${paper.title} ${paper.summary ?? ""}`;
|
|
601
|
+
return BENCHMARK_WORD_RE.test(text) && !METHOD_WORD_RE.test(text);
|
|
602
|
+
}
|
|
603
|
+
function isSurveyPaper(paper) {
|
|
604
|
+
const text = `${paper.title} ${paper.summary ?? ""}`;
|
|
605
|
+
return SURVEY_WORD_RE.test(text);
|
|
606
|
+
}
|
|
607
|
+
function isFoundationalPaper(args) {
|
|
608
|
+
const year = args.year;
|
|
609
|
+
const nowYear = new Date().getUTCFullYear();
|
|
610
|
+
const oldEnough = typeof year === "number" ? year <= nowYear - 2 : false;
|
|
611
|
+
const title = normalizeText(args.paper.title).toLowerCase();
|
|
612
|
+
const tokenHit = args.topicTokens.some((token) => token.length >= 4 && title.includes(token));
|
|
613
|
+
return oldEnough || tokenHit;
|
|
449
614
|
}
|
|
450
615
|
function countTokenOverlap(tokens, text) {
|
|
451
|
-
const hay = ` ${normalizeText(text)
|
|
616
|
+
const hay = ` ${normalizeText(text)
|
|
617
|
+
.toLowerCase()
|
|
618
|
+
.replace(/[_-]+/g, " ")
|
|
619
|
+
.replace(/[^\p{L}\p{N}\s]+/gu, " ")
|
|
620
|
+
.replace(/\s+/g, " ")} `;
|
|
452
621
|
let score = 0;
|
|
453
622
|
for (const token of tokens) {
|
|
454
623
|
if (token.length < 2)
|
|
455
624
|
continue;
|
|
456
|
-
|
|
625
|
+
const normalizedToken = token
|
|
626
|
+
.toLowerCase()
|
|
627
|
+
.replace(/[_-]+/g, " ")
|
|
628
|
+
.replace(/[^\p{L}\p{N}\s]+/gu, " ")
|
|
629
|
+
.trim();
|
|
630
|
+
if (!normalizedToken)
|
|
631
|
+
continue;
|
|
632
|
+
if (hay.includes(` ${normalizedToken} `))
|
|
457
633
|
score += 1;
|
|
458
634
|
}
|
|
459
635
|
return score;
|
|
460
636
|
}
|
|
461
|
-
function scoreFallbackCandidate(topicTokens, paper) {
|
|
637
|
+
function scoreFallbackCandidate(topicTokens, paper, tier, requirements) {
|
|
462
638
|
const titleOverlap = countTokenOverlap(topicTokens, paper.title);
|
|
463
639
|
const abstractOverlap = countTokenOverlap(topicTokens, paper.summary ?? "");
|
|
464
640
|
const publishedAt = paper.published ? Date.parse(paper.published) : NaN;
|
|
465
641
|
const recencyBoost = Number.isFinite(publishedAt)
|
|
466
642
|
? Math.max(0, Math.min(8, (Date.now() - publishedAt) / (1000 * 60 * 60 * 24 * -180)))
|
|
467
643
|
: 0;
|
|
468
|
-
const
|
|
644
|
+
const tierBoost = tier === "tierA" ? 8 : tier === "tierB" ? 4 : 1;
|
|
645
|
+
const year = inferCandidateYear(paper);
|
|
646
|
+
const isBenchmarkOnly = isBenchmarkOnlyPaper(paper);
|
|
647
|
+
const isSurvey = isSurveyPaper(paper);
|
|
648
|
+
const isFoundational = isFoundationalPaper({ paper, year, topicTokens });
|
|
649
|
+
const nowYear = new Date().getUTCFullYear();
|
|
650
|
+
const recencyPenalty = typeof year === "number" && year >= nowYear ? 4 : 0;
|
|
651
|
+
let rawScore = 60 + tierBoost + titleOverlap * 8 + abstractOverlap * 3 + recencyBoost - recencyPenalty;
|
|
652
|
+
if (requirements.foundationalFirst) {
|
|
653
|
+
rawScore += isFoundational ? 10 : -4;
|
|
654
|
+
}
|
|
655
|
+
if (requirements.preferSurvey) {
|
|
656
|
+
rawScore += isSurvey ? 8 : 0;
|
|
657
|
+
}
|
|
658
|
+
if (requirements.preferAuthority) {
|
|
659
|
+
rawScore += isSurvey ? 3 : 0;
|
|
660
|
+
if (isFoundational)
|
|
661
|
+
rawScore += 2;
|
|
662
|
+
}
|
|
663
|
+
if (requirements.preferRecent && typeof year === "number" && year >= nowYear - 1) {
|
|
664
|
+
rawScore += 4;
|
|
665
|
+
}
|
|
666
|
+
if (requirements.avoidBenchmarkOnly && isBenchmarkOnly) {
|
|
667
|
+
rawScore -= 15;
|
|
668
|
+
}
|
|
469
669
|
return Math.max(50, Math.min(99, Math.round(rawScore)));
|
|
470
670
|
}
|
|
471
671
|
async function fetchArxivFallbackByQuery(query) {
|
|
@@ -498,34 +698,139 @@ async function fetchArxivFallbackByQuery(query) {
|
|
|
498
698
|
}
|
|
499
699
|
}
|
|
500
700
|
async function strictCoreFallbackSeed(args) {
|
|
501
|
-
const
|
|
701
|
+
const tieredQueries = buildTieredFallbackQueries(args.topic);
|
|
502
702
|
const byId = new Map();
|
|
503
703
|
const traces = [];
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
704
|
+
const tierStats = {
|
|
705
|
+
tierA: { candidates: 0, selected: 0 },
|
|
706
|
+
tierB: { candidates: 0, selected: 0 },
|
|
707
|
+
tierC: { candidates: 0, selected: 0 },
|
|
708
|
+
};
|
|
709
|
+
for (const tier of ["tierA", "tierB", "tierC"]) {
|
|
710
|
+
for (const query of tieredQueries[tier]) {
|
|
711
|
+
const rows = await fetchArxivFallbackByQuery(query);
|
|
712
|
+
tierStats[tier].candidates += rows.length;
|
|
713
|
+
traces.push({
|
|
714
|
+
query,
|
|
715
|
+
reason: `strict_core_backfill_seed_${tier}`,
|
|
716
|
+
source: "arxiv",
|
|
717
|
+
candidates: rows.length,
|
|
718
|
+
filteredTo: rows.length,
|
|
719
|
+
resultCount: rows.length,
|
|
720
|
+
});
|
|
721
|
+
for (const row of rows) {
|
|
722
|
+
if (!byId.has(row.id))
|
|
723
|
+
byId.set(row.id, { row, tier });
|
|
724
|
+
}
|
|
517
725
|
}
|
|
518
726
|
}
|
|
519
727
|
const topicTokens = tokenizeKeywords(args.topic);
|
|
728
|
+
const scoringTokens = buildScoringTokens(args.topic);
|
|
520
729
|
const ranked = [...byId.values()]
|
|
521
|
-
.map((row) =>
|
|
522
|
-
row
|
|
523
|
-
|
|
524
|
-
|
|
730
|
+
.map(({ row, tier }) => {
|
|
731
|
+
const year = inferCandidateYear(row);
|
|
732
|
+
const isSurvey = isSurveyPaper(row);
|
|
733
|
+
const isBenchmarkOnly = isBenchmarkOnlyPaper(row);
|
|
734
|
+
const isFoundational = isFoundationalPaper({ paper: row, year, topicTokens });
|
|
735
|
+
const relevance = countTokenOverlap(scoringTokens, `${row.title} ${row.summary ?? ""}`);
|
|
736
|
+
return {
|
|
737
|
+
row,
|
|
738
|
+
tier,
|
|
739
|
+
year,
|
|
740
|
+
isSurvey,
|
|
741
|
+
isBenchmarkOnly,
|
|
742
|
+
isFoundational,
|
|
743
|
+
relevance,
|
|
744
|
+
score: scoreFallbackCandidate(scoringTokens.length > 0 ? scoringTokens : topicTokens, row, tier, args.requirements),
|
|
745
|
+
};
|
|
746
|
+
})
|
|
525
747
|
.sort((a, b) => b.score - a.score);
|
|
526
748
|
const unseen = ranked.filter((item) => !args.knownPaperIds.has(item.row.id));
|
|
527
|
-
const
|
|
528
|
-
const
|
|
749
|
+
const poolBeforeRelevance = unseen.length > 0 ? unseen : ranked;
|
|
750
|
+
const minRelevance = scoringTokens.length >= 2 ? 2 : 1;
|
|
751
|
+
const candidatePool = Math.max(1, Math.min(40, Math.floor(args.candidatePool ?? Math.max(DEFAULT_STRICT_CANDIDATE_POOL, args.maxPapers * 4))));
|
|
752
|
+
const minCoreFloor = Math.max(1, Math.min(args.maxPapers, args.minCoreFloor ?? DEFAULT_STRICT_MIN_CORE_FLOOR));
|
|
753
|
+
const effectivePoolByRelevance = poolBeforeRelevance.filter((item) => item.relevance >= minRelevance);
|
|
754
|
+
const focusTokens = scoringTokens.filter((token) => token.length >= 5);
|
|
755
|
+
const weakRelevanceWithFocusPool = poolBeforeRelevance.filter((item) => {
|
|
756
|
+
if (item.relevance < 1)
|
|
757
|
+
return false;
|
|
758
|
+
if (focusTokens.length === 0)
|
|
759
|
+
return true;
|
|
760
|
+
const focusHit = countTokenOverlap(focusTokens, `${item.row.title} ${item.row.summary ?? ""}`);
|
|
761
|
+
return focusHit >= 1;
|
|
762
|
+
});
|
|
763
|
+
const weakRelevancePool = weakRelevanceWithFocusPool.length > 0
|
|
764
|
+
? weakRelevanceWithFocusPool
|
|
765
|
+
: poolBeforeRelevance.filter((item) => item.relevance >= 1);
|
|
766
|
+
const effectivePool = effectivePoolByRelevance.length >= minCoreFloor
|
|
767
|
+
? effectivePoolByRelevance
|
|
768
|
+
: weakRelevancePool.length > 0
|
|
769
|
+
? weakRelevancePool
|
|
770
|
+
: poolBeforeRelevance;
|
|
771
|
+
const targetCount = Math.max(minCoreFloor, Math.min(args.maxPapers, candidatePool));
|
|
772
|
+
const tierTargets = {
|
|
773
|
+
tierA: Math.max(1, Math.round(targetCount * TIER_A_RATIO)),
|
|
774
|
+
tierB: Math.max(1, Math.round(targetCount * TIER_B_RATIO)),
|
|
775
|
+
tierC: Math.max(0, targetCount - Math.round(targetCount * TIER_A_RATIO) - Math.round(targetCount * TIER_B_RATIO)),
|
|
776
|
+
};
|
|
777
|
+
if (tierTargets.tierA + tierTargets.tierB + tierTargets.tierC < targetCount) {
|
|
778
|
+
tierTargets.tierA += targetCount - (tierTargets.tierA + tierTargets.tierB + tierTargets.tierC);
|
|
779
|
+
}
|
|
780
|
+
const selected = [];
|
|
781
|
+
const selectedIds = new Set();
|
|
782
|
+
for (const tier of ["tierA", "tierB", "tierC"]) {
|
|
783
|
+
const picked = effectivePool
|
|
784
|
+
.filter((item) => item.tier === tier && !selectedIds.has(item.row.id))
|
|
785
|
+
.slice(0, tierTargets[tier]);
|
|
786
|
+
for (const item of picked) {
|
|
787
|
+
selected.push(item);
|
|
788
|
+
selectedIds.add(item.row.id);
|
|
789
|
+
tierStats[tier].selected += 1;
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
if (selected.length < targetCount) {
|
|
793
|
+
const fill = effectivePool.filter((item) => !selectedIds.has(item.row.id)).slice(0, targetCount - selected.length);
|
|
794
|
+
for (const item of fill) {
|
|
795
|
+
selected.push(item);
|
|
796
|
+
selectedIds.add(item.row.id);
|
|
797
|
+
tierStats[item.tier].selected += 1;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
const ensureAtLeast = (predicate, need) => {
|
|
801
|
+
while (selected.filter(predicate).length < need) {
|
|
802
|
+
const candidate = effectivePool.find((item) => !selectedIds.has(item.row.id) && predicate(item));
|
|
803
|
+
if (!candidate)
|
|
804
|
+
break;
|
|
805
|
+
const replaceIndex = selected.findIndex((item) => !predicate(item));
|
|
806
|
+
if (replaceIndex < 0)
|
|
807
|
+
break;
|
|
808
|
+
selectedIds.delete(selected[replaceIndex].row.id);
|
|
809
|
+
selected[replaceIndex] = candidate;
|
|
810
|
+
selectedIds.add(candidate.row.id);
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
if (args.requirements.foundationalFirst) {
|
|
814
|
+
ensureAtLeast((item) => item.isFoundational, Math.min(2, targetCount));
|
|
815
|
+
}
|
|
816
|
+
if (args.requirements.preferSurvey) {
|
|
817
|
+
ensureAtLeast((item) => item.isSurvey, 1);
|
|
818
|
+
}
|
|
819
|
+
if (args.requirements.avoidBenchmarkOnly) {
|
|
820
|
+
for (let i = 0; i < selected.length; i += 1) {
|
|
821
|
+
if (!selected[i].isBenchmarkOnly)
|
|
822
|
+
continue;
|
|
823
|
+
const replacement = effectivePool.find((item) => !selectedIds.has(item.row.id) && !item.isBenchmarkOnly);
|
|
824
|
+
if (!replacement)
|
|
825
|
+
break;
|
|
826
|
+
selectedIds.delete(selected[i].row.id);
|
|
827
|
+
selected[i] = replacement;
|
|
828
|
+
selectedIds.add(replacement.row.id);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
tierStats.tierA.selected = selected.filter((item) => item.tier === "tierA").length;
|
|
832
|
+
tierStats.tierB.selected = selected.filter((item) => item.tier === "tierB").length;
|
|
833
|
+
tierStats.tierC.selected = selected.filter((item) => item.tier === "tierC").length;
|
|
529
834
|
const papers = selected.map(({ row, score }) => ({
|
|
530
835
|
id: row.id,
|
|
531
836
|
title: row.title,
|
|
@@ -550,7 +855,233 @@ async function strictCoreFallbackSeed(args) {
|
|
|
550
855
|
papers,
|
|
551
856
|
corePapers,
|
|
552
857
|
explorationTrace: traces,
|
|
553
|
-
notes: `strict_core_backfill_seed selected=${selected.length}
|
|
858
|
+
notes: `strict_core_backfill_seed selected=${selected.length} pool=${candidatePool} floor=${minCoreFloor} relevance_floor=${minRelevance} req_foundational=${args.requirements.foundationalFirst} req_avoid_benchmark=${args.requirements.avoidBenchmarkOnly} req_survey=${args.requirements.preferSurvey}`,
|
|
859
|
+
recallTierStats: tierStats,
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
function isPaperFullTextRead(paper) {
|
|
863
|
+
return paper.fullTextRead === true || paper.readStatus === "fulltext";
|
|
864
|
+
}
|
|
865
|
+
function hasStrictEvidenceAnchor(paper) {
|
|
866
|
+
const anchors = paper.evidenceAnchors ?? [];
|
|
867
|
+
return anchors.some((anchor) => Boolean(anchor?.section?.trim()) &&
|
|
868
|
+
Boolean(anchor?.locator?.trim()) &&
|
|
869
|
+
Boolean(anchor?.quote?.trim()));
|
|
870
|
+
}
|
|
871
|
+
function firstNonEmptyText(values) {
|
|
872
|
+
for (const value of values) {
|
|
873
|
+
if (typeof value !== "string")
|
|
874
|
+
continue;
|
|
875
|
+
const normalized = normalizeText(value);
|
|
876
|
+
if (normalized.length > 0)
|
|
877
|
+
return normalized;
|
|
878
|
+
}
|
|
879
|
+
return undefined;
|
|
880
|
+
}
|
|
881
|
+
function toEvidencePaperId(paper) {
|
|
882
|
+
return derivePaperId({ id: paper.id, title: paper.title, url: paper.url });
|
|
883
|
+
}
|
|
884
|
+
function dedupeEvidenceIds(ids) {
|
|
885
|
+
const seen = new Set();
|
|
886
|
+
const out = [];
|
|
887
|
+
for (const id of ids) {
|
|
888
|
+
const normalized = normalizeText(id);
|
|
889
|
+
if (!normalized)
|
|
890
|
+
continue;
|
|
891
|
+
const key = normalized.toLowerCase();
|
|
892
|
+
if (seen.has(key))
|
|
893
|
+
continue;
|
|
894
|
+
seen.add(key);
|
|
895
|
+
out.push(normalized);
|
|
896
|
+
}
|
|
897
|
+
return out;
|
|
898
|
+
}
|
|
899
|
+
function applyLightweightEvidenceBinding(args) {
|
|
900
|
+
if (!args.knowledgeState) {
|
|
901
|
+
return { knowledgeState: args.knowledgeState, anchorsAdded: 0, evidenceIdsFilled: 0 };
|
|
902
|
+
}
|
|
903
|
+
const corePapers = args.knowledgeState.corePapers ?? [];
|
|
904
|
+
if (corePapers.length === 0) {
|
|
905
|
+
return { knowledgeState: args.knowledgeState, anchorsAdded: 0, evidenceIdsFilled: 0 };
|
|
906
|
+
}
|
|
907
|
+
let anchorsAdded = 0;
|
|
908
|
+
const nextCore = corePapers.map((paper) => {
|
|
909
|
+
if (!isPaperFullTextRead(paper))
|
|
910
|
+
return paper;
|
|
911
|
+
if (hasStrictEvidenceAnchor(paper))
|
|
912
|
+
return paper;
|
|
913
|
+
const quote = firstNonEmptyText([
|
|
914
|
+
paper.keyEvidenceSpans?.[0],
|
|
915
|
+
paper.summary,
|
|
916
|
+
paper.reason,
|
|
917
|
+
paper.title,
|
|
918
|
+
]);
|
|
919
|
+
if (!quote)
|
|
920
|
+
return paper;
|
|
921
|
+
const nextQuote = quote.slice(0, 260);
|
|
922
|
+
anchorsAdded += 1;
|
|
923
|
+
return {
|
|
924
|
+
...paper,
|
|
925
|
+
evidenceAnchors: [
|
|
926
|
+
...(paper.evidenceAnchors ?? []),
|
|
927
|
+
{
|
|
928
|
+
section: "AutoExtract",
|
|
929
|
+
locator: paper.fullTextRef?.trim() || "excerpt:1",
|
|
930
|
+
claim: firstNonEmptyText([paper.researchGoal, paper.reason, paper.title, "auto-bound claim"]) ?? "auto-bound claim",
|
|
931
|
+
quote: nextQuote,
|
|
932
|
+
},
|
|
933
|
+
],
|
|
934
|
+
};
|
|
935
|
+
});
|
|
936
|
+
const fallbackEvidenceIds = dedupeEvidenceIds(nextCore.filter((paper) => isPaperFullTextRead(paper)).map((paper) => toEvidencePaperId(paper)).slice(0, 2));
|
|
937
|
+
let evidenceIdsFilled = 0;
|
|
938
|
+
const patchEvidenceIds = (raw, allowAuto = true) => {
|
|
939
|
+
const existing = dedupeEvidenceIds(raw ?? []);
|
|
940
|
+
if (existing.length > 0)
|
|
941
|
+
return existing;
|
|
942
|
+
if (!allowAuto || fallbackEvidenceIds.length === 0)
|
|
943
|
+
return undefined;
|
|
944
|
+
evidenceIdsFilled += 1;
|
|
945
|
+
return [...fallbackEvidenceIds];
|
|
946
|
+
};
|
|
947
|
+
const nextKnowledgeChanges = (args.knowledgeState.knowledgeChanges ?? []).map((change) => ({
|
|
948
|
+
...change,
|
|
949
|
+
...(change.type === "BRIDGE"
|
|
950
|
+
? { evidenceIds: patchEvidenceIds(change.evidenceIds, false) }
|
|
951
|
+
: { evidenceIds: patchEvidenceIds(change.evidenceIds, true) }),
|
|
952
|
+
}));
|
|
953
|
+
const nextKnowledgeUpdates = (args.knowledgeState.knowledgeUpdates ?? []).map((update) => ({
|
|
954
|
+
...update,
|
|
955
|
+
evidenceIds: patchEvidenceIds(update.evidenceIds, true),
|
|
956
|
+
}));
|
|
957
|
+
const nextHypotheses = (args.knowledgeState.hypotheses ?? []).map((hypothesis) => ({
|
|
958
|
+
...hypothesis,
|
|
959
|
+
evidenceIds: patchEvidenceIds(hypothesis.evidenceIds, true),
|
|
960
|
+
}));
|
|
961
|
+
if (anchorsAdded === 0 && evidenceIdsFilled === 0) {
|
|
962
|
+
return { knowledgeState: args.knowledgeState, anchorsAdded: 0, evidenceIdsFilled: 0 };
|
|
963
|
+
}
|
|
964
|
+
const existingRunLog = args.knowledgeState.runLog;
|
|
965
|
+
const runLog = existingRunLog || args.runProfile
|
|
966
|
+
? {
|
|
967
|
+
...(existingRunLog ?? {}),
|
|
968
|
+
...(existingRunLog?.runProfile ? {} : args.runProfile ? { runProfile: args.runProfile } : {}),
|
|
969
|
+
notes: [existingRunLog?.notes, `auto_evidence_binding anchors_added=${anchorsAdded} ids_filled=${evidenceIdsFilled}`]
|
|
970
|
+
.filter((item) => Boolean(item && item.trim().length > 0))
|
|
971
|
+
.join(" || "),
|
|
972
|
+
}
|
|
973
|
+
: undefined;
|
|
974
|
+
return {
|
|
975
|
+
knowledgeState: {
|
|
976
|
+
...args.knowledgeState,
|
|
977
|
+
corePapers: nextCore,
|
|
978
|
+
...(nextKnowledgeChanges.length > 0 ? { knowledgeChanges: nextKnowledgeChanges } : {}),
|
|
979
|
+
...(nextKnowledgeUpdates.length > 0 ? { knowledgeUpdates: nextKnowledgeUpdates } : {}),
|
|
980
|
+
...(nextHypotheses.length > 0 ? { hypotheses: nextHypotheses } : {}),
|
|
981
|
+
...(runLog ? { runLog } : {}),
|
|
982
|
+
},
|
|
983
|
+
anchorsAdded,
|
|
984
|
+
evidenceIdsFilled,
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
function buildReflectionFollowupQuery(topic, hint) {
|
|
988
|
+
const tokens = tokenizeKeywords(`${topic} ${hint}`).slice(0, 8);
|
|
989
|
+
if (tokens.length === 0)
|
|
990
|
+
return normalizeText(topic);
|
|
991
|
+
return tokens.join(" ");
|
|
992
|
+
}
|
|
993
|
+
function resolveSingleStepReflectionSeed(args) {
|
|
994
|
+
const changes = args.knowledgeState?.knowledgeChanges ?? [];
|
|
995
|
+
const bridgeChanges = changes.filter((item) => item.type === "BRIDGE");
|
|
996
|
+
const newChanges = changes.filter((item) => item.type === "NEW");
|
|
997
|
+
const reviseChanges = changes.filter((item) => item.type === "REVISE");
|
|
998
|
+
const unreadCore = (args.knowledgeState?.corePapers ?? []).filter((paper) => !isPaperFullTextRead(paper));
|
|
999
|
+
if (bridgeChanges.length > 0) {
|
|
1000
|
+
const seed = bridgeChanges[0]?.statement ?? args.topic;
|
|
1001
|
+
return {
|
|
1002
|
+
trigger: "BRIDGE",
|
|
1003
|
+
reason: "bridge_followup",
|
|
1004
|
+
query: buildReflectionFollowupQuery(args.topic, seed),
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
1007
|
+
if (newChanges.length >= 2 && reviseChanges.length >= 1) {
|
|
1008
|
+
const seed = `${newChanges[0]?.statement ?? ""} ${reviseChanges[0]?.statement ?? ""}`.trim();
|
|
1009
|
+
return {
|
|
1010
|
+
trigger: "CONFLICT",
|
|
1011
|
+
reason: "new_revise_followup",
|
|
1012
|
+
query: buildReflectionFollowupQuery(args.topic, seed || args.topic),
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
if (unreadCore.length > 0) {
|
|
1016
|
+
const seed = unreadCore[0]?.id ?? unreadCore[0]?.title ?? args.topic;
|
|
1017
|
+
return {
|
|
1018
|
+
trigger: "UNREAD_CORE",
|
|
1019
|
+
reason: "unread_core_followup",
|
|
1020
|
+
query: buildReflectionFollowupQuery(args.topic, seed),
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
return undefined;
|
|
1024
|
+
}
|
|
1025
|
+
async function executeSingleStepReflection(args) {
|
|
1026
|
+
const seed = resolveSingleStepReflectionSeed({
|
|
1027
|
+
topic: args.topic,
|
|
1028
|
+
knowledgeState: args.knowledgeState,
|
|
1029
|
+
});
|
|
1030
|
+
if (!seed) {
|
|
1031
|
+
return {
|
|
1032
|
+
executed: false,
|
|
1033
|
+
resultCount: 0,
|
|
1034
|
+
papers: [],
|
|
1035
|
+
changes: [],
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
const rows = await fetchArxivFallbackByQuery(seed.query);
|
|
1039
|
+
const localKnownIds = new Set(args.knownPaperIds);
|
|
1040
|
+
for (const paper of args.effectivePapers) {
|
|
1041
|
+
localKnownIds.add(derivePaperId(paper));
|
|
1042
|
+
}
|
|
1043
|
+
for (const paper of args.knowledgeState?.corePapers ?? []) {
|
|
1044
|
+
localKnownIds.add(derivePaperId({ id: paper.id, title: paper.title, url: paper.url }));
|
|
1045
|
+
}
|
|
1046
|
+
for (const paper of args.knowledgeState?.explorationPapers ?? []) {
|
|
1047
|
+
localKnownIds.add(derivePaperId({ id: paper.id, title: paper.title, url: paper.url }));
|
|
1048
|
+
}
|
|
1049
|
+
const selected = rows.filter((row) => !localKnownIds.has(row.id)).slice(0, REFLECTION_MAX_ADDED_PAPERS);
|
|
1050
|
+
const papers = selected.map((row) => ({
|
|
1051
|
+
id: row.id,
|
|
1052
|
+
title: row.title,
|
|
1053
|
+
url: row.url,
|
|
1054
|
+
source: "arxiv",
|
|
1055
|
+
...(row.published ? { publishedAt: row.published } : {}),
|
|
1056
|
+
...(row.summary ? { summary: row.summary } : {}),
|
|
1057
|
+
fullTextRead: false,
|
|
1058
|
+
readStatus: "metadata",
|
|
1059
|
+
unreadReason: "single_step_reflection_added_without_fulltext",
|
|
1060
|
+
}));
|
|
1061
|
+
const changes = selected.length > 0
|
|
1062
|
+
? [
|
|
1063
|
+
{
|
|
1064
|
+
type: "NEW",
|
|
1065
|
+
statement: `Reflection follow-up added ${selected.length} adjacent paper(s) for ${args.topic}.`,
|
|
1066
|
+
evidenceIds: selected.map((row) => row.id).slice(0, 3),
|
|
1067
|
+
topic: args.topic,
|
|
1068
|
+
},
|
|
1069
|
+
]
|
|
1070
|
+
: [];
|
|
1071
|
+
return {
|
|
1072
|
+
executed: true,
|
|
1073
|
+
resultCount: selected.length,
|
|
1074
|
+
trace: {
|
|
1075
|
+
query: seed.query,
|
|
1076
|
+
reason: seed.reason,
|
|
1077
|
+
source: "arxiv",
|
|
1078
|
+
candidates: rows.length,
|
|
1079
|
+
filteredTo: selected.length,
|
|
1080
|
+
...(selected.length === 0 ? { filteredOutReasons: ["no_unseen_reflection_candidates"] } : {}),
|
|
1081
|
+
resultCount: selected.length,
|
|
1082
|
+
},
|
|
1083
|
+
papers,
|
|
1084
|
+
changes,
|
|
554
1085
|
};
|
|
555
1086
|
}
|
|
556
1087
|
function dedupePaperRecords(records) {
|
|
@@ -943,7 +1474,7 @@ export async function recordIncrementalPush(args) {
|
|
|
943
1474
|
effectiveRunLog.requiredCorePapers = Math.max(1, requiredCoreRaw);
|
|
944
1475
|
}
|
|
945
1476
|
else {
|
|
946
|
-
|
|
1477
|
+
effectiveRunLog.requiredCorePapers = Math.max(1, Math.min(topicState.preferences.maxPapers, DEFAULT_STRICT_MIN_CORE_FLOOR));
|
|
947
1478
|
}
|
|
948
1479
|
if (typeof effectiveRunLog.requiredFullTextCoveragePct !== "number" ||
|
|
949
1480
|
!Number.isFinite(effectiveRunLog.requiredFullTextCoveragePct) ||
|
|
@@ -957,8 +1488,18 @@ export async function recordIncrementalPush(args) {
|
|
|
957
1488
|
...(effectiveRunLog ? { runLog: effectiveRunLog } : {}),
|
|
958
1489
|
}
|
|
959
1490
|
: undefined;
|
|
1491
|
+
const requirementProfile = inferRequirementProfile([
|
|
1492
|
+
topicState.topic,
|
|
1493
|
+
args.note,
|
|
1494
|
+
effectiveRunLog?.notes,
|
|
1495
|
+
effectiveKnowledgeState?.runLog?.notes,
|
|
1496
|
+
]
|
|
1497
|
+
.filter((item) => Boolean(item && item.trim().length > 0))
|
|
1498
|
+
.join(" "));
|
|
960
1499
|
if (incomingRunProfile === "strict") {
|
|
961
|
-
const
|
|
1500
|
+
const strictMinCoreFloor = Math.max(1, Math.min(topicState.preferences.maxPapers, DEFAULT_STRICT_MIN_CORE_FLOOR));
|
|
1501
|
+
const requiredCoreFloor = Math.max(1, Math.min(topicState.preferences.maxPapers, effectiveRunLog?.requiredCorePapers ?? strictMinCoreFloor));
|
|
1502
|
+
const strictCandidatePool = Math.max(DEFAULT_STRICT_CANDIDATE_POOL, topicState.preferences.maxPapers * 4);
|
|
962
1503
|
const existingCorePapers = effectiveKnowledgeState?.corePapers ?? [];
|
|
963
1504
|
const strictSignalCount = Math.max(existingCorePapers.length, effectivePapers.length);
|
|
964
1505
|
if (strictSignalCount < requiredCoreFloor) {
|
|
@@ -970,8 +1511,11 @@ export async function recordIncrementalPush(args) {
|
|
|
970
1511
|
}
|
|
971
1512
|
const fallback = await strictCoreFallbackSeed({
|
|
972
1513
|
topic: topicState.topic,
|
|
973
|
-
maxPapers:
|
|
1514
|
+
maxPapers: topicState.preferences.maxPapers,
|
|
1515
|
+
candidatePool: strictCandidatePool,
|
|
1516
|
+
minCoreFloor: requiredCoreFloor,
|
|
974
1517
|
knownPaperIds: knownIds,
|
|
1518
|
+
requirements: requirementProfile,
|
|
975
1519
|
});
|
|
976
1520
|
if (fallback.papers.length > 0) {
|
|
977
1521
|
const existingIds = new Set(effectivePapers.map((paper) => derivePaperId(paper)));
|
|
@@ -987,6 +1531,7 @@ export async function recordIncrementalPush(args) {
|
|
|
987
1531
|
effectivePapers = dedupePaperRecords([...effectivePapers, ...fallbackPapers]);
|
|
988
1532
|
const mergedRunLog = {
|
|
989
1533
|
...(effectiveRunLog ?? { runProfile: "strict" }),
|
|
1534
|
+
recallTierStats: fallback.recallTierStats,
|
|
990
1535
|
notes: [
|
|
991
1536
|
effectiveRunLog?.notes,
|
|
992
1537
|
fallback.notes,
|
|
@@ -1050,6 +1595,74 @@ export async function recordIncrementalPush(args) {
|
|
|
1050
1595
|
};
|
|
1051
1596
|
}
|
|
1052
1597
|
}
|
|
1598
|
+
const reflection = await executeSingleStepReflection({
|
|
1599
|
+
topic: topicState.topic,
|
|
1600
|
+
knownPaperIds: new Set(Object.keys(topicState.pushedPapers)),
|
|
1601
|
+
effectivePapers,
|
|
1602
|
+
knowledgeState: effectiveKnowledgeState,
|
|
1603
|
+
});
|
|
1604
|
+
const reflectionRunLogBase = effectiveRunLog ??
|
|
1605
|
+
(incomingRunProfile ? { runProfile: incomingRunProfile } : undefined);
|
|
1606
|
+
if (reflection.executed) {
|
|
1607
|
+
const reflectionPaperRecords = reflection.papers.map((paper) => ({
|
|
1608
|
+
...(paper.id ? { id: paper.id } : {}),
|
|
1609
|
+
...(paper.title ? { title: paper.title } : {}),
|
|
1610
|
+
...(paper.url ? { url: paper.url } : {}),
|
|
1611
|
+
...(typeof paper.score === "number" && Number.isFinite(paper.score) ? { score: paper.score } : {}),
|
|
1612
|
+
reason: "single_step_reflection_followup",
|
|
1613
|
+
}));
|
|
1614
|
+
effectivePapers = dedupePaperRecords([...effectivePapers, ...reflectionPaperRecords]);
|
|
1615
|
+
const mergedRunLog = {
|
|
1616
|
+
...(reflectionRunLogBase ?? {}),
|
|
1617
|
+
reflectionStepExecuted: true,
|
|
1618
|
+
reflectionStepResultCount: reflection.resultCount,
|
|
1619
|
+
notes: [
|
|
1620
|
+
reflectionRunLogBase?.notes,
|
|
1621
|
+
`single_step_reflection result_count=${reflection.resultCount}`,
|
|
1622
|
+
]
|
|
1623
|
+
.filter((item) => Boolean(item && item.trim().length > 0))
|
|
1624
|
+
.join(" || "),
|
|
1625
|
+
};
|
|
1626
|
+
effectiveRunLog = mergedRunLog;
|
|
1627
|
+
effectiveKnowledgeState = {
|
|
1628
|
+
...(effectiveKnowledgeState ?? {}),
|
|
1629
|
+
explorationTrace: [
|
|
1630
|
+
...(effectiveKnowledgeState?.explorationTrace ?? []),
|
|
1631
|
+
...(reflection.trace ? [reflection.trace] : []),
|
|
1632
|
+
],
|
|
1633
|
+
explorationPapers: dedupeKnowledgePapers([
|
|
1634
|
+
...(effectiveKnowledgeState?.explorationPapers ?? []),
|
|
1635
|
+
...reflection.papers,
|
|
1636
|
+
]),
|
|
1637
|
+
knowledgeChanges: [
|
|
1638
|
+
...(effectiveKnowledgeState?.knowledgeChanges ?? []),
|
|
1639
|
+
...(reflection.changes ?? []),
|
|
1640
|
+
],
|
|
1641
|
+
runLog: mergedRunLog,
|
|
1642
|
+
};
|
|
1643
|
+
}
|
|
1644
|
+
else if (reflectionRunLogBase) {
|
|
1645
|
+
const mergedRunLog = {
|
|
1646
|
+
...reflectionRunLogBase,
|
|
1647
|
+
reflectionStepExecuted: false,
|
|
1648
|
+
reflectionStepResultCount: 0,
|
|
1649
|
+
};
|
|
1650
|
+
effectiveRunLog = mergedRunLog;
|
|
1651
|
+
effectiveKnowledgeState = {
|
|
1652
|
+
...(effectiveKnowledgeState ?? {}),
|
|
1653
|
+
runLog: mergedRunLog,
|
|
1654
|
+
};
|
|
1655
|
+
}
|
|
1656
|
+
const autoEvidence = applyLightweightEvidenceBinding({
|
|
1657
|
+
knowledgeState: effectiveKnowledgeState,
|
|
1658
|
+
runProfile: incomingRunProfile,
|
|
1659
|
+
});
|
|
1660
|
+
effectiveKnowledgeState = autoEvidence.knowledgeState;
|
|
1661
|
+
if (autoEvidence.anchorsAdded > 0 || autoEvidence.evidenceIdsFilled > 0) {
|
|
1662
|
+
effectiveRunLog = effectiveKnowledgeState?.runLog
|
|
1663
|
+
? { ...effectiveKnowledgeState.runLog }
|
|
1664
|
+
: effectiveRunLog;
|
|
1665
|
+
}
|
|
1053
1666
|
const statusRaw = normalizeText(args.status ?? "").toLowerCase();
|
|
1054
1667
|
const researchArtifactsCount = effectivePapers.length +
|
|
1055
1668
|
(effectiveKnowledgeState?.explorationPapers?.length ?? 0) +
|