@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,162 @@
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 { queryMemory, queryEvents, queryStructured } from "./query.js";
6
+ import { ThreadStore } from "./store.js";
7
+ let dir;
8
+ let store;
9
+ beforeAll(() => {
10
+ dir = mkdtempSync(join(tmpdir(), "thread-query-"));
11
+ store = new ThreadStore({ eventsPath: join(dir, "events.db"), structuredPath: join(dir, "structured.db"), projectKey: "test-proj" });
12
+ store.append({
13
+ session_id: "q1",
14
+ kind: "user_message",
15
+ ts: "2026-08-13T00:00:00.000Z",
16
+ body: "帮我实现登录功能,密码用 bcrypt 加密",
17
+ });
18
+ store.append({
19
+ session_id: "q1",
20
+ kind: "tool_call",
21
+ ts: "2026-08-13T00:00:01.000Z",
22
+ body: "创建 auth 模块,使用 bcrypt 加密密码",
23
+ meta: { tool_name: "Write", file_path: "src/auth.ts" },
24
+ });
25
+ store.append({
26
+ session_id: "q1",
27
+ kind: "tool_result",
28
+ ts: "2026-08-13T00:00:02.000Z",
29
+ body: "auth 模块已创建,含 bcrypt 哈希函数",
30
+ });
31
+ store.append({
32
+ session_id: "q2",
33
+ kind: "user_message",
34
+ ts: "2026-08-13T00:00:03.000Z",
35
+ body: "完全无关的内容:今天天气不错",
36
+ });
37
+ });
38
+ afterAll(() => {
39
+ store.close();
40
+ rmSync(dir, { recursive: true, force: true });
41
+ });
42
+ describe("queryMemory", () => {
43
+ it("returns found with BM25 hits", () => {
44
+ const result = queryMemory(store, "bcrypt 加密", { sessionId: "q1" });
45
+ expect(result.status).toBe("found");
46
+ expect(result.results.length).toBeGreaterThan(0);
47
+ expect(result.results[0].kind).toBe("user");
48
+ });
49
+ it("returns not-found with actionable note", () => {
50
+ const result = queryMemory(store, "不存在的概念量子咖啡机", { sessionId: "q1" });
51
+ expect(result.status).toBe("not-found");
52
+ expect(result.results).toHaveLength(0);
53
+ expect(result.note).toContain("未检索到");
54
+ expect(result.note).toContain("建议");
55
+ });
56
+ it("filters by session and hides isolated content from others", () => {
57
+ // 未隔离内容跨会话可见(跨会话继承检索语义)
58
+ const visible = queryMemory(store, "bcrypt", { sessionId: "q2" });
59
+ expect(visible.status).toBe("found");
60
+ // 隔离内容对其他会话不可见(全链路过滤)
61
+ store.append({
62
+ session_id: "q-iso",
63
+ kind: "user_message",
64
+ ts: "2026-08-13T00:00:04.000Z",
65
+ body: "秘密计划代号红隼",
66
+ }, { isolation: true });
67
+ const hidden = queryMemory(store, "红隼", { sessionId: "q2" });
68
+ expect(hidden.status).toBe("not-found");
69
+ // 隔离会话自己可见
70
+ const self = queryMemory(store, "红隼", { sessionId: "q-iso" });
71
+ expect(self.status).toBe("found");
72
+ });
73
+ it("respects token budget", () => {
74
+ const small = queryMemory(store, "登录 模块 bcrypt auth", {
75
+ sessionId: "q1",
76
+ tokenBudget: 10,
77
+ });
78
+ const large = queryMemory(store, "登录 模块 bcrypt auth", {
79
+ sessionId: "q1",
80
+ tokenBudget: 4000,
81
+ });
82
+ const smallChars = small.results.reduce((n, r) => n + r.body.length, 0);
83
+ expect(smallChars).toBeLessThanOrEqual(20 * 2 + 64);
84
+ expect(large.results.length).toBeGreaterThanOrEqual(small.results.length);
85
+ });
86
+ it("falls through to not-found when no episode summary exists", () => {
87
+ const result = queryMemory(store, "summary-marker", { sessionId: "q1" });
88
+ expect(result.status).toBe("not-found");
89
+ });
90
+ it("degrades to episode summary on partial-token mismatch", () => {
91
+ store.append({
92
+ session_id: "q1",
93
+ kind: "user_message",
94
+ ts: "2026-08-13T00:00:10.000Z",
95
+ body: "继续开发",
96
+ });
97
+ const result = queryMemory(store, "bcryp", { sessionId: "q1" });
98
+ expect(result.status).toBe("degraded");
99
+ expect(result.results[0].kind).toBe("summary");
100
+ expect(result.results[0].body).toContain("bcrypt");
101
+ });
102
+ it("queryEvents filters by kind and time range", () => {
103
+ const result = queryEvents(store, {
104
+ sessionId: "q1",
105
+ kind: "user_message",
106
+ timeRange: { since: "2026-08-13T00:00:00.000Z", until: "2026-08-13T00:00:11.000Z" },
107
+ order: "asc",
108
+ });
109
+ expect(result.status).toBe("found");
110
+ expect(result.results.length).toBeGreaterThan(0);
111
+ expect(result.results.every((r) => r.kind === "user")).toBe(true);
112
+ const first = result.results[0];
113
+ expect(first.ts).toBe("2026-08-13T00:00:00.000Z");
114
+ });
115
+ it("queryEvents counts events", () => {
116
+ const result = queryEvents(store, {
117
+ sessionId: "q1",
118
+ kind: ["tool_call", "tool_result"],
119
+ count: true,
120
+ });
121
+ expect(result.status).toBe("found");
122
+ expect(result.count).toBe(2);
123
+ });
124
+ it("queryEvents returns not-found on empty range", () => {
125
+ const result = queryEvents(store, {
126
+ sessionId: "q1",
127
+ timeRange: { since: "2030-01-01T00:00:00.000Z" },
128
+ });
129
+ expect(result.status).toBe("not-found");
130
+ expect(result.results).toHaveLength(0);
131
+ });
132
+ it("queryStructured returns table rows with ids (② 后置入口)", () => {
133
+ store.addGoal("q1", "登录用 JWT");
134
+ const fb = store.addFeedback("q1", "不要用 pwsh", "correction");
135
+ const goals = queryStructured(store, { sessionId: "q1", table: "goals" });
136
+ expect(goals.status).toBe("found");
137
+ expect(goals.results.some((r) => r.body === "登录用 JWT")).toBe(true);
138
+ const fbRes = queryStructured(store, { sessionId: "q1", table: "feedback" });
139
+ expect(fbRes.results[0].segment_id).toBe(fb.id);
140
+ expect(fbRes.results[0].body).toBe("不要用 pwsh");
141
+ });
142
+ it("queryStructured applies isolation filtering (隔离语义与事件一致)", () => {
143
+ store.addFeedback("q-iso", "秘密偏好", "preference", { isolation: true });
144
+ const other = queryStructured(store, { sessionId: "q1", table: "feedback" });
145
+ expect(other.results.some((r) => r.body === "秘密偏好")).toBe(false);
146
+ const self = queryStructured(store, { sessionId: "q-iso", table: "feedback" });
147
+ expect(self.results.some((r) => r.body === "秘密偏好")).toBe(true);
148
+ });
149
+ it("queryStructured supports count", () => {
150
+ const res = queryStructured(store, { sessionId: "q1", table: "goals", count: true });
151
+ expect(res.count).toBeGreaterThanOrEqual(1);
152
+ });
153
+ it("queryEvents exposes isolation flag on event rows (⑨)", () => {
154
+ const result = queryEvents(store, { sessionId: "q-iso", order: "desc" });
155
+ expect(result.status).toBe("found");
156
+ const hit = result.results.find((r) => r.body.includes("红隼"));
157
+ expect(hit?.isolation).toBe(1);
158
+ const shared = queryEvents(store, { sessionId: "q1", order: "desc" });
159
+ expect(shared.results.every((r) => r.isolation === 0)).toBe(true);
160
+ });
161
+ });
162
+ //# sourceMappingURL=query.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.test.js","sourceRoot":"","sources":["../src/query.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type Database from "better-sqlite3";
2
+ export declare const SCHEMA_VERSION = 3;
3
+ export type SchemaKind = "events" | "structured";
4
+ export declare function ensureSchema(db: Database.Database, kind: SchemaKind): void;
package/dist/schema.js ADDED
@@ -0,0 +1,92 @@
1
+ export const SCHEMA_VERSION = 3;
2
+ // 双库(B④):事件库(events/episodes/spills/lineage_edges 事件域)与结构化库
3
+ // (goals/decisions/feedback/entities/decision_entities/metrics/lineage_edges 结构化域)
4
+ // 各库独立 schema_version 表、独立迁移链,共用 SCHEMA_VERSION。
5
+ // 迁移只负责:旧库按需补 v2 列/表,幂等(列存在则跳过),最后写 schema_version。
6
+ export function ensureSchema(db, kind) {
7
+ db.exec(`CREATE TABLE IF NOT EXISTS schema_version (
8
+ version INTEGER NOT NULL,
9
+ applied_at TEXT NOT NULL
10
+ );`);
11
+ const row = db.prepare(`SELECT COALESCE(MAX(version), 0) AS v FROM schema_version`).get();
12
+ if (row.v >= SCHEMA_VERSION)
13
+ return;
14
+ db.transaction(() => {
15
+ const cols = (t) => new Set(db.prepare(`PRAGMA table_info(${t})`).all().map((c) => c.name));
16
+ if (kind === "events") {
17
+ const eventsCols = cols("events");
18
+ if (!eventsCols.has("project_key"))
19
+ db.exec(`ALTER TABLE events ADD COLUMN project_key TEXT`);
20
+ if (!eventsCols.has("scope"))
21
+ db.exec(`ALTER TABLE events ADD COLUMN scope TEXT NOT NULL DEFAULT 'project'`);
22
+ if (!eventsCols.has("origin"))
23
+ db.exec(`ALTER TABLE events ADD COLUMN origin TEXT`);
24
+ if (!eventsCols.has("spilled"))
25
+ db.exec(`ALTER TABLE events ADD COLUMN spilled INTEGER NOT NULL DEFAULT 0`);
26
+ if (!eventsCols.has("isolation"))
27
+ db.exec(`ALTER TABLE events ADD COLUMN isolation INTEGER NOT NULL DEFAULT 0`);
28
+ db.exec(`CREATE INDEX IF NOT EXISTS idx_events_project ON events(project_key, ts)`);
29
+ db.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_events_origin ON events(origin) WHERE origin IS NOT NULL`);
30
+ db.exec(`CREATE TABLE IF NOT EXISTS spills (
31
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
32
+ event_id INTEGER NOT NULL REFERENCES events(id),
33
+ ref TEXT NOT NULL,
34
+ blob TEXT,
35
+ sha256 TEXT NOT NULL,
36
+ created_at TEXT NOT NULL
37
+ );`);
38
+ }
39
+ else {
40
+ for (const t of ["goals", "decisions"]) {
41
+ const c = cols(t);
42
+ if (!c.has("project_key"))
43
+ db.exec(`ALTER TABLE ${t} ADD COLUMN project_key TEXT`);
44
+ if (!c.has("scope"))
45
+ db.exec(`ALTER TABLE ${t} ADD COLUMN scope TEXT NOT NULL DEFAULT 'project'`);
46
+ if (!c.has("origin"))
47
+ db.exec(`ALTER TABLE ${t} ADD COLUMN origin TEXT`);
48
+ if (!c.has("isolation"))
49
+ db.exec(`ALTER TABLE ${t} ADD COLUMN isolation INTEGER NOT NULL DEFAULT 0`);
50
+ db.exec(`CREATE INDEX IF NOT EXISTS idx_${t}_project ON ${t}(project_key, status)`);
51
+ }
52
+ {
53
+ const c = cols("feedback");
54
+ if (!c.has("project_key"))
55
+ db.exec(`ALTER TABLE feedback ADD COLUMN project_key TEXT`);
56
+ if (!c.has("scope"))
57
+ db.exec(`ALTER TABLE feedback ADD COLUMN scope TEXT NOT NULL DEFAULT 'project'`);
58
+ if (!c.has("origin"))
59
+ db.exec(`ALTER TABLE feedback ADD COLUMN origin TEXT`);
60
+ if (!c.has("isolation"))
61
+ db.exec(`ALTER TABLE feedback ADD COLUMN isolation INTEGER NOT NULL DEFAULT 0`);
62
+ db.exec(`CREATE INDEX IF NOT EXISTS idx_feedback_project ON feedback(project_key, kind)`);
63
+ }
64
+ db.exec(`CREATE TABLE IF NOT EXISTS session_isolation (
65
+ session_id TEXT PRIMARY KEY,
66
+ isolated INTEGER NOT NULL DEFAULT 0,
67
+ updated_at TEXT NOT NULL
68
+ );`);
69
+ db.exec(`CREATE TABLE IF NOT EXISTS entities (
70
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
71
+ name TEXT NOT NULL UNIQUE,
72
+ kind TEXT NOT NULL
73
+ );`);
74
+ db.exec(`CREATE TABLE IF NOT EXISTS decision_entities (
75
+ decision_id INTEGER NOT NULL REFERENCES decisions(id),
76
+ entity_id INTEGER NOT NULL REFERENCES entities(id),
77
+ edge TEXT NOT NULL DEFAULT 'references',
78
+ ts TEXT NOT NULL,
79
+ PRIMARY KEY (decision_id, entity_id)
80
+ );`);
81
+ db.exec(`CREATE TABLE IF NOT EXISTS metrics (
82
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
83
+ event_id INTEGER,
84
+ name TEXT NOT NULL,
85
+ value REAL NOT NULL,
86
+ ts TEXT NOT NULL
87
+ );`);
88
+ }
89
+ db.prepare(`INSERT INTO schema_version (version, applied_at) VALUES (?, ?)`).run(SCHEMA_VERSION, new Date().toISOString());
90
+ })();
91
+ }
92
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,80 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import Database from "better-sqlite3";
3
+ import { mkdtempSync, rmSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { ensureSchema } from "./schema.js";
7
+ describe("schema v2 migration (B④ 双库)", () => {
8
+ it("migrates a v1 events db (adds columns/tables)", () => {
9
+ const dir = mkdtempSync(join(tmpdir(), "thread-migrate-events-"));
10
+ const dbPath = join(dir, "events.db");
11
+ const db = new Database(dbPath);
12
+ db.exec(`CREATE TABLE events (
13
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
14
+ session_id TEXT NOT NULL,
15
+ kind TEXT NOT NULL,
16
+ ts TEXT NOT NULL,
17
+ seq INTEGER NOT NULL,
18
+ body TEXT NOT NULL,
19
+ meta TEXT,
20
+ truncated INTEGER NOT NULL DEFAULT 0
21
+ );
22
+ INSERT INTO events (session_id, kind, ts, seq, body) VALUES ('s-old', 'user_message', '2026-08-13T00:00:00.000Z', 1, 'legacy data');`);
23
+ ensureSchema(db, "events");
24
+ const count = db.prepare("SELECT COUNT(*) AS c FROM events").get();
25
+ expect(count.c).toBe(1);
26
+ const cols = new Set(db.prepare("PRAGMA table_info(events)").all().map((c) => c.name));
27
+ for (const col of ["project_key", "scope", "origin", "spilled", "isolation"])
28
+ expect(cols.has(col)).toBe(true);
29
+ for (const t of ["spills", "schema_version"]) {
30
+ expect(db.prepare(`SELECT 1 FROM sqlite_master WHERE type='table' AND name=?`).get(t), `table ${t}`).toBeTruthy();
31
+ }
32
+ expect(db.prepare("SELECT MAX(version) AS v FROM schema_version").get().v).toBe(3);
33
+ db.close();
34
+ rmSync(dir, { recursive: true, force: true });
35
+ });
36
+ it("migrates a v1 structured db (adds columns/tables)", () => {
37
+ const dir = mkdtempSync(join(tmpdir(), "thread-migrate-structured-"));
38
+ const dbPath = join(dir, "structured.db");
39
+ const db = new Database(dbPath);
40
+ db.exec(`CREATE TABLE goals (
41
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
42
+ session_id TEXT NOT NULL,
43
+ text TEXT NOT NULL,
44
+ status TEXT NOT NULL DEFAULT 'active',
45
+ source_event INTEGER,
46
+ created_at TEXT NOT NULL
47
+ );
48
+ CREATE TABLE decisions (
49
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
50
+ session_id TEXT NOT NULL,
51
+ text TEXT NOT NULL,
52
+ status TEXT NOT NULL DEFAULT 'proposed',
53
+ superseded_by INTEGER,
54
+ source_event INTEGER,
55
+ created_at TEXT NOT NULL,
56
+ updated_at TEXT NOT NULL
57
+ );
58
+ CREATE TABLE feedback (
59
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
60
+ session_id TEXT NOT NULL,
61
+ text TEXT NOT NULL,
62
+ kind TEXT NOT NULL DEFAULT 'preference',
63
+ source_event INTEGER,
64
+ created_at TEXT NOT NULL
65
+ );`);
66
+ ensureSchema(db, "structured");
67
+ for (const t of ["goals", "decisions", "feedback"]) {
68
+ const cols = new Set(db.prepare(`PRAGMA table_info(${t})`).all().map((c) => c.name));
69
+ for (const col of ["project_key", "scope", "origin", "isolation"])
70
+ expect(cols.has(col), `${t}.${col}`).toBe(true);
71
+ }
72
+ for (const t of ["entities", "decision_entities", "metrics", "session_isolation", "schema_version"]) {
73
+ expect(db.prepare(`SELECT 1 FROM sqlite_master WHERE type='table' AND name=?`).get(t), `table ${t}`).toBeTruthy();
74
+ }
75
+ expect(db.prepare("SELECT MAX(version) AS v FROM schema_version").get().v).toBe(3);
76
+ db.close();
77
+ rmSync(dir, { recursive: true, force: true });
78
+ });
79
+ });
80
+ //# sourceMappingURL=schema.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.test.js","sourceRoot":"","sources":["../src/schema.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export type DecisionStatus = "proposed" | "active" | "superseded" | "revoked";
2
+ export declare function canTransition(from: DecisionStatus, to: DecisionStatus): boolean;
3
+ export declare function assertTransition(from: DecisionStatus, to: DecisionStatus): void;
4
+ export type GoalStatus = "active" | "completed" | "abandoned";
package/dist/state.js ADDED
@@ -0,0 +1,15 @@
1
+ const TRANSITIONS = {
2
+ proposed: ["active", "superseded", "revoked"],
3
+ active: ["superseded", "revoked"],
4
+ superseded: [],
5
+ revoked: [],
6
+ };
7
+ export function canTransition(from, to) {
8
+ return TRANSITIONS[from].includes(to);
9
+ }
10
+ export function assertTransition(from, to) {
11
+ if (!canTransition(from, to)) {
12
+ throw new Error(`invalid decision transition: ${from} -> ${to}`);
13
+ }
14
+ }
15
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,GAA6C;IAC5D,QAAQ,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,SAAS,CAAC;IAC7C,MAAM,EAAE,CAAC,YAAY,EAAE,SAAS,CAAC;IACjC,UAAU,EAAE,EAAE;IACd,OAAO,EAAE,EAAE;CACZ,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,IAAoB,EAAE,EAAkB;IACpE,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAoB,EAAE,EAAkB;IACvE,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,gCAAgC,IAAI,OAAO,EAAE,EAAE,CAAC,CAAC;IACnE,CAAC;AACH,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { ThreadStore } from "./store.js";
2
+ export interface BuildStatusCardOptions {
3
+ sessionId: string;
4
+ projectKey?: string;
5
+ budgetLines?: number;
6
+ recentCount?: number;
7
+ isolated?: boolean;
8
+ firstTurn?: boolean;
9
+ }
10
+ export declare function buildStatusCard(store: ThreadStore, opts: BuildStatusCardOptions): string;
@@ -0,0 +1,65 @@
1
+ import { applyScopePriority } from "./store.js";
2
+ export function buildStatusCard(store, opts) {
3
+ const sessionId = opts.sessionId;
4
+ const projectKey = opts.projectKey;
5
+ const budgetLines = opts.budgetLines ?? 100;
6
+ const firstTurn = opts.firstTurn ?? false;
7
+ const recentCount = opts.recentCount ?? (firstTurn ? 5 : 3);
8
+ const listLimit = firstTurn ? 8 : 5;
9
+ const feedbackLimit = firstTurn ? 8 : 5;
10
+ const isolated = opts.isolated ?? false;
11
+ let goals = [];
12
+ let decisions = [];
13
+ let feedback = [];
14
+ let recent = [];
15
+ try {
16
+ if (isolated) {
17
+ // 隔离模式:只显示本会话内容(不继承项目/全局),状态卡不随其他代理变动
18
+ goals = store.getActiveGoals(sessionId);
19
+ decisions = store.getActiveDecisions(sessionId);
20
+ feedback = store.getFeedback(sessionId, feedbackLimit);
21
+ }
22
+ else {
23
+ goals = applyScopePriority(store.getActiveGoalsMerged(sessionId, projectKey));
24
+ decisions = applyScopePriority(store.getActiveDecisionsMerged(sessionId, projectKey));
25
+ feedback = applyScopePriority(store.getFeedbackMerged(sessionId, projectKey, feedbackLimit));
26
+ }
27
+ recent = store.getRecentEvents(sessionId, recentCount);
28
+ }
29
+ catch {
30
+ // 状态卡是主路径增强,任何失败都降级为最小卡,绝不阻塞
31
+ }
32
+ const shareMark = (row) => row.scope === "global" ? "(全局)" : row.session_id !== sessionId ? "(来自其他会话)" : "";
33
+ const lines = [];
34
+ lines.push(isolated ? "[Thread 会话记忆状态卡](本会话已隔离,内容仅自己可见)" : "[Thread 会话记忆状态卡]");
35
+ if (goals.length > 0) {
36
+ lines.push("目标:");
37
+ goals
38
+ .slice()
39
+ .reverse()
40
+ .slice(0, listLimit)
41
+ .forEach((g, i) => lines.push(` ${i + 1}. ${g.text.slice(0, 120)}${shareMark(g)} #${g.id}`));
42
+ }
43
+ if (decisions.length > 0) {
44
+ lines.push("决策(生效中):");
45
+ decisions.slice(0, listLimit).forEach((d, i) => lines.push(` ${i + 1}. ${d.text.slice(0, 120)}${shareMark(d)} #${d.id}`));
46
+ }
47
+ if (feedback.length > 0) {
48
+ lines.push("偏好:");
49
+ feedback.forEach((f) => {
50
+ lines.push(` - ${f.text.slice(0, 120)}${shareMark(f)} #${f.id}`);
51
+ });
52
+ }
53
+ if (recent.length > 0) {
54
+ lines.push("最近事件:");
55
+ recent
56
+ .slice()
57
+ .reverse()
58
+ .forEach((e) => lines.push(` - ${e.kind}: ${e.body.slice(0, 60)}`));
59
+ }
60
+ // 收束语(外部借鉴③):绑定式行动收束,防止纯"再想想"式开放引导
61
+ lines.push("需要更早的历史细节时,调用 query_session_memory 工具查询,并基于结果给出结论。");
62
+ lines.push("收到 隔离//unisolate//thread-publish 单命令时,只回一句状态确认,不展开思考。");
63
+ return lines.slice(0, budgetLines).join("\n");
64
+ }
65
+ //# sourceMappingURL=status-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-card.js","sourceRoot":"","sources":["../src/status-card.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,53 @@
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 { buildStatusCard } from "./status-card.js";
6
+ import { ThreadStore } from "./store.js";
7
+ let dir;
8
+ let store;
9
+ beforeAll(() => {
10
+ dir = mkdtempSync(join(tmpdir(), "thread-card-"));
11
+ store = new ThreadStore({ eventsPath: join(dir, "events.db"), structuredPath: join(dir, "structured.db"), projectKey: "card-proj" });
12
+ for (let i = 1; i <= 6; i++) {
13
+ store.addGoal("s1", `目标 ${i}`);
14
+ store.proposeDecision("s1", `决策 ${i}`);
15
+ store.confirmLatestProposed("s1");
16
+ }
17
+ store.addFeedback("s1", "偏好 1", "preference");
18
+ store.append({ session_id: "s1", kind: "user_message", ts: new Date().toISOString(), body: "事件 1" });
19
+ store.append({ session_id: "s1", kind: "assistant_message", ts: new Date().toISOString(), body: "事件 2" });
20
+ });
21
+ afterAll(() => {
22
+ store.close();
23
+ rmSync(dir, { recursive: true, force: true });
24
+ });
25
+ describe("buildStatusCard(外部借鉴 ①③:首轮加权 + 收束语)", () => {
26
+ it("尾行收束语为绑定式行动引导(③)", () => {
27
+ const card = buildStatusCard(store, { sessionId: "s1", projectKey: "card-proj" });
28
+ expect(card).toContain("需要更早的历史细节时,调用 query_session_memory 工具查询,并基于结果给出结论。");
29
+ });
30
+ it("首轮档展示更多目标/决策(① 锚点全量)", () => {
31
+ const normal = buildStatusCard(store, { sessionId: "s1", projectKey: "card-proj" });
32
+ const first = buildStatusCard(store, { sessionId: "s1", projectKey: "card-proj", firstTurn: true });
33
+ const countOf = (card, marker) => card.split("\n").filter((l) => l.includes(marker)).length;
34
+ expect(countOf(first, "决策 ")).toBeGreaterThan(countOf(normal, "决策 "));
35
+ expect(countOf(first, "目标 ")).toBeGreaterThan(countOf(normal, "目标 "));
36
+ });
37
+ it("首轮档默认 recent 更多(5 条档)", () => {
38
+ const first = buildStatusCard(store, { sessionId: "s1", projectKey: "card-proj", firstTurn: true });
39
+ const events = first.split("\n").filter((l) => l.startsWith(" - "));
40
+ expect(events.length).toBeGreaterThanOrEqual(2);
41
+ });
42
+ it("隔离 + 首轮组合不降级崩溃", () => {
43
+ const card = buildStatusCard(store, { sessionId: "s1", projectKey: "card-proj", isolated: true, firstTurn: true });
44
+ expect(card).toContain("本会话已隔离");
45
+ });
46
+ it("行尾带行 id(① 治理可见性:目标/决策/偏好均可定位)", () => {
47
+ const card = buildStatusCard(store, { sessionId: "s1", projectKey: "card-proj" });
48
+ expect(card).toMatch(/决策 1 #\d+/);
49
+ expect(card).toMatch(/目标 \d+ #\d+/);
50
+ expect(card).toMatch(/偏好 1 #\d+/);
51
+ });
52
+ });
53
+ //# sourceMappingURL=status-card.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-card.test.js","sourceRoot":"","sources":["../src/status-card.test.ts"],"names":[],"mappings":"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"}