scientify 1.13.4 → 1.13.6

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.
Files changed (35) hide show
  1. package/dist/src/hooks/research-mode.d.ts.map +1 -1
  2. package/dist/src/hooks/research-mode.js +30 -1
  3. package/dist/src/hooks/research-mode.js.map +1 -1
  4. package/dist/src/knowledge-state/render.d.ts.map +1 -1
  5. package/dist/src/knowledge-state/render.js +29 -0
  6. package/dist/src/knowledge-state/render.js.map +1 -1
  7. package/dist/src/knowledge-state/store.d.ts.map +1 -1
  8. package/dist/src/knowledge-state/store.js +216 -0
  9. package/dist/src/knowledge-state/store.js.map +1 -1
  10. package/dist/src/knowledge-state/types.d.ts +7 -0
  11. package/dist/src/knowledge-state/types.d.ts.map +1 -1
  12. package/dist/src/literature/subscription-state.d.ts.map +1 -1
  13. package/dist/src/literature/subscription-state.js +184 -8
  14. package/dist/src/literature/subscription-state.js.map +1 -1
  15. package/dist/src/research-subscriptions/handlers.d.ts.map +1 -1
  16. package/dist/src/research-subscriptions/handlers.js +11 -1
  17. package/dist/src/research-subscriptions/handlers.js.map +1 -1
  18. package/dist/src/research-subscriptions/parse.d.ts.map +1 -1
  19. package/dist/src/research-subscriptions/parse.js +15 -0
  20. package/dist/src/research-subscriptions/parse.js.map +1 -1
  21. package/dist/src/research-subscriptions/prompt.d.ts +1 -1
  22. package/dist/src/research-subscriptions/prompt.d.ts.map +1 -1
  23. package/dist/src/research-subscriptions/prompt.js +66 -12
  24. package/dist/src/research-subscriptions/prompt.js.map +1 -1
  25. package/dist/src/research-subscriptions/types.d.ts +1 -0
  26. package/dist/src/research-subscriptions/types.d.ts.map +1 -1
  27. package/dist/src/tools/scientify-cron.d.ts +2 -0
  28. package/dist/src/tools/scientify-cron.d.ts.map +1 -1
  29. package/dist/src/tools/scientify-cron.js +88 -5
  30. package/dist/src/tools/scientify-cron.js.map +1 -1
  31. package/dist/src/tools/scientify-literature-state.d.ts +14 -0
  32. package/dist/src/tools/scientify-literature-state.d.ts.map +1 -1
  33. package/dist/src/tools/scientify-literature-state.js +51 -0
  34. package/dist/src/tools/scientify-literature-state.js.map +1 -1
  35. package/package.json +1 -1
@@ -562,8 +562,9 @@ function buildTieredFallbackQueries(topic) {
562
562
  ], STRICT_EMPTY_FALLBACK_MAX_QUERIES);
