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.
- package/dist/src/hooks/research-mode.d.ts.map +1 -1
- package/dist/src/hooks/research-mode.js +30 -1
- package/dist/src/hooks/research-mode.js.map +1 -1
- package/dist/src/knowledge-state/render.d.ts.map +1 -1
- package/dist/src/knowledge-state/render.js +29 -0
- package/dist/src/knowledge-state/render.js.map +1 -1
- package/dist/src/knowledge-state/store.d.ts.map +1 -1
- package/dist/src/knowledge-state/store.js +216 -0
- package/dist/src/knowledge-state/store.js.map +1 -1
- package/dist/src/knowledge-state/types.d.ts +7 -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 +184 -8
- package/dist/src/literature/subscription-state.js.map +1 -1
- package/dist/src/research-subscriptions/handlers.d.ts.map +1 -1
- package/dist/src/research-subscriptions/handlers.js +11 -1
- package/dist/src/research-subscriptions/handlers.js.map +1 -1
- package/dist/src/research-subscriptions/parse.d.ts.map +1 -1
- package/dist/src/research-subscriptions/parse.js +15 -0
- package/dist/src/research-subscriptions/parse.js.map +1 -1
- package/dist/src/research-subscriptions/prompt.d.ts +1 -1
- package/dist/src/research-subscriptions/prompt.d.ts.map +1 -1
- package/dist/src/research-subscriptions/prompt.js +66 -12
- package/dist/src/research-subscriptions/prompt.js.map +1 -1
- package/dist/src/research-subscriptions/types.d.ts +1 -0
- package/dist/src/research-subscriptions/types.d.ts.map +1 -1
- package/dist/src/tools/scientify-cron.d.ts +2 -0
- package/dist/src/tools/scientify-cron.d.ts.map +1 -1
- package/dist/src/tools/scientify-cron.js +88 -5
- package/dist/src/tools/scientify-cron.js.map +1 -1
- package/dist/src/tools/scientify-literature-state.d.ts +14 -0
- package/dist/src/tools/scientify-literature-state.d.ts.map +1 -1
- package/dist/src/tools/scientify-literature-state.js +51 -0
- package/dist/src/tools/scientify-literature-state.js.map +1 -1
- 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}
|
|
566
|
-
...tokens.slice(0, 5).map((token) => `${token}
|
|
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 *
|
|
774
|
-
tierB: Math.max(1, Math.round(targetCount *
|
|
775
|
-
tierC: Math.max(0, targetCount - Math.round(targetCount *
|
|
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
|
|
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({
|