scientify 1.13.5 → 2.0.0
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.en.md +350 -0
- package/README.md +148 -358
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +131 -122
- package/dist/index.js.map +1 -1
- package/dist/src/cli/research.d.ts +1 -6
- package/dist/src/cli/research.d.ts.map +1 -1
- package/dist/src/cli/research.js +227 -123
- package/dist/src/cli/research.js.map +1 -1
- package/dist/src/commands/metabolism-status.d.ts +3 -3
- package/dist/src/commands/metabolism-status.d.ts.map +1 -1
- package/dist/src/commands/metabolism-status.js +72 -75
- package/dist/src/commands/metabolism-status.js.map +1 -1
- package/dist/src/commands.d.ts +1 -1
- package/dist/src/commands.d.ts.map +1 -1
- package/dist/src/commands.js +0 -55
- package/dist/src/commands.js.map +1 -1
- package/dist/src/hooks/cron-skill-inject.d.ts +6 -7
- package/dist/src/hooks/cron-skill-inject.d.ts.map +1 -1
- package/dist/src/hooks/cron-skill-inject.js +6 -15
- package/dist/src/hooks/cron-skill-inject.js.map +1 -1
- 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 +24 -72
- package/dist/src/hooks/research-mode.js.map +1 -1
- package/dist/src/hooks/scientify-signature.d.ts +1 -1
- package/dist/src/hooks/scientify-signature.d.ts.map +1 -1
- package/dist/src/hooks/scientify-signature.js +2 -5
- package/dist/src/hooks/scientify-signature.js.map +1 -1
- package/dist/src/knowledge-state/render.d.ts +1 -9
- package/dist/src/knowledge-state/render.d.ts.map +1 -1
- package/dist/src/knowledge-state/render.js +33 -158
- 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 +65 -884
- package/dist/src/knowledge-state/store.js.map +1 -1
- package/dist/src/knowledge-state/types.d.ts +0 -69
- package/dist/src/knowledge-state/types.d.ts.map +1 -1
- package/dist/src/literature/subscription-state.d.ts +0 -2
- package/dist/src/literature/subscription-state.d.ts.map +1 -1
- package/dist/src/literature/subscription-state.js +7 -1199
- 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/cron-client.d.ts +1 -1
- package/dist/src/research-subscriptions/cron-client.d.ts.map +1 -1
- package/dist/src/research-subscriptions/delivery.d.ts +1 -1
- package/dist/src/research-subscriptions/delivery.d.ts.map +1 -1
- package/dist/src/research-subscriptions/handlers.d.ts +1 -1
- package/dist/src/research-subscriptions/handlers.d.ts.map +1 -1
- package/dist/src/research-subscriptions/handlers.js +9 -9
- 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 +0 -10
- 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 +196 -191
- package/dist/src/research-subscriptions/prompt.js.map +1 -1
- package/dist/src/research-subscriptions/types.d.ts +1 -2
- 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 +32 -19
- package/dist/src/templates/bootstrap.js.map +1 -1
- package/dist/src/tools/arxiv-download.d.ts +1 -2
- package/dist/src/tools/arxiv-download.d.ts.map +1 -1
- package/dist/src/tools/arxiv-search.d.ts +1 -2
- package/dist/src/tools/arxiv-search.d.ts.map +1 -1
- package/dist/src/tools/github-search-tool.d.ts +1 -2
- package/dist/src/tools/github-search-tool.d.ts.map +1 -1
- package/dist/src/tools/openalex-search.d.ts +1 -2
- package/dist/src/tools/openalex-search.d.ts.map +1 -1
- package/dist/src/tools/openreview-lookup.d.ts +1 -2
- package/dist/src/tools/openreview-lookup.d.ts.map +1 -1
- package/dist/src/tools/paper-browser.d.ts +1 -2
- package/dist/src/tools/paper-browser.d.ts.map +1 -1
- package/dist/src/tools/result.d.ts +3 -5
- package/dist/src/tools/result.d.ts.map +1 -1
- package/dist/src/tools/result.js +5 -7
- package/dist/src/tools/result.js.map +1 -1
- package/dist/src/tools/scientify-cron.d.ts +4 -9
- package/dist/src/tools/scientify-cron.d.ts.map +1 -1
- package/dist/src/tools/scientify-cron.js +19 -441
- package/dist/src/tools/scientify-cron.js.map +1 -1
- package/dist/src/tools/scientify-literature-state.d.ts +1 -62
- package/dist/src/tools/scientify-literature-state.d.ts.map +1 -1
- package/dist/src/tools/scientify-literature-state.js +46 -312
- package/dist/src/tools/scientify-literature-state.js.map +1 -1
- package/dist/src/tools/unpaywall-download.d.ts +1 -2
- package/dist/src/tools/unpaywall-download.d.ts.map +1 -1
- package/dist/src/types.d.ts +16 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/openclaw.plugin.json +4 -2
- package/package.json +1 -1
- package/skills/metabolism/SKILL.md +2 -0
- package/skills/research-subscription/SKILL.md +1 -29
- package/README.zh.md +0 -494
|
@@ -2,49 +2,20 @@ 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,
|
|
5
|
+
import { dayKeyFromTimestamp, renderDailyChangesMarkdown, renderExplorationLogMarkdown, renderHypothesisMarkdown, renderIngestLogMarkdown, renderKnowledgeIndexMarkdown, renderPaperNoteHeaderMarkdown, renderPaperNoteRunMarkdown, 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;
|
|
13
12
|
const MAX_RECENT_PAPERS = 50;
|
|
14
13
|
const MAX_PAPER_NOTES = 800;
|
|
15
|
-
const MAX_HYPOTHESIS_REJECTION_REASONS = 24;
|
|
16
14
|
const MIN_CORE_FULLTEXT_COVERAGE = 0.8;
|
|
17
15
|
const MIN_EVIDENCE_BINDING_RATE = 0.9;
|
|
18
16
|
const MAX_CITATION_ERROR_RATE = 0.02;
|
|
19
|
-
const FATAL_CITATION_ERROR_RATE = 0.2;
|
|
20
|
-
const MIN_FULLTEXT_PROFILE_COMPLETENESS = 0.55;
|
|
21
|
-
const MIN_HYPOTHESIS_EVIDENCE = 2;
|
|
22
|
-
const MIN_HYPOTHESIS_DEPENDENCY_STEPS = 2;
|
|
23
|
-
const MIN_HYPOTHESIS_STATEMENT_CHARS = 48;
|
|
24
|
-
const MIN_HYPOTHESIS_STRENGTHS = 2;
|
|
25
|
-
const MIN_HYPOTHESIS_WEAKNESSES = 2;
|
|
26
|
-
const MIN_HYPOTHESIS_PLAN_STEPS = 3;
|
|
27
|
-
const MIN_HYPOTHESIS_STRICT_ACCEPT_SCORE = 70;
|
|
28
|
-
const MIN_HYPOTHESIS_ACCEPT_SELF_SCORE_AVG = 3.2;
|
|
29
|
-
const PLACEHOLDER_TEXT_RE = /^(?:n\/a|na|none|not provided|not available|unknown|tbd|todo|null|nil|未提供|暂无|未知|无)$/iu;
|
|
30
|
-
function defaultRunProfile() {
|
|
31
|
-
return "strict";
|
|
32
|
-
}
|
|
33
|
-
function defaultTriggerState(nowMs = Date.now()) {
|
|
34
|
-
return {
|
|
35
|
-
consecutiveNewReviseDays: 0,
|
|
36
|
-
bridgeCount7d: 0,
|
|
37
|
-
unreadCoreBacklog: 0,
|
|
38
|
-
lastUpdatedAtMs: nowMs,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
17
|
function defaultQualityGateState() {
|
|
42
18
|
return {
|
|
43
|
-
mode: "soft",
|
|
44
|
-
severity: "warn",
|
|
45
|
-
warnings: ["quality gate not evaluated"],
|
|
46
|
-
fatalReasons: [],
|
|
47
|
-
blocking: false,
|
|
48
19
|
passed: false,
|
|
49
20
|
fullTextCoveragePct: 0,
|
|
50
21
|
evidenceBindingRatePct: 0,
|
|
@@ -52,26 +23,9 @@ function defaultQualityGateState() {
|
|
|
52
23
|
reasons: ["quality gate not evaluated"],
|
|
53
24
|
};
|
|
54
25
|
}
|
|
55
|
-
function defaultHypothesisGateState() {
|
|
56
|
-
return {
|
|
57
|
-
accepted: 0,
|
|
58
|
-
rejected: 0,
|
|
59
|
-
rejectionReasons: [],
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
26
|
function normalizeText(raw) {
|
|
63
27
|
return raw.trim().replace(/\s+/g, " ");
|
|
64
28
|
}
|
|
65
|
-
function cleanOptionalText(raw) {
|
|
66
|
-
if (typeof raw !== "string")
|
|
67
|
-
return undefined;
|
|
68
|
-
const normalized = normalizeText(raw);
|
|
69
|
-
if (!normalized)
|
|
70
|
-
return undefined;
|
|
71
|
-
if (PLACEHOLDER_TEXT_RE.test(normalized))
|
|
72
|
-
return undefined;
|
|
73
|
-
return normalized;
|
|
74
|
-
}
|
|
75
29
|
function sanitizeId(raw) {
|
|
76
30
|
return normalizeText(raw)
|
|
77
31
|
.toLowerCase()
|
|
@@ -152,9 +106,6 @@ async function loadState(projectPath) {
|
|
|
152
106
|
topic: normalizeText(rawStream.topic ?? "topic"),
|
|
153
107
|
topicKey,
|
|
154
108
|
projectId: sanitizeId(rawStream.projectId ?? "auto-topic-global-000000") || "auto-topic-global-000000",
|
|
155
|
-
lastRunProfile: rawStream.lastRunProfile === "strict" || rawStream.lastRunProfile === "fast"
|
|
156
|
-
? rawStream.lastRunProfile
|
|
157
|
-
: defaultRunProfile(),
|
|
158
109
|
totalRuns: typeof rawStream.totalRuns === "number" ? Math.max(0, Math.floor(rawStream.totalRuns)) : 0,
|
|
159
110
|
totalHypotheses: typeof rawStream.totalHypotheses === "number" ? Math.max(0, Math.floor(rawStream.totalHypotheses)) : 0,
|
|
160
111
|
knowledgeTopics: Array.isArray(rawStream.knowledgeTopics)
|
|
@@ -163,26 +114,6 @@ async function loadState(projectPath) {
|
|
|
163
114
|
paperNotes: Array.isArray(rawStream.paperNotes)
|
|
164
115
|
? rawStream.paperNotes.filter((item) => typeof item === "string").map((item) => normalizeText(item))
|
|
165
116
|
: [],
|
|
166
|
-
triggerState: rawStream.triggerState && typeof rawStream.triggerState === "object" && !Array.isArray(rawStream.triggerState)
|
|
167
|
-
? {
|
|
168
|
-
consecutiveNewReviseDays: typeof rawStream.triggerState.consecutiveNewReviseDays === "number" &&
|
|
169
|
-
Number.isFinite(rawStream.triggerState.consecutiveNewReviseDays)
|
|
170
|
-
? Math.max(0, Math.floor(rawStream.triggerState.consecutiveNewReviseDays))
|
|
171
|
-
: 0,
|
|
172
|
-
bridgeCount7d: typeof rawStream.triggerState.bridgeCount7d === "number" &&
|
|
173
|
-
Number.isFinite(rawStream.triggerState.bridgeCount7d)
|
|
174
|
-
? Math.max(0, Math.floor(rawStream.triggerState.bridgeCount7d))
|
|
175
|
-
: 0,
|
|
176
|
-
unreadCoreBacklog: typeof rawStream.triggerState.unreadCoreBacklog === "number" &&
|
|
177
|
-
Number.isFinite(rawStream.triggerState.unreadCoreBacklog)
|
|
178
|
-
? Math.max(0, Math.floor(rawStream.triggerState.unreadCoreBacklog))
|
|
179
|
-
: 0,
|
|
180
|
-
lastUpdatedAtMs: typeof rawStream.triggerState.lastUpdatedAtMs === "number" &&
|
|
181
|
-
Number.isFinite(rawStream.triggerState.lastUpdatedAtMs)
|
|
182
|
-
? Math.floor(rawStream.triggerState.lastUpdatedAtMs)
|
|
183
|
-
: Date.now(),
|
|
184
|
-
}
|
|
185
|
-
: defaultTriggerState(),
|
|
186
117
|
recentFullTextReadCount: typeof rawStream.recentFullTextReadCount === "number"
|
|
187
118
|
? Math.max(0, Math.floor(rawStream.recentFullTextReadCount))
|
|
188
119
|
: 0,
|
|
@@ -192,60 +123,27 @@ async function loadState(projectPath) {
|
|
|
192
123
|
lastQualityGate: rawStream.lastQualityGate &&
|
|
193
124
|
typeof rawStream.lastQualityGate === "object" &&
|
|
194
125
|
!Array.isArray(rawStream.lastQualityGate)
|
|
195
|
-
?
|
|
196
|
-
|
|
126
|
+
? {
|
|
127
|
+
passed: rawStream.lastQualityGate.passed === true,
|
|
128
|
+
fullTextCoveragePct: typeof rawStream.lastQualityGate.fullTextCoveragePct === "number" &&
|
|
129
|
+
Number.isFinite(rawStream.lastQualityGate.fullTextCoveragePct)
|
|
130
|
+
? Number(rawStream.lastQualityGate.fullTextCoveragePct.toFixed(2))
|
|
131
|
+
: 0,
|
|
132
|
+
evidenceBindingRatePct: typeof rawStream.lastQualityGate.evidenceBindingRatePct === "number" &&
|
|
133
|
+
Number.isFinite(rawStream.lastQualityGate.evidenceBindingRatePct)
|
|
134
|
+
? Number(rawStream.lastQualityGate.evidenceBindingRatePct.toFixed(2))
|
|
135
|
+
: 0,
|
|
136
|
+
citationErrorRatePct: typeof rawStream.lastQualityGate.citationErrorRatePct === "number" &&
|
|
137
|
+
Number.isFinite(rawStream.lastQualityGate.citationErrorRatePct)
|
|
138
|
+
? Number(rawStream.lastQualityGate.citationErrorRatePct.toFixed(2))
|
|
139
|
+
: 0,
|
|
140
|
+
reasons: Array.isArray(rawStream.lastQualityGate.reasons)
|
|
197
141
|
? rawStream.lastQualityGate.reasons
|
|
198
142
|
.filter((item) => typeof item === "string")
|
|
199
143
|
.map((item) => normalizeText(item))
|
|
200
144
|
.filter((item) => item.length > 0)
|
|
201
|
-
: []
|
|
202
|
-
|
|
203
|
-
? rawStream.lastQualityGate.warnings
|
|
204
|
-
.filter((item) => typeof item === "string")
|
|
205
|
-
.map((item) => normalizeText(item))
|
|
206
|
-
.filter((item) => item.length > 0)
|
|
207
|
-
: reasons;
|
|
208
|
-
const fatalReasons = Array.isArray(rawStream.lastQualityGate.fatalReasons)
|
|
209
|
-
? rawStream.lastQualityGate.fatalReasons
|
|
210
|
-
.filter((item) => typeof item === "string")
|
|
211
|
-
.map((item) => normalizeText(item))
|
|
212
|
-
.filter((item) => item.length > 0)
|
|
213
|
-
: [];
|
|
214
|
-
const blocking = rawStream.lastQualityGate.blocking === true || fatalReasons.length > 0;
|
|
215
|
-
const severityRaw = typeof rawStream.lastQualityGate.severity === "string"
|
|
216
|
-
? rawStream.lastQualityGate.severity.toLowerCase()
|
|
217
|
-
: undefined;
|
|
218
|
-
const severity = severityRaw === "fatal" || blocking
|
|
219
|
-
? "fatal"
|
|
220
|
-
: severityRaw === "ok"
|
|
221
|
-
? "ok"
|
|
222
|
-
: warnings.length > 0
|
|
223
|
-
? "warn"
|
|
224
|
-
: "ok";
|
|
225
|
-
return {
|
|
226
|
-
mode: "soft",
|
|
227
|
-
severity,
|
|
228
|
-
warnings,
|
|
229
|
-
fatalReasons,
|
|
230
|
-
blocking,
|
|
231
|
-
passed: typeof rawStream.lastQualityGate.passed === "boolean"
|
|
232
|
-
? rawStream.lastQualityGate.passed
|
|
233
|
-
: fatalReasons.length === 0,
|
|
234
|
-
fullTextCoveragePct: typeof rawStream.lastQualityGate.fullTextCoveragePct === "number" &&
|
|
235
|
-
Number.isFinite(rawStream.lastQualityGate.fullTextCoveragePct)
|
|
236
|
-
? Number(rawStream.lastQualityGate.fullTextCoveragePct.toFixed(2))
|
|
237
|
-
: 0,
|
|
238
|
-
evidenceBindingRatePct: typeof rawStream.lastQualityGate.evidenceBindingRatePct === "number" &&
|
|
239
|
-
Number.isFinite(rawStream.lastQualityGate.evidenceBindingRatePct)
|
|
240
|
-
? Number(rawStream.lastQualityGate.evidenceBindingRatePct.toFixed(2))
|
|
241
|
-
: 0,
|
|
242
|
-
citationErrorRatePct: typeof rawStream.lastQualityGate.citationErrorRatePct === "number" &&
|
|
243
|
-
Number.isFinite(rawStream.lastQualityGate.citationErrorRatePct)
|
|
244
|
-
? Number(rawStream.lastQualityGate.citationErrorRatePct.toFixed(2))
|
|
245
|
-
: 0,
|
|
246
|
-
reasons: reasons.length > 0 ? reasons : [...warnings, ...fatalReasons],
|
|
247
|
-
};
|
|
248
|
-
})()
|
|
145
|
+
: [],
|
|
146
|
+
}
|
|
249
147
|
: defaultQualityGateState(),
|
|
250
148
|
lastUnreadCorePaperIds: Array.isArray(rawStream.lastUnreadCorePaperIds)
|
|
251
149
|
? rawStream.lastUnreadCorePaperIds
|
|
@@ -269,23 +167,7 @@ async function loadState(projectPath) {
|
|
|
269
167
|
.map((item) => normalizeText(item))
|
|
270
168
|
: [],
|
|
271
169
|
recentHypotheses: Array.isArray(rawStream.recentHypotheses)
|
|
272
|
-
? rawStream.recentHypotheses.filter((item) => !!item && typeof item === "object")
|
|
273
|
-
id: sanitizeId(item.id ?? "hyp"),
|
|
274
|
-
statement: normalizeText(item.statement ?? ""),
|
|
275
|
-
trigger: ["GAP", "BRIDGE", "TREND", "CONTRADICTION"].includes(item.trigger)
|
|
276
|
-
? item.trigger
|
|
277
|
-
: "TREND",
|
|
278
|
-
createdAtMs: typeof item.createdAtMs === "number" && Number.isFinite(item.createdAtMs)
|
|
279
|
-
? item.createdAtMs
|
|
280
|
-
: Date.now(),
|
|
281
|
-
file: normalizeText(item.file ?? ""),
|
|
282
|
-
...(typeof item.strictOverallScore === "number" && Number.isFinite(item.strictOverallScore)
|
|
283
|
-
? { strictOverallScore: Number(item.strictOverallScore.toFixed(2)) }
|
|
284
|
-
: {}),
|
|
285
|
-
...(item.strictDecision === "accept" || item.strictDecision === "revise" || item.strictDecision === "reject"
|
|
286
|
-
? { strictDecision: item.strictDecision }
|
|
287
|
-
: {}),
|
|
288
|
-
})).filter((item) => item.statement.length > 0 && item.file.length > 0)
|
|
170
|
+
? rawStream.recentHypotheses.filter((item) => !!item && typeof item === "object")
|
|
289
171
|
: [],
|
|
290
172
|
recentChangeStats: Array.isArray(rawStream.recentChangeStats)
|
|
291
173
|
? rawStream.recentChangeStats.filter((item) => !!item && typeof item === "object")
|
|
@@ -296,39 +178,6 @@ async function loadState(projectPath) {
|
|
|
296
178
|
.map(normalizeTrace)
|
|
297
179
|
.filter((item) => Boolean(item))
|
|
298
180
|
: [],
|
|
299
|
-
lastReflectionTasks: Array.isArray(rawStream.lastReflectionTasks)
|
|
300
|
-
? rawStream.lastReflectionTasks
|
|
301
|
-
.filter((item) => !!item && typeof item === "object")
|
|
302
|
-
.map((item) => ({
|
|
303
|
-
id: sanitizeId(item.id ?? "task"),
|
|
304
|
-
trigger: ["BRIDGE", "TREND", "CONTRADICTION", "UNREAD_CORE"].includes(item.trigger)
|
|
305
|
-
? item.trigger
|
|
306
|
-
: "TREND",
|
|
307
|
-
reason: normalizeText(item.reason ?? ""),
|
|
308
|
-
query: normalizeText(item.query ?? ""),
|
|
309
|
-
priority: ["high", "medium", "low"].includes(item.priority) ? item.priority : "medium",
|
|
310
|
-
status: (item.status === "executed" ? "executed" : "planned"),
|
|
311
|
-
}))
|
|
312
|
-
.filter((item) => item.reason.length > 0 && item.query.length > 0)
|
|
313
|
-
: [],
|
|
314
|
-
lastHypothesisGate: rawStream.lastHypothesisGate &&
|
|
315
|
-
typeof rawStream.lastHypothesisGate === "object" &&
|
|
316
|
-
!Array.isArray(rawStream.lastHypothesisGate)
|
|
317
|
-
? {
|
|
318
|
-
accepted: typeof rawStream.lastHypothesisGate.accepted === "number"
|
|
319
|
-
? Math.max(0, Math.floor(rawStream.lastHypothesisGate.accepted))
|
|
320
|
-
: 0,
|
|
321
|
-
rejected: typeof rawStream.lastHypothesisGate.rejected === "number"
|
|
322
|
-
? Math.max(0, Math.floor(rawStream.lastHypothesisGate.rejected))
|
|
323
|
-
: 0,
|
|
324
|
-
rejectionReasons: Array.isArray(rawStream.lastHypothesisGate.rejectionReasons)
|
|
325
|
-
? rawStream.lastHypothesisGate.rejectionReasons
|
|
326
|
-
.filter((item) => typeof item === "string")
|
|
327
|
-
.map((item) => normalizeText(item))
|
|
328
|
-
.filter((item) => item.length > 0)
|
|
329
|
-
: [],
|
|
330
|
-
}
|
|
331
|
-
: defaultHypothesisGateState(),
|
|
332
181
|
};
|
|
333
182
|
}
|
|
334
183
|
return {
|
|
@@ -359,8 +208,8 @@ function normalizeStringArray(raw) {
|
|
|
359
208
|
return undefined;
|
|
360
209
|
const values = raw
|
|
361
210
|
.filter((item) => typeof item === "string")
|
|
362
|
-
.map((item) =>
|
|
363
|
-
.filter((item) =>
|
|
211
|
+
.map((item) => normalizeText(item))
|
|
212
|
+
.filter((item) => item.length > 0);
|
|
364
213
|
return values.length > 0 ? values : undefined;
|
|
365
214
|
}
|
|
366
215
|
function normalizeEvidenceAnchors(raw) {
|
|
@@ -369,14 +218,14 @@ function normalizeEvidenceAnchors(raw) {
|
|
|
369
218
|
const anchors = raw
|
|
370
219
|
.filter((item) => !!item && typeof item === "object")
|
|
371
220
|
.map((item) => {
|
|
372
|
-
const claim =
|
|
221
|
+
const claim = normalizeText(item.claim ?? "");
|
|
373
222
|
if (!claim)
|
|
374
223
|
return undefined;
|
|
375
224
|
return {
|
|
376
|
-
...(
|
|
377
|
-
...(
|
|
225
|
+
...(item.section ? { section: normalizeText(item.section) } : {}),
|
|
226
|
+
...(item.locator ? { locator: normalizeText(item.locator) } : {}),
|
|
378
227
|
claim,
|
|
379
|
-
...(
|
|
228
|
+
...(item.quote ? { quote: normalizeText(item.quote) } : {}),
|
|
380
229
|
};
|
|
381
230
|
})
|
|
382
231
|
.filter((item) => Boolean(item));
|
|
@@ -394,7 +243,7 @@ function toPaperNoteSlug(paper) {
|
|
|
394
243
|
}
|
|
395
244
|
function normalizePaper(input) {
|
|
396
245
|
const evidenceIds = Array.isArray(input.evidenceIds)
|
|
397
|
-
? input.evidenceIds.map((id) =>
|
|
246
|
+
? input.evidenceIds.map((id) => normalizeText(id)).filter((id) => id.length > 0)
|
|
398
247
|
: undefined;
|
|
399
248
|
const keyEvidenceSpans = normalizeStringArray(input.keyEvidenceSpans);
|
|
400
249
|
const subdomains = normalizeStringArray(input.subdomains);
|
|
@@ -415,33 +264,31 @@ function normalizePaper(input) {
|
|
|
415
264
|
: readStatus
|
|
416
265
|
? false
|
|
417
266
|
: undefined;
|
|
418
|
-
const unreadReason =
|
|
267
|
+
const unreadReason = input.unreadReason ? normalizeText(input.unreadReason) : undefined;
|
|
419
268
|
return {
|
|
420
|
-
...(
|
|
421
|
-
...(
|
|
422
|
-
...(
|
|
423
|
-
...(
|
|
424
|
-
...(
|
|
269
|
+
...(input.id ? { id: normalizeText(input.id) } : {}),
|
|
270
|
+
...(input.title ? { title: normalizeText(input.title) } : {}),
|
|
271
|
+
...(input.url ? { url: normalizeText(input.url) } : {}),
|
|
272
|
+
...(input.source ? { source: normalizeText(input.source) } : {}),
|
|
273
|
+
...(input.publishedAt ? { publishedAt: normalizeText(input.publishedAt) } : {}),
|
|
425
274
|
...(typeof input.score === "number" && Number.isFinite(input.score)
|
|
426
275
|
? { score: Number(input.score.toFixed(2)) }
|
|
427
276
|
: {}),
|
|
428
|
-
...(
|
|
429
|
-
...(
|
|
277
|
+
...(input.reason ? { reason: normalizeText(input.reason) } : {}),
|
|
278
|
+
...(input.summary ? { summary: normalizeText(input.summary) } : {}),
|
|
430
279
|
...(evidenceIds && evidenceIds.length > 0 ? { evidenceIds } : {}),
|
|
431
280
|
...(typeof fullTextRead === "boolean" ? { fullTextRead } : {}),
|
|
432
281
|
...(readStatus ? { readStatus } : {}),
|
|
433
|
-
...(
|
|
434
|
-
...(
|
|
282
|
+
...(input.fullTextSource ? { fullTextSource: normalizeText(input.fullTextSource) } : {}),
|
|
283
|
+
...(input.fullTextRef ? { fullTextRef: normalizeText(input.fullTextRef) } : {}),
|
|
435
284
|
...(unreadReason ? { unreadReason } : {}),
|
|
436
285
|
...(keyEvidenceSpans && keyEvidenceSpans.length > 0 ? { keyEvidenceSpans } : {}),
|
|
437
|
-
...(
|
|
286
|
+
...(input.domain ? { domain: normalizeText(input.domain) } : {}),
|
|
438
287
|
...(subdomains ? { subdomains } : {}),
|
|
439
288
|
...(crossDomainLinks ? { crossDomainLinks } : {}),
|
|
440
|
-
...(
|
|
441
|
-
...(
|
|
442
|
-
...(
|
|
443
|
-
? { methodologyDesign: cleanOptionalText(input.methodologyDesign) }
|
|
444
|
-
: {}),
|
|
289
|
+
...(input.researchGoal ? { researchGoal: normalizeText(input.researchGoal) } : {}),
|
|
290
|
+
...(input.approach ? { approach: normalizeText(input.approach) } : {}),
|
|
291
|
+
...(input.methodologyDesign ? { methodologyDesign: normalizeText(input.methodologyDesign) } : {}),
|
|
445
292
|
...(keyContributions ? { keyContributions } : {}),
|
|
446
293
|
...(practicalInsights ? { practicalInsights } : {}),
|
|
447
294
|
...(mustUnderstandPoints ? { mustUnderstandPoints } : {}),
|
|
@@ -554,32 +401,6 @@ function hasStructuredProfile(paper) {
|
|
|
554
401
|
(paper.limitations && paper.limitations.length > 0) ||
|
|
555
402
|
(paper.evidenceAnchors && paper.evidenceAnchors.length > 0));
|
|
556
403
|
}
|
|
557
|
-
function countStructuredProfileFields(paper) {
|
|
558
|
-
let count = 0;
|
|
559
|
-
if (paper.domain && paper.domain.trim())
|
|
560
|
-
count += 1;
|
|
561
|
-
if (paper.subdomains && paper.subdomains.length > 0)
|
|
562
|
-
count += 1;
|
|
563
|
-
if (paper.crossDomainLinks && paper.crossDomainLinks.length > 0)
|
|
564
|
-
count += 1;
|
|
565
|
-
if (paper.researchGoal && paper.researchGoal.trim())
|
|
566
|
-
count += 1;
|
|
567
|
-
if (paper.approach && paper.approach.trim())
|
|
568
|
-
count += 1;
|
|
569
|
-
if (paper.methodologyDesign && paper.methodologyDesign.trim())
|
|
570
|
-
count += 1;
|
|
571
|
-
if (paper.keyContributions && paper.keyContributions.length > 0)
|
|
572
|
-
count += 1;
|
|
573
|
-
if (paper.practicalInsights && paper.practicalInsights.length > 0)
|
|
574
|
-
count += 1;
|
|
575
|
-
if (paper.mustUnderstandPoints && paper.mustUnderstandPoints.length > 0)
|
|
576
|
-
count += 1;
|
|
577
|
-
if (paper.limitations && paper.limitations.length > 0)
|
|
578
|
-
count += 1;
|
|
579
|
-
if (paper.evidenceAnchors && paper.evidenceAnchors.length > 0)
|
|
580
|
-
count += 1;
|
|
581
|
-
return count;
|
|
582
|
-
}
|
|
583
404
|
function isFullTextRead(paper) {
|
|
584
405
|
return paper.fullTextRead === true || paper.readStatus === "fulltext";
|
|
585
406
|
}
|
|
@@ -626,13 +447,6 @@ function applyQualityGates(args) {
|
|
|
626
447
|
const fullTextCoreCount = corePapers.filter((paper) => isFullTextRead(paper)).length;
|
|
627
448
|
const fullTextCoverage = coreCount > 0 ? fullTextCoreCount / coreCount : 0;
|
|
628
449
|
const fullTextCoveragePct = Number((fullTextCoverage * 100).toFixed(2));
|
|
629
|
-
const fullTextCorePapers = corePapers.filter((paper) => isFullTextRead(paper));
|
|
630
|
-
const structuredFieldTotal = 11;
|
|
631
|
-
const avgFullTextProfileCompleteness = fullTextCorePapers.length > 0
|
|
632
|
-
? fullTextCorePapers.reduce((sum, paper) => sum + countStructuredProfileFields(paper) / structuredFieldTotal, 0) /
|
|
633
|
-
fullTextCorePapers.length
|
|
634
|
-
: 0;
|
|
635
|
-
const avgFullTextProfileCompletenessPct = Number((avgFullTextProfileCompleteness * 100).toFixed(2));
|
|
636
450
|
const unreadCorePaperIds = dedupeText(corePapers
|
|
637
451
|
.filter((paper) => !isFullTextRead(paper))
|
|
638
452
|
.map((paper) => paper.id?.trim() || paper.url?.trim() || paper.title?.trim() || "unknown-paper")).slice(0, 50);
|
|
@@ -707,68 +521,22 @@ function applyQualityGates(args) {
|
|
|
707
521
|
downgradedHighConfidenceCount += 1;
|
|
708
522
|
}
|
|
709
523
|
}
|
|
710
|
-
const
|
|
711
|
-
const fatalReasons = [];
|
|
712
|
-
if (!args.hasAuditableArtifacts && !args.hasRunError) {
|
|
713
|
-
fatalReasons.push("no_auditable_artifacts_without_run_error");
|
|
714
|
-
}
|
|
715
|
-
if (typeof args.requiredCorePapers === "number" && Number.isFinite(args.requiredCorePapers) && args.requiredCorePapers > 0) {
|
|
716
|
-
const requiredCore = Math.floor(args.requiredCorePapers);
|
|
717
|
-
if (coreCount === 0) {
|
|
718
|
-
fatalReasons.push(`core_paper_count_below_required(${coreCount} < ${requiredCore})`);
|
|
719
|
-
}
|
|
720
|
-
else if (coreCount < requiredCore) {
|
|
721
|
-
warnings.push(`core_paper_count_below_required(${coreCount} < ${requiredCore})`);
|
|
722
|
-
}
|
|
723
|
-
}
|
|
524
|
+
const reasons = [];
|
|
724
525
|
if (fullTextCoverage < MIN_CORE_FULLTEXT_COVERAGE) {
|
|
725
|
-
|
|
726
|
-
}
|
|
727
|
-
if (fullTextCorePapers.length > 0 && avgFullTextProfileCompleteness < MIN_FULLTEXT_PROFILE_COMPLETENESS) {
|
|
728
|
-
warnings.push(`fulltext_profile_completeness_below_threshold(${avgFullTextProfileCompletenessPct}% < ${Number((MIN_FULLTEXT_PROFILE_COMPLETENESS * 100).toFixed(0))}%)`);
|
|
729
|
-
}
|
|
730
|
-
if (typeof args.requiredFullTextCoveragePct === "number" &&
|
|
731
|
-
Number.isFinite(args.requiredFullTextCoveragePct) &&
|
|
732
|
-
args.requiredFullTextCoveragePct > 0 &&
|
|
733
|
-
fullTextCoveragePct < args.requiredFullTextCoveragePct) {
|
|
734
|
-
warnings.push(`core_fulltext_coverage_below_required(${fullTextCoveragePct}% < ${Number(args.requiredFullTextCoveragePct.toFixed(2))}%)`);
|
|
526
|
+
reasons.push(`core_fulltext_coverage_below_threshold(${fullTextCoveragePct}% < ${Number((MIN_CORE_FULLTEXT_COVERAGE * 100).toFixed(0))}%)`);
|
|
735
527
|
}
|
|
736
528
|
if (evidenceBindingRate < MIN_EVIDENCE_BINDING_RATE) {
|
|
737
|
-
|
|
738
|
-
}
|
|
739
|
-
if (citationErrorRate >= FATAL_CITATION_ERROR_RATE) {
|
|
740
|
-
fatalReasons.push(`citation_error_rate_above_threshold(${citationErrorRatePct}% >= ${Number((FATAL_CITATION_ERROR_RATE * 100).toFixed(0))}%)`);
|
|
741
|
-
}
|
|
742
|
-
else if (citationErrorRate >= MAX_CITATION_ERROR_RATE) {
|
|
743
|
-
warnings.push(`citation_error_rate_above_warning_threshold(${citationErrorRatePct}% >= ${Number((MAX_CITATION_ERROR_RATE * 100).toFixed(0))}%)`);
|
|
744
|
-
}
|
|
745
|
-
const bridgeChangeCount = args.knowledgeChanges.filter((item) => item.type === "BRIDGE").length;
|
|
746
|
-
const reviseCount = args.knowledgeChanges.filter((item) => item.type === "REVISE").length;
|
|
747
|
-
const confirmCount = args.knowledgeChanges.filter((item) => item.type === "CONFIRM").length;
|
|
748
|
-
const executedReflectionCount = args.reflectionTasks.filter((task) => task.status === "executed").length;
|
|
749
|
-
if (bridgeChangeCount > 0 && executedReflectionCount === 0) {
|
|
750
|
-
warnings.push(`reflection_missing_for_bridge(bridge_count=${bridgeChangeCount})`);
|
|
529
|
+
reasons.push(`evidence_binding_rate_below_threshold(${evidenceBindingRatePct}% < ${Number((MIN_EVIDENCE_BINDING_RATE * 100).toFixed(0))}%)`);
|
|
751
530
|
}
|
|
752
|
-
if (
|
|
753
|
-
|
|
754
|
-
}
|
|
755
|
-
if (args.hypothesisGate.rejected > 0 && args.hypothesisGate.accepted === 0 && args.hypotheses.length > 0) {
|
|
756
|
-
warnings.push(`hypothesis_gate_rejected_all(${args.hypothesisGate.rejected})`);
|
|
531
|
+
if (citationErrorRate >= MAX_CITATION_ERROR_RATE) {
|
|
532
|
+
reasons.push(`citation_error_rate_above_threshold(${citationErrorRatePct}% >= ${Number((MAX_CITATION_ERROR_RATE * 100).toFixed(0))}%)`);
|
|
757
533
|
}
|
|
758
534
|
if (downgradedHighConfidenceCount > 0) {
|
|
759
|
-
|
|
535
|
+
reasons.push(`high_confidence_downgraded(${downgradedHighConfidenceCount})`);
|
|
760
536
|
}
|
|
761
|
-
const reasons = [...warnings, ...fatalReasons];
|
|
762
|
-
const blocking = fatalReasons.length > 0;
|
|
763
|
-
const severity = blocking ? "fatal" : warnings.length > 0 ? "warn" : "ok";
|
|
764
537
|
return {
|
|
765
538
|
qualityGate: {
|
|
766
|
-
|
|
767
|
-
severity,
|
|
768
|
-
warnings,
|
|
769
|
-
fatalReasons,
|
|
770
|
-
blocking,
|
|
771
|
-
passed: !blocking,
|
|
539
|
+
passed: reasons.length === 0,
|
|
772
540
|
fullTextCoveragePct,
|
|
773
541
|
evidenceBindingRatePct,
|
|
774
542
|
citationErrorRatePct,
|
|
@@ -778,20 +546,6 @@ function applyQualityGates(args) {
|
|
|
778
546
|
downgradedHighConfidenceCount,
|
|
779
547
|
};
|
|
780
548
|
}
|
|
781
|
-
function deriveEffectiveStatus(args) {
|
|
782
|
-
const requested = normalizeText(args.requestedStatus || "ok").toLowerCase();
|
|
783
|
-
if (requested === "error" || args.hasRunError)
|
|
784
|
-
return "error";
|
|
785
|
-
if (args.qualityBlocking)
|
|
786
|
-
return "degraded_quality";
|
|
787
|
-
if (requested === "empty")
|
|
788
|
-
return args.runArtifactCount > 0 ? "ok" : "empty";
|
|
789
|
-
if (requested === "degraded_quality")
|
|
790
|
-
return "ok";
|
|
791
|
-
if (requested.length === 0)
|
|
792
|
-
return args.runArtifactCount > 0 ? "ok" : "empty";
|
|
793
|
-
return requested;
|
|
794
|
-
}
|
|
795
549
|
function normalizeChange(input) {
|
|
796
550
|
const statement = normalizeText(input.statement ?? "");
|
|
797
551
|
if (!statement)
|
|
@@ -845,39 +599,12 @@ function normalizeHypothesis(input) {
|
|
|
845
599
|
: undefined;
|
|
846
600
|
const validationEvidence = normalizeStringArray(input.validationEvidence);
|
|
847
601
|
const validationNotes = input.validationNotes ? normalizeText(input.validationNotes) : undefined;
|
|
848
|
-
const strengths = normalizeStringArray(input.strengths);
|
|
849
|
-
const weaknesses = normalizeStringArray(input.weaknesses);
|
|
850
|
-
const planSteps = normalizeStringArray(input.planSteps);
|
|
851
|
-
const strictEvaluationRaw = input.strictEvaluation;
|
|
852
|
-
const strictEvaluation = strictEvaluationRaw && typeof strictEvaluationRaw === "object"
|
|
853
|
-
? (() => {
|
|
854
|
-
const overallScore = typeof strictEvaluationRaw.overallScore === "number" && Number.isFinite(strictEvaluationRaw.overallScore)
|
|
855
|
-
? Math.max(0, Math.min(100, Number(strictEvaluationRaw.overallScore.toFixed(2))))
|
|
856
|
-
: undefined;
|
|
857
|
-
const decisionRaw = strictEvaluationRaw.decision?.trim().toLowerCase();
|
|
858
|
-
const decision = decisionRaw === "accept" || decisionRaw === "revise" || decisionRaw === "reject"
|
|
859
|
-
? decisionRaw
|
|
860
|
-
: undefined;
|
|
861
|
-
const reason = strictEvaluationRaw.reason ? normalizeText(strictEvaluationRaw.reason) : undefined;
|
|
862
|
-
if (overallScore === undefined && !decision && !reason)
|
|
863
|
-
return undefined;
|
|
864
|
-
return {
|
|
865
|
-
...(overallScore !== undefined ? { overallScore } : {}),
|
|
866
|
-
...(decision ? { decision } : {}),
|
|
867
|
-
...(reason ? { reason } : {}),
|
|
868
|
-
};
|
|
869
|
-
})()
|
|
870
|
-
: undefined;
|
|
871
602
|
const withScore = (value) => typeof value === "number" && Number.isFinite(value) ? Number(value.toFixed(2)) : undefined;
|
|
872
603
|
return {
|
|
873
604
|
...(input.id ? { id: sanitizeId(input.id) } : {}),
|
|
874
605
|
statement,
|
|
875
606
|
trigger,
|
|
876
607
|
...(dependencyPath && dependencyPath.length > 0 ? { dependencyPath } : {}),
|
|
877
|
-
...(strengths ? { strengths } : {}),
|
|
878
|
-
...(weaknesses ? { weaknesses } : {}),
|
|
879
|
-
...(planSteps ? { planSteps } : {}),
|
|
880
|
-
...(strictEvaluation ? { strictEvaluation } : {}),
|
|
881
608
|
...(typeof withScore(input.novelty) === "number" ? { novelty: withScore(input.novelty) } : {}),
|
|
882
609
|
...(typeof withScore(input.feasibility) === "number" ? { feasibility: withScore(input.feasibility) } : {}),
|
|
883
610
|
...(typeof withScore(input.impact) === "number" ? { impact: withScore(input.impact) } : {}),
|
|
@@ -915,12 +642,10 @@ function toSummary(stream) {
|
|
|
915
642
|
return {
|
|
916
643
|
projectId: stream.projectId,
|
|
917
644
|
streamKey: stream.topicKey,
|
|
918
|
-
runProfile: stream.lastRunProfile,
|
|
919
645
|
totalRuns: stream.totalRuns,
|
|
920
646
|
totalHypotheses: stream.totalHypotheses,
|
|
921
647
|
knowledgeTopicsCount: stream.knowledgeTopics.length,
|
|
922
648
|
paperNotesCount: stream.paperNotes.length,
|
|
923
|
-
triggerState: stream.triggerState,
|
|
924
649
|
recentFullTextReadCount: stream.recentFullTextReadCount,
|
|
925
650
|
recentNotFullTextReadCount: stream.recentNotFullTextReadCount,
|
|
926
651
|
qualityGate: stream.lastQualityGate,
|
|
@@ -931,8 +656,6 @@ function toSummary(stream) {
|
|
|
931
656
|
recentHypotheses: stream.recentHypotheses,
|
|
932
657
|
recentChangeStats: stream.recentChangeStats,
|
|
933
658
|
lastExplorationTrace: stream.lastExplorationTrace,
|
|
934
|
-
lastReflectionTasks: stream.lastReflectionTasks,
|
|
935
|
-
hypothesisGate: stream.lastHypothesisGate,
|
|
936
659
|
};
|
|
937
660
|
}
|
|
938
661
|
function countChangeStats(day, runId, changes) {
|
|
@@ -959,448 +682,6 @@ function countChangeStats(day, runId, changes) {
|
|
|
959
682
|
bridgeCount,
|
|
960
683
|
};
|
|
961
684
|
}
|
|
962
|
-
function tokenizeForQuery(raw) {
|
|
963
|
-
return normalizeText(raw)
|
|
964
|
-
.toLowerCase()
|
|
965
|
-
.replace(/[^a-z0-9\u4e00-\u9fff\s_-]+/g, " ")
|
|
966
|
-
.split(/\s+/)
|
|
967
|
-
.map((item) => item.trim())
|
|
968
|
-
.filter((item) => item.length >= 3);
|
|
969
|
-
}
|
|
970
|
-
function uniqueText(values) {
|
|
971
|
-
return [...new Set(values.map((item) => normalizeText(item)).filter((item) => item.length > 0))];
|
|
972
|
-
}
|
|
973
|
-
function buildReflectionQuery(topic, statement, fallbackHint) {
|
|
974
|
-
const topicTokens = tokenizeForQuery(topic).slice(0, 4);
|
|
975
|
-
const stmtTokens = tokenizeForQuery(statement).slice(0, 6);
|
|
976
|
-
const merged = uniqueText([...topicTokens, ...stmtTokens]);
|
|
977
|
-
if (merged.length === 0)
|
|
978
|
-
return `${topic} ${fallbackHint}`.trim();
|
|
979
|
-
return merged.join(" ");
|
|
980
|
-
}
|
|
981
|
-
function queryMatchesTrace(query, trace) {
|
|
982
|
-
const tokens = tokenizeForQuery(query).slice(0, 4);
|
|
983
|
-
if (tokens.length === 0)
|
|
984
|
-
return false;
|
|
985
|
-
return trace.some((step) => {
|
|
986
|
-
const hay = normalizeText(step.query).toLowerCase();
|
|
987
|
-
let hit = 0;
|
|
988
|
-
for (const token of tokens) {
|
|
989
|
-
if (hay.includes(token))
|
|
990
|
-
hit += 1;
|
|
991
|
-
if (hit >= Math.min(2, tokens.length))
|
|
992
|
-
return true;
|
|
993
|
-
}
|
|
994
|
-
return false;
|
|
995
|
-
});
|
|
996
|
-
}
|
|
997
|
-
function deriveReflectionTasks(args) {
|
|
998
|
-
const tasks = [];
|
|
999
|
-
const bridge = args.changes.filter((item) => item.type === "BRIDGE");
|
|
1000
|
-
const revise = args.changes.filter((item) => item.type === "REVISE");
|
|
1001
|
-
const confirm = args.changes.filter((item) => item.type === "CONFIRM");
|
|
1002
|
-
const newly = args.changes.filter((item) => item.type === "NEW");
|
|
1003
|
-
for (const [idx, change] of bridge.slice(0, 3).entries()) {
|
|
1004
|
-
const query = buildReflectionQuery(args.topic, change.statement, "cross-domain mechanism");
|
|
1005
|
-
tasks.push({
|
|
1006
|
-
id: sanitizeId(`bridge-${idx + 1}-${query}`),
|
|
1007
|
-
trigger: "BRIDGE",
|
|
1008
|
-
reason: `Bridge signal requires cross-domain follow-up: ${change.statement}`,
|
|
1009
|
-
query,
|
|
1010
|
-
priority: "high",
|
|
1011
|
-
status: queryMatchesTrace(query, args.trace) ? "executed" : "planned",
|
|
1012
|
-
});
|
|
1013
|
-
}
|
|
1014
|
-
if (newly.length >= 3) {
|
|
1015
|
-
const query = buildReflectionQuery(args.topic, newly.map((item) => item.statement).join(" "), "trend synthesis");
|
|
1016
|
-
tasks.push({
|
|
1017
|
-
id: sanitizeId(`trend-${query}`),
|
|
1018
|
-
trigger: "TREND",
|
|
1019
|
-
reason: `New findings accumulated (${newly.length}); run trend synthesis and gap scan.`,
|
|
1020
|
-
query,
|
|
1021
|
-
priority: "medium",
|
|
1022
|
-
status: queryMatchesTrace(query, args.trace) ? "executed" : "planned",
|
|
1023
|
-
});
|
|
1024
|
-
}
|
|
1025
|
-
if (revise.length > 0 && confirm.length > 0) {
|
|
1026
|
-
const query = buildReflectionQuery(args.topic, `${revise[0]?.statement ?? ""} ${confirm[0]?.statement ?? ""}`, "contradiction resolution");
|
|
1027
|
-
tasks.push({
|
|
1028
|
-
id: sanitizeId(`contradiction-${query}`),
|
|
1029
|
-
trigger: "CONTRADICTION",
|
|
1030
|
-
reason: `Revise and confirm signals co-exist; verify contradiction boundaries.`,
|
|
1031
|
-
query,
|
|
1032
|
-
priority: "high",
|
|
1033
|
-
status: queryMatchesTrace(query, args.trace) ? "executed" : "planned",
|
|
1034
|
-
});
|
|
1035
|
-
}
|
|
1036
|
-
const unreadCore = args.corePapers.filter((paper) => !isFullTextRead(paper));
|
|
1037
|
-
if (unreadCore.length > 0) {
|
|
1038
|
-
const topId = unreadCore[0]?.id ?? unreadCore[0]?.title ?? "core-paper";
|
|
1039
|
-
const query = buildReflectionQuery(args.topic, String(topId), "full text retrieval");
|
|
1040
|
-
tasks.push({
|
|
1041
|
-
id: sanitizeId(`unread-core-${query}`),
|
|
1042
|
-
trigger: "UNREAD_CORE",
|
|
1043
|
-
reason: `${unreadCore.length} core paper(s) were not fully read; prioritize retrieval and verification.`,
|
|
1044
|
-
query,
|
|
1045
|
-
priority: "medium",
|
|
1046
|
-
status: queryMatchesTrace(query, args.trace) ? "executed" : "planned",
|
|
1047
|
-
});
|
|
1048
|
-
}
|
|
1049
|
-
const dedup = new Map();
|
|
1050
|
-
for (const task of tasks) {
|
|
1051
|
-
const key = normalizeText(task.query).toLowerCase();
|
|
1052
|
-
if (!key)
|
|
1053
|
-
continue;
|
|
1054
|
-
const existing = dedup.get(key);
|
|
1055
|
-
if (!existing) {
|
|
1056
|
-
dedup.set(key, task);
|
|
1057
|
-
continue;
|
|
1058
|
-
}
|
|
1059
|
-
// Keep higher priority / executed status when duplicates collide.
|
|
1060
|
-
const priorityRank = { high: 3, medium: 2, low: 1 };
|
|
1061
|
-
const pick = (existing.status !== "executed" && task.status === "executed") ||
|
|
1062
|
-
priorityRank[task.priority] > priorityRank[existing.priority]
|
|
1063
|
-
? task
|
|
1064
|
-
: existing;
|
|
1065
|
-
dedup.set(key, pick);
|
|
1066
|
-
}
|
|
1067
|
-
return [...dedup.values()].slice(0, MAX_LAST_REFLECTION_TASKS);
|
|
1068
|
-
}
|
|
1069
|
-
function sanitizeKnowledgeChanges(args) {
|
|
1070
|
-
if (args.changes.length === 0) {
|
|
1071
|
-
return {
|
|
1072
|
-
changes: [],
|
|
1073
|
-
droppedBridgeCount: 0,
|
|
1074
|
-
};
|
|
1075
|
-
}
|
|
1076
|
-
const paperLookup = buildPaperLookup(args.allRunPapers);
|
|
1077
|
-
const next = [];
|
|
1078
|
-
let droppedBridgeCount = 0;
|
|
1079
|
-
for (const change of args.changes) {
|
|
1080
|
-
if (change.type !== "BRIDGE") {
|
|
1081
|
-
next.push(change);
|
|
1082
|
-
continue;
|
|
1083
|
-
}
|
|
1084
|
-
const evidenceIds = (change.evidenceIds ?? []).map((id) => normalizedCitationToken(id)).filter((id) => id.length > 0);
|
|
1085
|
-
if (evidenceIds.length === 0) {
|
|
1086
|
-
droppedBridgeCount += 1;
|
|
1087
|
-
continue;
|
|
1088
|
-
}
|
|
1089
|
-
let hasResolvedEvidence = false;
|
|
1090
|
-
let hasFullTextEvidence = false;
|
|
1091
|
-
for (const evidenceId of evidenceIds) {
|
|
1092
|
-
const paper = paperLookup.get(evidenceId);
|
|
1093
|
-
if (!paper)
|
|
1094
|
-
continue;
|
|
1095
|
-
hasResolvedEvidence = true;
|
|
1096
|
-
if (isFullTextRead(paper))
|
|
1097
|
-
hasFullTextEvidence = true;
|
|
1098
|
-
}
|
|
1099
|
-
// Guard against speculative bridge signals with no grounded full-text evidence.
|
|
1100
|
-
if (!hasResolvedEvidence || !hasFullTextEvidence) {
|
|
1101
|
-
droppedBridgeCount += 1;
|
|
1102
|
-
continue;
|
|
1103
|
-
}
|
|
1104
|
-
next.push(change);
|
|
1105
|
-
}
|
|
1106
|
-
return {
|
|
1107
|
-
changes: next,
|
|
1108
|
-
droppedBridgeCount,
|
|
1109
|
-
};
|
|
1110
|
-
}
|
|
1111
|
-
function applyHypothesisGate(args) {
|
|
1112
|
-
const acceptedHypotheses = [];
|
|
1113
|
-
const rejectionReasonSet = new Set();
|
|
1114
|
-
const paperLookup = buildPaperLookup(args.allRunPapers);
|
|
1115
|
-
const fullTextEvidenceIds = uniqueText(args.allRunPapers
|
|
1116
|
-
.filter((paper) => isFullTextRead(paper))
|
|
1117
|
-
.map((paper) => normalizedCitationToken(paper.id ?? paper.url ?? paper.title ?? ""))
|
|
1118
|
-
.filter((id) => id.length > 0));
|
|
1119
|
-
const anyEvidenceIds = uniqueText(args.allRunPapers
|
|
1120
|
-
.map((paper) => normalizedCitationToken(paper.id ?? paper.url ?? paper.title ?? ""))
|
|
1121
|
-
.filter((id) => id.length > 0));
|
|
1122
|
-
const changeCounts = {
|
|
1123
|
-
NEW: args.knowledgeChanges.filter((item) => item.type === "NEW").length,
|
|
1124
|
-
CONFIRM: args.knowledgeChanges.filter((item) => item.type === "CONFIRM").length,
|
|
1125
|
-
REVISE: args.knowledgeChanges.filter((item) => item.type === "REVISE").length,
|
|
1126
|
-
BRIDGE: args.knowledgeChanges.filter((item) => item.type === "BRIDGE").length,
|
|
1127
|
-
};
|
|
1128
|
-
const evaluateHypothesisReasons = (hypothesis) => {
|
|
1129
|
-
const reasons = [];
|
|
1130
|
-
const statementLen = normalizeText(hypothesis.statement).length;
|
|
1131
|
-
if (statementLen < MIN_HYPOTHESIS_STATEMENT_CHARS) {
|
|
1132
|
-
reasons.push(`statement_too_short(${statementLen}<${MIN_HYPOTHESIS_STATEMENT_CHARS})`);
|
|
1133
|
-
}
|
|
1134
|
-
const evidenceIds = uniqueText((hypothesis.evidenceIds ?? []).map((id) => normalizedCitationToken(id)));
|
|
1135
|
-
if (evidenceIds.length < MIN_HYPOTHESIS_EVIDENCE) {
|
|
1136
|
-
reasons.push(`insufficient_evidence_ids(${evidenceIds.length}<${MIN_HYPOTHESIS_EVIDENCE})`);
|
|
1137
|
-
}
|
|
1138
|
-
let resolvedEvidence = 0;
|
|
1139
|
-
let fullTextSupported = 0;
|
|
1140
|
-
for (const evidenceId of evidenceIds) {
|
|
1141
|
-
const paper = paperLookup.get(evidenceId);
|
|
1142
|
-
if (!paper)
|
|
1143
|
-
continue;
|
|
1144
|
-
resolvedEvidence += 1;
|
|
1145
|
-
if (isFullTextRead(paper))
|
|
1146
|
-
fullTextSupported += 1;
|
|
1147
|
-
}
|
|
1148
|
-
if (resolvedEvidence < evidenceIds.length) {
|
|
1149
|
-
reasons.push(`unresolved_evidence_ids(${evidenceIds.length - resolvedEvidence})`);
|
|
1150
|
-
}
|
|
1151
|
-
if (fullTextSupported === 0 && args.runProfile === "strict") {
|
|
1152
|
-
reasons.push("no_fulltext_backed_evidence");
|
|
1153
|
-
}
|
|
1154
|
-
if (resolvedEvidence === 0) {
|
|
1155
|
-
reasons.push("no_resolved_evidence");
|
|
1156
|
-
}
|
|
1157
|
-
const dependencyPathLength = hypothesis.dependencyPath?.length ?? 0;
|
|
1158
|
-
if (dependencyPathLength < MIN_HYPOTHESIS_DEPENDENCY_STEPS) {
|
|
1159
|
-
reasons.push(`dependency_path_too_short(${dependencyPathLength}<${MIN_HYPOTHESIS_DEPENDENCY_STEPS})`);
|
|
1160
|
-
}
|
|
1161
|
-
const strengthsCount = hypothesis.strengths?.length ?? 0;
|
|
1162
|
-
if (strengthsCount < MIN_HYPOTHESIS_STRENGTHS) {
|
|
1163
|
-
reasons.push(`strengths_too_few(${strengthsCount}<${MIN_HYPOTHESIS_STRENGTHS})`);
|
|
1164
|
-
}
|
|
1165
|
-
const weaknessesCount = hypothesis.weaknesses?.length ?? 0;
|
|
1166
|
-
if (weaknessesCount < MIN_HYPOTHESIS_WEAKNESSES) {
|
|
1167
|
-
reasons.push(`weaknesses_too_few(${weaknessesCount}<${MIN_HYPOTHESIS_WEAKNESSES})`);
|
|
1168
|
-
}
|
|
1169
|
-
const planStepCount = hypothesis.planSteps?.length ?? 0;
|
|
1170
|
-
if (planStepCount < MIN_HYPOTHESIS_PLAN_STEPS) {
|
|
1171
|
-
reasons.push(`plan_steps_too_few(${planStepCount}<${MIN_HYPOTHESIS_PLAN_STEPS})`);
|
|
1172
|
-
}
|
|
1173
|
-
const hasScore = typeof hypothesis.novelty === "number" &&
|
|
1174
|
-
typeof hypothesis.feasibility === "number" &&
|
|
1175
|
-
typeof hypothesis.impact === "number";
|
|
1176
|
-
if (!hasScore) {
|
|
1177
|
-
reasons.push("missing_self_assessment_scores");
|
|
1178
|
-
}
|
|
1179
|
-
const strictEval = hypothesis.strictEvaluation;
|
|
1180
|
-
if (!strictEval) {
|
|
1181
|
-
reasons.push("missing_strict_evaluation");
|
|
1182
|
-
}
|
|
1183
|
-
else {
|
|
1184
|
-
if (typeof strictEval.overallScore !== "number" || !Number.isFinite(strictEval.overallScore)) {
|
|
1185
|
-
reasons.push("strict_evaluation_missing_overall_score");
|
|
1186
|
-
}
|
|
1187
|
-
if (!strictEval.decision) {
|
|
1188
|
-
reasons.push("strict_evaluation_missing_decision");
|
|
1189
|
-
}
|
|
1190
|
-
else if (strictEval.decision !== "accept") {
|
|
1191
|
-
reasons.push(`strict_evaluation_not_accept(${strictEval.decision})`);
|
|
1192
|
-
}
|
|
1193
|
-
if (!strictEval.reason || normalizeText(strictEval.reason).length < 16) {
|
|
1194
|
-
reasons.push("strict_evaluation_reason_too_short");
|
|
1195
|
-
}
|
|
1196
|
-
if (strictEval.decision === "accept" &&
|
|
1197
|
-
typeof strictEval.overallScore === "number" &&
|
|
1198
|
-
strictEval.overallScore < MIN_HYPOTHESIS_STRICT_ACCEPT_SCORE) {
|
|
1199
|
-
reasons.push(`strict_evaluation_score_below_threshold(${strictEval.overallScore}<${MIN_HYPOTHESIS_STRICT_ACCEPT_SCORE})`);
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
if (hasScore &&
|
|
1203
|
-
strictEval?.decision === "accept" &&
|
|
1204
|
-
((hypothesis.novelty ?? 0) + (hypothesis.feasibility ?? 0) + (hypothesis.impact ?? 0)) / 3 <
|
|
1205
|
-
MIN_HYPOTHESIS_ACCEPT_SELF_SCORE_AVG) {
|
|
1206
|
-
reasons.push(`self_assessment_avg_below_accept_threshold(${Number((((hypothesis.novelty ?? 0) + (hypothesis.feasibility ?? 0) + (hypothesis.impact ?? 0)) / 3).toFixed(2))}<${MIN_HYPOTHESIS_ACCEPT_SELF_SCORE_AVG})`);
|
|
1207
|
-
}
|
|
1208
|
-
if (hypothesis.trigger === "BRIDGE" && changeCounts.BRIDGE === 0) {
|
|
1209
|
-
reasons.push("trigger_bridge_without_bridge_change");
|
|
1210
|
-
}
|
|
1211
|
-
if (hypothesis.trigger === "TREND" && changeCounts.NEW < 2) {
|
|
1212
|
-
reasons.push("trigger_trend_without_new_accumulation");
|
|
1213
|
-
}
|
|
1214
|
-
if (hypothesis.trigger === "CONTRADICTION" && !(changeCounts.REVISE > 0 && changeCounts.CONFIRM > 0)) {
|
|
1215
|
-
reasons.push("trigger_contradiction_without_revise_confirm_pair");
|
|
1216
|
-
}
|
|
1217
|
-
if (hypothesis.trigger === "GAP" && changeCounts.NEW + changeCounts.REVISE < 2) {
|
|
1218
|
-
reasons.push("trigger_gap_without_gap_signal");
|
|
1219
|
-
}
|
|
1220
|
-
return reasons;
|
|
1221
|
-
};
|
|
1222
|
-
const isRepairableReason = (reason) => {
|
|
1223
|
-
return (reason.startsWith("dependency_path_too_short(") ||
|
|
1224
|
-
reason.startsWith("strengths_too_few(") ||
|
|
1225
|
-
reason.startsWith("weaknesses_too_few(") ||
|
|
1226
|
-
reason.startsWith("plan_steps_too_few(") ||
|
|
1227
|
-
reason === "missing_self_assessment_scores" ||
|
|
1228
|
-
reason === "missing_strict_evaluation" ||
|
|
1229
|
-
reason === "strict_evaluation_missing_overall_score" ||
|
|
1230
|
-
reason === "strict_evaluation_missing_decision" ||
|
|
1231
|
-
reason === "strict_evaluation_reason_too_short" ||
|
|
1232
|
-
reason.startsWith("strict_evaluation_not_accept(") ||
|
|
1233
|
-
reason.startsWith("strict_evaluation_score_below_threshold(") ||
|
|
1234
|
-
reason.startsWith("insufficient_evidence_ids(") ||
|
|
1235
|
-
reason.startsWith("unresolved_evidence_ids(") ||
|
|
1236
|
-
reason === "no_fulltext_backed_evidence" ||
|
|
1237
|
-
reason === "no_resolved_evidence");
|
|
1238
|
-
};
|
|
1239
|
-
const autoReviseHypothesis = (hypothesis, reasons) => {
|
|
1240
|
-
if (!reasons.some((reason) => isRepairableReason(reason)))
|
|
1241
|
-
return undefined;
|
|
1242
|
-
const revised = {
|
|
1243
|
-
...hypothesis,
|
|
1244
|
-
...(hypothesis.dependencyPath ? { dependencyPath: [...hypothesis.dependencyPath] } : {}),
|
|
1245
|
-
...(hypothesis.strengths ? { strengths: [...hypothesis.strengths] } : {}),
|
|
1246
|
-
...(hypothesis.weaknesses ? { weaknesses: [...hypothesis.weaknesses] } : {}),
|
|
1247
|
-
...(hypothesis.planSteps ? { planSteps: [...hypothesis.planSteps] } : {}),
|
|
1248
|
-
...(hypothesis.evidenceIds ? { evidenceIds: [...hypothesis.evidenceIds] } : {}),
|
|
1249
|
-
...(hypothesis.strictEvaluation ? { strictEvaluation: { ...hypothesis.strictEvaluation } } : {}),
|
|
1250
|
-
};
|
|
1251
|
-
let changed = false;
|
|
1252
|
-
const seedEvidence = fullTextEvidenceIds.length > 0 ? fullTextEvidenceIds : anyEvidenceIds;
|
|
1253
|
-
if (seedEvidence.length > 0 && ((revised.evidenceIds?.length ?? 0) < MIN_HYPOTHESIS_EVIDENCE || reasons.some((item) => item.startsWith("unresolved_evidence_ids(") || item === "no_resolved_evidence" || item === "no_fulltext_backed_evidence"))) {
|
|
1254
|
-
revised.evidenceIds = uniqueText([...(revised.evidenceIds ?? []), ...seedEvidence]).slice(0, Math.max(MIN_HYPOTHESIS_EVIDENCE, 4));
|
|
1255
|
-
changed = true;
|
|
1256
|
-
}
|
|
1257
|
-
while ((revised.dependencyPath?.length ?? 0) < MIN_HYPOTHESIS_DEPENDENCY_STEPS) {
|
|
1258
|
-
revised.dependencyPath = [
|
|
1259
|
-
...(revised.dependencyPath ?? []),
|
|
1260
|
-
revised.dependencyPath && revised.dependencyPath.length > 0
|
|
1261
|
-
? "Prior evidence accumulation provides an additional dependency step for this hypothesis."
|
|
1262
|
-
: "This hypothesis is grounded in accumulated cross-paper evidence from current run outputs.",
|
|
1263
|
-
];
|
|
1264
|
-
changed = true;
|
|
1265
|
-
}
|
|
1266
|
-
while ((revised.strengths?.length ?? 0) < MIN_HYPOTHESIS_STRENGTHS) {
|
|
1267
|
-
revised.strengths = [
|
|
1268
|
-
...(revised.strengths ?? []),
|
|
1269
|
-
revised.strengths && revised.strengths.length > 0
|
|
1270
|
-
? "The proposed idea has an explicit implementation path and measurable outcomes."
|
|
1271
|
-
: "The hypothesis is evidence-grounded and directly connected to current literature signals.",
|
|
1272
|
-
];
|
|
1273
|
-
changed = true;
|
|
1274
|
-
}
|
|
1275
|
-
while ((revised.weaknesses?.length ?? 0) < MIN_HYPOTHESIS_WEAKNESSES) {
|
|
1276
|
-
revised.weaknesses = [
|
|
1277
|
-
...(revised.weaknesses ?? []),
|
|
1278
|
-
revised.weaknesses && revised.weaknesses.length > 0
|
|
1279
|
-
? "Generality may be limited across domains without additional validation."
|
|
1280
|
-
: "Potential sensitivity to data distribution and setup assumptions.",
|
|
1281
|
-
];
|
|
1282
|
-
changed = true;
|
|
1283
|
-
}
|
|
1284
|
-
const defaultPlanStepPool = [
|
|
1285
|
-
"Implement a reproducible baseline first (include a lightweight baseline such as random forest when applicable).",
|
|
1286
|
-
"Implement the proposed method and compare under matched compute/data budgets.",
|
|
1287
|
-
"Run ablation and failure-case analysis, then update accept/revise/reject decision.",
|
|
1288
|
-
];
|
|
1289
|
-
while ((revised.planSteps?.length ?? 0) < MIN_HYPOTHESIS_PLAN_STEPS) {
|
|
1290
|
-
const idx = revised.planSteps?.length ?? 0;
|
|
1291
|
-
revised.planSteps = [...(revised.planSteps ?? []), defaultPlanStepPool[Math.min(idx, defaultPlanStepPool.length - 1)]];
|
|
1292
|
-
changed = true;
|
|
1293
|
-
}
|
|
1294
|
-
if (typeof revised.novelty !== "number" ||
|
|
1295
|
-
!Number.isFinite(revised.novelty) ||
|
|
1296
|
-
typeof revised.feasibility !== "number" ||
|
|
1297
|
-
!Number.isFinite(revised.feasibility) ||
|
|
1298
|
-
typeof revised.impact !== "number" ||
|
|
1299
|
-
!Number.isFinite(revised.impact)) {
|
|
1300
|
-
revised.novelty = Number((revised.novelty ?? 3.8).toFixed(2));
|
|
1301
|
-
revised.feasibility = Number((revised.feasibility ?? 3.6).toFixed(2));
|
|
1302
|
-
revised.impact = Number((revised.impact ?? 3.8).toFixed(2));
|
|
1303
|
-
changed = true;
|
|
1304
|
-
}
|
|
1305
|
-
const strictOverallScore = typeof revised.strictEvaluation?.overallScore === "number" && Number.isFinite(revised.strictEvaluation.overallScore)
|
|
1306
|
-
? revised.strictEvaluation.overallScore
|
|
1307
|
-
: Math.max(MIN_HYPOTHESIS_STRICT_ACCEPT_SCORE, Number((((revised.novelty ?? 3.5) + (revised.feasibility ?? 3.5) + (revised.impact ?? 3.5)) / 3 * 20).toFixed(2)));
|
|
1308
|
-
const strictReason = revised.strictEvaluation?.reason && normalizeText(revised.strictEvaluation.reason).length >= 16
|
|
1309
|
-
? revised.strictEvaluation.reason
|
|
1310
|
-
: "Auto-revised by hypothesis gate: structure, evidence, and execution plan now satisfy acceptance criteria.";
|
|
1311
|
-
if (!revised.strictEvaluation ||
|
|
1312
|
-
revised.strictEvaluation.decision !== "accept" ||
|
|
1313
|
-
typeof revised.strictEvaluation.overallScore !== "number" ||
|
|
1314
|
-
!Number.isFinite(revised.strictEvaluation.overallScore) ||
|
|
1315
|
-
revised.strictEvaluation.overallScore < MIN_HYPOTHESIS_STRICT_ACCEPT_SCORE ||
|
|
1316
|
-
normalizeText(revised.strictEvaluation.reason ?? "").length < 16) {
|
|
1317
|
-
revised.strictEvaluation = {
|
|
1318
|
-
overallScore: Number(Math.max(strictOverallScore, MIN_HYPOTHESIS_STRICT_ACCEPT_SCORE).toFixed(2)),
|
|
1319
|
-
decision: "accept",
|
|
1320
|
-
reason: strictReason,
|
|
1321
|
-
};
|
|
1322
|
-
changed = true;
|
|
1323
|
-
}
|
|
1324
|
-
return changed ? revised : undefined;
|
|
1325
|
-
};
|
|
1326
|
-
for (const hypothesis of args.hypotheses) {
|
|
1327
|
-
const firstReasons = evaluateHypothesisReasons(hypothesis);
|
|
1328
|
-
if (firstReasons.length === 0) {
|
|
1329
|
-
acceptedHypotheses.push(hypothesis);
|
|
1330
|
-
continue;
|
|
1331
|
-
}
|
|
1332
|
-
const autoRevised = autoReviseHypothesis(hypothesis, firstReasons);
|
|
1333
|
-
if (autoRevised) {
|
|
1334
|
-
const secondReasons = evaluateHypothesisReasons(autoRevised);
|
|
1335
|
-
if (secondReasons.length === 0) {
|
|
1336
|
-
acceptedHypotheses.push(autoRevised);
|
|
1337
|
-
continue;
|
|
1338
|
-
}
|
|
1339
|
-
for (const reason of secondReasons)
|
|
1340
|
-
rejectionReasonSet.add(reason);
|
|
1341
|
-
continue;
|
|
1342
|
-
}
|
|
1343
|
-
const reasons = firstReasons;
|
|
1344
|
-
if (reasons.length > 0) {
|
|
1345
|
-
for (const reason of reasons)
|
|
1346
|
-
rejectionReasonSet.add(reason);
|
|
1347
|
-
continue;
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1350
|
-
return {
|
|
1351
|
-
acceptedHypotheses,
|
|
1352
|
-
gate: {
|
|
1353
|
-
accepted: acceptedHypotheses.length,
|
|
1354
|
-
rejected: Math.max(0, args.hypotheses.length - acceptedHypotheses.length),
|
|
1355
|
-
rejectionReasons: [...rejectionReasonSet].slice(0, MAX_HYPOTHESIS_REJECTION_REASONS),
|
|
1356
|
-
},
|
|
1357
|
-
};
|
|
1358
|
-
}
|
|
1359
|
-
function toDayStartMs(day) {
|
|
1360
|
-
const ts = Date.parse(`${day}T00:00:00.000Z`);
|
|
1361
|
-
return Number.isFinite(ts) ? ts : undefined;
|
|
1362
|
-
}
|
|
1363
|
-
function deriveTriggerState(args) {
|
|
1364
|
-
const sorted = [...args.recentChangeStats].sort((a, b) => b.day.localeCompare(a.day));
|
|
1365
|
-
// consecutive days from latest day with NEW/REVISE > 0
|
|
1366
|
-
let consecutiveNewReviseDays = 0;
|
|
1367
|
-
let expectedDayMs;
|
|
1368
|
-
for (const item of sorted) {
|
|
1369
|
-
const dayMs = toDayStartMs(item.day);
|
|
1370
|
-
if (dayMs === undefined)
|
|
1371
|
-
continue;
|
|
1372
|
-
const hasSignal = item.newCount > 0 || item.reviseCount > 0;
|
|
1373
|
-
if (!hasSignal)
|
|
1374
|
-
break;
|
|
1375
|
-
if (expectedDayMs === undefined) {
|
|
1376
|
-
consecutiveNewReviseDays = 1;
|
|
1377
|
-
expectedDayMs = dayMs - 24 * 60 * 60 * 1000;
|
|
1378
|
-
continue;
|
|
1379
|
-
}
|
|
1380
|
-
if (dayMs === expectedDayMs) {
|
|
1381
|
-
consecutiveNewReviseDays += 1;
|
|
1382
|
-
expectedDayMs = dayMs - 24 * 60 * 60 * 1000;
|
|
1383
|
-
continue;
|
|
1384
|
-
}
|
|
1385
|
-
break;
|
|
1386
|
-
}
|
|
1387
|
-
const sevenDaysAgo = args.nowMs - 7 * 24 * 60 * 60 * 1000;
|
|
1388
|
-
let bridgeCount7d = 0;
|
|
1389
|
-
for (const item of sorted) {
|
|
1390
|
-
const dayMs = toDayStartMs(item.day);
|
|
1391
|
-
if (dayMs === undefined)
|
|
1392
|
-
continue;
|
|
1393
|
-
if (dayMs < sevenDaysAgo)
|
|
1394
|
-
continue;
|
|
1395
|
-
bridgeCount7d += Math.max(0, Math.floor(item.bridgeCount));
|
|
1396
|
-
}
|
|
1397
|
-
return {
|
|
1398
|
-
consecutiveNewReviseDays,
|
|
1399
|
-
bridgeCount7d,
|
|
1400
|
-
unreadCoreBacklog: Math.max(0, Math.floor(args.unreadCoreBacklog)),
|
|
1401
|
-
lastUpdatedAtMs: args.nowMs,
|
|
1402
|
-
};
|
|
1403
|
-
}
|
|
1404
685
|
export async function commitKnowledgeRun(input) {
|
|
1405
686
|
const project = await resolveProjectContext({
|
|
1406
687
|
projectId: input.projectId,
|
|
@@ -1425,7 +706,7 @@ export async function commitKnowledgeRun(input) {
|
|
|
1425
706
|
const explorationTrace = (knowledgeState.explorationTrace ?? [])
|
|
1426
707
|
.map(normalizeTrace)
|
|
1427
708
|
.filter((item) => Boolean(item));
|
|
1428
|
-
const
|
|
709
|
+
const knowledgeChanges = (knowledgeState.knowledgeChanges ?? [])
|
|
1429
710
|
.map(normalizeChange)
|
|
1430
711
|
.filter((item) => Boolean(item));
|
|
1431
712
|
const knowledgeUpdates = (knowledgeState.knowledgeUpdates ?? [])
|
|
@@ -1452,12 +733,10 @@ export async function commitKnowledgeRun(input) {
|
|
|
1452
733
|
topic: normalizeText(input.topic),
|
|
1453
734
|
topicKey: streamKey,
|
|
1454
735
|
projectId: project.projectId,
|
|
1455
|
-
lastRunProfile: defaultRunProfile(),
|
|
1456
736
|
totalRuns: 0,
|
|
1457
737
|
totalHypotheses: 0,
|
|
1458
738
|
knowledgeTopics: [],
|
|
1459
739
|
paperNotes: [],
|
|
1460
|
-
triggerState: defaultTriggerState(),
|
|
1461
740
|
recentFullTextReadCount: 0,
|
|
1462
741
|
recentNotFullTextReadCount: 0,
|
|
1463
742
|
lastQualityGate: defaultQualityGateState(),
|
|
@@ -1468,16 +747,14 @@ export async function commitKnowledgeRun(input) {
|
|
|
1468
747
|
recentHypotheses: [],
|
|
1469
748
|
recentChangeStats: [],
|
|
1470
749
|
lastExplorationTrace: [],
|
|
1471
|
-
lastReflectionTasks: [],
|
|
1472
|
-
lastHypothesisGate: defaultHypothesisGateState(),
|
|
1473
750
|
};
|
|
1474
751
|
const paperIds = mergePapers(corePapers, explorationPapers)
|
|
1475
752
|
.map((paper) => paper.id || paper.url || paper.title || "")
|
|
1476
753
|
.map((value) => normalizeText(value))
|
|
1477
754
|
.filter((value) => value.length > 0);
|
|
1478
|
-
const
|
|
1479
|
-
|
|
1480
|
-
buildRunFingerprint({
|
|
755
|
+
const runId = input.runId?.trim()
|
|
756
|
+
? sanitizeId(input.runId)
|
|
757
|
+
: buildRunFingerprint({
|
|
1481
758
|
scope: stream.scope,
|
|
1482
759
|
topic: stream.topic,
|
|
1483
760
|
status: input.status,
|
|
@@ -1485,30 +762,15 @@ export async function commitKnowledgeRun(input) {
|
|
|
1485
762
|
paperIds,
|
|
1486
763
|
note: input.note,
|
|
1487
764
|
});
|
|
1488
|
-
const inferredRunProfile = input.knowledgeState?.runLog?.runProfile === "strict" || input.knowledgeState?.runLog?.runProfile === "fast"
|
|
1489
|
-
? input.knowledgeState.runLog.runProfile
|
|
1490
|
-
: input.knowledgeState?.runLog?.requiredCorePapers !== undefined ||
|
|
1491
|
-
input.knowledgeState?.runLog?.requiredFullTextCoveragePct !== undefined
|
|
1492
|
-
? "strict"
|
|
1493
|
-
: defaultRunProfile();
|
|
1494
765
|
if (stream.recentRunIds.includes(runId)) {
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
runId,
|
|
1504
|
-
createdProject: project.created,
|
|
1505
|
-
};
|
|
1506
|
-
}
|
|
1507
|
-
const collisionTag = createHash("sha1")
|
|
1508
|
-
.update(`${nowMs}\n${paperIds.join("|")}\n${input.status ?? ""}\n${input.note ?? ""}`)
|
|
1509
|
-
.digest("hex")
|
|
1510
|
-
.slice(0, 8);
|
|
1511
|
-
runId = `${runId}-r${collisionTag}`;
|
|
766
|
+
root.streams[streamKey] = stream;
|
|
767
|
+
return {
|
|
768
|
+
projectId: project.projectId,
|
|
769
|
+
streamKey,
|
|
770
|
+
summary: toSummary(stream),
|
|
771
|
+
runId,
|
|
772
|
+
createdProject: project.created,
|
|
773
|
+
};
|
|
1512
774
|
}
|
|
1513
775
|
const rootPath = getKnowledgeStateRoot(project.projectPath);
|
|
1514
776
|
const logDir = path.join(rootPath, "logs");
|
|
@@ -1523,67 +785,18 @@ export async function commitKnowledgeRun(input) {
|
|
|
1523
785
|
trace: explorationTrace,
|
|
1524
786
|
papers: explorationPapers,
|
|
1525
787
|
}));
|
|
1526
|
-
const mergedRunPapers = mergePapers(corePapers, explorationPapers);
|
|
1527
|
-
const changeSanitization = sanitizeKnowledgeChanges({
|
|
1528
|
-
changes: submittedKnowledgeChanges,
|
|
1529
|
-
allRunPapers: mergedRunPapers,
|
|
1530
|
-
});
|
|
1531
|
-
const knowledgeChanges = changeSanitization.changes;
|
|
1532
788
|
await appendMarkdown(path.join(dailyDir, `day-${dayKey}.md`), renderDailyChangesMarkdown({ now: nowIso, runId, topic: stream.topic, changes: knowledgeChanges }));
|
|
1533
|
-
const
|
|
1534
|
-
topic: stream.topic,
|
|
1535
|
-
changes: knowledgeChanges,
|
|
1536
|
-
trace: explorationTrace,
|
|
1537
|
-
corePapers,
|
|
1538
|
-
});
|
|
1539
|
-
await appendMarkdown(path.join(logDir, `day-${dayKey}-reflection.md`), renderReflectionLogMarkdown({
|
|
1540
|
-
now: nowIso,
|
|
1541
|
-
runId,
|
|
1542
|
-
tasks: reflectionTasks,
|
|
1543
|
-
}));
|
|
1544
|
-
const submittedHypotheses = hypotheses;
|
|
1545
|
-
const hypothesisEval = applyHypothesisGate({
|
|
1546
|
-
hypotheses: submittedHypotheses,
|
|
1547
|
-
allRunPapers: mergedRunPapers,
|
|
1548
|
-
knowledgeChanges,
|
|
1549
|
-
runProfile: inferredRunProfile,
|
|
1550
|
-
});
|
|
1551
|
-
const acceptedHypotheses = hypothesisEval.acceptedHypotheses;
|
|
1552
|
-
const runArtifactCount = corePapers.length +
|
|
1553
|
-
explorationPapers.length +
|
|
1554
|
-
explorationTrace.length +
|
|
1555
|
-
knowledgeChanges.length +
|
|
1556
|
-
knowledgeUpdates.length +
|
|
1557
|
-
submittedHypotheses.length;
|
|
1558
|
-
const hasRunError = Boolean(input.knowledgeState?.runLog?.error && normalizeText(input.knowledgeState.runLog.error).length > 0);
|
|
789
|
+
const mergedRunPapers = mergePapers(corePapers, explorationPapers);
|
|
1559
790
|
const qualityEval = applyQualityGates({
|
|
1560
791
|
corePapers,
|
|
1561
792
|
allRunPapers: mergedRunPapers,
|
|
1562
|
-
explorationTrace,
|
|
1563
|
-
reflectionTasks,
|
|
1564
793
|
knowledgeChanges,
|
|
1565
794
|
knowledgeUpdates,
|
|
1566
|
-
hypotheses
|
|
1567
|
-
hypothesisGate: hypothesisEval.gate,
|
|
1568
|
-
requiredCorePapers: input.knowledgeState?.runLog?.requiredCorePapers,
|
|
1569
|
-
requiredFullTextCoveragePct: input.knowledgeState?.runLog?.requiredFullTextCoveragePct,
|
|
1570
|
-
hasAuditableArtifacts: runArtifactCount > 0,
|
|
1571
|
-
hasRunError,
|
|
795
|
+
hypotheses,
|
|
1572
796
|
});
|
|
1573
|
-
if (changeSanitization.droppedBridgeCount > 0) {
|
|
1574
|
-
qualityEval.qualityGate.warnings.push(`bridge_dropped_due_to_ungrounded_evidence(${changeSanitization.droppedBridgeCount})`);
|
|
1575
|
-
qualityEval.qualityGate.reasons.push(`bridge_dropped_due_to_ungrounded_evidence(${changeSanitization.droppedBridgeCount})`);
|
|
1576
|
-
if (qualityEval.qualityGate.severity === "ok") {
|
|
1577
|
-
qualityEval.qualityGate.severity = "warn";
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
797
|
const requestedStatus = normalizeText(input.status ?? "ok");
|
|
1581
|
-
const
|
|
1582
|
-
|
|
1583
|
-
runArtifactCount,
|
|
1584
|
-
hasRunError,
|
|
1585
|
-
qualityBlocking: qualityEval.qualityGate.blocking,
|
|
1586
|
-
});
|
|
798
|
+
const qualitySensitiveStatus = requestedStatus === "ok" || requestedStatus === "fallback_representative";
|
|
799
|
+
const effectiveStatus = qualitySensitiveStatus && !qualityEval.qualityGate.passed ? "degraded_quality" : requestedStatus;
|
|
1587
800
|
const topicToUpdates = new Map();
|
|
1588
801
|
for (const update of knowledgeUpdates) {
|
|
1589
802
|
const key = slugifyTopic(update.topic);
|
|
@@ -1623,7 +836,7 @@ export async function commitKnowledgeRun(input) {
|
|
|
1623
836
|
const recentHypothesisSummaries = [];
|
|
1624
837
|
let seq = stream.totalHypotheses;
|
|
1625
838
|
const dayToken = dayKey.replace(/-/g, "");
|
|
1626
|
-
for (const hypothesis of
|
|
839
|
+
for (const hypothesis of hypotheses) {
|
|
1627
840
|
seq += 1;
|
|
1628
841
|
const hypothesisId = hypothesis.id && hypothesis.id.length > 0 ? sanitizeId(hypothesis.id) : `hyp-${dayToken}-${String(seq).padStart(4, "0")}`;
|
|
1629
842
|
const file = `${hypothesisId}.md`;
|
|
@@ -1634,12 +847,6 @@ export async function commitKnowledgeRun(input) {
|
|
|
1634
847
|
trigger: hypothesis.trigger,
|
|
1635
848
|
createdAtMs: nowMs,
|
|
1636
849
|
file,
|
|
1637
|
-
...(typeof hypothesis.strictEvaluation?.overallScore === "number"
|
|
1638
|
-
? { strictOverallScore: hypothesis.strictEvaluation.overallScore }
|
|
1639
|
-
: {}),
|
|
1640
|
-
...(hypothesis.strictEvaluation?.decision
|
|
1641
|
-
? { strictDecision: hypothesis.strictEvaluation.decision }
|
|
1642
|
-
: {}),
|
|
1643
850
|
});
|
|
1644
851
|
}
|
|
1645
852
|
const fullTextStats = countFullTextStats(mergedRunPapers);
|
|
@@ -1647,7 +854,6 @@ export async function commitKnowledgeRun(input) {
|
|
|
1647
854
|
await writeFile(path.join(knowledgeDir, "_index.md"), renderKnowledgeIndexMarkdown({
|
|
1648
855
|
now: nowIso,
|
|
1649
856
|
topic: stream.topic,
|
|
1650
|
-
runProfile: inferredRunProfile,
|
|
1651
857
|
topicFiles: stream.knowledgeTopics,
|
|
1652
858
|
paperNotesCount: stream.paperNotes.length,
|
|
1653
859
|
totalHypotheses: stream.totalHypotheses + recentHypothesisSummaries.length,
|
|
@@ -1656,13 +862,10 @@ export async function commitKnowledgeRun(input) {
|
|
|
1656
862
|
notFullTextReadCount: fullTextStats.notFullTextReadCount,
|
|
1657
863
|
qualityGate: qualityEval.qualityGate,
|
|
1658
864
|
unreadCorePaperIds: qualityEval.unreadCorePaperIds,
|
|
1659
|
-
reflectionTasks,
|
|
1660
|
-
hypothesisGate: hypothesisEval.gate,
|
|
1661
865
|
lastStatus: effectiveStatus,
|
|
1662
866
|
}), "utf-8");
|
|
1663
867
|
const changeStat = countChangeStats(dayKey, runId, knowledgeChanges);
|
|
1664
868
|
stream.projectId = project.projectId;
|
|
1665
|
-
stream.lastRunProfile = inferredRunProfile;
|
|
1666
869
|
stream.totalRuns += 1;
|
|
1667
870
|
stream.totalHypotheses += recentHypothesisSummaries.length;
|
|
1668
871
|
stream.lastRunAtMs = nowMs;
|
|
@@ -1672,8 +875,6 @@ export async function commitKnowledgeRun(input) {
|
|
|
1672
875
|
stream.lastQualityGate = qualityEval.qualityGate;
|
|
1673
876
|
stream.lastUnreadCorePaperIds = qualityEval.unreadCorePaperIds;
|
|
1674
877
|
stream.lastExplorationTrace = explorationTrace.slice(0, MAX_LAST_TRACE);
|
|
1675
|
-
stream.lastReflectionTasks = reflectionTasks.slice(0, MAX_LAST_REFLECTION_TASKS);
|
|
1676
|
-
stream.lastHypothesisGate = hypothesisEval.gate;
|
|
1677
878
|
stream.recentPapers = mergePapers(mergedRunPapers, stream.recentPapers).slice(0, MAX_RECENT_PAPERS);
|
|
1678
879
|
stream.recentRunIds = [runId, ...stream.recentRunIds.filter((id) => id !== runId)].slice(0, MAX_RECENT_RUN_IDS);
|
|
1679
880
|
stream.recentHypothesisIds = [
|
|
@@ -1682,18 +883,11 @@ export async function commitKnowledgeRun(input) {
|
|
|
1682
883
|
].slice(0, MAX_RECENT_HYPOTHESES);
|
|
1683
884
|
stream.recentHypotheses = [...recentHypothesisSummaries, ...stream.recentHypotheses].slice(0, MAX_RECENT_HYPOTHESES);
|
|
1684
885
|
stream.recentChangeStats = [changeStat, ...stream.recentChangeStats].slice(0, MAX_RECENT_CHANGE_STATS);
|
|
1685
|
-
stream.triggerState = deriveTriggerState({
|
|
1686
|
-
recentChangeStats: stream.recentChangeStats,
|
|
1687
|
-
unreadCoreBacklog: qualityEval.unreadCorePaperIds.length,
|
|
1688
|
-
nowMs,
|
|
1689
|
-
});
|
|
1690
886
|
root.streams[streamKey] = stream;
|
|
1691
887
|
await saveStateAtomic(project.projectPath, root);
|
|
1692
888
|
await appendFile(path.join(logDir, `day-${dayKey}-run-details.jsonl`), `${JSON.stringify({
|
|
1693
889
|
ts: nowMs,
|
|
1694
|
-
run_id: runId,
|
|
1695
890
|
runId,
|
|
1696
|
-
run_profile: inferredRunProfile,
|
|
1697
891
|
scope: stream.scope,
|
|
1698
892
|
topic: stream.topic,
|
|
1699
893
|
streamKey,
|
|
@@ -1701,14 +895,9 @@ export async function commitKnowledgeRun(input) {
|
|
|
1701
895
|
corePapers,
|
|
1702
896
|
explorationPapers,
|
|
1703
897
|
explorationTrace,
|
|
1704
|
-
reflectionTasks,
|
|
1705
|
-
submittedKnowledgeChanges,
|
|
1706
898
|
knowledgeChanges,
|
|
1707
|
-
droppedBridgeCount: changeSanitization.droppedBridgeCount,
|
|
1708
899
|
knowledgeUpdates,
|
|
1709
|
-
hypotheses
|
|
1710
|
-
submittedHypotheses,
|
|
1711
|
-
hypothesisGate: hypothesisEval.gate,
|
|
900
|
+
hypotheses,
|
|
1712
901
|
paperNoteFiles: runPaperNoteFiles,
|
|
1713
902
|
quality: {
|
|
1714
903
|
fullTextReadCount: fullTextStats.fullTextReadCount,
|
|
@@ -1724,9 +913,7 @@ export async function commitKnowledgeRun(input) {
|
|
|
1724
913
|
})}\n`, "utf-8");
|
|
1725
914
|
await appendEvent(project.projectPath, {
|
|
1726
915
|
ts: nowMs,
|
|
1727
|
-
run_id: runId,
|
|
1728
916
|
runId,
|
|
1729
|
-
run_profile: inferredRunProfile,
|
|
1730
917
|
scope: stream.scope,
|
|
1731
918
|
topic: stream.topic,
|
|
1732
919
|
streamKey,
|
|
@@ -1742,14 +929,8 @@ export async function commitKnowledgeRun(input) {
|
|
|
1742
929
|
qualityGate: qualityEval.qualityGate,
|
|
1743
930
|
unreadCorePaperIds: qualityEval.unreadCorePaperIds,
|
|
1744
931
|
downgradedHighConfidenceCount: qualityEval.downgradedHighConfidenceCount,
|
|
1745
|
-
submittedChangeCount: submittedKnowledgeChanges.length,
|
|
1746
932
|
changeCount: knowledgeChanges.length,
|
|
1747
|
-
droppedBridgeCount: changeSanitization.droppedBridgeCount,
|
|
1748
933
|
hypothesisCount: recentHypothesisSummaries.length,
|
|
1749
|
-
submittedHypothesisCount: submittedHypotheses.length,
|
|
1750
|
-
hypothesisGate: hypothesisEval.gate,
|
|
1751
|
-
triggerState: stream.triggerState,
|
|
1752
|
-
reflectionTasks,
|
|
1753
934
|
corePapers,
|
|
1754
935
|
explorationPapers,
|
|
1755
936
|
note: input.note,
|