563
563
  const tierC = dedupeQueries([
564
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`),
565
+ ...tokens.slice(0, 5).map((token) => `${token} survey review`),
566
+ ...tokens.slice(0, 5).map((token) => `${token} criticism limitations`),
567
+ ...tokens.slice(0, 5).map((token) => `${token} failure analysis`),
567
568
  `${normalizedTopic} cross domain`,
568
569
  ], STRICT_EMPTY_FALLBACK_MAX_QUERIES);
569
570
  return {
@@ -582,6 +583,94 @@ function inferRequirementProfile(raw) {
582
583
  preferRecent: RECENT_HINT_RE.test(text),
583
584
  };
584
585
  }
586
+ function clampRecallTierRatios(input) {
587
+ const a = Number.isFinite(input.tierA) ? Math.max(0, input.tierA) : 0;
588
+ const b = Number.isFinite(input.tierB) ? Math.max(0, input.tierB) : 0;
589
+ const c = Number.isFinite(input.tierC) ? Math.max(0, input.tierC) : 0;
590
+ const sum = a + b + c;
591
+ if (sum <= 0) {
592
+ return { tierA: TIER_A_RATIO, tierB: TIER_B_RATIO, tierC: TIER_C_RATIO };
593
+ }
594
+ return {
595
+ tierA: a / sum,
596
+ tierB: b / sum,
597
+ tierC: c / sum,
598
+ };
599
+ }
600
+ function pickAdaptiveResearchStage(args) {
601
+ if (args.emptyRunStreak >= 2)
602
+ return "pivot";
603
+ if (args.hasSubmittedHypothesis)
604
+ return "hypothesis_validation";
605
+ if (args.totalRuns <= 2 || args.knownPaperCount < 6)
606
+ return "bootstrap";
607
+ return "expansion";
608
+ }
609
+ function deriveAdaptiveRequirementProfile(args) {
610
+ if (args.stage === "bootstrap") {
611
+ return {
612
+ ...args.base,
613
+ foundationalFirst: true,
614
+ preferSurvey: true,
615
+ preferAuthority: true,
616
+ };
617
+ }
618
+ if (args.stage === "hypothesis_validation") {
619
+ return {
620
+ ...args.base,
621
+ avoidBenchmarkOnly: true,
622
+ preferAuthority: true,
623
+ preferRecent: true,
624
+ };
625
+ }
626
+ if (args.stage === "pivot") {
627
+ return {
628
+ ...args.base,
629
+ avoidBenchmarkOnly: true,
630
+ preferAuthority: true,
631
+ preferSurvey: false,
632
+ foundationalFirst: false,
633
+ preferRecent: true,
634
+ };
635
+ }
636
+ return args.base;
637
+ }
638
+ function deriveAdaptiveRecallTierRatios(stage) {
639
+ if (stage === "bootstrap") {
640
+ return clampRecallTierRatios({ tierA: 0.45, tierB: 0.4, tierC: 0.15 });
641
+ }
642
+ if (stage === "hypothesis_validation") {
643
+ return clampRecallTierRatios({ tierA: 0.4, tierB: 0.3, tierC: 0.3 });
644
+ }
645
+ if (stage === "pivot") {
646
+ return clampRecallTierRatios({ tierA: 0.3, tierB: 0.3, tierC: 0.4 });
647
+ }
648
+ return clampRecallTierRatios({ tierA: TIER_A_RATIO, tierB: TIER_B_RATIO, tierC: TIER_C_RATIO });
649
+ }
650
+ function buildEmptyCycleAutoDiagnosis(args) {
651
+ const baseTopic = normalizeText(args.topic);
652
+ const nextQueries = [
653
+ `${baseTopic} survey review foundational`,
654
+ `${baseTopic} method theory variant`,
655
+ args.stage === "hypothesis_validation" || args.stage === "pivot"
656
+ ? `${baseTopic} criticism failure limitations related work`
657
+ : `${baseTopic} adjacent transfer domain adaptation`,
658
+ ].map((item) => normalizeText(item));
659
+ const isLikelySaturation = args.knownPaperCount >= 8 && args.totalRuns >= 3;
660
+ const diagnosis = args.emptyRunStreak >= 2
661
+ ? "repeated_empty_cycle_likely_scope_saturation_or_query_mismatch"
662
+ : isLikelySaturation
663
+ ? "likely_scope_saturation_check_adjacent_subdomains"
664
+ : "likely_query_or_recency_mismatch_expand_terms_and_time_window";
665
+ const pivotHint = args.stage === "pivot" || args.emptyRunStreak >= 2
666
+ ? "consider a nearby sub-direction that serves current idea validation (supporting and critical evidence)."
667
+ : "continue current direction with broadened query and include foundational plus adjacent variants.";
668
+ return {
669
+ diagnosis,
670
+ nextQueries,
671
+ pivotHint,
672
+ };
673
+ }
585
674
  function inferCandidateYear(paper) {
586
675
  if (paper.published) {
587
676
  const ts = Date.parse(paper.published);
@@ -750,6 +839,7 @@ async function strictCoreFallbackSeed(args) {
750
839
  const minRelevance = scoringTokens.length >= 2 ? 2 : 1;
751
840
  const candidatePool = Math.max(1, Math.min(40, Math.floor(args.candidatePool ?? Math.max(DEFAULT_STRICT_CANDIDATE_POOL, args.maxPapers * 4))));
752
841
  const minCoreFloor = Math.max(1, Math.min(args.maxPapers, args.minCoreFloor ?? DEFAULT_STRICT_MIN_CORE_FLOOR));
842
+ const tierRatios = clampRecallTierRatios(args.tierRatios ?? { tierA: TIER_A_RATIO, tierB: TIER_B_RATIO, tierC: TIER_C_RATIO });
753
843
  const effectivePoolByRelevance = poolBeforeRelevance.filter((item) => item.relevance >= minRelevance);
754
844
  const focusTokens = scoringTokens.filter((token) => token.length >= 5);
755
845
  const weakRelevanceWithFocusPool = poolBeforeRelevance.filter((item) => {
@@ -770,9 +860,9 @@ async function strictCoreFallbackSeed(args) {
770
860
  : poolBeforeRelevance;
771
861
  const targetCount = Math.max(minCoreFloor, Math.min(args.maxPapers, candidatePool));
772
862
  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)),
863
+ tierA: Math.max(1, Math.round(targetCount * tierRatios.tierA)),
864
+ tierB: Math.max(1, Math.round(targetCount * tierRatios.tierB)),
865
+ tierC: Math.max(0, targetCount - Math.round(targetCount * tierRatios.tierA) - Math.round(targetCount * tierRatios.tierB)),
776
866
  };
777
867
  if (tierTargets.tierA + tierTargets.tierB + tierTargets.tierC < targetCount) {
778
868
  tierTargets.tierA += targetCount - (tierTargets.tierA + tierTargets.tierB + tierTargets.tierC);
@@ -991,11 +1081,20 @@ function buildReflectionFollowupQuery(topic, hint) {
991
1081
  return tokens.join(" ");
992
1082
  }
993
1083
  function resolveSingleStepReflectionSeed(args) {
1084
+ const hypotheses = args.knowledgeState?.hypotheses ?? [];
994
1085
  const changes = args.knowledgeState?.knowledgeChanges ?? [];
995
1086
  const bridgeChanges = changes.filter((item) => item.type === "BRIDGE");
996
1087
  const newChanges = changes.filter((item) => item.type === "NEW");
997
1088
  const reviseChanges = changes.filter((item) => item.type === "REVISE");
998
1089
  const unreadCore = (args.knowledgeState?.corePapers ?? []).filter((paper) => !isPaperFullTextRead(paper));
1090
+ if (hypotheses.length > 0) {
1091
+ const seed = hypotheses[0]?.statement ?? args.topic;
1092
+ return {
1093
+ trigger: "HYPOTHESIS_VALIDATE",
1094
+ reason: "hypothesis_validation_followup",
1095
+ query: buildReflectionFollowupQuery(args.topic, `${seed} supporting evidence critique related work limitations`),
1096
+ };
1097
+ }
999
1098
  if (bridgeChanges.length > 0) {
1000
1099
  const seed = bridgeChanges[0]?.statement ?? args.topic;
1001
1100
  return {
@@ -1289,6 +1388,9 @@ function getOrCreateTopicState(root, scope, topic, incomingPrefs) {
1289
1388
  if (!Number.isFinite(existing.totalRuns)) {
1290
1389
  existing.totalRuns = 0;
1291
1390
  }
1391
+ if (!Number.isFinite(existing.emptyRunStreak)) {
1392
+ existing.emptyRunStreak = 0;
1393
+ }
1292
1394
  existing.preferences = mergePreferences(existing.preferences, incomingPrefs);
1293
1395
  // Merge duplicate legacy buckets produced by old scope normalization rules.
1294
1396
  for (const [otherKey, other] of Object.entries(root.topics)) {
@@ -1365,6 +1467,15 @@ function getOrCreateTopicState(root, scope, topic, incomingPrefs) {
1365
1467
  const existingRuns = Number.isFinite(existing.totalRuns) ? Math.max(0, Math.floor(existing.totalRuns)) : 0;
1366
1468
  const otherRuns = Number.isFinite(other.totalRuns) ? Math.max(0, Math.floor(other.totalRuns)) : 0;
1367
1469
  existing.totalRuns = existingRuns + otherRuns;
1470
+ const existingEmptyRaw = existing.emptyRunStreak;
1471
+ const otherEmptyRaw = other.emptyRunStreak;
1472
+ const existingEmptyRuns = typeof existingEmptyRaw === "number" && Number.isFinite(existingEmptyRaw)
1473
+ ? Math.max(0, Math.floor(existingEmptyRaw))
1474
+ : 0;
1475
+ const otherEmptyRuns = typeof otherEmptyRaw === "number" && Number.isFinite(otherEmptyRaw)
1476
+ ? Math.max(0, Math.floor(otherEmptyRaw))
1477
+ : 0;
1478
+ existing.emptyRunStreak = Math.max(existingEmptyRuns, otherEmptyRuns);
1368
1479
  const existingLastRun = existing.lastRunAtMs ?? 0;
1369
1480
  const otherLastRun = other.lastRunAtMs ?? 0;
1370
1481
  if (otherLastRun > existingLastRun) {
@@ -1389,6 +1500,7 @@ function getOrCreateTopicState(root, scope, topic, incomingPrefs) {
1389
1500
  memory: defaultTopicMemoryState(),
1390
1501
  pushedPapers: {},
1391
1502
  totalRuns: 0,
1503
+ emptyRunStreak: 0,
1392
1504
  };
1393
1505
  root.topics[key] = created;
1394
1506
  return created;
@@ -1488,7 +1600,8 @@ export async function recordIncrementalPush(args) {
1488
1600
  ...(effectiveRunLog ? { runLog: effectiveRunLog } : {}),
1489
1601
  }
1490
1602
  : undefined;
1491
- const requirementProfile = inferRequirementProfile([
1603
+ const knownPaperCount = Object.keys(topicState.pushedPapers).length;
1604
+ const baseRequirementProfile = inferRequirementProfile([
1492
1605
  topicState.topic,
1493
1606
  args.note,
1494
1607
  effectiveRunLog?.notes,
@@ -1496,10 +1609,38 @@ export async function recordIncrementalPush(args) {
1496
1609
  ]
1497
1610
  .filter((item) => Boolean(item && item.trim().length > 0))
1498
1611
  .join(" "));
1612
+ const adaptiveStage = pickAdaptiveResearchStage({
1613
+ totalRuns: topicState.totalRuns,
1614
+ knownPaperCount,
1615
+ hasSubmittedHypothesis: (effectiveKnowledgeState?.hypotheses?.length ?? 0) > 0,
1616
+ emptyRunStreak: Math.max(0, Math.floor(topicState.emptyRunStreak ?? 0)),
1617
+ });
1618
+ const requirementProfile = deriveAdaptiveRequirementProfile({
1619
+ base: baseRequirementProfile,
1620
+ stage: adaptiveStage,
1621
+ });
1622
+ const adaptiveTierRatios = deriveAdaptiveRecallTierRatios(adaptiveStage);
1623
+ if (effectiveRunLog || effectiveKnowledgeState?.runLog) {
1624
+ const mergedRunLog = {
1625
+ ...(effectiveRunLog ?? effectiveKnowledgeState?.runLog ?? {}),
1626
+ notes: [
1627
+ effectiveRunLog?.notes,
1628
+ effectiveKnowledgeState?.runLog?.notes,
1629
+ `adaptive_stage=${adaptiveStage} known_papers=${knownPaperCount} total_runs=${topicState.totalRuns}`,
1630
+ ]
1631
+ .filter((item) => Boolean(item && item.trim().length > 0))
1632
+ .join(" || "),
1633
+ };
1634
+ effectiveRunLog = mergedRunLog;
1635
+ effectiveKnowledgeState = {
1636
+ ...(effectiveKnowledgeState ?? {}),
1637
+ runLog: mergedRunLog,
1638
+ };
1639
+ }
1499
1640
  if (incomingRunProfile === "strict") {
1500
1641
  const strictMinCoreFloor = Math.max(1, Math.min(topicState.preferences.maxPapers, DEFAULT_STRICT_MIN_CORE_FLOOR));
1501
1642
  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);
1643
+ const strictCandidatePool = Math.max(DEFAULT_STRICT_CANDIDATE_POOL, topicState.preferences.maxPapers * 4, adaptiveStage === "bootstrap" ? 30 : 0, adaptiveStage === "hypothesis_validation" ? 28 : 0);
1503
1644
  const existingCorePapers = effectiveKnowledgeState?.corePapers ?? [];
1504
1645
  const strictSignalCount = Math.max(existingCorePapers.length, effectivePapers.length);
1505
1646
  if (strictSignalCount < requiredCoreFloor) {
@@ -1516,6 +1657,7 @@ export async function recordIncrementalPush(args) {
1516
1657
  minCoreFloor: requiredCoreFloor,
1517
1658
  knownPaperIds: knownIds,
1518
1659
  requirements: requirementProfile,
1660
+ tierRatios: adaptiveTierRatios,
1519
1661
  });
1520
1662
  if (fallback.papers.length > 0) {
1521
1663
  const existingIds = new Set(effectivePapers.map((paper) => derivePaperId(paper)));
@@ -1535,7 +1677,7 @@ export async function recordIncrementalPush(args) {
1535
1677
  notes: [
1536
1678
  effectiveRunLog?.notes,
1537
1679
  fallback.notes,
1538
- `strict_core_topup required=${requiredCoreFloor} before=${strictSignalCount} added=${fallbackPapers.length}`,
1680
+ `strict_core_topup required=${requiredCoreFloor} before=${strictSignalCount} added=${fallbackPapers.length} stage=${adaptiveStage}`,
1539
1681
  ]
1540
1682
  .filter((item) => Boolean(item && item.trim().length > 0))
1541
1683
  .join(" || "),
@@ -1671,6 +1813,34 @@ export async function recordIncrementalPush(args) {
1671
1813
  (effectiveKnowledgeState?.hypotheses?.length ?? 0) +
1672
1814
  (effectiveKnowledgeState?.explorationTrace?.length ?? 0);
1673
1815
  let normalizedStatus = statusRaw.length > 0 ? statusRaw : undefined;
1816
+ if ((normalizedStatus ?? "") === "empty" && researchArtifactsCount === 0) {
1817
+ const emptyDiagnosis = buildEmptyCycleAutoDiagnosis({
1818
+ topic: topicState.topic,
1819
+ stage: adaptiveStage,
1820
+ knownPaperCount,
1821
+ totalRuns: topicState.totalRuns,
1822
+ emptyRunStreak: Math.max(0, Math.floor(topicState.emptyRunStreak ?? 0)),
1823
+ });
1824
+ const mergedRunLog = {
1825
+ ...(effectiveRunLog ?? effectiveKnowledgeState?.runLog ?? { runProfile: incomingRunProfile ?? "strict" }),
1826
+ notes: [
1827
+ effectiveRunLog?.notes,
1828
+ effectiveKnowledgeState?.runLog?.notes,
1829
+ `empty_cycle_diagnosis=${emptyDiagnosis.diagnosis}`,
1830
+ `empty_cycle_next_queries=${emptyDiagnosis.nextQueries.join(" || ")}`,
1831
+ `empty_cycle_pivot_hint=${emptyDiagnosis.pivotHint}`,
1832
+ ]
1833
+ .filter((item) => Boolean(item && item.trim().length > 0))
1834
+ .join(" || "),
1835
+ reflectionStepExecuted: false,
1836
+ reflectionStepResultCount: 0,
1837
+ };
1838
+ effectiveRunLog = mergedRunLog;
1839
+ effectiveKnowledgeState = {
1840
+ ...(effectiveKnowledgeState ?? {}),
1841
+ runLog: mergedRunLog,
1842
+ };
1843
+ }
1674
1844
  const coercedFromEmptyWithArtifacts = normalizedStatus === "empty" && researchArtifactsCount > 0;
1675
1845
  if (coercedFromEmptyWithArtifacts) {
1676
1846
  normalizedStatus = "degraded_quality";
@@ -1734,6 +1904,12 @@ export async function recordIncrementalPush(args) {
1734
1904
  knowledgeState: effectiveKnowledgeState,
1735
1905
  });
1736
1906
  topicState.lastStatus = knowledgeCommitted.summary.lastStatus ?? topicState.lastStatus;
1907
+ if (topicState.lastStatus === "empty") {
1908
+ topicState.emptyRunStreak = Math.max(0, Math.floor(topicState.emptyRunStreak ?? 0)) + 1;
1909
+ }
1910
+ else {
1911
+ topicState.emptyRunStreak = 0;
1912
+ }
1737
1913
  topicState.lastProjectId = knowledgeCommitted.projectId;
1738
1914
  await saveState(root);
1739
1915
  await appendPushLog({