scientify 1.12.0 → 1.12.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/dist/src/hooks/research-mode.d.ts +1 -1
- package/dist/src/hooks/research-mode.d.ts.map +1 -1
- package/dist/src/hooks/research-mode.js +28 -2
- package/dist/src/hooks/research-mode.js.map +1 -1
- package/dist/src/knowledge-state/render.d.ts +8 -1
- package/dist/src/knowledge-state/render.d.ts.map +1 -1
- package/dist/src/knowledge-state/render.js +31 -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 +340 -6
- package/dist/src/knowledge-state/store.js.map +1 -1
- package/dist/src/knowledge-state/types.d.ts +19 -0
- package/dist/src/knowledge-state/types.d.ts.map +1 -1
- package/dist/src/literature/subscription-state.d.ts +2 -0
- package/dist/src/literature/subscription-state.d.ts.map +1 -1
- package/dist/src/literature/subscription-state.js +7 -0
- 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 +44 -16
- package/dist/src/research-subscriptions/prompt.js.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 +33 -1
- package/dist/src/tools/scientify-cron.js.map +1 -1
- package/dist/src/tools/scientify-literature-state.d.ts +4 -0
- package/dist/src/tools/scientify-literature-state.d.ts.map +1 -1
- package/dist/src/tools/scientify-literature-state.js +45 -0
- package/dist/src/tools/scientify-literature-state.js.map +1 -1
- package/package.json +1 -1
- package/skills/research-subscription/SKILL.md +16 -0
|
@@ -2,18 +2,23 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { appendFile, mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { dayKeyFromTimestamp, renderDailyChangesMarkdown, renderExplorationLogMarkdown, renderHypothesisMarkdown, renderIngestLogMarkdown, renderKnowledgeIndexMarkdown, renderPaperNoteHeaderMarkdown, renderPaperNoteRunMarkdown, renderTopicUpdateMarkdown, slugifyTopic, } from "./render.js";
|
|
5
|
+
import { dayKeyFromTimestamp, renderDailyChangesMarkdown, renderExplorationLogMarkdown, renderHypothesisMarkdown, renderIngestLogMarkdown, renderKnowledgeIndexMarkdown, renderPaperNoteHeaderMarkdown, renderPaperNoteRunMarkdown, renderReflectionLogMarkdown, renderTopicUpdateMarkdown, slugifyTopic, } from "./render.js";
|
|
6
6
|
import { resolveProjectContext } from "./project.js";
|
|
7
7
|
const STATE_VERSION = 1;
|
|
8
8
|
const MAX_RECENT_RUN_IDS = 200;
|
|
9
9
|
const MAX_RECENT_HYPOTHESES = 50;
|
|
10
10
|
const MAX_RECENT_CHANGE_STATS = 30;
|
|
11
11
|
const MAX_LAST_TRACE = 20;
|
|
12
|
+
const MAX_LAST_REFLECTION_TASKS = 20;
|
|
12
13
|
const MAX_RECENT_PAPERS = 50;
|
|
13
14
|
const MAX_PAPER_NOTES = 800;
|
|
15
|
+
const MAX_HYPOTHESIS_REJECTION_REASONS = 24;
|
|
14
16
|
const MIN_CORE_FULLTEXT_COVERAGE = 0.8;
|
|
15
17
|
const MIN_EVIDENCE_BINDING_RATE = 0.9;
|
|
16
18
|
const MAX_CITATION_ERROR_RATE = 0.02;
|
|
19
|
+
const MIN_HYPOTHESIS_EVIDENCE = 2;
|
|
20
|
+
const MIN_HYPOTHESIS_DEPENDENCY_STEPS = 2;
|
|
21
|
+
const MIN_HYPOTHESIS_STATEMENT_CHARS = 48;
|
|
17
22
|
function defaultQualityGateState() {
|
|
18
23
|
return {
|
|
19
24
|
passed: false,
|
|
@@ -23,6 +28,13 @@ function defaultQualityGateState() {
|
|
|
23
28
|
reasons: ["quality gate not evaluated"],
|
|
24
29
|
};
|
|
25
30
|
}
|
|
31
|
+
function defaultHypothesisGateState() {
|
|
32
|
+
return {
|
|
33
|
+
accepted: 0,
|
|
34
|
+
rejected: 0,
|
|
35
|
+
rejectionReasons: [],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
26
38
|
function normalizeText(raw) {
|
|
27
39
|
return raw.trim().replace(/\s+/g, " ");
|
|
28
40
|
}
|
|
@@ -178,6 +190,39 @@ async function loadState(projectPath) {
|
|
|
178
190
|
.map(normalizeTrace)
|
|
179
191
|
.filter((item) => Boolean(item))
|
|
180
192
|
: [],
|
|
193
|
+
lastReflectionTasks: Array.isArray(rawStream.lastReflectionTasks)
|
|
194
|
+
? rawStream.lastReflectionTasks
|
|
195
|
+
.filter((item) => !!item && typeof item === "object")
|
|
196
|
+
.map((item) => ({
|
|
197
|
+
id: sanitizeId(item.id ?? "task"),
|
|
198
|
+
trigger: ["BRIDGE", "TREND", "CONTRADICTION", "UNREAD_CORE"].includes(item.trigger)
|
|
199
|
+
? item.trigger
|
|
200
|
+
: "TREND",
|
|
201
|
+
reason: normalizeText(item.reason ?? ""),
|
|
202
|
+
query: normalizeText(item.query ?? ""),
|
|
203
|
+
priority: ["high", "medium", "low"].includes(item.priority) ? item.priority : "medium",
|
|
204
|
+
status: (item.status === "executed" ? "executed" : "planned"),
|
|
205
|
+
}))
|
|
206
|
+
.filter((item) => item.reason.length > 0 && item.query.length > 0)
|
|
207
|
+
: [],
|
|
208
|
+
lastHypothesisGate: rawStream.lastHypothesisGate &&
|
|
209
|
+
typeof rawStream.lastHypothesisGate === "object" &&
|
|
210
|
+
!Array.isArray(rawStream.lastHypothesisGate)
|
|
211
|
+
? {
|
|
212
|
+
accepted: typeof rawStream.lastHypothesisGate.accepted === "number"
|
|
213
|
+
? Math.max(0, Math.floor(rawStream.lastHypothesisGate.accepted))
|
|
214
|
+
: 0,
|
|
215
|
+
rejected: typeof rawStream.lastHypothesisGate.rejected === "number"
|
|
216
|
+
? Math.max(0, Math.floor(rawStream.lastHypothesisGate.rejected))
|
|
217
|
+
: 0,
|
|
218
|
+
rejectionReasons: Array.isArray(rawStream.lastHypothesisGate.rejectionReasons)
|
|
219
|
+
? rawStream.lastHypothesisGate.rejectionReasons
|
|
220
|
+
.filter((item) => typeof item === "string")
|
|
221
|
+
.map((item) => normalizeText(item))
|
|
222
|
+
.filter((item) => item.length > 0)
|
|
223
|
+
: [],
|
|
224
|
+
}
|
|
225
|
+
: defaultHypothesisGateState(),
|
|
181
226
|
};
|
|
182
227
|
}
|
|
183
228
|
return {
|
|
@@ -522,15 +567,35 @@ function applyQualityGates(args) {
|
|
|
522
567
|
}
|
|
523
568
|
}
|
|
524
569
|
const reasons = [];
|
|
570
|
+
if (typeof args.requiredCorePapers === "number" &&
|
|
571
|
+
Number.isFinite(args.requiredCorePapers) &&
|
|
572
|
+
args.requiredCorePapers > 0 &&
|
|
573
|
+
coreCount < args.requiredCorePapers) {
|
|
574
|
+
reasons.push(`core_paper_count_below_required(${coreCount} < ${Math.floor(args.requiredCorePapers)})`);
|
|
575
|
+
}
|
|
525
576
|
if (fullTextCoverage < MIN_CORE_FULLTEXT_COVERAGE) {
|
|
526
577
|
reasons.push(`core_fulltext_coverage_below_threshold(${fullTextCoveragePct}% < ${Number((MIN_CORE_FULLTEXT_COVERAGE * 100).toFixed(0))}%)`);
|
|
527
578
|
}
|
|
579
|
+
if (typeof args.requiredFullTextCoveragePct === "number" &&
|
|
580
|
+
Number.isFinite(args.requiredFullTextCoveragePct) &&
|
|
581
|
+
args.requiredFullTextCoveragePct > 0 &&
|
|
582
|
+
fullTextCoveragePct < args.requiredFullTextCoveragePct) {
|
|
583
|
+
reasons.push(`core_fulltext_coverage_below_required(${fullTextCoveragePct}% < ${Number(args.requiredFullTextCoveragePct.toFixed(2))}%)`);
|
|
584
|
+
}
|
|
528
585
|
if (evidenceBindingRate < MIN_EVIDENCE_BINDING_RATE) {
|
|
529
586
|
reasons.push(`evidence_binding_rate_below_threshold(${evidenceBindingRatePct}% < ${Number((MIN_EVIDENCE_BINDING_RATE * 100).toFixed(0))}%)`);
|
|
530
587
|
}
|
|
531
588
|
if (citationErrorRate >= MAX_CITATION_ERROR_RATE) {
|
|
532
589
|
reasons.push(`citation_error_rate_above_threshold(${citationErrorRatePct}% >= ${Number((MAX_CITATION_ERROR_RATE * 100).toFixed(0))}%)`);
|
|
533
590
|
}
|
|
591
|
+
const bridgeChangeCount = args.knowledgeChanges.filter((item) => item.type === "BRIDGE").length;
|
|
592
|
+
const executedReflectionCount = args.reflectionTasks.filter((task) => task.status === "executed").length;
|
|
593
|
+
if (bridgeChangeCount > 0 && executedReflectionCount === 0) {
|
|
594
|
+
reasons.push(`reflection_missing_for_bridge(bridge_count=${bridgeChangeCount})`);
|
|
595
|
+
}
|
|
596
|
+
if (args.hypothesisGate.rejected > 0 && args.hypothesisGate.accepted === 0 && args.hypotheses.length > 0) {
|
|
597
|
+
reasons.push(`hypothesis_gate_rejected_all(${args.hypothesisGate.rejected})`);
|
|
598
|
+
}
|
|
534
599
|
if (downgradedHighConfidenceCount > 0) {
|
|
535
600
|
reasons.push(`high_confidence_downgraded(${downgradedHighConfidenceCount})`);
|
|
536
601
|
}
|
|
@@ -656,6 +721,8 @@ function toSummary(stream) {
|
|
|
656
721
|
recentHypotheses: stream.recentHypotheses,
|
|
657
722
|
recentChangeStats: stream.recentChangeStats,
|
|
658
723
|
lastExplorationTrace: stream.lastExplorationTrace,
|
|
724
|
+
lastReflectionTasks: stream.lastReflectionTasks,
|
|
725
|
+
hypothesisGate: stream.lastHypothesisGate,
|
|
659
726
|
};
|
|
660
727
|
}
|
|
661
728
|
function countChangeStats(day, runId, changes) {
|
|
@@ -682,6 +749,229 @@ function countChangeStats(day, runId, changes) {
|
|
|
682
749
|
bridgeCount,
|
|
683
750
|
};
|
|
684
751
|
}
|
|
752
|
+
function tokenizeForQuery(raw) {
|
|
753
|
+
return normalizeText(raw)
|
|
754
|
+
.toLowerCase()
|
|
755
|
+
.replace(/[^a-z0-9\u4e00-\u9fff\s_-]+/g, " ")
|
|
756
|
+
.split(/\s+/)
|
|
757
|
+
.map((item) => item.trim())
|
|
758
|
+
.filter((item) => item.length >= 3);
|
|
759
|
+
}
|
|
760
|
+
function uniqueText(values) {
|
|
761
|
+
return [...new Set(values.map((item) => normalizeText(item)).filter((item) => item.length > 0))];
|
|
762
|
+
}
|
|
763
|
+
function buildReflectionQuery(topic, statement, fallbackHint) {
|
|
764
|
+
const topicTokens = tokenizeForQuery(topic).slice(0, 4);
|
|
765
|
+
const stmtTokens = tokenizeForQuery(statement).slice(0, 6);
|
|
766
|
+
const merged = uniqueText([...topicTokens, ...stmtTokens]);
|
|
767
|
+
if (merged.length === 0)
|
|
768
|
+
return `${topic} ${fallbackHint}`.trim();
|
|
769
|
+
return merged.join(" ");
|
|
770
|
+
}
|
|
771
|
+
function queryMatchesTrace(query, trace) {
|
|
772
|
+
const tokens = tokenizeForQuery(query).slice(0, 4);
|
|
773
|
+
if (tokens.length === 0)
|
|
774
|
+
return false;
|
|
775
|
+
return trace.some((step) => {
|
|
776
|
+
const hay = normalizeText(step.query).toLowerCase();
|
|
777
|
+
let hit = 0;
|
|
778
|
+
for (const token of tokens) {
|
|
779
|
+
if (hay.includes(token))
|
|
780
|
+
hit += 1;
|
|
781
|
+
if (hit >= Math.min(2, tokens.length))
|
|
782
|
+
return true;
|
|
783
|
+
}
|
|
784
|
+
return false;
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
function deriveReflectionTasks(args) {
|
|
788
|
+
const tasks = [];
|
|
789
|
+
const bridge = args.changes.filter((item) => item.type === "BRIDGE");
|
|
790
|
+
const revise = args.changes.filter((item) => item.type === "REVISE");
|
|
791
|
+
const confirm = args.changes.filter((item) => item.type === "CONFIRM");
|
|
792
|
+
const newly = args.changes.filter((item) => item.type === "NEW");
|
|
793
|
+
for (const [idx, change] of bridge.slice(0, 3).entries()) {
|
|
794
|
+
const query = buildReflectionQuery(args.topic, change.statement, "cross-domain mechanism");
|
|
795
|
+
tasks.push({
|
|
796
|
+
id: sanitizeId(`bridge-${idx + 1}-${query}`),
|
|
797
|
+
trigger: "BRIDGE",
|
|
798
|
+
reason: `Bridge signal requires cross-domain follow-up: ${change.statement}`,
|
|
799
|
+
query,
|
|
800
|
+
priority: "high",
|
|
801
|
+
status: queryMatchesTrace(query, args.trace) ? "executed" : "planned",
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
if (newly.length >= 3) {
|
|
805
|
+
const query = buildReflectionQuery(args.topic, newly.map((item) => item.statement).join(" "), "trend synthesis");
|
|
806
|
+
tasks.push({
|
|
807
|
+
id: sanitizeId(`trend-${query}`),
|
|
808
|
+
trigger: "TREND",
|
|
809
|
+
reason: `New findings accumulated (${newly.length}); run trend synthesis and gap scan.`,
|
|
810
|
+
query,
|
|
811
|
+
priority: "medium",
|
|
812
|
+
status: queryMatchesTrace(query, args.trace) ? "executed" : "planned",
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
if (revise.length > 0 && confirm.length > 0) {
|
|
816
|
+
const query = buildReflectionQuery(args.topic, `${revise[0]?.statement ?? ""} ${confirm[0]?.statement ?? ""}`, "contradiction resolution");
|
|
817
|
+
tasks.push({
|
|
818
|
+
id: sanitizeId(`contradiction-${query}`),
|
|
819
|
+
trigger: "CONTRADICTION",
|
|
820
|
+
reason: `Revise and confirm signals co-exist; verify contradiction boundaries.`,
|
|
821
|
+
query,
|
|
822
|
+
priority: "high",
|
|
823
|
+
status: queryMatchesTrace(query, args.trace) ? "executed" : "planned",
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
const unreadCore = args.corePapers.filter((paper) => !isFullTextRead(paper));
|
|
827
|
+
if (unreadCore.length > 0) {
|
|
828
|
+
const topId = unreadCore[0]?.id ?? unreadCore[0]?.title ?? "core-paper";
|
|
829
|
+
const query = buildReflectionQuery(args.topic, String(topId), "full text retrieval");
|
|
830
|
+
tasks.push({
|
|
831
|
+
id: sanitizeId(`unread-core-${query}`),
|
|
832
|
+
trigger: "UNREAD_CORE",
|
|
833
|
+
reason: `${unreadCore.length} core paper(s) were not fully read; prioritize retrieval and verification.`,
|
|
834
|
+
query,
|
|
835
|
+
priority: "medium",
|
|
836
|
+
status: queryMatchesTrace(query, args.trace) ? "executed" : "planned",
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
const dedup = new Map();
|
|
840
|
+
for (const task of tasks) {
|
|
841
|
+
const key = normalizeText(task.query).toLowerCase();
|
|
842
|
+
if (!key)
|
|
843
|
+
continue;
|
|
844
|
+
const existing = dedup.get(key);
|
|
845
|
+
if (!existing) {
|
|
846
|
+
dedup.set(key, task);
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
// Keep higher priority / executed status when duplicates collide.
|
|
850
|
+
const priorityRank = { high: 3, medium: 2, low: 1 };
|
|
851
|
+
const pick = (existing.status !== "executed" && task.status === "executed") ||
|
|
852
|
+
priorityRank[task.priority] > priorityRank[existing.priority]
|
|
853
|
+
? task
|
|
854
|
+
: existing;
|
|
855
|
+
dedup.set(key, pick);
|
|
856
|
+
}
|
|
857
|
+
return [...dedup.values()].slice(0, MAX_LAST_REFLECTION_TASKS);
|
|
858
|
+
}
|
|
859
|
+
function sanitizeKnowledgeChanges(args) {
|
|
860
|
+
if (args.changes.length === 0) {
|
|
861
|
+
return {
|
|
862
|
+
changes: [],
|
|
863
|
+
droppedBridgeCount: 0,
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
const paperLookup = buildPaperLookup(args.allRunPapers);
|
|
867
|
+
const next = [];
|
|
868
|
+
let droppedBridgeCount = 0;
|
|
869
|
+
for (const change of args.changes) {
|
|
870
|
+
if (change.type !== "BRIDGE") {
|
|
871
|
+
next.push(change);
|
|
872
|
+
continue;
|
|
873
|
+
}
|
|
874
|
+
const evidenceIds = (change.evidenceIds ?? []).map((id) => normalizedCitationToken(id)).filter((id) => id.length > 0);
|
|
875
|
+
if (evidenceIds.length === 0) {
|
|
876
|
+
droppedBridgeCount += 1;
|
|
877
|
+
continue;
|
|
878
|
+
}
|
|
879
|
+
let hasResolvedEvidence = false;
|
|
880
|
+
let hasFullTextEvidence = false;
|
|
881
|
+
for (const evidenceId of evidenceIds) {
|
|
882
|
+
const paper = paperLookup.get(evidenceId);
|
|
883
|
+
if (!paper)
|
|
884
|
+
continue;
|
|
885
|
+
hasResolvedEvidence = true;
|
|
886
|
+
if (isFullTextRead(paper))
|
|
887
|
+
hasFullTextEvidence = true;
|
|
888
|
+
}
|
|
889
|
+
// Guard against speculative bridge signals with no grounded full-text evidence.
|
|
890
|
+
if (!hasResolvedEvidence || !hasFullTextEvidence) {
|
|
891
|
+
droppedBridgeCount += 1;
|
|
892
|
+
continue;
|
|
893
|
+
}
|
|
894
|
+
next.push(change);
|
|
895
|
+
}
|
|
896
|
+
return {
|
|
897
|
+
changes: next,
|
|
898
|
+
droppedBridgeCount,
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
function applyHypothesisGate(args) {
|
|
902
|
+
const acceptedHypotheses = [];
|
|
903
|
+
const rejectionReasonSet = new Set();
|
|
904
|
+
const paperLookup = buildPaperLookup(args.allRunPapers);
|
|
905
|
+
const changeCounts = {
|
|
906
|
+
NEW: args.knowledgeChanges.filter((item) => item.type === "NEW").length,
|
|
907
|
+
CONFIRM: args.knowledgeChanges.filter((item) => item.type === "CONFIRM").length,
|
|
908
|
+
REVISE: args.knowledgeChanges.filter((item) => item.type === "REVISE").length,
|
|
909
|
+
BRIDGE: args.knowledgeChanges.filter((item) => item.type === "BRIDGE").length,
|
|
910
|
+
};
|
|
911
|
+
for (const hypothesis of args.hypotheses) {
|
|
912
|
+
const reasons = [];
|
|
913
|
+
const statementLen = normalizeText(hypothesis.statement).length;
|
|
914
|
+
if (statementLen < MIN_HYPOTHESIS_STATEMENT_CHARS) {
|
|
915
|
+
reasons.push(`statement_too_short(${statementLen}<${MIN_HYPOTHESIS_STATEMENT_CHARS})`);
|
|
916
|
+
}
|
|
917
|
+
const evidenceIds = uniqueText((hypothesis.evidenceIds ?? []).map((id) => normalizedCitationToken(id)));
|
|
918
|
+
if (evidenceIds.length < MIN_HYPOTHESIS_EVIDENCE) {
|
|
919
|
+
reasons.push(`insufficient_evidence_ids(${evidenceIds.length}<${MIN_HYPOTHESIS_EVIDENCE})`);
|
|
920
|
+
}
|
|
921
|
+
let resolvedEvidence = 0;
|
|
922
|
+
let fullTextSupported = 0;
|
|
923
|
+
for (const evidenceId of evidenceIds) {
|
|
924
|
+
const paper = paperLookup.get(evidenceId);
|
|
925
|
+
if (!paper)
|
|
926
|
+
continue;
|
|
927
|
+
resolvedEvidence += 1;
|
|
928
|
+
if (isFullTextRead(paper))
|
|
929
|
+
fullTextSupported += 1;
|
|
930
|
+
}
|
|
931
|
+
if (resolvedEvidence < evidenceIds.length) {
|
|
932
|
+
reasons.push(`unresolved_evidence_ids(${evidenceIds.length - resolvedEvidence})`);
|
|
933
|
+
}
|
|
934
|
+
if (fullTextSupported === 0) {
|
|
935
|
+
reasons.push("no_fulltext_backed_evidence");
|
|
936
|
+
}
|
|
937
|
+
const dependencyPathLength = hypothesis.dependencyPath?.length ?? 0;
|
|
938
|
+
if (dependencyPathLength < MIN_HYPOTHESIS_DEPENDENCY_STEPS) {
|
|
939
|
+
reasons.push(`dependency_path_too_short(${dependencyPathLength}<${MIN_HYPOTHESIS_DEPENDENCY_STEPS})`);
|
|
940
|
+
}
|
|
941
|
+
const hasScore = typeof hypothesis.novelty === "number" &&
|
|
942
|
+
typeof hypothesis.feasibility === "number" &&
|
|
943
|
+
typeof hypothesis.impact === "number";
|
|
944
|
+
if (!hasScore) {
|
|
945
|
+
reasons.push("missing_self_assessment_scores");
|
|
946
|
+
}
|
|
947
|
+
if (hypothesis.trigger === "BRIDGE" && changeCounts.BRIDGE === 0) {
|
|
948
|
+
reasons.push("trigger_bridge_without_bridge_change");
|
|
949
|
+
}
|
|
950
|
+
if (hypothesis.trigger === "TREND" && changeCounts.NEW < 2) {
|
|
951
|
+
reasons.push("trigger_trend_without_new_accumulation");
|
|
952
|
+
}
|
|
953
|
+
if (hypothesis.trigger === "CONTRADICTION" && !(changeCounts.REVISE > 0 && changeCounts.CONFIRM > 0)) {
|
|
954
|
+
reasons.push("trigger_contradiction_without_revise_confirm_pair");
|
|
955
|
+
}
|
|
956
|
+
if (hypothesis.trigger === "GAP" && changeCounts.NEW + changeCounts.REVISE < 2) {
|
|
957
|
+
reasons.push("trigger_gap_without_gap_signal");
|
|
958
|
+
}
|
|
959
|
+
if (reasons.length > 0) {
|
|
960
|
+
for (const reason of reasons)
|
|
961
|
+
rejectionReasonSet.add(reason);
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
964
|
+
acceptedHypotheses.push(hypothesis);
|
|
965
|
+
}
|
|
966
|
+
return {
|
|
967
|
+
acceptedHypotheses,
|
|
968
|
+
gate: {
|
|
969
|
+
accepted: acceptedHypotheses.length,
|
|
970
|
+
rejected: Math.max(0, args.hypotheses.length - acceptedHypotheses.length),
|
|
971
|
+
rejectionReasons: [...rejectionReasonSet].slice(0, MAX_HYPOTHESIS_REJECTION_REASONS),
|
|
972
|
+
},
|
|
973
|
+
};
|
|
974
|
+
}
|
|
685
975
|
export async function commitKnowledgeRun(input) {
|
|
686
976
|
const project = await resolveProjectContext({
|
|
687
977
|
projectId: input.projectId,
|
|
@@ -706,7 +996,7 @@ export async function commitKnowledgeRun(input) {
|
|
|
706
996
|
const explorationTrace = (knowledgeState.explorationTrace ?? [])
|
|
707
997
|
.map(normalizeTrace)
|
|
708
998
|
.filter((item) => Boolean(item));
|
|
709
|
-
const
|
|
999
|
+
const submittedKnowledgeChanges = (knowledgeState.knowledgeChanges ?? [])
|
|
710
1000
|
.map(normalizeChange)
|
|
711
1001
|
.filter((item) => Boolean(item));
|
|
712
1002
|
const knowledgeUpdates = (knowledgeState.knowledgeUpdates ?? [])
|
|
@@ -747,6 +1037,8 @@ export async function commitKnowledgeRun(input) {
|
|
|
747
1037
|
recentHypotheses: [],
|
|
748
1038
|
recentChangeStats: [],
|
|
749
1039
|
lastExplorationTrace: [],
|
|
1040
|
+
lastReflectionTasks: [],
|
|
1041
|
+
lastHypothesisGate: defaultHypothesisGateState(),
|
|
750
1042
|
};
|
|
751
1043
|
const paperIds = mergePapers(corePapers, explorationPapers)
|
|
752
1044
|
.map((paper) => paper.id || paper.url || paper.title || "")
|
|
@@ -785,14 +1077,42 @@ export async function commitKnowledgeRun(input) {
|
|
|
785
1077
|
trace: explorationTrace,
|
|
786
1078
|
papers: explorationPapers,
|
|
787
1079
|
}));
|
|
788
|
-
await appendMarkdown(path.join(dailyDir, `day-${dayKey}.md`), renderDailyChangesMarkdown({ now: nowIso, runId, topic: stream.topic, changes: knowledgeChanges }));
|
|
789
1080
|
const mergedRunPapers = mergePapers(corePapers, explorationPapers);
|
|
1081
|
+
const changeSanitization = sanitizeKnowledgeChanges({
|
|
1082
|
+
changes: submittedKnowledgeChanges,
|
|
1083
|
+
allRunPapers: mergedRunPapers,
|
|
1084
|
+
});
|
|
1085
|
+
const knowledgeChanges = changeSanitization.changes;
|
|
1086
|
+
await appendMarkdown(path.join(dailyDir, `day-${dayKey}.md`), renderDailyChangesMarkdown({ now: nowIso, runId, topic: stream.topic, changes: knowledgeChanges }));
|
|
1087
|
+
const reflectionTasks = deriveReflectionTasks({
|
|
1088
|
+
topic: stream.topic,
|
|
1089
|
+
changes: knowledgeChanges,
|
|
1090
|
+
trace: explorationTrace,
|
|
1091
|
+
corePapers,
|
|
1092
|
+
});
|
|
1093
|
+
await appendMarkdown(path.join(logDir, `day-${dayKey}-reflection.md`), renderReflectionLogMarkdown({
|
|
1094
|
+
now: nowIso,
|
|
1095
|
+
runId,
|
|
1096
|
+
tasks: reflectionTasks,
|
|
1097
|
+
}));
|
|
1098
|
+
const submittedHypotheses = hypotheses;
|
|
1099
|
+
const hypothesisEval = applyHypothesisGate({
|
|
1100
|
+
hypotheses: submittedHypotheses,
|
|
1101
|
+
allRunPapers: mergedRunPapers,
|
|
1102
|
+
knowledgeChanges,
|
|
1103
|
+
});
|
|
1104
|
+
const acceptedHypotheses = hypothesisEval.acceptedHypotheses;
|
|
790
1105
|
const qualityEval = applyQualityGates({
|
|
791
1106
|
corePapers,
|
|
792
1107
|
allRunPapers: mergedRunPapers,
|
|
1108
|
+
explorationTrace,
|
|
1109
|
+
reflectionTasks,
|
|
793
1110
|
knowledgeChanges,
|
|
794
1111
|
knowledgeUpdates,
|
|
795
|
-
hypotheses,
|
|
1112
|
+
hypotheses: acceptedHypotheses,
|
|
1113
|
+
hypothesisGate: hypothesisEval.gate,
|
|
1114
|
+
requiredCorePapers: input.knowledgeState?.runLog?.requiredCorePapers,
|
|
1115
|
+
requiredFullTextCoveragePct: input.knowledgeState?.runLog?.requiredFullTextCoveragePct,
|
|
796
1116
|
});
|
|
797
1117
|
const requestedStatus = normalizeText(input.status ?? "ok");
|
|
798
1118
|
const qualitySensitiveStatus = requestedStatus === "ok" || requestedStatus === "fallback_representative";
|
|
@@ -836,7 +1156,7 @@ export async function commitKnowledgeRun(input) {
|
|
|
836
1156
|
const recentHypothesisSummaries = [];
|
|
837
1157
|
let seq = stream.totalHypotheses;
|
|
838
1158
|
const dayToken = dayKey.replace(/-/g, "");
|
|
839
|
-
for (const hypothesis of
|
|
1159
|
+
for (const hypothesis of acceptedHypotheses) {
|
|
840
1160
|
seq += 1;
|
|
841
1161
|
const hypothesisId = hypothesis.id && hypothesis.id.length > 0 ? sanitizeId(hypothesis.id) : `hyp-${dayToken}-${String(seq).padStart(4, "0")}`;
|
|
842
1162
|
const file = `${hypothesisId}.md`;
|
|
@@ -862,6 +1182,8 @@ export async function commitKnowledgeRun(input) {
|
|
|
862
1182
|
notFullTextReadCount: fullTextStats.notFullTextReadCount,
|
|
863
1183
|
qualityGate: qualityEval.qualityGate,
|
|
864
1184
|
unreadCorePaperIds: qualityEval.unreadCorePaperIds,
|
|
1185
|
+
reflectionTasks,
|
|
1186
|
+
hypothesisGate: hypothesisEval.gate,
|
|
865
1187
|
lastStatus: effectiveStatus,
|
|
866
1188
|
}), "utf-8");
|
|
867
1189
|
const changeStat = countChangeStats(dayKey, runId, knowledgeChanges);
|
|
@@ -875,6 +1197,8 @@ export async function commitKnowledgeRun(input) {
|
|
|
875
1197
|
stream.lastQualityGate = qualityEval.qualityGate;
|
|
876
1198
|
stream.lastUnreadCorePaperIds = qualityEval.unreadCorePaperIds;
|
|
877
1199
|
stream.lastExplorationTrace = explorationTrace.slice(0, MAX_LAST_TRACE);
|
|
1200
|
+
stream.lastReflectionTasks = reflectionTasks.slice(0, MAX_LAST_REFLECTION_TASKS);
|
|
1201
|
+
stream.lastHypothesisGate = hypothesisEval.gate;
|
|
878
1202
|
stream.recentPapers = mergePapers(mergedRunPapers, stream.recentPapers).slice(0, MAX_RECENT_PAPERS);
|
|
879
1203
|
stream.recentRunIds = [runId, ...stream.recentRunIds.filter((id) => id !== runId)].slice(0, MAX_RECENT_RUN_IDS);
|
|
880
1204
|
stream.recentHypothesisIds = [
|
|
@@ -895,9 +1219,14 @@ export async function commitKnowledgeRun(input) {
|
|
|
895
1219
|
corePapers,
|
|
896
1220
|
explorationPapers,
|
|
897
1221
|
explorationTrace,
|
|
1222
|
+
reflectionTasks,
|
|
1223
|
+
submittedKnowledgeChanges,
|
|
898
1224
|
knowledgeChanges,
|
|
1225
|
+
droppedBridgeCount: changeSanitization.droppedBridgeCount,
|
|
899
1226
|
knowledgeUpdates,
|
|
900
|
-
hypotheses,
|
|
1227
|
+
hypotheses: acceptedHypotheses,
|
|
1228
|
+
submittedHypotheses,
|
|
1229
|
+
hypothesisGate: hypothesisEval.gate,
|
|
901
1230
|
paperNoteFiles: runPaperNoteFiles,
|
|
902
1231
|
quality: {
|
|
903
1232
|
fullTextReadCount: fullTextStats.fullTextReadCount,
|
|
@@ -929,8 +1258,13 @@ export async function commitKnowledgeRun(input) {
|
|
|
929
1258
|
qualityGate: qualityEval.qualityGate,
|
|
930
1259
|
unreadCorePaperIds: qualityEval.unreadCorePaperIds,
|
|
931
1260
|
downgradedHighConfidenceCount: qualityEval.downgradedHighConfidenceCount,
|
|
1261
|
+
submittedChangeCount: submittedKnowledgeChanges.length,
|
|
932
1262
|
changeCount: knowledgeChanges.length,
|
|
1263
|
+
droppedBridgeCount: changeSanitization.droppedBridgeCount,
|
|
933
1264
|
hypothesisCount: recentHypothesisSummaries.length,
|
|
1265
|
+
submittedHypothesisCount: submittedHypotheses.length,
|
|
1266
|
+
hypothesisGate: hypothesisEval.gate,
|
|
1267
|
+
reflectionTasks,
|
|
934
1268
|
corePapers,
|
|
935
1269
|
explorationPapers,
|
|
936
1270
|
note: input.note,
|