scientify 2.1.0 → 3.1.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 +21 -1
- package/README.md +27 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -77
- package/dist/index.js.map +1 -1
- package/dist/src/cli/research.d.ts.map +1 -1
- package/dist/src/cli/research.js +47 -23
- package/dist/src/cli/research.js.map +1 -1
- package/dist/src/commands/metabolism-status.d.ts.map +1 -1
- package/dist/src/commands/metabolism-status.js +5 -25
- package/dist/src/commands/metabolism-status.js.map +1 -1
- package/dist/src/commands.d.ts +8 -8
- package/dist/src/commands.d.ts.map +1 -1
- package/dist/src/commands.js +230 -243
- package/dist/src/commands.js.map +1 -1
- package/dist/src/release-gate.d.ts +14 -0
- package/dist/src/release-gate.d.ts.map +1 -0
- package/dist/src/release-gate.js +124 -0
- package/dist/src/release-gate.js.map +1 -0
- package/dist/src/templates/bootstrap.d.ts.map +1 -1
- package/dist/src/templates/bootstrap.js +157 -94
- package/dist/src/templates/bootstrap.js.map +1 -1
- package/dist/src/types.d.ts +2 -10
- package/dist/src/types.d.ts.map +1 -1
- package/openclaw.plugin.json +11 -17
- package/package.json +2 -3
- package/skills/algorithm-selection/SKILL.md +103 -0
- package/skills/algorithm-selection/references/candidate-template.md +13 -0
- package/skills/algorithm-selection/references/selection-template.md +39 -0
- package/skills/artifact-review/SKILL.md +146 -0
- package/skills/artifact-review/references/release-gate-template.md +40 -0
- package/skills/artifact-review/references/review-checklist.md +45 -0
- package/skills/artifact-review/references/style-review-checklist.md +30 -0
- package/skills/baseline-runner/SKILL.md +103 -0
- package/skills/baseline-runner/references/baseline-matrix-template.md +9 -0
- package/skills/baseline-runner/references/baseline-report-template.md +25 -0
- package/skills/dataset-validate/SKILL.md +104 -0
- package/skills/dataset-validate/references/data-validation-template.md +38 -0
- package/skills/figure-standardize/SKILL.md +110 -0
- package/skills/figure-standardize/references/caption-template.md +12 -0
- package/skills/figure-standardize/references/figure-placement-template.md +30 -0
- package/skills/figure-standardize/references/figure-style-guide.md +36 -0
- package/skills/idea-generation/SKILL.md +20 -44
- package/skills/idea-generation/references/code-mapping.md +3 -3
- package/skills/idea-generation/references/idea-template.md +1 -1
- package/skills/idea-generation/references/reading-long-papers.md +3 -3
- package/skills/metabolism/SKILL.md +80 -36
- package/skills/paper-download/SKILL.md +61 -0
- package/skills/release-layout/SKILL.md +73 -0
- package/skills/release-layout/references/page-structure.md +14 -0
- package/skills/research-collect/SKILL.md +41 -111
- package/skills/research-experiment/SKILL.md +20 -12
- package/skills/research-implement/SKILL.md +10 -11
- package/skills/research-pipeline/SKILL.md +23 -31
- package/skills/research-plan/SKILL.md +7 -11
- package/skills/research-review/SKILL.md +21 -22
- package/skills/research-survey/SKILL.md +28 -25
- package/skills/write-paper/SKILL.md +252 -0
- package/skills/write-paper/references/boundary-notes-template.md +34 -0
- package/skills/write-paper/references/claim-inventory-template.md +32 -0
- package/skills/write-paper/references/evidence-contract.md +57 -0
- package/skills/write-paper/references/figure-callout-template.md +38 -0
- package/skills/write-paper/references/figures-manifest-template.md +44 -0
- package/skills/write-paper/references/latex/README.md +22 -0
- package/skills/write-paper/references/latex/build_paper.sh +41 -0
- package/skills/write-paper/references/latex/manuscript.tex +39 -0
- package/skills/write-paper/references/latex/references.bib +10 -0
- package/skills/write-paper/references/latex/sections/ablations.tex +3 -0
- package/skills/write-paper/references/latex/sections/abstract.tex +3 -0
- package/skills/write-paper/references/latex/sections/conclusion.tex +3 -0
- package/skills/write-paper/references/latex/sections/discussion_scope.tex +7 -0
- package/skills/write-paper/references/latex/sections/experimental_protocol.tex +3 -0
- package/skills/write-paper/references/latex/sections/introduction.tex +3 -0
- package/skills/write-paper/references/latex/sections/main_results.tex +9 -0
- package/skills/write-paper/references/latex/sections/method_system.tex +3 -0
- package/skills/write-paper/references/latex/sections/problem_setup.tex +3 -0
- package/skills/write-paper/references/latex/sections/related_work.tex +3 -0
- package/skills/write-paper/references/paper-template.md +155 -0
- package/skills/write-paper/references/paragraph-contract.md +139 -0
- package/skills/write-paper/references/paragraph-examples.md +171 -0
- package/skills/write-paper/references/style-banlist.md +81 -0
- package/skills/write-review-paper/SKILL.md +22 -16
- package/skills/write-review-paper/references/note-template.md +1 -1
- package/skills/write-review-paper/references/survey-template.md +1 -1
- package/dist/src/hooks/research-mode.d.ts +0 -22
- package/dist/src/hooks/research-mode.d.ts.map +0 -1
- package/dist/src/hooks/research-mode.js +0 -35
- package/dist/src/hooks/research-mode.js.map +0 -1
- package/dist/src/hooks/scientify-cron-autofill.d.ts +0 -15
- package/dist/src/hooks/scientify-cron-autofill.d.ts.map +0 -1
- package/dist/src/hooks/scientify-cron-autofill.js +0 -156
- package/dist/src/hooks/scientify-cron-autofill.js.map +0 -1
- package/dist/src/hooks/scientify-signature.d.ts +0 -21
- package/dist/src/hooks/scientify-signature.d.ts.map +0 -1
- package/dist/src/hooks/scientify-signature.js +0 -150
- package/dist/src/hooks/scientify-signature.js.map +0 -1
- package/dist/src/knowledge-state/project.d.ts +0 -13
- package/dist/src/knowledge-state/project.d.ts.map +0 -1
- package/dist/src/knowledge-state/project.js +0 -88
- package/dist/src/knowledge-state/project.js.map +0 -1
- package/dist/src/knowledge-state/render.d.ts +0 -63
- package/dist/src/knowledge-state/render.d.ts.map +0 -1
- package/dist/src/knowledge-state/render.js +0 -368
- package/dist/src/knowledge-state/render.js.map +0 -1
- package/dist/src/knowledge-state/store.d.ts +0 -19
- package/dist/src/knowledge-state/store.d.ts.map +0 -1
- package/dist/src/knowledge-state/store.js +0 -978
- package/dist/src/knowledge-state/store.js.map +0 -1
- package/dist/src/knowledge-state/types.d.ts +0 -182
- package/dist/src/knowledge-state/types.d.ts.map +0 -1
- package/dist/src/knowledge-state/types.js +0 -2
- package/dist/src/knowledge-state/types.js.map +0 -1
- package/dist/src/literature/subscription-state.d.ts +0 -112
- package/dist/src/literature/subscription-state.d.ts.map +0 -1
- package/dist/src/literature/subscription-state.js +0 -696
- package/dist/src/literature/subscription-state.js.map +0 -1
- package/dist/src/research-subscriptions/constants.d.ts +0 -16
- package/dist/src/research-subscriptions/constants.d.ts.map +0 -1
- package/dist/src/research-subscriptions/constants.js +0 -59
- package/dist/src/research-subscriptions/constants.js.map +0 -1
- package/dist/src/research-subscriptions/cron-client.d.ts +0 -8
- package/dist/src/research-subscriptions/cron-client.d.ts.map +0 -1
- package/dist/src/research-subscriptions/cron-client.js +0 -81
- package/dist/src/research-subscriptions/cron-client.js.map +0 -1
- package/dist/src/research-subscriptions/delivery.d.ts +0 -10
- package/dist/src/research-subscriptions/delivery.d.ts.map +0 -1
- package/dist/src/research-subscriptions/delivery.js +0 -82
- package/dist/src/research-subscriptions/delivery.js.map +0 -1
- package/dist/src/research-subscriptions/handlers.d.ts +0 -6
- package/dist/src/research-subscriptions/handlers.d.ts.map +0 -1
- package/dist/src/research-subscriptions/handlers.js +0 -204
- package/dist/src/research-subscriptions/handlers.js.map +0 -1
- package/dist/src/research-subscriptions/parse.d.ts +0 -11
- package/dist/src/research-subscriptions/parse.d.ts.map +0 -1
- package/dist/src/research-subscriptions/parse.js +0 -492
- package/dist/src/research-subscriptions/parse.js.map +0 -1
- package/dist/src/research-subscriptions/prompt.d.ts +0 -5
- package/dist/src/research-subscriptions/prompt.d.ts.map +0 -1
- package/dist/src/research-subscriptions/prompt.js +0 -347
- package/dist/src/research-subscriptions/prompt.js.map +0 -1
- package/dist/src/research-subscriptions/types.d.ts +0 -66
- package/dist/src/research-subscriptions/types.d.ts.map +0 -1
- package/dist/src/research-subscriptions/types.js +0 -2
- package/dist/src/research-subscriptions/types.js.map +0 -1
- package/dist/src/research-subscriptions.d.ts +0 -2
- package/dist/src/research-subscriptions.d.ts.map +0 -1
- package/dist/src/research-subscriptions.js +0 -2
- package/dist/src/research-subscriptions.js.map +0 -1
- package/dist/src/services/auto-updater.d.ts +0 -15
- package/dist/src/services/auto-updater.d.ts.map +0 -1
- package/dist/src/services/auto-updater.js +0 -188
- package/dist/src/services/auto-updater.js.map +0 -1
- package/dist/src/tools/arxiv-download.d.ts +0 -24
- package/dist/src/tools/arxiv-download.d.ts.map +0 -1
- package/dist/src/tools/arxiv-download.js +0 -177
- package/dist/src/tools/arxiv-download.js.map +0 -1
- package/dist/src/tools/github-search-tool.d.ts +0 -25
- package/dist/src/tools/github-search-tool.d.ts.map +0 -1
- package/dist/src/tools/github-search-tool.js +0 -114
- package/dist/src/tools/github-search-tool.js.map +0 -1
- package/dist/src/tools/openreview-lookup.d.ts +0 -31
- package/dist/src/tools/openreview-lookup.d.ts.map +0 -1
- package/dist/src/tools/openreview-lookup.js +0 -414
- package/dist/src/tools/openreview-lookup.js.map +0 -1
- package/dist/src/tools/paper-browser.d.ts +0 -23
- package/dist/src/tools/paper-browser.d.ts.map +0 -1
- package/dist/src/tools/paper-browser.js +0 -121
- package/dist/src/tools/paper-browser.js.map +0 -1
- package/dist/src/tools/scientify-cron.d.ts +0 -63
- package/dist/src/tools/scientify-cron.d.ts.map +0 -1
- package/dist/src/tools/scientify-cron.js +0 -265
- package/dist/src/tools/scientify-cron.js.map +0 -1
- package/dist/src/tools/scientify-literature-state.d.ts +0 -303
- package/dist/src/tools/scientify-literature-state.d.ts.map +0 -1
- package/dist/src/tools/scientify-literature-state.js +0 -957
- package/dist/src/tools/scientify-literature-state.js.map +0 -1
- package/dist/src/tools/unpaywall-download.d.ts +0 -21
- package/dist/src/tools/unpaywall-download.d.ts.map +0 -1
- package/dist/src/tools/unpaywall-download.js +0 -169
- package/dist/src/tools/unpaywall-download.js.map +0 -1
- package/dist/src/tools/workspace.d.ts +0 -32
- package/dist/src/tools/workspace.d.ts.map +0 -1
- package/dist/src/tools/workspace.js +0 -69
- package/dist/src/tools/workspace.js.map +0 -1
- package/skills/metabolism-init/SKILL.md +0 -80
- package/skills/research-subscription/SKILL.md +0 -119
|
@@ -1,696 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { mkdir, readFile, rename, writeFile, appendFile } from "node:fs/promises";
|
|
3
|
-
import { existsSync } from "node:fs";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import { commitKnowledgeRun, readKnowledgeSummary } from "../knowledge-state/store.js";
|
|
6
|
-
const STATE_VERSION = 1;
|
|
7
|
-
const DEFAULT_MAX_PAPERS = 5;
|
|
8
|
-
const DEFAULT_SOURCES = ["openalex", "arxiv"];
|
|
9
|
-
const MAX_MEMORY_NOTES = 30;
|
|
10
|
-
const MAX_MEMORY_KEYS = 60;
|
|
11
|
-
const TOP_HINT_LIMIT = 8;
|
|
12
|
-
const FEEDBACK_SIGNAL_DELTA = {
|
|
13
|
-
read: 1,
|
|
14
|
-
skip: -1,
|
|
15
|
-
star: 2,
|
|
16
|
-
};
|
|
17
|
-
function getStateDir() {
|
|
18
|
-
const home = process.env.HOME || process.env.USERPROFILE || process.cwd();
|
|
19
|
-
return path.join(home, ".openclaw", "workspace", "scientify");
|
|
20
|
-
}
|
|
21
|
-
function getStatePath() {
|
|
22
|
-
return path.join(getStateDir(), "literature-state.json");
|
|
23
|
-
}
|
|
24
|
-
function getPushLogPath() {
|
|
25
|
-
return path.join(getStateDir(), "literature-push-log.jsonl");
|
|
26
|
-
}
|
|
27
|
-
function normalizeText(raw) {
|
|
28
|
-
return raw.trim().replace(/\s+/g, " ");
|
|
29
|
-
}
|
|
30
|
-
function normalizeTopic(raw) {
|
|
31
|
-
return normalizeText(raw).toLowerCase();
|
|
32
|
-
}
|
|
33
|
-
function sanitizeScopePart(raw) {
|
|
34
|
-
const normalized = normalizeText(raw).toLowerCase();
|
|
35
|
-
const cleaned = normalized
|
|
36
|
-
.replace(/[^a-z0-9_-]+/g, "-")
|
|
37
|
-
.replace(/-+/g, "-")
|
|
38
|
-
.replace(/^-|-$/g, "");
|
|
39
|
-
return cleaned || "unknown";
|
|
40
|
-
}
|
|
41
|
-
function normalizeScope(raw) {
|
|
42
|
-
const trimmed = normalizeText(raw).toLowerCase();
|
|
43
|
-
if (trimmed.length === 0)
|
|
44
|
-
return "global";
|
|
45
|
-
const split = trimmed.split(":");
|
|
46
|
-
if (split.length === 1) {
|
|
47
|
-
return sanitizeScopePart(split[0]);
|
|
48
|
-
}
|
|
49
|
-
const channel = sanitizeScopePart(split[0]);
|
|
50
|
-
const target = sanitizeScopePart(split.slice(1).join(":"));
|
|
51
|
-
return `${channel}:${target}`;
|
|
52
|
-
}
|
|
53
|
-
function topicKey(scope, topic) {
|
|
54
|
-
const hash = createHash("sha1")
|
|
55
|
-
.update(`${normalizeScope(scope)}\n${normalizeTopic(topic)}`)
|
|
56
|
-
.digest("hex");
|
|
57
|
-
return hash.slice(0, 20);
|
|
58
|
-
}
|
|
59
|
-
function dedupeSources(raw) {
|
|
60
|
-
if (!raw || raw.length === 0)
|
|
61
|
-
return [...DEFAULT_SOURCES];
|
|
62
|
-
const seen = new Set();
|
|
63
|
-
for (const item of raw) {
|
|
64
|
-
const value = normalizeText(item).toLowerCase();
|
|
65
|
-
if (value.length === 0)
|
|
66
|
-
continue;
|
|
67
|
-
seen.add(value);
|
|
68
|
-
}
|
|
69
|
-
if (seen.size === 0)
|
|
70
|
-
return [...DEFAULT_SOURCES];
|
|
71
|
-
return [...seen];
|
|
72
|
-
}
|
|
73
|
-
function mergePreferences(base, incoming) {
|
|
74
|
-
const maxPapersRaw = incoming?.maxPapers ?? base?.maxPapers ?? DEFAULT_MAX_PAPERS;
|
|
75
|
-
const maxPapers = Number.isFinite(maxPapersRaw)
|
|
76
|
-
? Math.min(20, Math.max(1, Math.floor(maxPapersRaw)))
|
|
77
|
-
: DEFAULT_MAX_PAPERS;
|
|
78
|
-
const recencyRaw = incoming?.recencyDays ?? base?.recencyDays;
|
|
79
|
-
const recencyDays = recencyRaw !== undefined && Number.isFinite(recencyRaw) && recencyRaw > 0
|
|
80
|
-
? Math.min(3650, Math.floor(recencyRaw))
|
|
81
|
-
: undefined;
|
|
82
|
-
const sources = dedupeSources(incoming?.sources ?? base?.sources);
|
|
83
|
-
return {
|
|
84
|
-
maxPapers,
|
|
85
|
-
...(recencyDays ? { recencyDays } : {}),
|
|
86
|
-
sources,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
function defaultState() {
|
|
90
|
-
return {
|
|
91
|
-
version: STATE_VERSION,
|
|
92
|
-
updatedAtMs: Date.now(),
|
|
93
|
-
topics: {},
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
function defaultTopicMemoryState() {
|
|
97
|
-
return {
|
|
98
|
-
feedbackCounts: {
|
|
99
|
-
read: 0,
|
|
100
|
-
skip: 0,
|
|
101
|
-
star: 0,
|
|
102
|
-
},
|
|
103
|
-
keywordScores: {},
|
|
104
|
-
sourceScores: {},
|
|
105
|
-
recentNotes: [],
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
async function ensureStateDir() {
|
|
109
|
-
await mkdir(getStateDir(), { recursive: true });
|
|
110
|
-
}
|
|
111
|
-
async function loadState() {
|
|
112
|
-
const file = getStatePath();
|
|
113
|
-
if (!existsSync(file))
|
|
114
|
-
return defaultState();
|
|
115
|
-
try {
|
|
116
|
-
const raw = await readFile(file, "utf-8");
|
|
117
|
-
const parsed = JSON.parse(raw);
|
|
118
|
-
if (parsed.version !== STATE_VERSION || !parsed.topics || typeof parsed.topics !== "object") {
|
|
119
|
-
return defaultState();
|
|
120
|
-
}
|
|
121
|
-
return {
|
|
122
|
-
version: STATE_VERSION,
|
|
123
|
-
updatedAtMs: typeof parsed.updatedAtMs === "number" ? parsed.updatedAtMs : Date.now(),
|
|
124
|
-
topics: parsed.topics,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
catch {
|
|
128
|
-
return defaultState();
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
async function saveState(state) {
|
|
132
|
-
await ensureStateDir();
|
|
133
|
-
state.updatedAtMs = Date.now();
|
|
134
|
-
const file = getStatePath();
|
|
135
|
-
const tmp = `${file}.tmp`;
|
|
136
|
-
await writeFile(tmp, JSON.stringify(state, null, 2), "utf-8");
|
|
137
|
-
await rename(tmp, file);
|
|
138
|
-
}
|
|
139
|
-
function normalizePaperId(raw) {
|
|
140
|
-
if (!raw)
|
|
141
|
-
return undefined;
|
|
142
|
-
const normalized = normalizeText(raw).toLowerCase();
|
|
143
|
-
if (!normalized)
|
|
144
|
-
return undefined;
|
|
145
|
-
return normalized;
|
|
146
|
-
}
|
|
147
|
-
function extractArxivId(text) {
|
|
148
|
-
const m = text.match(/\b(\d{4}\.\d{4,5}(?:v\d+)?)\b/i);
|
|
149
|
-
if (m?.[1])
|
|
150
|
-
return `arxiv:${m[1].toLowerCase()}`;
|
|
151
|
-
return undefined;
|
|
152
|
-
}
|
|
153
|
-
function extractDoi(text) {
|
|
154
|
-
const m = text.match(/\b(10\.\d{4,9}\/[-._;()/:a-z0-9]+)\b/i);
|
|
155
|
-
if (m?.[1])
|
|
156
|
-
return `doi:${m[1].toLowerCase()}`;
|
|
157
|
-
return undefined;
|
|
158
|
-
}
|
|
159
|
-
function derivePaperId(paper) {
|
|
160
|
-
const explicit = normalizePaperId(paper.id);
|
|
161
|
-
if (explicit)
|
|
162
|
-
return explicit;
|
|
163
|
-
const text = [paper.url, paper.title].filter((part) => Boolean(part)).join(" ");
|
|
164
|
-
const arxiv = extractArxivId(text);
|
|
165
|
-
if (arxiv)
|
|
166
|
-
return arxiv;
|
|
167
|
-
const doi = extractDoi(text);
|
|
168
|
-
if (doi)
|
|
169
|
-
return doi;
|
|
170
|
-
const fallback = normalizeText(`${paper.title ?? ""} ${paper.url ?? ""}`);
|
|
171
|
-
const digest = createHash("sha1").update(fallback || JSON.stringify(paper)).digest("hex");
|
|
172
|
-
return `hash:${digest.slice(0, 20)}`;
|
|
173
|
-
}
|
|
174
|
-
function sanitizeKeyword(raw) {
|
|
175
|
-
const normalized = normalizeText(raw).toLowerCase();
|
|
176
|
-
if (normalized.length < 2 || normalized.length > 48)
|
|
177
|
-
return undefined;
|
|
178
|
-
return normalized;
|
|
179
|
-
}
|
|
180
|
-
function tokenizeKeywords(raw) {
|
|
181
|
-
const tokens = raw.match(/[\p{L}\p{N}][\p{L}\p{N}_-]{1,47}/gu) ?? [];
|
|
182
|
-
const seen = new Set();
|
|
183
|
-
for (const token of tokens) {
|
|
184
|
-
const keyword = sanitizeKeyword(token);
|
|
185
|
-
if (!keyword)
|
|
186
|
-
continue;
|
|
187
|
-
seen.add(keyword);
|
|
188
|
-
}
|
|
189
|
-
return [...seen];
|
|
190
|
-
}
|
|
191
|
-
function normalizeSource(raw) {
|
|
192
|
-
if (!raw)
|
|
193
|
-
return undefined;
|
|
194
|
-
const value = raw.trim().toLowerCase();
|
|
195
|
-
if (!value)
|
|
196
|
-
return undefined;
|
|
197
|
-
const direct = value.replace(/^https?:\/\//, "").replace(/^www\./, "").split("/")[0];
|
|
198
|
-
const host = direct.length > 0 ? direct : value;
|
|
199
|
-
if (host.includes("arxiv"))
|
|
200
|
-
return "arxiv";
|
|
201
|
-
if (host.includes("openalex"))
|
|
202
|
-
return "openalex";
|
|
203
|
-
if (host.includes("doi.org"))
|
|
204
|
-
return "doi";
|
|
205
|
-
if (host.includes("semanticscholar"))
|
|
206
|
-
return "semanticscholar";
|
|
207
|
-
if (host.includes("nature"))
|
|
208
|
-
return "nature";
|
|
209
|
-
if (host.includes("ieeexplore"))
|
|
210
|
-
return "ieee";
|
|
211
|
-
if (host.includes("acm.org"))
|
|
212
|
-
return "acm";
|
|
213
|
-
if (host.includes("sciencedirect"))
|
|
214
|
-
return "sciencedirect";
|
|
215
|
-
return host;
|
|
216
|
-
}
|
|
217
|
-
function sourceFromPaper(paper) {
|
|
218
|
-
const url = paper?.url?.trim();
|
|
219
|
-
if (!url)
|
|
220
|
-
return undefined;
|
|
221
|
-
try {
|
|
222
|
-
const host = new URL(url).host;
|
|
223
|
-
return normalizeSource(host);
|
|
224
|
-
}
|
|
225
|
-
catch {
|
|
226
|
-
return normalizeSource(url);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
function updateScoreMap(map, key, delta) {
|
|
230
|
-
const current = map[key] ?? 0;
|
|
231
|
-
const next = Math.max(-20, Math.min(20, current + delta));
|
|
232
|
-
if (Math.abs(next) < 0.01) {
|
|
233
|
-
delete map[key];
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
map[key] = Number(next.toFixed(2));
|
|
237
|
-
}
|
|
238
|
-
function limitScoreMap(map) {
|
|
239
|
-
const items = Object.entries(map)
|
|
240
|
-
.filter(([, value]) => Number.isFinite(value) && value !== 0)
|
|
241
|
-
.sort((a, b) => Math.abs(b[1]) - Math.abs(a[1]))
|
|
242
|
-
.slice(0, MAX_MEMORY_KEYS);
|
|
243
|
-
const trimmed = {};
|
|
244
|
-
for (const [key, value] of items) {
|
|
245
|
-
trimmed[key] = value;
|
|
246
|
-
}
|
|
247
|
-
return trimmed;
|
|
248
|
-
}
|
|
249
|
-
function ensureTopicMemoryState(topicState) {
|
|
250
|
-
const memory = topicState.memory;
|
|
251
|
-
if (!memory || typeof memory !== "object") {
|
|
252
|
-
topicState.memory = defaultTopicMemoryState();
|
|
253
|
-
return topicState.memory;
|
|
254
|
-
}
|
|
255
|
-
if (!memory.feedbackCounts || typeof memory.feedbackCounts !== "object") {
|
|
256
|
-
memory.feedbackCounts = { read: 0, skip: 0, star: 0 };
|
|
257
|
-
}
|
|
258
|
-
for (const key of ["read", "skip", "star"]) {
|
|
259
|
-
const raw = memory.feedbackCounts[key];
|
|
260
|
-
memory.feedbackCounts[key] = Number.isFinite(raw) ? Math.max(0, Math.floor(raw)) : 0;
|
|
261
|
-
}
|
|
262
|
-
if (!memory.keywordScores || typeof memory.keywordScores !== "object") {
|
|
263
|
-
memory.keywordScores = {};
|
|
264
|
-
}
|
|
265
|
-
else {
|
|
266
|
-
memory.keywordScores = limitScoreMap(memory.keywordScores);
|
|
267
|
-
}
|
|
268
|
-
if (!memory.sourceScores || typeof memory.sourceScores !== "object") {
|
|
269
|
-
memory.sourceScores = {};
|
|
270
|
-
}
|
|
271
|
-
else {
|
|
272
|
-
memory.sourceScores = limitScoreMap(memory.sourceScores);
|
|
273
|
-
}
|
|
274
|
-
if (!Array.isArray(memory.recentNotes)) {
|
|
275
|
-
memory.recentNotes = [];
|
|
276
|
-
}
|
|
277
|
-
else {
|
|
278
|
-
memory.recentNotes = memory.recentNotes
|
|
279
|
-
.filter((item) => item &&
|
|
280
|
-
typeof item === "object" &&
|
|
281
|
-
Number.isFinite(item.ts) &&
|
|
282
|
-
typeof item.signal === "string" &&
|
|
283
|
-
typeof item.text === "string")
|
|
284
|
-
.slice(-MAX_MEMORY_NOTES)
|
|
285
|
-
.map((item) => ({
|
|
286
|
-
ts: Math.floor(item.ts),
|
|
287
|
-
signal: item.signal,
|
|
288
|
-
text: normalizeText(item.text),
|
|
289
|
-
}));
|
|
290
|
-
}
|
|
291
|
-
if (!Number.isFinite(memory.lastFeedbackAtMs)) {
|
|
292
|
-
delete memory.lastFeedbackAtMs;
|
|
293
|
-
}
|
|
294
|
-
return memory;
|
|
295
|
-
}
|
|
296
|
-
function topKeysByScore(map, polarity, limit) {
|
|
297
|
-
const threshold = 0.1;
|
|
298
|
-
const entries = Object.entries(map).filter(([, score]) => polarity === "positive" ? score > threshold : score < -threshold);
|
|
299
|
-
entries.sort((a, b) => (polarity === "positive" ? b[1] - a[1] : a[1] - b[1]));
|
|
300
|
-
return entries.slice(0, limit).map(([key]) => key);
|
|
301
|
-
}
|
|
302
|
-
function buildMemoryHints(memory) {
|
|
303
|
-
return {
|
|
304
|
-
preferredKeywords: topKeysByScore(memory.keywordScores, "positive", TOP_HINT_LIMIT),
|
|
305
|
-
avoidedKeywords: topKeysByScore(memory.keywordScores, "negative", TOP_HINT_LIMIT),
|
|
306
|
-
preferredSources: topKeysByScore(memory.sourceScores, "positive", 4),
|
|
307
|
-
avoidedSources: topKeysByScore(memory.sourceScores, "negative", 4),
|
|
308
|
-
feedbackCounts: {
|
|
309
|
-
read: memory.feedbackCounts.read,
|
|
310
|
-
skip: memory.feedbackCounts.skip,
|
|
311
|
-
star: memory.feedbackCounts.star,
|
|
312
|
-
},
|
|
313
|
-
...(memory.lastFeedbackAtMs ? { lastFeedbackAtMs: memory.lastFeedbackAtMs } : {}),
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
function getOrCreateTopicState(root, scope, topic, incomingPrefs) {
|
|
317
|
-
const normalizedScope = normalizeScope(scope);
|
|
318
|
-
const normalizedTopicDisplay = normalizeText(topic);
|
|
319
|
-
const key = topicKey(normalizedScope, normalizedTopicDisplay);
|
|
320
|
-
const expectedNormalizedTopic = normalizeTopic(normalizedTopicDisplay);
|
|
321
|
-
let existing = root.topics[key];
|
|
322
|
-
if (!existing) {
|
|
323
|
-
for (const [legacyKey, candidate] of Object.entries(root.topics)) {
|
|
324
|
-
if (!candidate || typeof candidate !== "object")
|
|
325
|
-
continue;
|
|
326
|
-
if (normalizeScope(candidate.scope) !== normalizedScope)
|
|
327
|
-
continue;
|
|
328
|
-
if (normalizeTopic(candidate.topic) !== expectedNormalizedTopic)
|
|
329
|
-
continue;
|
|
330
|
-
existing = candidate;
|
|
331
|
-
if (legacyKey !== key) {
|
|
332
|
-
delete root.topics[legacyKey];
|
|
333
|
-
root.topics[key] = candidate;
|
|
334
|
-
}
|
|
335
|
-
break;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
if (existing) {
|
|
339
|
-
existing.scope = normalizedScope;
|
|
340
|
-
existing.topic = normalizedTopicDisplay;
|
|
341
|
-
existing.topicKey = key;
|
|
342
|
-
ensureTopicMemoryState(existing);
|
|
343
|
-
if (!existing.pushedPapers || typeof existing.pushedPapers !== "object") {
|
|
344
|
-
existing.pushedPapers = {};
|
|
345
|
-
}
|
|
346
|
-
if (!Number.isFinite(existing.totalRuns)) {
|
|
347
|
-
existing.totalRuns = 0;
|
|
348
|
-
}
|
|
349
|
-
existing.preferences = mergePreferences(existing.preferences, incomingPrefs);
|
|
350
|
-
// Merge duplicate legacy buckets produced by old scope normalization rules.
|
|
351
|
-
for (const [otherKey, other] of Object.entries(root.topics)) {
|
|
352
|
-
if (otherKey === key)
|
|
353
|
-
continue;
|
|
354
|
-
if (!other || typeof other !== "object")
|
|
355
|
-
continue;
|
|
356
|
-
if (normalizeScope(other.scope) !== normalizedScope)
|
|
357
|
-
continue;
|
|
358
|
-
if (normalizeTopic(other.topic) !== expectedNormalizedTopic)
|
|
359
|
-
continue;
|
|
360
|
-
ensureTopicMemoryState(other);
|
|
361
|
-
existing.preferences = mergePreferences(existing.preferences, other.preferences);
|
|
362
|
-
const existingMemory = ensureTopicMemoryState(existing);
|
|
363
|
-
const otherMemory = ensureTopicMemoryState(other);
|
|
364
|
-
existingMemory.feedbackCounts.read += otherMemory.feedbackCounts.read;
|
|
365
|
-
existingMemory.feedbackCounts.skip += otherMemory.feedbackCounts.skip;
|
|
366
|
-
existingMemory.feedbackCounts.star += otherMemory.feedbackCounts.star;
|
|
367
|
-
for (const [k, v] of Object.entries(otherMemory.keywordScores)) {
|
|
368
|
-
if (!Number.isFinite(v))
|
|
369
|
-
continue;
|
|
370
|
-
updateScoreMap(existingMemory.keywordScores, k, v);
|
|
371
|
-
}
|
|
372
|
-
for (const [k, v] of Object.entries(otherMemory.sourceScores)) {
|
|
373
|
-
if (!Number.isFinite(v))
|
|
374
|
-
continue;
|
|
375
|
-
updateScoreMap(existingMemory.sourceScores, k, v);
|
|
376
|
-
}
|
|
377
|
-
existingMemory.keywordScores = limitScoreMap(existingMemory.keywordScores);
|
|
378
|
-
existingMemory.sourceScores = limitScoreMap(existingMemory.sourceScores);
|
|
379
|
-
const mergedNotes = [...existingMemory.recentNotes, ...otherMemory.recentNotes]
|
|
380
|
-
.filter((item) => Number.isFinite(item.ts) && item.text.length > 0)
|
|
381
|
-
.sort((a, b) => a.ts - b.ts)
|
|
382
|
-
.slice(-MAX_MEMORY_NOTES);
|
|
383
|
-
existingMemory.recentNotes = mergedNotes;
|
|
384
|
-
const existingFb = existingMemory.lastFeedbackAtMs ?? 0;
|
|
385
|
-
const otherFb = otherMemory.lastFeedbackAtMs ?? 0;
|
|
386
|
-
if (otherFb > existingFb) {
|
|
387
|
-
existingMemory.lastFeedbackAtMs = otherFb;
|
|
388
|
-
}
|
|
389
|
-
for (const [paperId, paper] of Object.entries(other.pushedPapers ?? {})) {
|
|
390
|
-
const current = existing.pushedPapers[paperId];
|
|
391
|
-
if (!current) {
|
|
392
|
-
existing.pushedPapers[paperId] = { ...paper };
|
|
393
|
-
continue;
|
|
394
|
-
}
|
|
395
|
-
current.firstPushedAtMs = Math.min(current.firstPushedAtMs, paper.firstPushedAtMs);
|
|
396
|
-
const paperPushCount = Number.isFinite(paper.pushCount) ? Math.max(0, Math.floor(paper.pushCount)) : 0;
|
|
397
|
-
current.pushCount += paperPushCount;
|
|
398
|
-
if (paper.lastPushedAtMs > current.lastPushedAtMs) {
|
|
399
|
-
current.lastPushedAtMs = paper.lastPushedAtMs;
|
|
400
|
-
if (paper.title)
|
|
401
|
-
current.title = paper.title;
|
|
402
|
-
if (paper.url)
|
|
403
|
-
current.url = paper.url;
|
|
404
|
-
if (typeof paper.lastScore === "number" && Number.isFinite(paper.lastScore)) {
|
|
405
|
-
current.lastScore = paper.lastScore;
|
|
406
|
-
}
|
|
407
|
-
if (paper.lastReason)
|
|
408
|
-
current.lastReason = paper.lastReason;
|
|
409
|
-
}
|
|
410
|
-
else {
|
|
411
|
-
if (!current.title && paper.title)
|
|
412
|
-
current.title = paper.title;
|
|
413
|
-
if (!current.url && paper.url)
|
|
414
|
-
current.url = paper.url;
|
|
415
|
-
if (current.lastScore === undefined && typeof paper.lastScore === "number" && Number.isFinite(paper.lastScore)) {
|
|
416
|
-
current.lastScore = paper.lastScore;
|
|
417
|
-
}
|
|
418
|
-
if (!current.lastReason && paper.lastReason)
|
|
419
|
-
current.lastReason = paper.lastReason;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
const existingRuns = Number.isFinite(existing.totalRuns) ? Math.max(0, Math.floor(existing.totalRuns)) : 0;
|
|
423
|
-
const otherRuns = Number.isFinite(other.totalRuns) ? Math.max(0, Math.floor(other.totalRuns)) : 0;
|
|
424
|
-
existing.totalRuns = existingRuns + otherRuns;
|
|
425
|
-
const existingLastRun = existing.lastRunAtMs ?? 0;
|
|
426
|
-
const otherLastRun = other.lastRunAtMs ?? 0;
|
|
427
|
-
if (otherLastRun > existingLastRun) {
|
|
428
|
-
existing.lastRunAtMs = other.lastRunAtMs;
|
|
429
|
-
existing.lastStatus = other.lastStatus;
|
|
430
|
-
if (other.lastProjectId) {
|
|
431
|
-
existing.lastProjectId = other.lastProjectId;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
else if (!existing.lastStatus && other.lastStatus) {
|
|
435
|
-
existing.lastStatus = other.lastStatus;
|
|
436
|
-
}
|
|
437
|
-
delete root.topics[otherKey];
|
|
438
|
-
}
|
|
439
|
-
return existing;
|
|
440
|
-
}
|
|
441
|
-
const created = {
|
|
442
|
-
scope: normalizedScope,
|
|
443
|
-
topic: normalizedTopicDisplay,
|
|
444
|
-
topicKey: key,
|
|
445
|
-
preferences: mergePreferences(undefined, incomingPrefs),
|
|
446
|
-
memory: defaultTopicMemoryState(),
|
|
447
|
-
pushedPapers: {},
|
|
448
|
-
totalRuns: 0,
|
|
449
|
-
};
|
|
450
|
-
root.topics[key] = created;
|
|
451
|
-
return created;
|
|
452
|
-
}
|
|
453
|
-
function sortPaperIdsByRecency(papers) {
|
|
454
|
-
return Object.values(papers)
|
|
455
|
-
.sort((a, b) => b.lastPushedAtMs - a.lastPushedAtMs)
|
|
456
|
-
.map((item) => item.id);
|
|
457
|
-
}
|
|
458
|
-
function recentPapersByRecency(papers, limit) {
|
|
459
|
-
const normalizedLimit = Math.max(1, Math.min(50, Math.floor(limit)));
|
|
460
|
-
return Object.values(papers)
|
|
461
|
-
.sort((a, b) => b.lastPushedAtMs - a.lastPushedAtMs)
|
|
462
|
-
.slice(0, normalizedLimit)
|
|
463
|
-
.map((item) => ({
|
|
464
|
-
id: item.id,
|
|
465
|
-
...(item.title ? { title: item.title } : {}),
|
|
466
|
-
...(item.url ? { url: item.url } : {}),
|
|
467
|
-
...(typeof item.lastScore === "number" && Number.isFinite(item.lastScore)
|
|
468
|
-
? { lastScore: item.lastScore }
|
|
469
|
-
: {}),
|
|
470
|
-
...(item.lastReason ? { lastReason: item.lastReason } : {}),
|
|
471
|
-
firstPushedAtMs: item.firstPushedAtMs,
|
|
472
|
-
lastPushedAtMs: item.lastPushedAtMs,
|
|
473
|
-
pushCount: item.pushCount,
|
|
474
|
-
}));
|
|
475
|
-
}
|
|
476
|
-
async function appendPushLog(entry) {
|
|
477
|
-
await ensureStateDir();
|
|
478
|
-
await appendFile(getPushLogPath(), `${JSON.stringify(entry)}\n`, "utf-8");
|
|
479
|
-
}
|
|
480
|
-
export async function prepareIncrementalState(args) {
|
|
481
|
-
const root = await loadState();
|
|
482
|
-
const topicState = getOrCreateTopicState(root, args.scope, args.topic, args.preferences);
|
|
483
|
-
const memory = ensureTopicMemoryState(topicState);
|
|
484
|
-
await saveState(root);
|
|
485
|
-
const excludePaperIds = sortPaperIdsByRecency(topicState.pushedPapers);
|
|
486
|
-
const lastPushedAtMs = excludePaperIds.length
|
|
487
|
-
? topicState.pushedPapers[excludePaperIds[0]]?.lastPushedAtMs
|
|
488
|
-
: undefined;
|
|
489
|
-
return {
|
|
490
|
-
scope: topicState.scope,
|
|
491
|
-
topic: topicState.topic,
|
|
492
|
-
topicKey: topicState.topicKey,
|
|
493
|
-
preferences: topicState.preferences,
|
|
494
|
-
memoryHints: buildMemoryHints(memory),
|
|
495
|
-
excludePaperIds,
|
|
496
|
-
knownPaperCount: excludePaperIds.length,
|
|
497
|
-
...(lastPushedAtMs ? { lastPushedAtMs } : {}),
|
|
498
|
-
};
|
|
499
|
-
}
|
|
500
|
-
export async function recordIncrementalPush(args) {
|
|
501
|
-
const root = await loadState();
|
|
502
|
-
const topicState = getOrCreateTopicState(root, args.scope, args.topic, args.preferences);
|
|
503
|
-
const memory = ensureTopicMemoryState(topicState);
|
|
504
|
-
const now = Date.now();
|
|
505
|
-
let recordedPapers = 0;
|
|
506
|
-
for (const rawPaper of args.papers) {
|
|
507
|
-
const id = derivePaperId(rawPaper);
|
|
508
|
-
const existing = topicState.pushedPapers[id];
|
|
509
|
-
if (existing) {
|
|
510
|
-
existing.lastPushedAtMs = now;
|
|
511
|
-
existing.pushCount += 1;
|
|
512
|
-
if (rawPaper.title)
|
|
513
|
-
existing.title = rawPaper.title.trim();
|
|
514
|
-
if (rawPaper.url)
|
|
515
|
-
existing.url = rawPaper.url.trim();
|
|
516
|
-
if (typeof rawPaper.score === "number" && Number.isFinite(rawPaper.score)) {
|
|
517
|
-
existing.lastScore = rawPaper.score;
|
|
518
|
-
}
|
|
519
|
-
if (rawPaper.reason) {
|
|
520
|
-
existing.lastReason = rawPaper.reason.trim();
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
else {
|
|
524
|
-
topicState.pushedPapers[id] = {
|
|
525
|
-
id,
|
|
526
|
-
title: rawPaper.title?.trim(),
|
|
527
|
-
url: rawPaper.url?.trim(),
|
|
528
|
-
...(typeof rawPaper.score === "number" && Number.isFinite(rawPaper.score)
|
|
529
|
-
? { lastScore: rawPaper.score }
|
|
530
|
-
: {}),
|
|
531
|
-
...(rawPaper.reason ? { lastReason: rawPaper.reason.trim() } : {}),
|
|
532
|
-
firstPushedAtMs: now,
|
|
533
|
-
lastPushedAtMs: now,
|
|
534
|
-
pushCount: 1,
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
recordedPapers += 1;
|
|
538
|
-
}
|
|
539
|
-
topicState.totalRuns += 1;
|
|
540
|
-
topicState.lastRunAtMs = now;
|
|
541
|
-
topicState.lastStatus = args.status?.trim() || (recordedPapers > 0 ? "ok" : "empty");
|
|
542
|
-
const knowledgeCommitted = await commitKnowledgeRun({
|
|
543
|
-
projectId: args.projectId ?? topicState.lastProjectId,
|
|
544
|
-
scope: topicState.scope,
|
|
545
|
-
topic: topicState.topic,
|
|
546
|
-
topicKey: topicState.topicKey,
|
|
547
|
-
status: topicState.lastStatus,
|
|
548
|
-
runId: args.runId,
|
|
549
|
-
note: args.note,
|
|
550
|
-
papers: args.papers,
|
|
551
|
-
knowledgeState: args.knowledgeState,
|
|
552
|
-
});
|
|
553
|
-
topicState.lastStatus = knowledgeCommitted.summary.lastStatus ?? topicState.lastStatus;
|
|
554
|
-
topicState.lastProjectId = knowledgeCommitted.projectId;
|
|
555
|
-
await saveState(root);
|
|
556
|
-
await appendPushLog({
|
|
557
|
-
ts: now,
|
|
558
|
-
scope: topicState.scope,
|
|
559
|
-
topic: topicState.topic,
|
|
560
|
-
topicKey: topicState.topicKey,
|
|
561
|
-
status: topicState.lastStatus,
|
|
562
|
-
runId: knowledgeCommitted.runId,
|
|
563
|
-
projectId: knowledgeCommitted.projectId,
|
|
564
|
-
streamKey: knowledgeCommitted.streamKey,
|
|
565
|
-
preferences: topicState.preferences,
|
|
566
|
-
recordedPapers,
|
|
567
|
-
papers: args.papers.map((paper) => ({
|
|
568
|
-
id: derivePaperId(paper),
|
|
569
|
-
title: paper.title?.trim(),
|
|
570
|
-
url: paper.url?.trim(),
|
|
571
|
-
...(typeof paper.score === "number" && Number.isFinite(paper.score) ? { score: paper.score } : {}),
|
|
572
|
-
...(paper.reason ? { reason: paper.reason.trim() } : {}),
|
|
573
|
-
})),
|
|
574
|
-
note: args.note,
|
|
575
|
-
knowledgeStateSummary: knowledgeCommitted.summary,
|
|
576
|
-
});
|
|
577
|
-
return {
|
|
578
|
-
scope: topicState.scope,
|
|
579
|
-
topic: topicState.topic,
|
|
580
|
-
topicKey: topicState.topicKey,
|
|
581
|
-
preferences: topicState.preferences,
|
|
582
|
-
memoryHints: buildMemoryHints(memory),
|
|
583
|
-
recordedPapers,
|
|
584
|
-
totalKnownPapers: Object.keys(topicState.pushedPapers).length,
|
|
585
|
-
pushedAtMs: now,
|
|
586
|
-
projectId: knowledgeCommitted.projectId,
|
|
587
|
-
streamKey: knowledgeCommitted.streamKey,
|
|
588
|
-
knowledgeStateSummary: knowledgeCommitted.summary,
|
|
589
|
-
};
|
|
590
|
-
}
|
|
591
|
-
export async function recordUserFeedback(args) {
|
|
592
|
-
const root = await loadState();
|
|
593
|
-
const topicState = getOrCreateTopicState(root, args.scope, args.topic, args.preferences);
|
|
594
|
-
const memory = ensureTopicMemoryState(topicState);
|
|
595
|
-
const now = Date.now();
|
|
596
|
-
const signal = args.feedback.signal;
|
|
597
|
-
const delta = FEEDBACK_SIGNAL_DELTA[signal];
|
|
598
|
-
memory.feedbackCounts[signal] += 1;
|
|
599
|
-
memory.lastFeedbackAtMs = now;
|
|
600
|
-
const keywords = new Set();
|
|
601
|
-
for (const tag of args.feedback.tags ?? []) {
|
|
602
|
-
const normalized = sanitizeKeyword(tag);
|
|
603
|
-
if (normalized)
|
|
604
|
-
keywords.add(normalized);
|
|
605
|
-
}
|
|
606
|
-
if (args.feedback.note) {
|
|
607
|
-
for (const token of tokenizeKeywords(args.feedback.note)) {
|
|
608
|
-
keywords.add(token);
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
if (args.feedback.paper?.title) {
|
|
612
|
-
for (const token of tokenizeKeywords(args.feedback.paper.title)) {
|
|
613
|
-
keywords.add(token);
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
for (const keyword of keywords) {
|
|
617
|
-
updateScoreMap(memory.keywordScores, keyword, delta);
|
|
618
|
-
}
|
|
619
|
-
memory.keywordScores = limitScoreMap(memory.keywordScores);
|
|
620
|
-
const source = normalizeSource(args.feedback.source) ?? sourceFromPaper(args.feedback.paper);
|
|
621
|
-
if (source) {
|
|
622
|
-
updateScoreMap(memory.sourceScores, source, delta);
|
|
623
|
-
memory.sourceScores = limitScoreMap(memory.sourceScores);
|
|
624
|
-
}
|
|
625
|
-
const noteText = normalizeText(args.feedback.note ?? "");
|
|
626
|
-
if (noteText.length > 0) {
|
|
627
|
-
memory.recentNotes.push({
|
|
628
|
-
ts: now,
|
|
629
|
-
signal,
|
|
630
|
-
text: noteText,
|
|
631
|
-
});
|
|
632
|
-
if (memory.recentNotes.length > MAX_MEMORY_NOTES) {
|
|
633
|
-
memory.recentNotes = memory.recentNotes.slice(-MAX_MEMORY_NOTES);
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
await saveState(root);
|
|
637
|
-
await appendPushLog({
|
|
638
|
-
ts: now,
|
|
639
|
-
event: "feedback",
|
|
640
|
-
scope: topicState.scope,
|
|
641
|
-
topic: topicState.topic,
|
|
642
|
-
topicKey: topicState.topicKey,
|
|
643
|
-
signal,
|
|
644
|
-
source: source ?? null,
|
|
645
|
-
tags: [...keywords],
|
|
646
|
-
note: noteText || undefined,
|
|
647
|
-
runId: args.feedback.runId,
|
|
648
|
-
memoryHints: buildMemoryHints(memory),
|
|
649
|
-
});
|
|
650
|
-
return {
|
|
651
|
-
scope: topicState.scope,
|
|
652
|
-
topic: topicState.topic,
|
|
653
|
-
topicKey: topicState.topicKey,
|
|
654
|
-
signal,
|
|
655
|
-
preferences: topicState.preferences,
|
|
656
|
-
memoryHints: buildMemoryHints(memory),
|
|
657
|
-
updatedAtMs: now,
|
|
658
|
-
};
|
|
659
|
-
}
|
|
660
|
-
export async function getIncrementalStateStatus(args) {
|
|
661
|
-
const root = await loadState();
|
|
662
|
-
const topicState = getOrCreateTopicState(root, args.scope, args.topic);
|
|
663
|
-
const memory = ensureTopicMemoryState(topicState);
|
|
664
|
-
const knowledgeSummaryResult = await readKnowledgeSummary({
|
|
665
|
-
scope: topicState.scope,
|
|
666
|
-
topic: topicState.topic,
|
|
667
|
-
topicKey: topicState.topicKey,
|
|
668
|
-
projectId: args.projectId ?? topicState.lastProjectId,
|
|
669
|
-
});
|
|
670
|
-
if (knowledgeSummaryResult?.projectId) {
|
|
671
|
-
topicState.lastProjectId = knowledgeSummaryResult.projectId;
|
|
672
|
-
}
|
|
673
|
-
await saveState(root);
|
|
674
|
-
const excludePaperIds = sortPaperIdsByRecency(topicState.pushedPapers);
|
|
675
|
-
const lastPushedAtMs = excludePaperIds.length
|
|
676
|
-
? topicState.pushedPapers[excludePaperIds[0]]?.lastPushedAtMs
|
|
677
|
-
: undefined;
|
|
678
|
-
return {
|
|
679
|
-
scope: topicState.scope,
|
|
680
|
-
topic: topicState.topic,
|
|
681
|
-
topicKey: topicState.topicKey,
|
|
682
|
-
preferences: topicState.preferences,
|
|
683
|
-
memoryHints: buildMemoryHints(memory),
|
|
684
|
-
excludePaperIds,
|
|
685
|
-
knownPaperCount: excludePaperIds.length,
|
|
686
|
-
...(lastPushedAtMs ? { lastPushedAtMs } : {}),
|
|
687
|
-
totalRuns: topicState.totalRuns,
|
|
688
|
-
...(topicState.lastStatus ? { lastStatus: topicState.lastStatus } : {}),
|
|
689
|
-
recentPapers: recentPapersByRecency(topicState.pushedPapers, 10),
|
|
690
|
-
...(knowledgeSummaryResult ? { knowledgeStateSummary: knowledgeSummaryResult.summary } : {}),
|
|
691
|
-
recentHypotheses: knowledgeSummaryResult?.summary.recentHypotheses ?? [],
|
|
692
|
-
recentChangeStats: knowledgeSummaryResult?.summary.recentChangeStats ?? [],
|
|
693
|
-
lastExplorationTrace: knowledgeSummaryResult?.summary.lastExplorationTrace ?? [],
|
|
694
|
-
};
|
|
695
|
-
}
|
|
696
|
-
//# sourceMappingURL=subscription-state.js.map
|