scientify 1.12.1 → 1.12.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/README.md +3 -1
- package/README.zh.md +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -5
- package/dist/index.js.map +1 -1
- package/dist/src/cli/research.d.ts +1 -1
- package/dist/src/cli/research.d.ts.map +1 -1
- package/dist/src/cli/research.js +123 -227
- package/dist/src/cli/research.js.map +1 -1
- package/dist/src/commands/metabolism-status.d.ts +2 -2
- package/dist/src/commands/metabolism-status.d.ts.map +1 -1
- package/dist/src/commands/metabolism-status.js +75 -72
- package/dist/src/commands/metabolism-status.js.map +1 -1
- package/dist/src/commands.d.ts.map +1 -1
- package/dist/src/commands.js +55 -0
- package/dist/src/commands.js.map +1 -1
- package/dist/src/hooks/research-mode.d.ts.map +1 -1
- package/dist/src/hooks/research-mode.js +54 -37
- package/dist/src/hooks/research-mode.js.map +1 -1
- package/dist/src/hooks/scientify-signature.d.ts.map +1 -1
- package/dist/src/hooks/scientify-signature.js +5 -2
- package/dist/src/hooks/scientify-signature.js.map +1 -1
- package/dist/src/knowledge-state/render.d.ts +1 -0
- package/dist/src/knowledge-state/render.d.ts.map +1 -1
- package/dist/src/knowledge-state/render.js +101 -33
- 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 +206 -33
- package/dist/src/knowledge-state/store.js.map +1 -1
- package/dist/src/knowledge-state/types.d.ts +12 -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 +579 -7
- package/dist/src/literature/subscription-state.js.map +1 -1
- package/dist/src/research-subscriptions/constants.d.ts +1 -1
- package/dist/src/research-subscriptions/constants.js +1 -1
- package/dist/src/research-subscriptions/parse.d.ts.map +1 -1
- package/dist/src/research-subscriptions/parse.js +10 -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 +142 -221
- 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/templates/bootstrap.d.ts.map +1 -1
- package/dist/src/templates/bootstrap.js +19 -32
- package/dist/src/templates/bootstrap.js.map +1 -1
- package/dist/src/tools/scientify-cron.d.ts +4 -2
- package/dist/src/tools/scientify-cron.d.ts.map +1 -1
- package/dist/src/tools/scientify-cron.js +369 -17
- package/dist/src/tools/scientify-cron.js.map +1 -1
- package/dist/src/tools/scientify-literature-state.d.ts +8 -0
- package/dist/src/tools/scientify-literature-state.d.ts.map +1 -1
- package/dist/src/tools/scientify-literature-state.js +140 -71
- package/dist/src/tools/scientify-literature-state.js.map +1 -1
- package/openclaw.plugin.json +2 -4
- package/package.json +1 -1
- package/skills/research-subscription/SKILL.md +7 -0
|
@@ -16,9 +16,22 @@ const MAX_HYPOTHESIS_REJECTION_REASONS = 24;
|
|
|
16
16
|
const MIN_CORE_FULLTEXT_COVERAGE = 0.8;
|
|
17
17
|
const MIN_EVIDENCE_BINDING_RATE = 0.9;
|
|
18
18
|
const MAX_CITATION_ERROR_RATE = 0.02;
|
|
19
|
+
const MIN_FULLTEXT_PROFILE_COMPLETENESS = 0.55;
|
|
19
20
|
const MIN_HYPOTHESIS_EVIDENCE = 2;
|
|
20
21
|
const MIN_HYPOTHESIS_DEPENDENCY_STEPS = 2;
|
|
21
22
|
const MIN_HYPOTHESIS_STATEMENT_CHARS = 48;
|
|
23
|
+
const PLACEHOLDER_TEXT_RE = /^(?:n\/a|na|none|not provided|not available|unknown|tbd|todo|null|nil|未提供|暂无|未知|无)$/iu;
|
|
24
|
+
function defaultRunProfile() {
|
|
25
|
+
return "strict";
|
|
26
|
+
}
|
|
27
|
+
function defaultTriggerState(nowMs = Date.now()) {
|
|
28
|
+
return {
|
|
29
|
+
consecutiveNewReviseDays: 0,
|
|
30
|
+
bridgeCount7d: 0,
|
|
31
|
+
unreadCoreBacklog: 0,
|
|
32
|
+
lastUpdatedAtMs: nowMs,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
22
35
|
function defaultQualityGateState() {
|
|
23
36
|
return {
|
|
24
37
|
passed: false,
|
|
@@ -38,6 +51,16 @@ function defaultHypothesisGateState() {
|
|
|
38
51
|
function normalizeText(raw) {
|
|
39
52
|
return raw.trim().replace(/\s+/g, " ");
|
|
40
53
|
}
|
|
54
|
+
function cleanOptionalText(raw) {
|
|
55
|
+
if (typeof raw !== "string")
|
|
56
|
+
return undefined;
|
|
57
|
+
const normalized = normalizeText(raw);
|
|
58
|
+
if (!normalized)
|
|
59
|
+
return undefined;
|
|
60
|
+
if (PLACEHOLDER_TEXT_RE.test(normalized))
|
|
61
|
+
return undefined;
|
|
62
|
+
return normalized;
|
|
63
|
+
}
|
|
41
64
|
function sanitizeId(raw) {
|
|
42
65
|
return normalizeText(raw)
|
|
43
66
|
.toLowerCase()
|
|
@@ -118,6 +141,9 @@ async function loadState(projectPath) {
|
|
|
118
141
|
topic: normalizeText(rawStream.topic ?? "topic"),
|
|
119
142
|
topicKey,
|
|
120
143
|
projectId: sanitizeId(rawStream.projectId ?? "auto-topic-global-000000") || "auto-topic-global-000000",
|
|
144
|
+
lastRunProfile: rawStream.lastRunProfile === "strict" || rawStream.lastRunProfile === "fast"
|
|
145
|
+
? rawStream.lastRunProfile
|
|
146
|
+
: defaultRunProfile(),
|
|
121
147
|
totalRuns: typeof rawStream.totalRuns === "number" ? Math.max(0, Math.floor(rawStream.totalRuns)) : 0,
|
|
122
148
|
totalHypotheses: typeof rawStream.totalHypotheses === "number" ? Math.max(0, Math.floor(rawStream.totalHypotheses)) : 0,
|
|
123
149
|
knowledgeTopics: Array.isArray(rawStream.knowledgeTopics)
|
|
@@ -126,6 +152,26 @@ async function loadState(projectPath) {
|
|
|
126
152
|
paperNotes: Array.isArray(rawStream.paperNotes)
|
|
127
153
|
? rawStream.paperNotes.filter((item) => typeof item === "string").map((item) => normalizeText(item))
|
|
128
154
|
: [],
|
|
155
|
+
triggerState: rawStream.triggerState && typeof rawStream.triggerState === "object" && !Array.isArray(rawStream.triggerState)
|
|
156
|
+
? {
|
|
157
|
+
consecutiveNewReviseDays: typeof rawStream.triggerState.consecutiveNewReviseDays === "number" &&
|
|
158
|
+
Number.isFinite(rawStream.triggerState.consecutiveNewReviseDays)
|
|
159
|
+
? Math.max(0, Math.floor(rawStream.triggerState.consecutiveNewReviseDays))
|
|
160
|
+
: 0,
|
|
161
|
+
bridgeCount7d: typeof rawStream.triggerState.bridgeCount7d === "number" &&
|
|
162
|
+
Number.isFinite(rawStream.triggerState.bridgeCount7d)
|
|
163
|
+
? Math.max(0, Math.floor(rawStream.triggerState.bridgeCount7d))
|
|
164
|
+
: 0,
|
|
165
|
+
unreadCoreBacklog: typeof rawStream.triggerState.unreadCoreBacklog === "number" &&
|
|
166
|
+
Number.isFinite(rawStream.triggerState.unreadCoreBacklog)
|
|
167
|
+
? Math.max(0, Math.floor(rawStream.triggerState.unreadCoreBacklog))
|
|
168
|
+
: 0,
|
|
169
|
+
lastUpdatedAtMs: typeof rawStream.triggerState.lastUpdatedAtMs === "number" &&
|
|
170
|
+
Number.isFinite(rawStream.triggerState.lastUpdatedAtMs)
|
|
171
|
+
? Math.floor(rawStream.triggerState.lastUpdatedAtMs)
|
|
172
|
+
: Date.now(),
|
|
173
|
+
}
|
|
174
|
+
: defaultTriggerState(),
|
|
129
175
|
recentFullTextReadCount: typeof rawStream.recentFullTextReadCount === "number"
|
|
130
176
|
? Math.max(0, Math.floor(rawStream.recentFullTextReadCount))
|
|
131
177
|
: 0,
|
|
@@ -253,8 +299,8 @@ function normalizeStringArray(raw) {
|
|
|
253
299
|
return undefined;
|
|
254
300
|
const values = raw
|
|
255
301
|
.filter((item) => typeof item === "string")
|
|
256
|
-
.map((item) =>
|
|
257
|
-
.filter((item) => item
|
|
302
|
+
.map((item) => cleanOptionalText(item))
|
|
303
|
+
.filter((item) => Boolean(item));
|
|
258
304
|
return values.length > 0 ? values : undefined;
|
|
259
305
|
}
|
|
260
306
|
function normalizeEvidenceAnchors(raw) {
|
|
@@ -263,14 +309,14 @@ function normalizeEvidenceAnchors(raw) {
|
|
|
263
309
|
const anchors = raw
|
|
264
310
|
.filter((item) => !!item && typeof item === "object")
|
|
265
311
|
.map((item) => {
|
|
266
|
-
const claim =
|
|
312
|
+
const claim = cleanOptionalText(item.claim);
|
|
267
313
|
if (!claim)
|
|
268
314
|
return undefined;
|
|
269
315
|
return {
|
|
270
|
-
...(item.section ? { section:
|
|
271
|
-
...(item.locator ? { locator:
|
|
316
|
+
...(cleanOptionalText(item.section) ? { section: cleanOptionalText(item.section) } : {}),
|
|
317
|
+
...(cleanOptionalText(item.locator) ? { locator: cleanOptionalText(item.locator) } : {}),
|
|
272
318
|
claim,
|
|
273
|
-
...(item.quote ? { quote:
|
|
319
|
+
...(cleanOptionalText(item.quote) ? { quote: cleanOptionalText(item.quote) } : {}),
|
|
274
320
|
};
|
|
275
321
|
})
|
|
276
322
|
.filter((item) => Boolean(item));
|
|
@@ -288,7 +334,7 @@ function toPaperNoteSlug(paper) {
|
|
|
288
334
|
}
|
|
289
335
|
function normalizePaper(input) {
|
|
290
336
|
const evidenceIds = Array.isArray(input.evidenceIds)
|
|
291
|
-
? input.evidenceIds.map((id) =>
|
|
337
|
+
? input.evidenceIds.map((id) => cleanOptionalText(id)).filter((id) => Boolean(id))
|
|
292
338
|
: undefined;
|
|
293
339
|
const keyEvidenceSpans = normalizeStringArray(input.keyEvidenceSpans);
|
|
294
340
|
const subdomains = normalizeStringArray(input.subdomains);
|
|
@@ -309,31 +355,33 @@ function normalizePaper(input) {
|
|
|
309
355
|
: readStatus
|
|
310
356
|
? false
|
|
311
357
|
: undefined;
|
|
312
|
-
const unreadReason =
|
|
358
|
+
const unreadReason = cleanOptionalText(input.unreadReason);
|
|
313
359
|
return {
|
|
314
|
-
...(input.id ? { id:
|
|
315
|
-
...(input.title ? { title:
|
|
316
|
-
...(input.url ? { url:
|
|
317
|
-
...(input.source ? { source:
|
|
318
|
-
...(input.publishedAt ? { publishedAt:
|
|
360
|
+
...(cleanOptionalText(input.id) ? { id: cleanOptionalText(input.id) } : {}),
|
|
361
|
+
...(cleanOptionalText(input.title) ? { title: cleanOptionalText(input.title) } : {}),
|
|
362
|
+
...(cleanOptionalText(input.url) ? { url: cleanOptionalText(input.url) } : {}),
|
|
363
|
+
...(cleanOptionalText(input.source) ? { source: cleanOptionalText(input.source) } : {}),
|
|
364
|
+
...(cleanOptionalText(input.publishedAt) ? { publishedAt: cleanOptionalText(input.publishedAt) } : {}),
|
|
319
365
|
...(typeof input.score === "number" && Number.isFinite(input.score)
|
|
320
366
|
? { score: Number(input.score.toFixed(2)) }
|
|
321
367
|
: {}),
|
|
322
|
-
...(input.reason ? { reason:
|
|
323
|
-
...(input.summary ? { summary:
|
|
368
|
+
...(cleanOptionalText(input.reason) ? { reason: cleanOptionalText(input.reason) } : {}),
|
|
369
|
+
...(cleanOptionalText(input.summary) ? { summary: cleanOptionalText(input.summary) } : {}),
|
|
324
370
|
...(evidenceIds && evidenceIds.length > 0 ? { evidenceIds } : {}),
|
|
325
371
|
...(typeof fullTextRead === "boolean" ? { fullTextRead } : {}),
|
|
326
372
|
...(readStatus ? { readStatus } : {}),
|
|
327
|
-
...(input.fullTextSource ? { fullTextSource:
|
|
328
|
-
...(input.fullTextRef ? { fullTextRef:
|
|
373
|
+
...(cleanOptionalText(input.fullTextSource) ? { fullTextSource: cleanOptionalText(input.fullTextSource) } : {}),
|
|
374
|
+
...(cleanOptionalText(input.fullTextRef) ? { fullTextRef: cleanOptionalText(input.fullTextRef) } : {}),
|
|
329
375
|
...(unreadReason ? { unreadReason } : {}),
|
|
330
376
|
...(keyEvidenceSpans && keyEvidenceSpans.length > 0 ? { keyEvidenceSpans } : {}),
|
|
331
|
-
...(input.domain ? { domain:
|
|
377
|
+
...(cleanOptionalText(input.domain) ? { domain: cleanOptionalText(input.domain) } : {}),
|
|
332
378
|
...(subdomains ? { subdomains } : {}),
|
|
333
379
|
...(crossDomainLinks ? { crossDomainLinks } : {}),
|
|
334
|
-
...(input.researchGoal ? { researchGoal:
|
|
335
|
-
...(input.approach ? { approach:
|
|
336
|
-
...(
|
|
380
|
+
...(cleanOptionalText(input.researchGoal) ? { researchGoal: cleanOptionalText(input.researchGoal) } : {}),
|
|
381
|
+
...(cleanOptionalText(input.approach) ? { approach: cleanOptionalText(input.approach) } : {}),
|
|
382
|
+
...(cleanOptionalText(input.methodologyDesign)
|
|
383
|
+
? { methodologyDesign: cleanOptionalText(input.methodologyDesign) }
|
|
384
|
+
: {}),
|
|
337
385
|
...(keyContributions ? { keyContributions } : {}),
|
|
338
386
|
...(practicalInsights ? { practicalInsights } : {}),
|
|
339
387
|
...(mustUnderstandPoints ? { mustUnderstandPoints } : {}),
|
|
@@ -446,6 +494,32 @@ function hasStructuredProfile(paper) {
|
|
|
446
494
|
(paper.limitations && paper.limitations.length > 0) ||
|
|
447
495
|
(paper.evidenceAnchors && paper.evidenceAnchors.length > 0));
|
|
448
496
|
}
|
|
497
|
+
function countStructuredProfileFields(paper) {
|
|
498
|
+
let count = 0;
|
|
499
|
+
if (paper.domain && paper.domain.trim())
|
|
500
|
+
count += 1;
|
|
501
|
+
if (paper.subdomains && paper.subdomains.length > 0)
|
|
502
|
+
count += 1;
|
|
503
|
+
if (paper.crossDomainLinks && paper.crossDomainLinks.length > 0)
|
|
504
|
+
count += 1;
|
|
505
|
+
if (paper.researchGoal && paper.researchGoal.trim())
|
|
506
|
+
count += 1;
|
|
507
|
+
if (paper.approach && paper.approach.trim())
|
|
508
|
+
count += 1;
|
|
509
|
+
if (paper.methodologyDesign && paper.methodologyDesign.trim())
|
|
510
|
+
count += 1;
|
|
511
|
+
if (paper.keyContributions && paper.keyContributions.length > 0)
|
|
512
|
+
count += 1;
|
|
513
|
+
if (paper.practicalInsights && paper.practicalInsights.length > 0)
|
|
514
|
+
count += 1;
|
|
515
|
+
if (paper.mustUnderstandPoints && paper.mustUnderstandPoints.length > 0)
|
|
516
|
+
count += 1;
|
|
517
|
+
if (paper.limitations && paper.limitations.length > 0)
|
|
518
|
+
count += 1;
|
|
519
|
+
if (paper.evidenceAnchors && paper.evidenceAnchors.length > 0)
|
|
520
|
+
count += 1;
|
|
521
|
+
return count;
|
|
522
|
+
}
|
|
449
523
|
function isFullTextRead(paper) {
|
|
450
524
|
return paper.fullTextRead === true || paper.readStatus === "fulltext";
|
|
451
525
|
}
|
|
@@ -492,6 +566,13 @@ function applyQualityGates(args) {
|
|
|
492
566
|
const fullTextCoreCount = corePapers.filter((paper) => isFullTextRead(paper)).length;
|
|
493
567
|
const fullTextCoverage = coreCount > 0 ? fullTextCoreCount / coreCount : 0;
|
|
494
568
|
const fullTextCoveragePct = Number((fullTextCoverage * 100).toFixed(2));
|
|
569
|
+
const fullTextCorePapers = corePapers.filter((paper) => isFullTextRead(paper));
|
|
570
|
+
const structuredFieldTotal = 11;
|
|
571
|
+
const avgFullTextProfileCompleteness = fullTextCorePapers.length > 0
|
|
572
|
+
? fullTextCorePapers.reduce((sum, paper) => sum + countStructuredProfileFields(paper) / structuredFieldTotal, 0) /
|
|
573
|
+
fullTextCorePapers.length
|
|
574
|
+
: 0;
|
|
575
|
+
const avgFullTextProfileCompletenessPct = Number((avgFullTextProfileCompleteness * 100).toFixed(2));
|
|
495
576
|
const unreadCorePaperIds = dedupeText(corePapers
|
|
496
577
|
.filter((paper) => !isFullTextRead(paper))
|
|
497
578
|
.map((paper) => paper.id?.trim() || paper.url?.trim() || paper.title?.trim() || "unknown-paper")).slice(0, 50);
|
|
@@ -576,6 +657,9 @@ function applyQualityGates(args) {
|
|
|
576
657
|
if (fullTextCoverage < MIN_CORE_FULLTEXT_COVERAGE) {
|
|
577
658
|
reasons.push(`core_fulltext_coverage_below_threshold(${fullTextCoveragePct}% < ${Number((MIN_CORE_FULLTEXT_COVERAGE * 100).toFixed(0))}%)`);
|
|
578
659
|
}
|
|
660
|
+
if (fullTextCorePapers.length > 0 && avgFullTextProfileCompleteness < MIN_FULLTEXT_PROFILE_COMPLETENESS) {
|
|
661
|
+
reasons.push(`fulltext_profile_completeness_below_threshold(${avgFullTextProfileCompletenessPct}% < ${Number((MIN_FULLTEXT_PROFILE_COMPLETENESS * 100).toFixed(0))}%)`);
|
|
662
|
+
}
|
|
579
663
|
if (typeof args.requiredFullTextCoveragePct === "number" &&
|
|
580
664
|
Number.isFinite(args.requiredFullTextCoveragePct) &&
|
|
581
665
|
args.requiredFullTextCoveragePct > 0 &&
|
|
@@ -589,10 +673,15 @@ function applyQualityGates(args) {
|
|
|
589
673
|
reasons.push(`citation_error_rate_above_threshold(${citationErrorRatePct}% >= ${Number((MAX_CITATION_ERROR_RATE * 100).toFixed(0))}%)`);
|
|
590
674
|
}
|
|
591
675
|
const bridgeChangeCount = args.knowledgeChanges.filter((item) => item.type === "BRIDGE").length;
|
|
676
|
+
const reviseCount = args.knowledgeChanges.filter((item) => item.type === "REVISE").length;
|
|
677
|
+
const confirmCount = args.knowledgeChanges.filter((item) => item.type === "CONFIRM").length;
|
|
592
678
|
const executedReflectionCount = args.reflectionTasks.filter((task) => task.status === "executed").length;
|
|
593
679
|
if (bridgeChangeCount > 0 && executedReflectionCount === 0) {
|
|
594
680
|
reasons.push(`reflection_missing_for_bridge(bridge_count=${bridgeChangeCount})`);
|
|
595
681
|
}
|
|
682
|
+
if (reviseCount > 0 && confirmCount > 0 && executedReflectionCount === 0) {
|
|
683
|
+
reasons.push(`reflection_missing_for_conflict(revise_count=${reviseCount},confirm_count=${confirmCount})`);
|
|
684
|
+
}
|
|
596
685
|
if (args.hypothesisGate.rejected > 0 && args.hypothesisGate.accepted === 0 && args.hypotheses.length > 0) {
|
|
597
686
|
reasons.push(`hypothesis_gate_rejected_all(${args.hypothesisGate.rejected})`);
|
|
598
687
|
}
|
|
@@ -707,10 +796,12 @@ function toSummary(stream) {
|
|
|
707
796
|
return {
|
|
708
797
|
projectId: stream.projectId,
|
|
709
798
|
streamKey: stream.topicKey,
|
|
799
|
+
runProfile: stream.lastRunProfile,
|
|
710
800
|
totalRuns: stream.totalRuns,
|
|
711
801
|
totalHypotheses: stream.totalHypotheses,
|
|
712
802
|
knowledgeTopicsCount: stream.knowledgeTopics.length,
|
|
713
803
|
paperNotesCount: stream.paperNotes.length,
|
|
804
|
+
triggerState: stream.triggerState,
|
|
714
805
|
recentFullTextReadCount: stream.recentFullTextReadCount,
|
|
715
806
|
recentNotFullTextReadCount: stream.recentNotFullTextReadCount,
|
|
716
807
|
qualityGate: stream.lastQualityGate,
|
|
@@ -931,9 +1022,12 @@ function applyHypothesisGate(args) {
|
|
|
931
1022
|
if (resolvedEvidence < evidenceIds.length) {
|
|
932
1023
|
reasons.push(`unresolved_evidence_ids(${evidenceIds.length - resolvedEvidence})`);
|
|
933
1024
|
}
|
|
934
|
-
if (fullTextSupported === 0) {
|
|
1025
|
+
if (fullTextSupported === 0 && args.runProfile === "strict") {
|
|
935
1026
|
reasons.push("no_fulltext_backed_evidence");
|
|
936
1027
|
}
|
|
1028
|
+
if (resolvedEvidence === 0) {
|
|
1029
|
+
reasons.push("no_resolved_evidence");
|
|
1030
|
+
}
|
|
937
1031
|
const dependencyPathLength = hypothesis.dependencyPath?.length ?? 0;
|
|
938
1032
|
if (dependencyPathLength < MIN_HYPOTHESIS_DEPENDENCY_STEPS) {
|
|
939
1033
|
reasons.push(`dependency_path_too_short(${dependencyPathLength}<${MIN_HYPOTHESIS_DEPENDENCY_STEPS})`);
|
|
@@ -972,6 +1066,51 @@ function applyHypothesisGate(args) {
|
|
|
972
1066
|
},
|
|
973
1067
|
};
|
|
974
1068
|
}
|
|
1069
|
+
function toDayStartMs(day) {
|
|
1070
|
+
const ts = Date.parse(`${day}T00:00:00.000Z`);
|
|
1071
|
+
return Number.isFinite(ts) ? ts : undefined;
|
|
1072
|
+
}
|
|
1073
|
+
function deriveTriggerState(args) {
|
|
1074
|
+
const sorted = [...args.recentChangeStats].sort((a, b) => b.day.localeCompare(a.day));
|
|
1075
|
+
// consecutive days from latest day with NEW/REVISE > 0
|
|
1076
|
+
let consecutiveNewReviseDays = 0;
|
|
1077
|
+
let expectedDayMs;
|
|
1078
|
+
for (const item of sorted) {
|
|
1079
|
+
const dayMs = toDayStartMs(item.day);
|
|
1080
|
+
if (dayMs === undefined)
|
|
1081
|
+
continue;
|
|
1082
|
+
const hasSignal = item.newCount > 0 || item.reviseCount > 0;
|
|
1083
|
+
if (!hasSignal)
|
|
1084
|
+
break;
|
|
1085
|
+
if (expectedDayMs === undefined) {
|
|
1086
|
+
consecutiveNewReviseDays = 1;
|
|
1087
|
+
expectedDayMs = dayMs - 24 * 60 * 60 * 1000;
|
|
1088
|
+
continue;
|
|
1089
|
+
}
|
|
1090
|
+
if (dayMs === expectedDayMs) {
|
|
1091
|
+
consecutiveNewReviseDays += 1;
|
|
1092
|
+
expectedDayMs = dayMs - 24 * 60 * 60 * 1000;
|
|
1093
|
+
continue;
|
|
1094
|
+
}
|
|
1095
|
+
break;
|
|
1096
|
+
}
|
|
1097
|
+
const sevenDaysAgo = args.nowMs - 7 * 24 * 60 * 60 * 1000;
|
|
1098
|
+
let bridgeCount7d = 0;
|
|
1099
|
+
for (const item of sorted) {
|
|
1100
|
+
const dayMs = toDayStartMs(item.day);
|
|
1101
|
+
if (dayMs === undefined)
|
|
1102
|
+
continue;
|
|
1103
|
+
if (dayMs < sevenDaysAgo)
|
|
1104
|
+
continue;
|
|
1105
|
+
bridgeCount7d += Math.max(0, Math.floor(item.bridgeCount));
|
|
1106
|
+
}
|
|
1107
|
+
return {
|
|
1108
|
+
consecutiveNewReviseDays,
|
|
1109
|
+
bridgeCount7d,
|
|
1110
|
+
unreadCoreBacklog: Math.max(0, Math.floor(args.unreadCoreBacklog)),
|
|
1111
|
+
lastUpdatedAtMs: args.nowMs,
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
975
1114
|
export async function commitKnowledgeRun(input) {
|
|
976
1115
|
const project = await resolveProjectContext({
|
|
977
1116
|
projectId: input.projectId,
|
|
@@ -1023,10 +1162,12 @@ export async function commitKnowledgeRun(input) {
|
|
|
1023
1162
|
topic: normalizeText(input.topic),
|
|
1024
1163
|
topicKey: streamKey,
|
|
1025
1164
|
projectId: project.projectId,
|
|
1165
|
+
lastRunProfile: defaultRunProfile(),
|
|
1026
1166
|
totalRuns: 0,
|
|
1027
1167
|
totalHypotheses: 0,
|
|
1028
1168
|
knowledgeTopics: [],
|
|
1029
1169
|
paperNotes: [],
|
|
1170
|
+
triggerState: defaultTriggerState(),
|
|
1030
1171
|
recentFullTextReadCount: 0,
|
|
1031
1172
|
recentNotFullTextReadCount: 0,
|
|
1032
1173
|
lastQualityGate: defaultQualityGateState(),
|
|
@@ -1044,9 +1185,9 @@ export async function commitKnowledgeRun(input) {
|
|
|
1044
1185
|
.map((paper) => paper.id || paper.url || paper.title || "")
|
|
1045
1186
|
.map((value) => normalizeText(value))
|
|
1046
1187
|
.filter((value) => value.length > 0);
|
|
1047
|
-
const
|
|
1048
|
-
|
|
1049
|
-
|
|
1188
|
+
const explicitRunId = input.runId?.trim() ? sanitizeId(input.runId) : undefined;
|
|
1189
|
+
let runId = explicitRunId ??
|
|
1190
|
+
buildRunFingerprint({
|
|
1050
1191
|
scope: stream.scope,
|
|
1051
1192
|
topic: stream.topic,
|
|
1052
1193
|
status: input.status,
|
|
@@ -1054,15 +1195,30 @@ export async function commitKnowledgeRun(input) {
|
|
|
1054
1195
|
paperIds,
|
|
1055
1196
|
note: input.note,
|
|
1056
1197
|
});
|
|
1198
|
+
const inferredRunProfile = input.knowledgeState?.runLog?.runProfile === "strict" || input.knowledgeState?.runLog?.runProfile === "fast"
|
|
1199
|
+
? input.knowledgeState.runLog.runProfile
|
|
1200
|
+
: input.knowledgeState?.runLog?.requiredCorePapers !== undefined ||
|
|
1201
|
+
input.knowledgeState?.runLog?.requiredFullTextCoveragePct !== undefined
|
|
1202
|
+
? "strict"
|
|
1203
|
+
: defaultRunProfile();
|
|
1057
1204
|
if (stream.recentRunIds.includes(runId)) {
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
streamKey
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1205
|
+
// Preserve idempotency for fingerprint-based runs, but avoid silently dropping
|
|
1206
|
+
// valid new cron cycles that accidentally reuse an explicit run_id.
|
|
1207
|
+
if (!explicitRunId) {
|
|
1208
|
+
root.streams[streamKey] = stream;
|
|
1209
|
+
return {
|
|
1210
|
+
projectId: project.projectId,
|
|
1211
|
+
streamKey,
|
|
1212
|
+
summary: toSummary(stream),
|
|
1213
|
+
runId,
|
|
1214
|
+
createdProject: project.created,
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1217
|
+
const collisionTag = createHash("sha1")
|
|
1218
|
+
.update(`${nowMs}\n${paperIds.join("|")}\n${input.status ?? ""}\n${input.note ?? ""}`)
|
|
1219
|
+
.digest("hex")
|
|
1220
|
+
.slice(0, 8);
|
|
1221
|
+
runId = `${runId}-r${collisionTag}`;
|
|
1066
1222
|
}
|
|
1067
1223
|
const rootPath = getKnowledgeStateRoot(project.projectPath);
|
|
1068
1224
|
const logDir = path.join(rootPath, "logs");
|
|
@@ -1100,6 +1256,7 @@ export async function commitKnowledgeRun(input) {
|
|
|
1100
1256
|
hypotheses: submittedHypotheses,
|
|
1101
1257
|
allRunPapers: mergedRunPapers,
|
|
1102
1258
|
knowledgeChanges,
|
|
1259
|
+
runProfile: inferredRunProfile,
|
|
1103
1260
|
});
|
|
1104
1261
|
const acceptedHypotheses = hypothesisEval.acceptedHypotheses;
|
|
1105
1262
|
const qualityEval = applyQualityGates({
|
|
@@ -1114,6 +1271,10 @@ export async function commitKnowledgeRun(input) {
|
|
|
1114
1271
|
requiredCorePapers: input.knowledgeState?.runLog?.requiredCorePapers,
|
|
1115
1272
|
requiredFullTextCoveragePct: input.knowledgeState?.runLog?.requiredFullTextCoveragePct,
|
|
1116
1273
|
});
|
|
1274
|
+
if (changeSanitization.droppedBridgeCount > 0) {
|
|
1275
|
+
qualityEval.qualityGate.passed = false;
|
|
1276
|
+
qualityEval.qualityGate.reasons.push(`bridge_dropped_due_to_ungrounded_evidence(${changeSanitization.droppedBridgeCount})`);
|
|
1277
|
+
}
|
|
1117
1278
|
const requestedStatus = normalizeText(input.status ?? "ok");
|
|
1118
1279
|
const qualitySensitiveStatus = requestedStatus === "ok" || requestedStatus === "fallback_representative";
|
|
1119
1280
|
const effectiveStatus = qualitySensitiveStatus && !qualityEval.qualityGate.passed ? "degraded_quality" : requestedStatus;
|
|
@@ -1174,6 +1335,7 @@ export async function commitKnowledgeRun(input) {
|
|
|
1174
1335
|
await writeFile(path.join(knowledgeDir, "_index.md"), renderKnowledgeIndexMarkdown({
|
|
1175
1336
|
now: nowIso,
|
|
1176
1337
|
topic: stream.topic,
|
|
1338
|
+
runProfile: inferredRunProfile,
|
|
1177
1339
|
topicFiles: stream.knowledgeTopics,
|
|
1178
1340
|
paperNotesCount: stream.paperNotes.length,
|
|
1179
1341
|
totalHypotheses: stream.totalHypotheses + recentHypothesisSummaries.length,
|
|
@@ -1188,6 +1350,7 @@ export async function commitKnowledgeRun(input) {
|
|
|
1188
1350
|
}), "utf-8");
|
|
1189
1351
|
const changeStat = countChangeStats(dayKey, runId, knowledgeChanges);
|
|
1190
1352
|
stream.projectId = project.projectId;
|
|
1353
|
+
stream.lastRunProfile = inferredRunProfile;
|
|
1191
1354
|
stream.totalRuns += 1;
|
|
1192
1355
|
stream.totalHypotheses += recentHypothesisSummaries.length;
|
|
1193
1356
|
stream.lastRunAtMs = nowMs;
|
|
@@ -1207,11 +1370,18 @@ export async function commitKnowledgeRun(input) {
|
|
|
1207
1370
|
].slice(0, MAX_RECENT_HYPOTHESES);
|
|
1208
1371
|
stream.recentHypotheses = [...recentHypothesisSummaries, ...stream.recentHypotheses].slice(0, MAX_RECENT_HYPOTHESES);
|
|
1209
1372
|
stream.recentChangeStats = [changeStat, ...stream.recentChangeStats].slice(0, MAX_RECENT_CHANGE_STATS);
|
|
1373
|
+
stream.triggerState = deriveTriggerState({
|
|
1374
|
+
recentChangeStats: stream.recentChangeStats,
|
|
1375
|
+
unreadCoreBacklog: qualityEval.unreadCorePaperIds.length,
|
|
1376
|
+
nowMs,
|
|
1377
|
+
});
|
|
1210
1378
|
root.streams[streamKey] = stream;
|
|
1211
1379
|
await saveStateAtomic(project.projectPath, root);
|
|
1212
1380
|
await appendFile(path.join(logDir, `day-${dayKey}-run-details.jsonl`), `${JSON.stringify({
|
|
1213
1381
|
ts: nowMs,
|
|
1382
|
+
run_id: runId,
|
|
1214
1383
|
runId,
|
|
1384
|
+
run_profile: inferredRunProfile,
|
|
1215
1385
|
scope: stream.scope,
|
|
1216
1386
|
topic: stream.topic,
|
|
1217
1387
|
streamKey,
|
|
@@ -1242,7 +1412,9 @@ export async function commitKnowledgeRun(input) {
|
|
|
1242
1412
|
})}\n`, "utf-8");
|
|
1243
1413
|
await appendEvent(project.projectPath, {
|
|
1244
1414
|
ts: nowMs,
|
|
1415
|
+
run_id: runId,
|
|
1245
1416
|
runId,
|
|
1417
|
+
run_profile: inferredRunProfile,
|
|
1246
1418
|
scope: stream.scope,
|
|
1247
1419
|
topic: stream.topic,
|
|
1248
1420
|
streamKey,
|
|
@@ -1264,6 +1436,7 @@ export async function commitKnowledgeRun(input) {
|
|
|
1264
1436
|
hypothesisCount: recentHypothesisSummaries.length,
|
|
1265
1437
|
submittedHypothesisCount: submittedHypotheses.length,
|
|
1266
1438
|
hypothesisGate: hypothesisEval.gate,
|
|
1439
|
+
triggerState: stream.triggerState,
|
|
1267
1440
|
reflectionTasks,
|
|
1268
1441
|
corePapers,
|
|
1269
1442
|
explorationPapers,
|