trove-cli 0.0.0-stage → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +202 -2
- package/dist/adapters/file-source.d.ts +31 -0
- package/dist/adapters/file-source.js +97 -0
- package/dist/adapters/file-source.js.map +1 -0
- package/dist/adapters/index.d.ts +34 -0
- package/dist/adapters/index.js +146 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/opencode.d.ts +30 -0
- package/dist/adapters/opencode.js +135 -0
- package/dist/adapters/opencode.js.map +1 -0
- package/dist/cli.d.ts +58 -0
- package/dist/cli.js +974 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +81 -0
- package/dist/config.js +99 -0
- package/dist/config.js.map +1 -0
- package/dist/core/archive.d.ts +27 -0
- package/dist/core/archive.js +101 -0
- package/dist/core/archive.js.map +1 -0
- package/dist/core/chunker.d.ts +28 -0
- package/dist/core/chunker.js +67 -0
- package/dist/core/chunker.js.map +1 -0
- package/dist/core/clean.d.ts +36 -0
- package/dist/core/clean.js +182 -0
- package/dist/core/clean.js.map +1 -0
- package/dist/core/collect.d.ts +39 -0
- package/dist/core/collect.js +194 -0
- package/dist/core/collect.js.map +1 -0
- package/dist/core/concurrency.d.ts +2 -0
- package/dist/core/concurrency.js +16 -0
- package/dist/core/concurrency.js.map +1 -0
- package/dist/core/cursor.d.ts +31 -0
- package/dist/core/cursor.js +31 -0
- package/dist/core/cursor.js.map +1 -0
- package/dist/core/events/claude.d.ts +7 -0
- package/dist/core/events/claude.js +287 -0
- package/dist/core/events/claude.js.map +1 -0
- package/dist/core/events/codex.d.ts +6 -0
- package/dist/core/events/codex.js +149 -0
- package/dist/core/events/codex.js.map +1 -0
- package/dist/core/events/doubao.d.ts +9 -0
- package/dist/core/events/doubao.js +151 -0
- package/dist/core/events/doubao.js.map +1 -0
- package/dist/core/events/index.d.ts +6 -0
- package/dist/core/events/index.js +20 -0
- package/dist/core/events/index.js.map +1 -0
- package/dist/core/events/opencode.d.ts +7 -0
- package/dist/core/events/opencode.js +189 -0
- package/dist/core/events/opencode.js.map +1 -0
- package/dist/core/events/schema.d.ts +83 -0
- package/dist/core/events/schema.js +35 -0
- package/dist/core/events/schema.js.map +1 -0
- package/dist/core/layout.d.ts +26 -0
- package/dist/core/layout.js +122 -0
- package/dist/core/layout.js.map +1 -0
- package/dist/core/lessons/analyze.d.ts +40 -0
- package/dist/core/lessons/analyze.js +107 -0
- package/dist/core/lessons/analyze.js.map +1 -0
- package/dist/core/lessons/audit.d.ts +51 -0
- package/dist/core/lessons/audit.js +57 -0
- package/dist/core/lessons/audit.js.map +1 -0
- package/dist/core/lessons/compaction.d.ts +5 -0
- package/dist/core/lessons/compaction.js +58 -0
- package/dist/core/lessons/compaction.js.map +1 -0
- package/dist/core/lessons/config.d.ts +15 -0
- package/dist/core/lessons/config.js +31 -0
- package/dist/core/lessons/config.js.map +1 -0
- package/dist/core/lessons/existing.d.ts +4 -0
- package/dist/core/lessons/existing.js +22 -0
- package/dist/core/lessons/existing.js.map +1 -0
- package/dist/core/lessons/extract.d.ts +21 -0
- package/dist/core/lessons/extract.js +106 -0
- package/dist/core/lessons/extract.js.map +1 -0
- package/dist/core/lessons/frontmatter.d.ts +29 -0
- package/dist/core/lessons/frontmatter.js +87 -0
- package/dist/core/lessons/frontmatter.js.map +1 -0
- package/dist/core/lessons/graph.d.ts +256 -0
- package/dist/core/lessons/graph.js +86 -0
- package/dist/core/lessons/graph.js.map +1 -0
- package/dist/core/lessons/jev.d.ts +36 -0
- package/dist/core/lessons/jev.js +100 -0
- package/dist/core/lessons/jev.js.map +1 -0
- package/dist/core/lessons/layout.d.ts +2 -0
- package/dist/core/lessons/layout.js +56 -0
- package/dist/core/lessons/layout.js.map +1 -0
- package/dist/core/lessons/learn.d.ts +38 -0
- package/dist/core/lessons/learn.js +190 -0
- package/dist/core/lessons/learn.js.map +1 -0
- package/dist/core/lessons/llm.d.ts +42 -0
- package/dist/core/lessons/llm.js +106 -0
- package/dist/core/lessons/llm.js.map +1 -0
- package/dist/core/lessons/memo.d.ts +19 -0
- package/dist/core/lessons/memo.js +170 -0
- package/dist/core/lessons/memo.js.map +1 -0
- package/dist/core/lessons/merge.d.ts +39 -0
- package/dist/core/lessons/merge.js +79 -0
- package/dist/core/lessons/merge.js.map +1 -0
- package/dist/core/lessons/queue.d.ts +6 -0
- package/dist/core/lessons/queue.js +52 -0
- package/dist/core/lessons/queue.js.map +1 -0
- package/dist/core/lessons/recent.d.ts +21 -0
- package/dist/core/lessons/recent.js +111 -0
- package/dist/core/lessons/recent.js.map +1 -0
- package/dist/core/lessons/schema.d.ts +26 -0
- package/dist/core/lessons/schema.js +64 -0
- package/dist/core/lessons/schema.js.map +1 -0
- package/dist/core/lessons/select.d.ts +19 -0
- package/dist/core/lessons/select.js +55 -0
- package/dist/core/lessons/select.js.map +1 -0
- package/dist/core/lessons/sidecar.d.ts +23 -0
- package/dist/core/lessons/sidecar.js +171 -0
- package/dist/core/lessons/sidecar.js.map +1 -0
- package/dist/core/lessons/storage.d.ts +21 -0
- package/dist/core/lessons/storage.js +65 -0
- package/dist/core/lessons/storage.js.map +1 -0
- package/dist/core/lessons/usage.d.ts +31 -0
- package/dist/core/lessons/usage.js +91 -0
- package/dist/core/lessons/usage.js.map +1 -0
- package/dist/core/lessons/write.d.ts +18 -0
- package/dist/core/lessons/write.js +131 -0
- package/dist/core/lessons/write.js.map +1 -0
- package/dist/core/library/build.d.ts +22 -0
- package/dist/core/library/build.js +163 -0
- package/dist/core/library/build.js.map +1 -0
- package/dist/core/library/group.d.ts +14 -0
- package/dist/core/library/group.js +129 -0
- package/dist/core/library/group.js.map +1 -0
- package/dist/core/library/index-file.d.ts +7 -0
- package/dist/core/library/index-file.js +250 -0
- package/dist/core/library/index-file.js.map +1 -0
- package/dist/core/library/schema.d.ts +78 -0
- package/dist/core/library/schema.js +37 -0
- package/dist/core/library/schema.js.map +1 -0
- package/dist/core/library/skills.d.ts +26 -0
- package/dist/core/library/skills.js +168 -0
- package/dist/core/library/skills.js.map +1 -0
- package/dist/core/library/sync.d.ts +48 -0
- package/dist/core/library/sync.js +271 -0
- package/dist/core/library/sync.js.map +1 -0
- package/dist/core/lock.d.ts +6 -0
- package/dist/core/lock.js +56 -0
- package/dist/core/lock.js.map +1 -0
- package/dist/core/pull.d.ts +23 -0
- package/dist/core/pull.js +147 -0
- package/dist/core/pull.js.map +1 -0
- package/dist/core/push.d.ts +29 -0
- package/dist/core/push.js +170 -0
- package/dist/core/push.js.map +1 -0
- package/dist/core/remote-layout.d.ts +67 -0
- package/dist/core/remote-layout.js +94 -0
- package/dist/core/remote-layout.js.map +1 -0
- package/dist/core/sessions.d.ts +29 -0
- package/dist/core/sessions.js +96 -0
- package/dist/core/sessions.js.map +1 -0
- package/dist/core/state.d.ts +94 -0
- package/dist/core/state.js +62 -0
- package/dist/core/state.js.map +1 -0
- package/dist/core/sync.d.ts +48 -0
- package/dist/core/sync.js +140 -0
- package/dist/core/sync.js.map +1 -0
- package/dist/core/time-window.d.ts +8 -0
- package/dist/core/time-window.js +36 -0
- package/dist/core/time-window.js.map +1 -0
- package/dist/daemon.d.ts +73 -0
- package/dist/daemon.js +272 -0
- package/dist/daemon.js.map +1 -0
- package/dist/format.d.ts +4 -0
- package/dist/format.js +28 -0
- package/dist/format.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +9 -0
- package/dist/main.js.map +1 -0
- package/dist/paths.d.ts +19 -0
- package/dist/paths.js +19 -0
- package/dist/paths.js.map +1 -0
- package/dist/progress-console.d.ts +12 -0
- package/dist/progress-console.js +42 -0
- package/dist/progress-console.js.map +1 -0
- package/dist/progress.d.ts +7 -0
- package/dist/progress.js +6 -0
- package/dist/progress.js.map +1 -0
- package/dist/remote/create-remote.d.ts +20 -0
- package/dist/remote/create-remote.js +86 -0
- package/dist/remote/create-remote.js.map +1 -0
- package/dist/remote/prompt.d.ts +17 -0
- package/dist/remote/prompt.js +111 -0
- package/dist/remote/prompt.js.map +1 -0
- package/dist/remote/s3-sigv4.d.ts +28 -0
- package/dist/remote/s3-sigv4.js +100 -0
- package/dist/remote/s3-sigv4.js.map +1 -0
- package/dist/remote/s3-store.d.ts +75 -0
- package/dist/remote/s3-store.js +438 -0
- package/dist/remote/s3-store.js.map +1 -0
- package/dist/remote/s3-vendors.d.ts +31 -0
- package/dist/remote/s3-vendors.js +47 -0
- package/dist/remote/s3-vendors.js.map +1 -0
- package/dist/remote/store.d.ts +44 -0
- package/dist/remote/store.js +2 -0
- package/dist/remote/store.js.map +1 -0
- package/dist/remote/webdav-store.d.ts +41 -0
- package/dist/remote/webdav-store.js +285 -0
- package/dist/remote/webdav-store.js.map +1 -0
- package/dist/sqlite.d.ts +3 -0
- package/dist/sqlite.js +12 -0
- package/dist/sqlite.js.map +1 -0
- package/dist/status.d.ts +82 -0
- package/dist/status.js +162 -0
- package/dist/status.js.map +1 -0
- package/package.json +43 -4
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { truncateText } from './select.js';
|
|
2
|
+
const SUMMARY_SCHEMA = {
|
|
3
|
+
type: 'object',
|
|
4
|
+
properties: {
|
|
5
|
+
topic: { type: 'string' },
|
|
6
|
+
intent: { type: 'string' },
|
|
7
|
+
outcome: { type: 'string', enum: ['success', 'failure', 'partial', 'trivial'] },
|
|
8
|
+
keySteps: { type: 'array', items: { type: 'string' } },
|
|
9
|
+
hasError: { type: 'boolean' },
|
|
10
|
+
hasCorrection: { type: 'boolean' },
|
|
11
|
+
hasWorkflow: { type: 'boolean' },
|
|
12
|
+
},
|
|
13
|
+
required: ['topic', 'intent', 'outcome', 'keySteps', 'hasError', 'hasCorrection', 'hasWorkflow'],
|
|
14
|
+
};
|
|
15
|
+
const CLASSIFY_SCHEMA = {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
preferences: { type: 'boolean' },
|
|
19
|
+
entities: { type: 'boolean' },
|
|
20
|
+
experiences: { type: 'boolean' },
|
|
21
|
+
events: { type: 'boolean' },
|
|
22
|
+
resources: { type: 'boolean' },
|
|
23
|
+
},
|
|
24
|
+
required: ['preferences', 'entities', 'experiences', 'events', 'resources'],
|
|
25
|
+
};
|
|
26
|
+
const SCORE_TASK_SUCCESS_MIN = 0.5;
|
|
27
|
+
const SCORE_MEAN_MIN = 0.6;
|
|
28
|
+
export async function summarizeSession(events, llm) {
|
|
29
|
+
const raw = await llm.generate(renderEvents(events), SUMMARY_SCHEMA, 'learn:summarize');
|
|
30
|
+
return validateSummary(raw);
|
|
31
|
+
}
|
|
32
|
+
export async function classifySession(summary, llm) {
|
|
33
|
+
const raw = await llm.generate(JSON.stringify(summary), CLASSIFY_SCHEMA, 'learn:classify');
|
|
34
|
+
return validateClassification(raw);
|
|
35
|
+
}
|
|
36
|
+
export function anyCategory(c) {
|
|
37
|
+
return c.preferences || c.entities || c.experiences || c.events || c.resources;
|
|
38
|
+
}
|
|
39
|
+
/** 阶段 4 总入口:摘要 → 分类 → 打分,任一不达标即跳过并说明原因。 */
|
|
40
|
+
export async function analyzeSession(events, deps) {
|
|
41
|
+
const summary = await summarizeSession(events, deps.llm);
|
|
42
|
+
if (summary.outcome === 'trivial')
|
|
43
|
+
return { status: 'skip', reason: 'trivial' };
|
|
44
|
+
const classification = await classifySession(summary, deps.llm);
|
|
45
|
+
if (!anyCategory(classification))
|
|
46
|
+
return { status: 'skip', reason: 'no_category' };
|
|
47
|
+
// 只传有界转录而非完整 Event 对象:后者带未截断的 parts/tool.input/非字符串 output/extra,
|
|
48
|
+
// 体积远超 Jev 输入上限,会触发 max_tokens_exceeded。
|
|
49
|
+
const scores = await deps.jev.score({ summary, transcript: renderEvents(events) });
|
|
50
|
+
if (scores.taskSuccess < SCORE_TASK_SUCCESS_MIN || scores.meanScore < SCORE_MEAN_MIN) {
|
|
51
|
+
return { status: 'skip', reason: 'low_score', scores };
|
|
52
|
+
}
|
|
53
|
+
return { status: 'continue', summary, classification };
|
|
54
|
+
}
|
|
55
|
+
function validateSummary(raw) {
|
|
56
|
+
if (typeof raw !== 'object' || raw === null) {
|
|
57
|
+
throw new Error(`摘要解析失败:期望对象,得到 ${JSON.stringify(raw)}`);
|
|
58
|
+
}
|
|
59
|
+
const r = raw;
|
|
60
|
+
const outcome = r.outcome;
|
|
61
|
+
if (outcome !== 'success' && outcome !== 'failure' && outcome !== 'partial' && outcome !== 'trivial') {
|
|
62
|
+
throw new Error(`摘要 outcome 非法:${JSON.stringify(outcome)}`);
|
|
63
|
+
}
|
|
64
|
+
const steps = Array.isArray(r.keySteps) ? r.keySteps.map((s) => String(s)) : [];
|
|
65
|
+
return {
|
|
66
|
+
topic: String(r.topic ?? ''),
|
|
67
|
+
intent: String(r.intent ?? ''),
|
|
68
|
+
outcome,
|
|
69
|
+
keySteps: steps,
|
|
70
|
+
hasError: Boolean(r.hasError),
|
|
71
|
+
hasCorrection: Boolean(r.hasCorrection),
|
|
72
|
+
hasWorkflow: Boolean(r.hasWorkflow),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function validateClassification(raw) {
|
|
76
|
+
if (typeof raw !== 'object' || raw === null) {
|
|
77
|
+
throw new Error(`分类解析失败:期望对象,得到 ${JSON.stringify(raw)}`);
|
|
78
|
+
}
|
|
79
|
+
const r = raw;
|
|
80
|
+
return {
|
|
81
|
+
preferences: Boolean(r.preferences),
|
|
82
|
+
entities: Boolean(r.entities),
|
|
83
|
+
experiences: Boolean(r.experiences),
|
|
84
|
+
events: Boolean(r.events),
|
|
85
|
+
resources: Boolean(r.resources),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
export function renderEvents(events) {
|
|
89
|
+
return events
|
|
90
|
+
.map((e) => {
|
|
91
|
+
const text = e.text ?? (typeof e.result?.output === 'string' ? e.result.output : '');
|
|
92
|
+
const kind = e.kind === 'tool_result' && e.result?.status === 'error' ? 'error' : e.kind;
|
|
93
|
+
let line = `[${e.seq}] ${kind}${text ? `: ${text}` : ''}`;
|
|
94
|
+
// 溯源:raw 文件行号 + tool 入参(可能含 URL/路径),供 resources 记忆完全可溯源。
|
|
95
|
+
if (e.raw)
|
|
96
|
+
line += ` @${e.raw.path}:${e.raw.line}`;
|
|
97
|
+
if (e.tool?.input !== undefined) {
|
|
98
|
+
const input = typeof e.tool.input === 'string' ? e.tool.input : JSON.stringify(e.tool.input);
|
|
99
|
+
// 截断:Write/Edit 等工具的入参可能含整篇文件内容,不截会让转录超模型上限。
|
|
100
|
+
if (input && input !== 'undefined')
|
|
101
|
+
line += ` (input: ${truncateText(input)})`;
|
|
102
|
+
}
|
|
103
|
+
return line;
|
|
104
|
+
})
|
|
105
|
+
.join('\n');
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=analyze.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../../src/core/lessons/analyze.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** 阶段 8:审计日志。把阶段 6 的操作转成可回溯的 memory_diff 并落盘,提供人可读摘要。 */
|
|
2
|
+
import type { MemoryOp } from './merge.js';
|
|
3
|
+
import type { StorageAdapter } from './storage.js';
|
|
4
|
+
export interface AddEntry {
|
|
5
|
+
uri: string;
|
|
6
|
+
title: string;
|
|
7
|
+
evidence: number[];
|
|
8
|
+
}
|
|
9
|
+
export interface UpdateEntry {
|
|
10
|
+
uri: string;
|
|
11
|
+
title: string;
|
|
12
|
+
before: string;
|
|
13
|
+
after: string;
|
|
14
|
+
}
|
|
15
|
+
export interface AppendEntry {
|
|
16
|
+
uri: string;
|
|
17
|
+
}
|
|
18
|
+
export interface SkipEntry {
|
|
19
|
+
type: string;
|
|
20
|
+
title: string;
|
|
21
|
+
reason: string;
|
|
22
|
+
}
|
|
23
|
+
export interface MemoryDiff {
|
|
24
|
+
session_id: string;
|
|
25
|
+
project: string;
|
|
26
|
+
extracted_at: string;
|
|
27
|
+
/** 可选:跨会话聚合(recent)的输入指纹,用于滚动幂等;会话/memo 渠道无此字段。 */
|
|
28
|
+
inputHash?: string;
|
|
29
|
+
operations: {
|
|
30
|
+
adds: AddEntry[];
|
|
31
|
+
updates: UpdateEntry[];
|
|
32
|
+
appends: AppendEntry[];
|
|
33
|
+
skips: SkipEntry[];
|
|
34
|
+
};
|
|
35
|
+
summary: {
|
|
36
|
+
adds: number;
|
|
37
|
+
updates: number;
|
|
38
|
+
appends: number;
|
|
39
|
+
skips: number;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export interface AuditContext {
|
|
43
|
+
sessionId: string;
|
|
44
|
+
project: string;
|
|
45
|
+
extractedAt?: string;
|
|
46
|
+
}
|
|
47
|
+
export declare function buildMemoryDiff(ops: MemoryOp[], ctx: AuditContext): MemoryDiff;
|
|
48
|
+
export declare function writeMemoryDiff(diff: MemoryDiff, storage: StorageAdapter): Promise<void>;
|
|
49
|
+
export declare function readLatestDiff(storage: StorageAdapter): Promise<MemoryDiff | null>;
|
|
50
|
+
export declare function readSessionDiff(storage: StorageAdapter, sessionId: string): Promise<MemoryDiff | null>;
|
|
51
|
+
export declare function formatDiff(diff: MemoryDiff): string;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const LATEST_DIFF = 'memory_diff.json';
|
|
2
|
+
export function buildMemoryDiff(ops, ctx) {
|
|
3
|
+
const adds = [];
|
|
4
|
+
const updates = [];
|
|
5
|
+
const appends = [];
|
|
6
|
+
const skips = [];
|
|
7
|
+
for (const op of ops) {
|
|
8
|
+
if (op.op === 'add')
|
|
9
|
+
adds.push({ uri: op.path, title: op.title, evidence: op.evidence });
|
|
10
|
+
else if (op.op === 'patch')
|
|
11
|
+
updates.push({ uri: op.path, title: op.title, before: op.before, after: op.content });
|
|
12
|
+
else if (op.op === 'append')
|
|
13
|
+
appends.push({ uri: op.path });
|
|
14
|
+
else
|
|
15
|
+
skips.push({ type: op.type, title: op.title, reason: op.reason });
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
session_id: ctx.sessionId,
|
|
19
|
+
project: ctx.project,
|
|
20
|
+
extracted_at: ctx.extractedAt ?? new Date().toISOString(),
|
|
21
|
+
operations: { adds, updates, appends, skips },
|
|
22
|
+
summary: { adds: adds.length, updates: updates.length, appends: appends.length, skips: skips.length },
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export async function writeMemoryDiff(diff, storage) {
|
|
26
|
+
const json = JSON.stringify(diff, null, 2);
|
|
27
|
+
await storage.write(LATEST_DIFF, json);
|
|
28
|
+
await storage.write(`audit/${diff.session_id}.json`, json);
|
|
29
|
+
}
|
|
30
|
+
export async function readLatestDiff(storage) {
|
|
31
|
+
return readDiffAt(storage, LATEST_DIFF);
|
|
32
|
+
}
|
|
33
|
+
export async function readSessionDiff(storage, sessionId) {
|
|
34
|
+
return readDiffAt(storage, `audit/${sessionId}.json`);
|
|
35
|
+
}
|
|
36
|
+
async function readDiffAt(storage, path) {
|
|
37
|
+
const text = await storage.read(path);
|
|
38
|
+
if (text === null)
|
|
39
|
+
return null;
|
|
40
|
+
return JSON.parse(text);
|
|
41
|
+
}
|
|
42
|
+
export function formatDiff(diff) {
|
|
43
|
+
const s = diff.summary;
|
|
44
|
+
if (s.adds + s.updates + s.appends + s.skips === 0)
|
|
45
|
+
return '本次无新增记忆';
|
|
46
|
+
const lines = [`本次新增 ${s.adds} 条、更新 ${s.updates} 条、追加 ${s.appends} 条、跳过 ${s.skips} 条`];
|
|
47
|
+
for (const a of diff.operations.adds)
|
|
48
|
+
lines.push(` 新增: ${a.title} (${a.uri})`);
|
|
49
|
+
for (const u of diff.operations.updates)
|
|
50
|
+
lines.push(` 更新: ${u.title} (${u.uri})`);
|
|
51
|
+
for (const a of diff.operations.appends)
|
|
52
|
+
lines.push(` 追加: ${a.uri}`);
|
|
53
|
+
for (const sk of diff.operations.skips)
|
|
54
|
+
lines.push(` 跳过: ${sk.title}(${sk.reason})`);
|
|
55
|
+
return lines.join('\n');
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../../src/core/lessons/audit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** LLM 分析前的 Jev 快速压缩:只保留关键 tool 调用对,user/assistant 文字永不删。参照 fast-jev-compaction。 */
|
|
2
|
+
import type { Event } from '../events/schema.js';
|
|
3
|
+
import type { JevProvider } from './jev.js';
|
|
4
|
+
/** 对已过滤的 events 做 Jev 快速压缩;jev 不支持或判定失败时降级为确定性截断。 */
|
|
5
|
+
export declare function compactEvents(events: Event[], jev: JevProvider): Promise<Event[]>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { renderEvents } from './analyze.js';
|
|
2
|
+
import { truncateEvent, truncateEvents } from './select.js';
|
|
3
|
+
const KEEP_THRESHOLD = 0.5;
|
|
4
|
+
/** 调用对少于此数不值得调 Jev,直接走确定性兜底。 */
|
|
5
|
+
const MIN_CALLS_TO_COMPACT = 15;
|
|
6
|
+
/** 压缩后渲染文本的字符预算 ≈ 30k token(按 4 字符/token 粗估)。 */
|
|
7
|
+
const MAX_RENDER_CHARS = 30_000 * 4;
|
|
8
|
+
/** 对已过滤的 events 做 Jev 快速压缩;jev 不支持或判定失败时降级为确定性截断。 */
|
|
9
|
+
export async function compactEvents(events, jev) {
|
|
10
|
+
const calls = collectCalls(events);
|
|
11
|
+
if (calls.length <= MIN_CALLS_TO_COMPACT || !jev.keepCalls)
|
|
12
|
+
return truncateFallback(events);
|
|
13
|
+
const questions = {};
|
|
14
|
+
for (const c of calls) {
|
|
15
|
+
questions[c.id] = { instructions: '该工具调用及其结果,是否为沉淀本会话可复用经验所必需的关键信息?' };
|
|
16
|
+
}
|
|
17
|
+
let keep;
|
|
18
|
+
try {
|
|
19
|
+
keep = await jev.keepCalls(renderEvents(events), questions);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// 压缩 best-effort:判定失败退回确定性截断,不让一次 noul 请求拖垮整条提取。
|
|
23
|
+
return truncateFallback(events);
|
|
24
|
+
}
|
|
25
|
+
const drop = new Set();
|
|
26
|
+
for (const c of calls) {
|
|
27
|
+
if ((keep[c.id] ?? KEEP_THRESHOLD) < KEEP_THRESHOLD) {
|
|
28
|
+
drop.add(c.callSeq);
|
|
29
|
+
if (c.resultSeq !== undefined)
|
|
30
|
+
drop.add(c.resultSeq);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const kept = events.filter((e) => !drop.has(e.seq)).map(truncateEvent);
|
|
34
|
+
// 压缩后仍超预算则整体退回确定性截断,保证 LLM 输入有界。
|
|
35
|
+
return renderEvents(kept).length > MAX_RENDER_CHARS ? truncateFallback(events) : kept;
|
|
36
|
+
}
|
|
37
|
+
/** 确定性兜底:首尾各 40 条 + 单字段 1200 字符截断。 */
|
|
38
|
+
function truncateFallback(events) {
|
|
39
|
+
return truncateEvents(events).map(truncateEvent);
|
|
40
|
+
}
|
|
41
|
+
/** 提取 tool_call,按 call_id 关联其 tool_result;无 call_id 的 call 用 seq 作键。孤儿 result 不在返回中,天然保留。 */
|
|
42
|
+
function collectCalls(events) {
|
|
43
|
+
const resultSeqByCall = new Map();
|
|
44
|
+
for (const e of events) {
|
|
45
|
+
const callId = e.kind === 'tool_result' ? e.result?.call_id : undefined;
|
|
46
|
+
if (callId)
|
|
47
|
+
resultSeqByCall.set(callId, e.seq);
|
|
48
|
+
}
|
|
49
|
+
const calls = [];
|
|
50
|
+
for (const e of events) {
|
|
51
|
+
if (e.kind !== 'tool_call')
|
|
52
|
+
continue;
|
|
53
|
+
const callId = e.tool?.call_id;
|
|
54
|
+
calls.push({ id: callId ?? String(e.seq), callSeq: e.seq, resultSeq: callId ? resultSeqByCall.get(callId) : undefined });
|
|
55
|
+
}
|
|
56
|
+
return calls;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=compaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compaction.js","sourceRoot":"","sources":["../../../src/core/lessons/compaction.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** 模型端点配置读取:从 ~/.trove/config.json 的 llm/jev 段读取,缺配置时明确报错而非静默失败。 */
|
|
2
|
+
import type { Config } from '../../config.js';
|
|
3
|
+
export interface LlmConfig {
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
model: string;
|
|
6
|
+
apiKey: string;
|
|
7
|
+
}
|
|
8
|
+
export interface JevConfig {
|
|
9
|
+
apiKey: string;
|
|
10
|
+
model: string;
|
|
11
|
+
/** 缺省用官方端点;可指向代理/镜像 */
|
|
12
|
+
baseUrl?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function readLlmConfig(config?: Pick<Config, 'llm'>): LlmConfig;
|
|
15
|
+
export declare function readJevConfig(config?: Pick<Config, 'jev'>): JevConfig;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function readLlmConfig(config) {
|
|
2
|
+
const baseUrl = config?.llm?.baseUrl;
|
|
3
|
+
const model = config?.llm?.model;
|
|
4
|
+
const apiKey = config?.llm?.apiKey;
|
|
5
|
+
if (!baseUrl || !model || !apiKey) {
|
|
6
|
+
const missing = [
|
|
7
|
+
['llm.baseUrl', baseUrl],
|
|
8
|
+
['llm.model', model],
|
|
9
|
+
['llm.apiKey', apiKey],
|
|
10
|
+
]
|
|
11
|
+
.filter(([, v]) => !v)
|
|
12
|
+
.map(([k]) => k);
|
|
13
|
+
throw new Error(`缺少 LLM 配置:${missing.join('、')};请用 trove config set llm.baseUrl/llm.model/llm.apiKey 写入 ~/.trove/config.json`);
|
|
14
|
+
}
|
|
15
|
+
return { baseUrl, model, apiKey };
|
|
16
|
+
}
|
|
17
|
+
export function readJevConfig(config) {
|
|
18
|
+
const apiKey = config?.jev?.apiKey;
|
|
19
|
+
const model = config?.jev?.model;
|
|
20
|
+
if (!apiKey || !model) {
|
|
21
|
+
const missing = [
|
|
22
|
+
['jev.apiKey', apiKey],
|
|
23
|
+
['jev.model', model],
|
|
24
|
+
]
|
|
25
|
+
.filter(([, v]) => !v)
|
|
26
|
+
.map(([k]) => k);
|
|
27
|
+
throw new Error(`缺少 Jev 配置:${missing.join('、')};请用 trove config set jev.apiKey/jev.model 写入 ~/.trove/config.json`);
|
|
28
|
+
}
|
|
29
|
+
return { apiKey, model, ...(config?.jev?.baseUrl ? { baseUrl: config.jev.baseUrl } : {}) };
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/core/lessons/config.ts"],"names":[],"mappings":"AAgBA,MAAM,UAAU,aAAa,CAAC,MAA4B;IACxD,MAAM,OAAO,GAAG,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC;IACrC,MAAM,KAAK,GAAG,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC;IACjC,MAAM,MAAM,GAAG,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC;IACnC,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;QAClC,MAAM,OAAO,GAAG;YACd,CAAC,aAAa,EAAE,OAAO,CAAC;YACxB,CAAC,WAAW,EAAE,KAAK,CAAC;YACpB,CAAC,YAAY,EAAE,MAAM,CAAC;SACvB;aACE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;aACrB,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,aAAa,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,+EAA+E,CAAC,CAAC;IACjI,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAA4B;IACxD,MAAM,MAAM,GAAG,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC;IACnC,MAAM,KAAK,GAAG,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC;IACjC,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG;YACd,CAAC,YAAY,EAAE,MAAM,CAAC;YACtB,CAAC,WAAW,EAAE,KAAK,CAAC;SACrB;aACE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;aACrB,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,aAAa,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,mEAAmE,CAAC,CAAC;IACrH,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC7F,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** 阶段 6 前置:从 lessons 目录读回现有记忆,供合并去重。只认带合法 frontmatter 的记忆文件,忽略审计/归档/元数据。 */
|
|
2
|
+
import type { ExistingMemory } from './merge.js';
|
|
3
|
+
import type { StorageAdapter } from './storage.js';
|
|
4
|
+
export declare function loadExistingMemories(storage: StorageAdapter): Promise<ExistingMemory[]>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { parseFrontmatter, stripFrontmatter } from './frontmatter.js';
|
|
2
|
+
/** 非记忆文件(元数据/审计),不参与合并。 */
|
|
3
|
+
const NON_MEMORY = new Set(['INDEX.md', '.overview.md', 'memory_diff.json', 'README.md']);
|
|
4
|
+
export async function loadExistingMemories(storage) {
|
|
5
|
+
const files = await storage.list();
|
|
6
|
+
const out = [];
|
|
7
|
+
for (const path of files) {
|
|
8
|
+
if (path.startsWith('archive/') || path.startsWith('audit/'))
|
|
9
|
+
continue;
|
|
10
|
+
if (NON_MEMORY.has(path))
|
|
11
|
+
continue;
|
|
12
|
+
const text = await storage.read(path);
|
|
13
|
+
if (text === null)
|
|
14
|
+
continue;
|
|
15
|
+
const fm = parseFrontmatter(text);
|
|
16
|
+
if (!fm)
|
|
17
|
+
continue;
|
|
18
|
+
out.push({ type: fm.type, path, title: fm.title, content: (stripFrontmatter(text) ?? '').trim() });
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=existing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"existing.js","sourceRoot":"","sources":["../../../src/core/lessons/existing.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEtE,2BAA2B;AAC3B,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,cAAc,EAAE,kBAAkB,EAAE,WAAW,CAAC,CAAC,CAAC;AAE1F,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,OAAuB;IAChE,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;IACnC,MAAM,GAAG,GAAqB,EAAE,CAAC;IACjC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,SAAS;QACvE,IAAI,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QACnC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,IAAI,KAAK,IAAI;YAAE,SAAS;QAC5B,MAAM,EAAE,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,EAAE;YAAE,SAAS;QAClB,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACrG,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** 阶段 5:五类记忆提取器。按分类结果只跑为真的类别,产出带 evidence 的候选记忆;敏感信息与空候选在此过滤。 */
|
|
2
|
+
import type { Event } from '../events/schema.js';
|
|
3
|
+
import type { LlmProvider } from './llm.js';
|
|
4
|
+
import type { Classification, SessionSummary } from './analyze.js';
|
|
5
|
+
export interface MemoryCandidate {
|
|
6
|
+
/** 记忆类型 key,如 `experiences/debug_pattern`。 */
|
|
7
|
+
type: string;
|
|
8
|
+
title: string;
|
|
9
|
+
content: string;
|
|
10
|
+
slug?: string;
|
|
11
|
+
/** 来源事件 seq,必须落在输入事件范围内。 */
|
|
12
|
+
evidence: number[];
|
|
13
|
+
}
|
|
14
|
+
export interface ExtractDeps {
|
|
15
|
+
llm: LlmProvider;
|
|
16
|
+
/** 已有记忆的渲染文本,供提取器去重参考;缺省为空。 */
|
|
17
|
+
existing?: string;
|
|
18
|
+
}
|
|
19
|
+
/** 只跑分类为真的提取器,汇总各自候选。 */
|
|
20
|
+
export declare function extractMemories(summary: SessionSummary, classification: Classification, events: Event[], deps: ExtractDeps): Promise<MemoryCandidate[]>;
|
|
21
|
+
export declare function scrubSecrets(text: string): string;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { renderEvents } from './analyze.js';
|
|
2
|
+
import { getMemoryType } from './schema.js';
|
|
3
|
+
const TYPES_BY_CATEGORY = {
|
|
4
|
+
preferences: ['preferences/profile', 'preferences/coding_style', 'preferences/writing_style', 'preferences/dialogue_style'],
|
|
5
|
+
entities: ['entities/project', 'entities/people', 'entities/organization', 'entities/tool', 'entities/tech'],
|
|
6
|
+
experiences: ['experiences/debug_pattern', 'experiences/workflow', 'experiences/gotcha', 'experiences/correction'],
|
|
7
|
+
events: ['events/decision'],
|
|
8
|
+
resources: ['resources/doc', 'resources/repo', 'resources/link'],
|
|
9
|
+
};
|
|
10
|
+
const CATEGORY_LABEL = {
|
|
11
|
+
preferences: '偏好',
|
|
12
|
+
entities: '实体',
|
|
13
|
+
experiences: '经验',
|
|
14
|
+
events: '决策',
|
|
15
|
+
resources: '资源',
|
|
16
|
+
};
|
|
17
|
+
const CANDIDATE_SCHEMA = {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: {
|
|
20
|
+
memories: {
|
|
21
|
+
type: 'array',
|
|
22
|
+
items: {
|
|
23
|
+
type: 'object',
|
|
24
|
+
properties: {
|
|
25
|
+
type: { type: 'string' },
|
|
26
|
+
title: { type: 'string' },
|
|
27
|
+
content: { type: 'string', description: '记忆正文,用结构化 Markdown 输出:1~3 个 `##` 小节(按内容自然分段),每节 1~3 条 `-` 要点,不要整段纯文本。' },
|
|
28
|
+
slug: { type: 'string' },
|
|
29
|
+
evidence: { type: 'array', items: { type: 'integer' } },
|
|
30
|
+
},
|
|
31
|
+
required: ['type', 'title', 'content', 'evidence'],
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
required: ['memories'],
|
|
36
|
+
};
|
|
37
|
+
/** 只跑分类为真的提取器,汇总各自候选。 */
|
|
38
|
+
export async function extractMemories(summary, classification, events, deps) {
|
|
39
|
+
const out = [];
|
|
40
|
+
for (const category of Object.keys(TYPES_BY_CATEGORY)) {
|
|
41
|
+
if (!classification[category])
|
|
42
|
+
continue;
|
|
43
|
+
out.push(...(await extractCategory(category, summary, events, deps)));
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
async function extractCategory(category, summary, events, deps) {
|
|
48
|
+
const allowed = TYPES_BY_CATEGORY[category];
|
|
49
|
+
const prompt = [
|
|
50
|
+
`从会话中提取「${CATEGORY_LABEL[category]}」类记忆,type 只能是:${allowed.join('、')}。`,
|
|
51
|
+
'每条记忆的 content 用结构化 Markdown 输出:1~3 个 `##` 小节(如背景/要点/结论),每节 1~3 条 `-` 要点;不要输出一整段纯文本。',
|
|
52
|
+
...(category === 'preferences'
|
|
53
|
+
? ['各 type 提取口径:', ...allowed.map((t) => `- ${t}: ${getMemoryType(t)?.extractionPrompt ?? ''}`)]
|
|
54
|
+
: []),
|
|
55
|
+
...(category === 'resources'
|
|
56
|
+
? ['资源类记忆的 content 必须包含来源:URL(http/https)或文件路径(事件里的 @path:line),做到完全可溯源。']
|
|
57
|
+
: []),
|
|
58
|
+
`摘要:${JSON.stringify(summary)}`,
|
|
59
|
+
'事件:',
|
|
60
|
+
renderEvents(events),
|
|
61
|
+
...(deps.existing ? [`已有记忆(避免重复提取):${deps.existing}`] : []),
|
|
62
|
+
].join('\n');
|
|
63
|
+
const raw = await deps.llm.generate(prompt, CANDIDATE_SCHEMA, 'learn:extract');
|
|
64
|
+
const list = Array.isArray(raw?.memories) ? raw.memories : [];
|
|
65
|
+
const validTypes = new Set(allowed);
|
|
66
|
+
const seqs = new Set(events.map((e) => e.seq));
|
|
67
|
+
const out = [];
|
|
68
|
+
for (const item of list) {
|
|
69
|
+
const c = parseCandidate(item, validTypes, seqs);
|
|
70
|
+
if (c)
|
|
71
|
+
out.push(c);
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
function parseCandidate(raw, validTypes, seqs) {
|
|
76
|
+
if (typeof raw !== 'object' || raw === null)
|
|
77
|
+
return null;
|
|
78
|
+
const r = raw;
|
|
79
|
+
const type = String(r.type ?? '');
|
|
80
|
+
if (!validTypes.has(type))
|
|
81
|
+
return null;
|
|
82
|
+
const title = scrubSecrets(String(r.title ?? '').trim());
|
|
83
|
+
const content = scrubSecrets(String(r.content ?? '').trim());
|
|
84
|
+
if (!title || !content)
|
|
85
|
+
return null;
|
|
86
|
+
const evidence = Array.isArray(r.evidence)
|
|
87
|
+
? r.evidence.map((n) => Number(n)).filter((n) => Number.isInteger(n) && seqs.has(n))
|
|
88
|
+
: [];
|
|
89
|
+
if (evidence.length === 0)
|
|
90
|
+
return null;
|
|
91
|
+
const slug = typeof r.slug === 'string' && r.slug.trim() ? r.slug : undefined;
|
|
92
|
+
return { type, title, content, slug, evidence };
|
|
93
|
+
}
|
|
94
|
+
// shortcut: 敏感值抹除走关键词正则,覆盖常见密钥形态;命中即整段替换为 [REDACTED]。
|
|
95
|
+
const SECRET_PATTERNS = [
|
|
96
|
+
/AKIA[0-9A-Z]{16}/g,
|
|
97
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
|
|
98
|
+
/(?:password|passwd|token|secret|api[_-]?key|access[_-]?key|private[_-]?key)\s*[=:]\s*["']?[^\s"'`,;]+["']?/gi,
|
|
99
|
+
];
|
|
100
|
+
export function scrubSecrets(text) {
|
|
101
|
+
let out = text;
|
|
102
|
+
for (const re of SECRET_PATTERNS)
|
|
103
|
+
out = out.replace(re, '[REDACTED]');
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=extract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract.js","sourceRoot":"","sources":["../../../src/core/lessons/extract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** 记忆文件 frontmatter 规范:三类记忆共用的前置字段与缺省值。scope 只允许 global 或 project:<slug>。 */
|
|
2
|
+
export interface FrontmatterInput {
|
|
3
|
+
type: string;
|
|
4
|
+
title: string;
|
|
5
|
+
tags?: string[];
|
|
6
|
+
scope?: string;
|
|
7
|
+
source?: string;
|
|
8
|
+
createdAt?: string;
|
|
9
|
+
updatedAt?: string;
|
|
10
|
+
supersededBy?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function isValidScope(scope: string): boolean;
|
|
13
|
+
/** 解析后的 frontmatter 字段(与 FrontmatterInput 对称)。 */
|
|
14
|
+
export interface ParsedFrontmatter {
|
|
15
|
+
type: string;
|
|
16
|
+
title: string;
|
|
17
|
+
tags: string[];
|
|
18
|
+
scope: string;
|
|
19
|
+
source: string | null;
|
|
20
|
+
createdAt?: string;
|
|
21
|
+
updatedAt?: string;
|
|
22
|
+
supersededBy?: string;
|
|
23
|
+
}
|
|
24
|
+
/** 反向解析 renderFrontmatter 的输出;无合法 frontmatter 或缺 type 返回 null。 */
|
|
25
|
+
export declare function parseFrontmatter(text: string): ParsedFrontmatter | null;
|
|
26
|
+
/** 返回 frontmatter 之后的正文;无合法 frontmatter 返回 null。 */
|
|
27
|
+
export declare function stripFrontmatter(text: string): string | null;
|
|
28
|
+
/** 渲染成 YAML frontmatter;字符串用 JSON 序列化(JSON 字符串是合法 YAML 双引号标量)。 */
|
|
29
|
+
export declare function renderFrontmatter(input: FrontmatterInput): string;
|