@thread-memory/core 0.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.
Files changed (76) hide show
  1. package/dist/events.d.ts +18 -0
  2. package/dist/events.js +17 -0
  3. package/dist/events.js.map +1 -0
  4. package/dist/events.test.d.ts +1 -0
  5. package/dist/events.test.js +46 -0
  6. package/dist/events.test.js.map +1 -0
  7. package/dist/feedback-guard.d.ts +3 -0
  8. package/dist/feedback-guard.js +33 -0
  9. package/dist/feedback-guard.js.map +1 -0
  10. package/dist/feedback-guard.test.d.ts +1 -0
  11. package/dist/feedback-guard.test.js +67 -0
  12. package/dist/feedback-guard.test.js.map +1 -0
  13. package/dist/governor.d.ts +16 -0
  14. package/dist/governor.js +25 -0
  15. package/dist/governor.js.map +1 -0
  16. package/dist/index.d.ts +12 -0
  17. package/dist/index.js +13 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/index.test.d.ts +1 -0
  20. package/dist/index.test.js +8 -0
  21. package/dist/index.test.js.map +1 -0
  22. package/dist/isolation.test.d.ts +1 -0
  23. package/dist/isolation.test.js +102 -0
  24. package/dist/isolation.test.js.map +1 -0
  25. package/dist/light-confirm.d.ts +37 -0
  26. package/dist/light-confirm.js +149 -0
  27. package/dist/light-confirm.js.map +1 -0
  28. package/dist/light-confirm.test.d.ts +1 -0
  29. package/dist/light-confirm.test.js +154 -0
  30. package/dist/light-confirm.test.js.map +1 -0
  31. package/dist/lineage.test.d.ts +1 -0
  32. package/dist/lineage.test.js +58 -0
  33. package/dist/lineage.test.js.map +1 -0
  34. package/dist/migrate.d.ts +45 -0
  35. package/dist/migrate.js +349 -0
  36. package/dist/migrate.js.map +1 -0
  37. package/dist/migrate.test.d.ts +1 -0
  38. package/dist/migrate.test.js +111 -0
  39. package/dist/migrate.test.js.map +1 -0
  40. package/dist/paths.d.ts +7 -0
  41. package/dist/paths.js +16 -0
  42. package/dist/paths.js.map +1 -0
  43. package/dist/project-key.d.ts +3 -0
  44. package/dist/project-key.js +48 -0
  45. package/dist/project-key.js.map +1 -0
  46. package/dist/project-key.test.d.ts +1 -0
  47. package/dist/project-key.test.js +29 -0
  48. package/dist/project-key.test.js.map +1 -0
  49. package/dist/query.d.ts +49 -0
  50. package/dist/query.js +165 -0
  51. package/dist/query.js.map +1 -0
  52. package/dist/query.test.d.ts +1 -0
  53. package/dist/query.test.js +162 -0
  54. package/dist/query.test.js.map +1 -0
  55. package/dist/schema.d.ts +4 -0
  56. package/dist/schema.js +92 -0
  57. package/dist/schema.js.map +1 -0
  58. package/dist/schema.test.d.ts +1 -0
  59. package/dist/schema.test.js +80 -0
  60. package/dist/schema.test.js.map +1 -0
  61. package/dist/state.d.ts +4 -0
  62. package/dist/state.js +15 -0
  63. package/dist/state.js.map +1 -0
  64. package/dist/status-card.d.ts +10 -0
  65. package/dist/status-card.js +65 -0
  66. package/dist/status-card.js.map +1 -0
  67. package/dist/status-card.test.d.ts +1 -0
  68. package/dist/status-card.test.js +53 -0
  69. package/dist/status-card.test.js.map +1 -0
  70. package/dist/store.d.ts +170 -0
  71. package/dist/store.js +664 -0
  72. package/dist/store.js.map +1 -0
  73. package/dist/store.test.d.ts +1 -0
  74. package/dist/store.test.js +284 -0
  75. package/dist/store.test.js.map +1 -0
  76. package/package.json +29 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"light-confirm.js","sourceRoot":"","sources":["../src/light-confirm.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,154 @@
1
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { analyzeTurn, applyAnalysis, applyTurn } from "./light-confirm.js";
6
+ import { ThreadStore } from "./store.js";
7
+ import { canTransition } from "./state.js";
8
+ let dir;
9
+ let store;
10
+ beforeAll(() => {
11
+ dir = mkdtempSync(join(tmpdir(), "thread-confirm-"));
12
+ store = new ThreadStore({ eventsPath: join(dir, "events.db"), structuredPath: join(dir, "structured.db"), projectKey: "test-proj" });
13
+ });
14
+ afterAll(() => {
15
+ store.close();
16
+ rmSync(dir, { recursive: true, force: true });
17
+ });
18
+ describe("analyzeTurn", () => {
19
+ it("detects assistant decision declarations", () => {
20
+ expect(analyzeTurn({ assistant_msg: "我记下了方案 A" }).decisions).toEqual([
21
+ { action: "propose", text: "方案 A" },
22
+ ]);
23
+ expect(analyzeTurn({ assistant_msg: "我决定采用 Vite 构建" }).decisions).toEqual([
24
+ { action: "propose", text: "Vite 构建" },
25
+ ]);
26
+ });
27
+ it("detects pure acceptance short replies", () => {
28
+ for (const msg of ["嗯", "好的", "没问题", "收到", "就这样"]) {
29
+ expect(analyzeTurn({ user_msg: msg }).decisions).toEqual([{ action: "confirm" }]);
30
+ }
31
+ });
32
+ it("does not treat qualified replies as acceptance", () => {
33
+ expect(analyzeTurn({ user_msg: "好的,不过我还要想想" }).decisions).toEqual([]);
34
+ });
35
+ it("detects revocations", () => {
36
+ for (const msg of ["不要用 A 库了", "算了", "别用了", "放弃吧", "不用了"]) {
37
+ expect(analyzeTurn({ user_msg: msg }).decisions).toEqual([{ action: "revoke" }]);
38
+ }
39
+ });
40
+ it("detects supersede with replacement text", () => {
41
+ expect(analyzeTurn({ user_msg: "改用 B 库" }).decisions).toEqual([
42
+ { action: "supersede", text: "B 库" },
43
+ ]);
44
+ expect(analyzeTurn({ user_msg: "改成方案 C。" }).decisions).toEqual([
45
+ { action: "supersede", text: "方案 C" },
46
+ ]);
47
+ });
48
+ it("detects goals from imperative messages", () => {
49
+ const analysis = analyzeTurn({ user_msg: "帮我实现登录功能" });
50
+ expect(analysis.goals).toEqual([{ text: "帮我实现登录功能", action: "add" }]);
51
+ });
52
+ it("treats preference sentences as feedback, not revocation", () => {
53
+ const analysis = analyzeTurn({ user_msg: "以后不要用 jQuery" });
54
+ expect(analysis.decisions).toEqual([]);
55
+ expect(analysis.feedback).toEqual([
56
+ { text: "以后不要用 jQuery", kind: "correction" },
57
+ ]);
58
+ const pref = analyzeTurn({ user_msg: "以后优先用 pnpm" });
59
+ expect(pref.feedback).toEqual([{ text: "以后优先用 pnpm", kind: "preference" }]);
60
+ });
61
+ it("captures supersede and preference in the same message", () => {
62
+ const analysis = analyzeTurn({ user_msg: "改成用 Redis,以后都别用 Memcached" });
63
+ expect(analysis.decisions).toEqual([
64
+ { action: "supersede", text: "用 Redis,以后都别用 Memcached" },
65
+ ]);
66
+ expect(analysis.feedback).toEqual([
67
+ { text: "改成用 Redis,以后都别用 Memcached", kind: "correction" },
68
+ ]);
69
+ });
70
+ it("does not declare decisions from negated or unrelated phrases", () => {
71
+ expect(analyzeTurn({ assistant_msg: "我不确定这个方案是否可行" }).decisions).toEqual([]);
72
+ expect(analyzeTurn({ assistant_msg: "我忘记下载依赖包" }).decisions).toEqual([]);
73
+ expect(analyzeTurn({ assistant_msg: "我们还在讨论中" }).decisions).toEqual([]);
74
+ });
75
+ it("does not declare decisions from questions or technical terms", () => {
76
+ expect(analyzeTurn({ assistant_msg: "要我记下这个决策吗?" }).decisions).toEqual([]);
77
+ expect(analyzeTurn({ assistant_msg: "写入确定性摘要,degraded 可达" }).decisions).toEqual([]);
78
+ expect(analyzeTurn({ assistant_msg: "怎么确定当前是哪个项目?" }).decisions).toEqual([]);
79
+ expect(analyzeTurn({ assistant_msg: "这是一个决定性的因素" }).decisions).toEqual([]);
80
+ });
81
+ it("requires first-person subject for bare 决定/确定", () => {
82
+ expect(analyzeTurn({ assistant_msg: "产出直接决定 B/C 怎么做" }).decisions).toEqual([]);
83
+ expect(analyzeTurn({ assistant_msg: "调研结论确定下一步" }).decisions).toEqual([]);
84
+ expect(analyzeTurn({ assistant_msg: "我确定用 Vite" }).decisions).toEqual([
85
+ { action: "propose", text: "用 Vite" },
86
+ ]);
87
+ expect(analyzeTurn({ assistant_msg: "我们已经决定用 pnpm" }).decisions).toEqual([
88
+ { action: "propose", text: "用 pnpm" },
89
+ ]);
90
+ expect(analyzeTurn({ assistant_msg: "我决定采用 Vite 构建" }).decisions).toEqual([
91
+ { action: "propose", text: "Vite 构建" },
92
+ ]);
93
+ });
94
+ it("does not treat questions as goals", () => {
95
+ expect(analyzeTurn({ user_msg: "请问登录怎么做" }).goals).toEqual([]);
96
+ expect(analyzeTurn({ user_msg: "帮我查一下这个错误" }).goals).toEqual([]);
97
+ });
98
+ it("ignores unrelated messages", () => {
99
+ expect(analyzeTurn({ user_msg: "用 A 吧" })).toEqual({ goals: [], decisions: [], feedback: [] });
100
+ expect(analyzeTurn({})).toEqual({ goals: [], decisions: [], feedback: [] });
101
+ });
102
+ });
103
+ describe("applyTurn", () => {
104
+ it("propose -> confirm -> active", () => {
105
+ applyTurn(store, "s1", { assistant_msg: "我记下了用 Vite" });
106
+ const proposed = store.getLatestProposed("s1");
107
+ expect(proposed?.status).toBe("proposed");
108
+ applyTurn(store, "s1", { user_msg: "嗯" });
109
+ const active = store.getActiveDecisions("s1");
110
+ expect(active).toHaveLength(1);
111
+ expect(active[0].text).toBe("用 Vite");
112
+ expect(active[0].status).toBe("active");
113
+ });
114
+ it("supersede marks the old decision and replaces it", () => {
115
+ const applied = applyTurn(store, "s1", { user_msg: "改用 Webpack" });
116
+ expect(applied.decisions.map((d) => d.text)).toEqual(["用 Vite", "Webpack"]);
117
+ expect(applied.decisions[0].status).toBe("superseded");
118
+ expect(applied.decisions[0].superseded_by).toBe(applied.decisions[1].id);
119
+ expect(applied.decisions[1].status).toBe("active");
120
+ });
121
+ it("revoke ends the latest decision", () => {
122
+ applyTurn(store, "s1", { user_msg: "不要了" });
123
+ expect(store.getActiveDecisions("s1")).toHaveLength(0);
124
+ });
125
+ it("records goals and feedback", () => {
126
+ applyTurn(store, "s1", { user_msg: "帮我重构配置模块" });
127
+ expect(store.getActiveGoals("s1")).toHaveLength(1);
128
+ applyTurn(store, "s1", { user_msg: "以后别用回调" });
129
+ expect(store.getActiveGoals("s1")).toHaveLength(1);
130
+ });
131
+ it("applyAnalysis writes structured rows without appending events", () => {
132
+ const before = store.getRecentEvents("s2", 100).length;
133
+ const ev = store.append({
134
+ session_id: "s2",
135
+ kind: "assistant_message",
136
+ ts: "2026-08-13T00:00:00.000Z",
137
+ body: "我记下了用 pnpm 管理依赖",
138
+ });
139
+ const applied = applyAnalysis(store, "s2", { assistant_msg: "我记下了用 pnpm 管理依赖" }, { sourceEvent: ev.id });
140
+ expect(applied.decisions).toHaveLength(1);
141
+ expect(store.getRecentEvents("s2", 100).length).toBe(before + 1);
142
+ });
143
+ });
144
+ describe("decision state machine", () => {
145
+ it("allows documented transitions only", () => {
146
+ expect(canTransition("proposed", "active")).toBe(true);
147
+ expect(canTransition("proposed", "revoked")).toBe(true);
148
+ expect(canTransition("active", "superseded")).toBe(true);
149
+ expect(canTransition("active", "revoked")).toBe(true);
150
+ expect(canTransition("superseded", "active")).toBe(false);
151
+ expect(canTransition("revoked", "revoked")).toBe(false);
152
+ });
153
+ });
154
+ //# sourceMappingURL=light-confirm.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"light-confirm.test.js","sourceRoot":"","sources":["../src/light-confirm.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,58 @@
1
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { applyTurn } from "./light-confirm.js";
6
+ import { ThreadStore } from "./store.js";
7
+ let dir;
8
+ let store;
9
+ beforeAll(() => {
10
+ dir = mkdtempSync(join(tmpdir(), "thread-lineage-"));
11
+ store = new ThreadStore({ eventsPath: join(dir, "events.db"), structuredPath: join(dir, "structured.db"), projectKey: "test-proj" });
12
+ });
13
+ afterAll(() => {
14
+ store.close();
15
+ rmSync(dir, { recursive: true, force: true });
16
+ });
17
+ describe("lineage edges", () => {
18
+ it("records touches_file and uses_tool edges on event append", () => {
19
+ const ev = store.append({
20
+ session_id: "l1",
21
+ kind: "tool_call",
22
+ ts: "2026-08-13T00:00:00.000Z",
23
+ body: "edit src/auth.ts",
24
+ meta: { tool_name: "Edit", file_path: "src/auth.ts" },
25
+ });
26
+ const related = store.getRelatedEvents("l1", ev.id);
27
+ const types = related.map((r) => `${r.dst_type}:${r.edge_type}:${r.ref ?? ""}`).sort();
28
+ expect(types).toEqual(["file:touches_file:src/auth.ts", "tool:uses_tool:Edit"]);
29
+ });
30
+ it("finds events touching a file", () => {
31
+ const events = store.getEventsForFile("l1", "src/auth.ts");
32
+ expect(events.length).toBeGreaterThan(0);
33
+ });
34
+ it("links decisions to their source event (edge in structured db)", () => {
35
+ const ev = store.append({
36
+ session_id: "l1",
37
+ kind: "assistant_message",
38
+ ts: "2026-08-13T00:00:01.000Z",
39
+ body: "我记下了用 JWT 做认证",
40
+ });
41
+ const applied = applyTurn(store, "l1", { assistant_msg: "我记下了用 JWT 做认证" }, { sourceEvent: ev.id });
42
+ expect(applied.decisions).toHaveLength(1);
43
+ const decision = applied.decisions[0];
44
+ const related = store.getRelatedEdges("l1", "decision", decision.id);
45
+ expect(related.some((r) => r.edge_type === "derived_from" && r.dst_id === ev.id)).toBe(true);
46
+ });
47
+ it("records supersedes edges between decisions", () => {
48
+ const applied = applyTurn(store, "l1", { user_msg: "改用 Session" });
49
+ expect(applied.decisions.map((d) => d.status)).toEqual(["superseded", "active"]);
50
+ const superseded = applied.decisions[0];
51
+ const replacement = applied.decisions[1];
52
+ const edges = store.getRelatedEdges("l1", "decision", superseded.id);
53
+ const supersedes = edges.filter((e) => e.edge_type === "supersedes");
54
+ expect(supersedes).toHaveLength(1);
55
+ expect(supersedes[0].dst_id).toBe(replacement.id);
56
+ });
57
+ });
58
+ //# sourceMappingURL=lineage.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lineage.test.js","sourceRoot":"","sources":["../src/lineage.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ export interface MigrateOptions {
2
+ oldPath: string;
3
+ root: string;
4
+ projectKey: string;
5
+ dryRun?: boolean;
6
+ }
7
+ export interface MigrateResult {
8
+ projectKey: string;
9
+ events: {
10
+ copied: number;
11
+ backfilled: number;
12
+ };
13
+ structured: {
14
+ goals: number;
15
+ decisions: number;
16
+ feedback: number;
17
+ backfilled: number;
18
+ };
19
+ lineage: {
20
+ events: number;
21
+ structured: number;
22
+ };
23
+ integrity: {
24
+ events: boolean;
25
+ structured: boolean;
26
+ };
27
+ }
28
+ export interface TableMaxIds {
29
+ events: number;
30
+ episodes: number;
31
+ spills: number;
32
+ goals: number;
33
+ decisions: number;
34
+ feedback: number;
35
+ lineage_edges: number;
36
+ entities: number;
37
+ metrics: number;
38
+ }
39
+ export declare function snapshotTableMaxIds(oldPath: string): TableMaxIds;
40
+ export declare function migrateSplit(opts: MigrateOptions): MigrateResult;
41
+ export declare function replayIncrement(oldPath: string, root: string, projectKey: string, snapshot: TableMaxIds): {
42
+ events: number;
43
+ structured: number;
44
+ lineage: number;
45
+ };
@@ -0,0 +1,349 @@
1
+ import Database from "better-sqlite3";
2
+ import { copyFileSync, mkdirSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import { hashProjectKey } from "./project-key.js";
5
+ import { ThreadStore } from "./store.js";
6
+ // decision_entities 为复合主键表(无 id 列),不走按 id 快照/增量,全量 INSERT OR IGNORE
7
+ const TABLES = [
8
+ "events",
9
+ "episodes",
10
+ "spills",
11
+ "goals",
12
+ "decisions",
13
+ "feedback",
14
+ "lineage_edges",
15
+ "entities",
16
+ "metrics",
17
+ ];
18
+ function maxId(db, table) {
19
+ const row = db.prepare(`SELECT COALESCE(MAX(id), 0) AS m FROM ${table}`).get();
20
+ return row.m;
21
+ }
22
+ export function snapshotTableMaxIds(oldPath) {
23
+ const old = openOld(oldPath);
24
+ try {
25
+ return Object.fromEntries(TABLES.map((t) => [t, maxId(old, t)]));
26
+ }
27
+ finally {
28
+ old.close();
29
+ }
30
+ }
31
+ // 事件行 project_key 回填:NULL → 项目键;结构化行:scope='project' NULL → 沿 source_event 追溯,失败退化项目键;global → NULL
32
+ export function migrateSplit(opts) {
33
+ const old = openOld(opts.oldPath);
34
+ try {
35
+ const resolvedKey = resolveProjectKey(old, opts.projectKey);
36
+ if (opts.dryRun) {
37
+ const stats = dryRunStats(old);
38
+ old.close();
39
+ return {
40
+ projectKey: resolvedKey,
41
+ events: { copied: stats.events, backfilled: stats.eventsBackfilled },
42
+ structured: {
43
+ goals: stats.goals,
44
+ decisions: stats.decisions,
45
+ feedback: stats.feedback,
46
+ backfilled: stats.structuredBackfilled,
47
+ },
48
+ lineage: { events: stats.lineageEvents, structured: stats.lineageStructured },
49
+ integrity: { events: false, structured: false },
50
+ };
51
+ }
52
+ const eventsDbPath = join(opts.root, "projects", hashProjectKey(resolvedKey), "events.db");
53
+ const structuredDbPath = join(opts.root, "structured.db");
54
+ mkdirSync(dirname(eventsDbPath), { recursive: true });
55
+ mkdirSync(dirname(structuredDbPath), { recursive: true });
56
+ const store = new ThreadStore({ eventsPath: eventsDbPath, structuredPath: structuredDbPath, projectKey: resolvedKey });
57
+ const events = copyEvents(old, store.eventsDb, resolvedKey);
58
+ copyRaw(old, store.eventsDb, "episodes");
59
+ copyRaw(old, store.eventsDb, "spills");
60
+ const structured = copyStructured(old, store.structuredDb, old, resolvedKey);
61
+ copyRaw(old, store.structuredDb, "entities");
62
+ copyJoinTable(old, store.structuredDb, "decision_entities");
63
+ copyRaw(old, store.structuredDb, "metrics");
64
+ const lineage = copyLineage(old, store);
65
+ const integrity = {
66
+ events: checkIntegrity(store.eventsDb),
67
+ structured: checkIntegrity(store.structuredDb),
68
+ };
69
+ store.close();
70
+ return {
71
+ projectKey: resolvedKey,
72
+ events,
73
+ structured,
74
+ lineage,
75
+ integrity,
76
+ };
77
+ }
78
+ finally {
79
+ if (!opts.dryRun) {
80
+ try {
81
+ copyFileSync(opts.oldPath, `${opts.oldPath}.bak-b4`);
82
+ }
83
+ catch {
84
+ // 备份失败不阻塞迁移,旧库原样保留
85
+ }
86
+ }
87
+ old.close();
88
+ }
89
+ }
90
+ // 增量重放:快照后旧库新增行按原 id 复制到新库;无 origin 行按 (session_id,kind,ts,body) 兜底去重;写入带 SQLITE_BUSY 重试
91
+ export function replayIncrement(oldPath, root, projectKey, snapshot) {
92
+ const old = openOld(oldPath);
93
+ try {
94
+ const eventsDbPath = join(root, "projects", hashProjectKey(projectKey), "events.db");
95
+ const structuredDbPath = join(root, "structured.db");
96
+ const store = new ThreadStore({ eventsPath: eventsDbPath, structuredPath: structuredDbPath, projectKey });
97
+ const ev = copyEventsRange(old, store.eventsDb, projectKey, snapshot.events);
98
+ copyRawRange(old, store.eventsDb, "episodes", snapshot.episodes);
99
+ copyRawRange(old, store.eventsDb, "spills", snapshot.spills);
100
+ const st = copyStructuredRange(old, store.structuredDb, old, projectKey, snapshot);
101
+ copyRawRange(old, store.structuredDb, "entities", snapshot.entities);
102
+ copyJoinTable(old, store.structuredDb, "decision_entities");
103
+ copyRawRange(old, store.structuredDb, "metrics", snapshot.metrics);
104
+ const lineage = copyLineageRange(old, store, snapshot.lineage_edges);
105
+ store.close();
106
+ return { events: ev, structured: st, lineage };
107
+ }
108
+ finally {
109
+ old.close();
110
+ }
111
+ }
112
+ function openOld(path) {
113
+ return new Database(path, { readonly: true });
114
+ }
115
+ function resolveProjectKey(old, fallback) {
116
+ const row = old
117
+ .prepare(`SELECT project_key, COUNT(*) AS c FROM events WHERE project_key IS NOT NULL GROUP BY project_key ORDER BY c DESC LIMIT 1`)
118
+ .get();
119
+ return row?.project_key ?? fallback;
120
+ }
121
+ function dryRunStats(old) {
122
+ const events = count(old, "events");
123
+ const eventsBackfilled = countWhere(old, "events", "project_key IS NULL");
124
+ const goals = count(old, "goals");
125
+ const decisions = count(old, "decisions");
126
+ const feedback = count(old, "feedback");
127
+ const structuredBackfilled = countWhere(old, "feedback", "project_key IS NULL AND scope = 'project'")
128
+ + countWhere(old, "goals", "project_key IS NULL AND scope = 'project'")
129
+ + countWhere(old, "decisions", "project_key IS NULL AND scope = 'project'");
130
+ const lineage = allLineage(old);
131
+ const lineageEvents = lineage.filter((e) => isEventDomain(e.src_type, e.dst_type)).length;
132
+ const lineageStructured = lineage.length - lineageEvents;
133
+ return { events, eventsBackfilled, goals, decisions, feedback, structuredBackfilled, lineageEvents, lineageStructured };
134
+ }
135
+ function count(db, table) {
136
+ return db.prepare(`SELECT COUNT(*) AS c FROM ${table}`).get().c;
137
+ }
138
+ function countWhere(db, table, where) {
139
+ return db.prepare(`SELECT COUNT(*) AS c FROM ${table} WHERE ${where}`).get().c;
140
+ }
141
+ function isEventDomain(srcType, dstType) {
142
+ const s = srcType === "event" || srcType === "file" || srcType === "tool";
143
+ const d = dstType === "event" || dstType === "file" || dstType === "tool";
144
+ return s && d;
145
+ }
146
+ function allLineage(old) {
147
+ return old.prepare(`SELECT id, session_id, src_type, src_id, dst_type, dst_id, ref, edge_type, confidence, ts FROM lineage_edges ORDER BY id`).all();
148
+ }
149
+ const EVENT_COLS = ["id", "session_id", "kind", "ts", "seq", "body", "meta", "truncated", "project_key", "scope", "origin", "spilled", "isolation"];
150
+ function eventSourceCols(old) {
151
+ // 旧库可能无 isolation 列(v2 及以前)——动态裁剪,缺失列迁移后取 DEFAULT 0(共享)
152
+ const have = columns(old, "events");
153
+ return EVENT_COLS.filter((c) => have.includes(c));
154
+ }
155
+ function copyEvents(old, target, projectKey) {
156
+ const srcCols = eventSourceCols(old);
157
+ const dstCols = srcCols.includes("isolation") ? srcCols : [...srcCols, "isolation"];
158
+ const rows = old.prepare(`SELECT ${srcCols.join(", ")} FROM events ORDER BY id`).all();
159
+ const insert = target.prepare(`INSERT INTO events (${dstCols.join(", ")}) VALUES (${dstCols.map(() => "?").join(", ")})`);
160
+ let backfilled = 0;
161
+ const tx = target.transaction(() => {
162
+ for (const row of rows) {
163
+ if (row.project_key == null) {
164
+ row.project_key = projectKey;
165
+ backfilled++;
166
+ }
167
+ insert.run(...dstCols.map((c) => (c === "isolation" && row[c] == null ? 0 : row[c])));
168
+ }
169
+ });
170
+ tx();
171
+ return { copied: rows.length, backfilled };
172
+ }
173
+ function copyEventsRange(old, target, projectKey, afterId) {
174
+ const srcCols = eventSourceCols(old);
175
+ const dstCols = srcCols.includes("isolation") ? srcCols : [...srcCols, "isolation"];
176
+ const rows = old
177
+ .prepare(`SELECT ${srcCols.join(", ")} FROM events WHERE id > ? ORDER BY id`)
178
+ .all(afterId);
179
+ const insert = target.prepare(`INSERT INTO events (${dstCols.join(", ")}) VALUES (${dstCols.map(() => "?").join(", ")})`);
180
+ const tx = target.transaction(() => {
181
+ for (const row of rows) {
182
+ if (row.project_key == null) {
183
+ row.project_key = projectKey;
184
+ }
185
+ insert.run(...dstCols.map((c) => (c === "isolation" && row[c] == null ? 0 : row[c])));
186
+ }
187
+ });
188
+ tx();
189
+ return rows.length;
190
+ }
191
+ function copyRaw(old, target, table) {
192
+ const cols = columns(old, table);
193
+ const rows = old.prepare(`SELECT ${cols.join(", ")} FROM ${table} ORDER BY id`).all();
194
+ const insert = target.prepare(`INSERT INTO ${table} (${cols.join(", ")}) VALUES (${cols.map(() => "?").join(", ")})`);
195
+ const tx = target.transaction(() => {
196
+ for (const row of rows) {
197
+ insert.run(...cols.map((c) => row[c]));
198
+ }
199
+ });
200
+ tx();
201
+ return rows.length;
202
+ }
203
+ function copyRawRange(old, target, table, afterId) {
204
+ const cols = columns(old, table);
205
+ const rows = old
206
+ .prepare(`SELECT ${cols.join(", ")} FROM ${table} WHERE id > ? ORDER BY id`)
207
+ .all(afterId);
208
+ const insert = target.prepare(`INSERT INTO ${table} (${cols.join(", ")}) VALUES (${cols.map(() => "?").join(", ")})`);
209
+ const tx = target.transaction(() => {
210
+ for (const row of rows) {
211
+ insert.run(...cols.map((c) => row[c]));
212
+ }
213
+ });
214
+ tx();
215
+ return rows.length;
216
+ }
217
+ function copyJoinTable(old, target, table) {
218
+ const cols = columns(old, table);
219
+ const rows = old.prepare(`SELECT ${cols.join(", ")} FROM ${table}`).all();
220
+ const insert = target.prepare(`INSERT OR IGNORE INTO ${table} (${cols.join(", ")}) VALUES (${cols.map(() => "?").join(", ")})`);
221
+ const tx = target.transaction(() => {
222
+ for (const row of rows) {
223
+ insert.run(...cols.map((c) => row[c]));
224
+ }
225
+ });
226
+ tx();
227
+ return rows.length;
228
+ }
229
+ function copyStructured(old, target, eventSource, projectKey) {
230
+ const goals = copyStructuredTable(old, target, "goals", eventSource, projectKey);
231
+ const decisions = copyStructuredTable(old, target, "decisions", eventSource, projectKey);
232
+ const feedback = copyStructuredTable(old, target, "feedback", eventSource, projectKey);
233
+ const backfilled = goals.backfilled + decisions.backfilled + feedback.backfilled;
234
+ return {
235
+ goals: goals.copied,
236
+ decisions: decisions.copied,
237
+ feedback: feedback.copied,
238
+ backfilled,
239
+ };
240
+ }
241
+ function copyStructuredRange(old, target, eventSource, projectKey, snapshot) {
242
+ const g = copyStructuredTableRange(old, target, "goals", eventSource, projectKey, snapshot.goals);
243
+ const d = copyStructuredTableRange(old, target, "decisions", eventSource, projectKey, snapshot.decisions);
244
+ const f = copyStructuredTableRange(old, target, "feedback", eventSource, projectKey, snapshot.feedback);
245
+ return g + d + f;
246
+ }
247
+ function copyStructuredTable(old, target, table, eventSource, projectKey) {
248
+ const cols = columns(old, table);
249
+ const rows = old.prepare(`SELECT ${cols.join(", ")} FROM ${table} ORDER BY id`).all();
250
+ const insert = target.prepare(`INSERT INTO ${table} (${cols.join(", ")}) VALUES (${cols.map(() => "?").join(", ")})`);
251
+ let backfilled = 0;
252
+ const tx = target.transaction(() => {
253
+ for (const row of rows) {
254
+ if (row.project_key == null && row.scope === "project") {
255
+ row.project_key = traceEventKey(eventSource, row.source_event, projectKey);
256
+ backfilled++;
257
+ }
258
+ insert.run(...cols.map((c) => row[c]));
259
+ }
260
+ });
261
+ tx();
262
+ return { copied: rows.length, backfilled };
263
+ }
264
+ function copyStructuredTableRange(old, target, table, eventSource, projectKey, afterId) {
265
+ const cols = columns(old, table);
266
+ const rows = old
267
+ .prepare(`SELECT ${cols.join(", ")} FROM ${table} WHERE id > ? ORDER BY id`)
268
+ .all(afterId);
269
+ const insert = target.prepare(`INSERT INTO ${table} (${cols.join(", ")}) VALUES (${cols.map(() => "?").join(", ")})`);
270
+ const tx = target.transaction(() => {
271
+ for (const row of rows) {
272
+ if (row.project_key == null && row.scope === "project") {
273
+ row.project_key = traceEventKey(eventSource, row.source_event, projectKey);
274
+ }
275
+ insert.run(...cols.map((c) => row[c]));
276
+ }
277
+ });
278
+ tx();
279
+ return rows.length;
280
+ }
281
+ function traceEventKey(db, sourceEvent, fallback) {
282
+ if (typeof sourceEvent === "number") {
283
+ const row = db.prepare(`SELECT project_key FROM events WHERE id = ?`).get(sourceEvent);
284
+ if (row?.project_key) {
285
+ return row.project_key;
286
+ }
287
+ }
288
+ return fallback;
289
+ }
290
+ function copyLineage(old, store) {
291
+ const rows = allLineage(old);
292
+ const insert = (db) => db.prepare(`INSERT INTO lineage_edges (id, session_id, src_type, src_id, dst_type, dst_id, ref, edge_type, confidence, ts)
293
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
294
+ const evInsert = insert(store.eventsDb);
295
+ const stInsert = insert(store.structuredDb);
296
+ let events = 0;
297
+ let structured = 0;
298
+ const tx = store.eventsDb.transaction(() => {
299
+ store.structuredDb.transaction(() => {
300
+ for (const r of rows) {
301
+ const params = [r.id, r.session_id, r.src_type, r.src_id, r.dst_type, r.dst_id, r.ref, r.edge_type, r.confidence, r.ts];
302
+ if (isEventDomain(r.src_type, r.dst_type)) {
303
+ evInsert.run(...params);
304
+ events++;
305
+ }
306
+ else {
307
+ stInsert.run(...params);
308
+ structured++;
309
+ }
310
+ }
311
+ })();
312
+ });
313
+ tx();
314
+ return { events, structured };
315
+ }
316
+ function copyLineageRange(old, store, afterId) {
317
+ const rows = old
318
+ .prepare(`SELECT id, session_id, src_type, src_id, dst_type, dst_id, ref, edge_type, confidence, ts FROM lineage_edges WHERE id > ? ORDER BY id`)
319
+ .all(afterId);
320
+ const insert = (db) => db.prepare(`INSERT INTO lineage_edges (id, session_id, src_type, src_id, dst_type, dst_id, ref, edge_type, confidence, ts)
321
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
322
+ const evInsert = insert(store.eventsDb);
323
+ const stInsert = insert(store.structuredDb);
324
+ let copied = 0;
325
+ const tx = store.eventsDb.transaction(() => {
326
+ store.structuredDb.transaction(() => {
327
+ for (const r of rows) {
328
+ const params = [r.id, r.session_id, r.src_type, r.src_id, r.dst_type, r.dst_id, r.ref, r.edge_type, r.confidence, r.ts];
329
+ if (isEventDomain(r.src_type, r.dst_type)) {
330
+ evInsert.run(...params);
331
+ }
332
+ else {
333
+ stInsert.run(...params);
334
+ }
335
+ copied++;
336
+ }
337
+ })();
338
+ });
339
+ tx();
340
+ return copied;
341
+ }
342
+ function columns(db, table) {
343
+ return db.prepare(`PRAGMA table_info(${table})`).all().map((c) => c.name);
344
+ }
345
+ function checkIntegrity(db) {
346
+ const row = db.pragma("integrity_check");
347
+ return row.length === 1 && row[0].integrity_check === "ok";
348
+ }
349
+ //# sourceMappingURL=migrate.js.map