@whispering233/ai-editor-db 0.0.30 → 0.0.33
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/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/migrations/006_sessions_jsonl.d.ts +4 -0
- package/dist/migrations/006_sessions_jsonl.d.ts.map +1 -0
- package/dist/migrations/006_sessions_jsonl.js +95 -0
- package/dist/migrations/006_sessions_jsonl.js.map +1 -0
- package/dist/migrations/index.d.ts +8 -3
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +12 -4
- package/dist/migrations/index.js.map +1 -1
- package/dist/migrations/legacy-sessions.d.ts +24 -0
- package/dist/migrations/legacy-sessions.d.ts.map +1 -0
- package/dist/migrations/legacy-sessions.js +69 -0
- package/dist/migrations/legacy-sessions.js.map +1 -0
- package/dist/queries/delta.js +1 -1
- package/dist/queries/entity.js +1 -1
- package/dist/queries/migration.d.ts +6 -2
- package/dist/queries/migration.d.ts.map +1 -1
- package/dist/queries/migration.js +8 -3
- package/dist/queries/migration.js.map +1 -1
- package/dist/queries/relation.js +1 -1
- package/dist/schema.d.ts +3 -1
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +3 -1
- package/dist/schema.js.map +1 -1
- package/dist/tables.d.ts +3 -162
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +4 -30
- package/dist/tables.js.map +1 -1
- package/package.json +3 -4
- package/dist/queries/chat.d.ts +0 -85
- package/dist/queries/chat.d.ts.map +0 -1
- package/dist/queries/chat.js +0 -248
- package/dist/queries/chat.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,6 @@ export * from "./connection.js";
|
|
|
3
3
|
export * from "./storage/atomic.js";
|
|
4
4
|
export * from "./storage/outline.js";
|
|
5
5
|
export * from "./storage/project.js";
|
|
6
|
-
export * from "./queries/chat.js";
|
|
7
6
|
export * from "./queries/migration.js";
|
|
8
7
|
export * from "./migrations/index.js";
|
|
9
8
|
export * from "./queries/outline-ops.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,oBAAoB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
// @whispering233/ai-editor-db 入口:导出建表(schema)、连接(connection
|
|
1
|
+
// @whispering233/ai-editor-db 入口:导出建表(schema)、连接(connection)、
|
|
2
2
|
// JSON 存储(storage/:outline.json / project.json 原子读写,T2.2)、增量迁移(migrations/)
|
|
3
|
+
//
|
|
4
|
+
// 会话文件(sessions/*.jsonl)不在本包:新格式(pi session v3)的读写归 pi SessionManager
|
|
5
|
+
// (agent 包的 pi 运行时),旧 v1 格式只剩迁移 006 用一次的导出辅助(migrations/legacy-sessions.ts,不导出)
|
|
3
6
|
export * from "./schema.js";
|
|
4
7
|
export * from "./connection.js";
|
|
5
8
|
export * from "./storage/atomic.js";
|
|
6
9
|
export * from "./storage/outline.js";
|
|
7
10
|
export * from "./storage/project.js";
|
|
8
|
-
export * from "./queries/chat.js";
|
|
9
11
|
export * from "./queries/migration.js";
|
|
10
12
|
export * from "./migrations/index.js";
|
|
11
13
|
export * from "./queries/outline-ops.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,4EAA4E;AAC5E,EAAE;AACF,sEAAsE;AACtE,gFAAgF;AAChF,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,oBAAoB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"006_sessions_jsonl.d.ts","sourceRoot":"","sources":["../../src/migrations/006_sessions_jsonl.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAqD5C,QAAA,MAAM,YAAY,EAAE,SAwBnB,CAAC;AAgBF,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// 迁移 006:对话历史出库——`chat_messages` 表导出为 `sessions/<session_id>.jsonl` 后 DROP
|
|
2
|
+
//
|
|
3
|
+
// 背景:对话历史从 data.db 迁到项目目录(一 session 一 JSONL,契约见 docs/db/schema.md)。
|
|
4
|
+
// 好处:会话随书目录走(备份/导入/改名/移动天然携带)、追加写可读可 diff、思维链等扩展纯追加。
|
|
5
|
+
//
|
|
6
|
+
// 安全性/幂等性:
|
|
7
|
+
// - 导出是「按表内容整文件重写」(writeLegacySessionFile 原子写,输出仅由入参决定)——文件写不可回滚,
|
|
8
|
+
// 靠完整性收敛:崩溃在提交前 ⇒ 下次重跑按同一张表重写同内容;提交后 ⇒ 表已消失不再重跑
|
|
9
|
+
// - 事务由 runMigrations 保证(up + setUserVersion 原子提交,抛错整体回滚)
|
|
10
|
+
// - session_id 不合法(不匹配 `^sess_[A-Za-z0-9_-]{1,64}$`,无法直接作文件名)的旧会话
|
|
11
|
+
// 以 `sess_legacy_<sha256 前 16 位>` 为文件名落盘(**保数据不丢,会话 id 变更**)——
|
|
12
|
+
// 确定性映射 ⇒ 重跑产出同一文件名,幂等性不受影响
|
|
13
|
+
//
|
|
14
|
+
// 本迁移需要项目目录(写文件)——经 MigrationContext.projectRoot 取得(迁移框架透传)。
|
|
15
|
+
import { createHash } from "node:crypto";
|
|
16
|
+
import { isValidLegacySessionId, writeLegacySessionFile } from "./legacy-sessions.js";
|
|
17
|
+
/** tool_calls TEXT 列 → 数组;NULL / 非法 JSON / 非数组 → null(与旧 parseToolCalls 同口径) */
|
|
18
|
+
function parseToolCallsColumn(json) {
|
|
19
|
+
if (json === null)
|
|
20
|
+
return null;
|
|
21
|
+
try {
|
|
22
|
+
const parsed = JSON.parse(json);
|
|
23
|
+
return Array.isArray(parsed) ? parsed : null;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 读全表并按 session_id 分组(组内保持查询序:created_at 升序、同时间戳按 rowid 插入序)。
|
|
31
|
+
* 直接 `prepare` 属迁移管线(AGENTS 硬约束「查询层统一经 queryDb」的明确例外)。
|
|
32
|
+
*/
|
|
33
|
+
function readLegacyRowsBySession(db) {
|
|
34
|
+
const rows = db
|
|
35
|
+
.prepare(`SELECT id, session_id, role, content, tool_calls, tool_call_id, created_at
|
|
36
|
+
FROM chat_messages
|
|
37
|
+
ORDER BY session_id ASC, created_at ASC, rowid ASC`)
|
|
38
|
+
.all();
|
|
39
|
+
const bySession = new Map();
|
|
40
|
+
for (const r of rows) {
|
|
41
|
+
const list = bySession.get(r.session_id);
|
|
42
|
+
const row = {
|
|
43
|
+
id: r.id,
|
|
44
|
+
role: r.role,
|
|
45
|
+
content: r.content,
|
|
46
|
+
tool_calls: parseToolCallsColumn(r.tool_calls),
|
|
47
|
+
tool_call_id: r.tool_call_id,
|
|
48
|
+
created_at: r.created_at,
|
|
49
|
+
};
|
|
50
|
+
if (list === undefined)
|
|
51
|
+
bySession.set(r.session_id, [row]);
|
|
52
|
+
else
|
|
53
|
+
list.push(row);
|
|
54
|
+
}
|
|
55
|
+
return bySession;
|
|
56
|
+
}
|
|
57
|
+
const migration006 = {
|
|
58
|
+
version: 6,
|
|
59
|
+
up(db, ctx) {
|
|
60
|
+
// 旧库无 chat_messages(新库从未建过该表 / 已迁移过)→ 无事可做(不因缺表中断迁移链)
|
|
61
|
+
if (!tableExists(db, "chat_messages"))
|
|
62
|
+
return;
|
|
63
|
+
const bySession = readLegacyRowsBySession(db);
|
|
64
|
+
const exportable = [...bySession.keys()].filter((id) => isValidLegacySessionId(id));
|
|
65
|
+
if (exportable.length > 0 && ctx.projectRoot === "") {
|
|
66
|
+
// 防御:有会话要导出却无项目目录(ensureSchemaCompatible 恒传 dir;纯逻辑调用才缺失)
|
|
67
|
+
throw new Error("迁移 006 需要项目目录(projectRoot)才能导出会话文件");
|
|
68
|
+
}
|
|
69
|
+
let remapped = 0;
|
|
70
|
+
for (const [sessionId, rows] of bySession) {
|
|
71
|
+
const targetId = isValidLegacySessionId(sessionId) ? sessionId : legacySessionId(sessionId);
|
|
72
|
+
if (targetId !== sessionId)
|
|
73
|
+
remapped += 1;
|
|
74
|
+
writeLegacySessionFile(ctx.projectRoot, targetId, rows);
|
|
75
|
+
}
|
|
76
|
+
if (remapped > 0) {
|
|
77
|
+
console.warn(`[db] 迁移 006:${remapped} 个 session_id 非法的旧会话已以 sess_legacy_<hash> 文件名导出(数据未丢,id 已变更)`);
|
|
78
|
+
}
|
|
79
|
+
db.exec("DROP TABLE chat_messages");
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* 非法旧 id → 稳定的合法文件名 id:`sess_legacy_` + sha256 前 16 位十六进制。
|
|
84
|
+
* 同一输入恒同输出(迁移重跑幂等);碰撞概率可忽略;原始 id 不再保留(旧 id 仅作文件名,无对外语义)。
|
|
85
|
+
*/
|
|
86
|
+
function legacySessionId(originalId) {
|
|
87
|
+
return `sess_legacy_${createHash("sha256").update(originalId).digest("hex").slice(0, 16)}`;
|
|
88
|
+
}
|
|
89
|
+
/** 表是否存在(sqlite_master 查询;迁移内的存在性判定) */
|
|
90
|
+
function tableExists(db, name) {
|
|
91
|
+
const row = db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?").get(name);
|
|
92
|
+
return row !== undefined;
|
|
93
|
+
}
|
|
94
|
+
export default migration006;
|
|
95
|
+
//# sourceMappingURL=006_sessions_jsonl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"006_sessions_jsonl.js","sourceRoot":"","sources":["../../src/migrations/006_sessions_jsonl.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import type { Db } from "../connection.js";
|
|
2
|
+
/** 迁移运行上下文(写文件类迁移需要项目目录:006 的会话 JSONL 导出) */
|
|
3
|
+
export interface MigrationContext {
|
|
4
|
+
/** 项目根目录(books/<书名>/;会话文件写在 `<projectRoot>/sessions/`) */
|
|
5
|
+
projectRoot: string;
|
|
6
|
+
}
|
|
2
7
|
/** 单条增量迁移(version = 迁移完成后 data.db 的 user_version) */
|
|
3
8
|
export interface Migration {
|
|
4
9
|
version: number;
|
|
5
|
-
/** 迁移逻辑(DDL
|
|
6
|
-
up: (db: Db) => void;
|
|
10
|
+
/** 迁移逻辑(DDL/数据变更/文件导出);抛错 → 该迁移事务整体回滚,版本号不变 */
|
|
11
|
+
up: (db: Db, ctx: MigrationContext) => void;
|
|
7
12
|
}
|
|
8
13
|
/** 全量迁移集(按 version 升序:002 时间轴事件、003 时间标签点实体化(G2)、
|
|
9
|
-
* 004 设定分类字段 tags(K2 修订)、005 参考资料 reference
|
|
14
|
+
* 004 设定分类字段 tags(K2 修订)、005 参考资料 reference、006 对话历史出库) */
|
|
10
15
|
export declare const MIGRATIONS: readonly Migration[];
|
|
11
16
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAO3C,6CAA6C;AAC7C,MAAM,WAAW,gBAAgB;IAChC,0DAA0D;IACzD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,qDAAqD;AACrD,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC9C,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAC7C;AAED;2DAC2D;AAC3D,eAAO,MAAM,UAAU,EAAE,SAAS,SAAS,EAM1C,CAAC"}
|
package/dist/migrations/index.js
CHANGED
|
@@ -16,15 +16,23 @@
|
|
|
16
16
|
// - 失败 → 该迁移回滚 + 版本停在前一迁移后,下次 open 重试
|
|
17
17
|
// - 无迁移路径的旧版本(如 v0 且无 0→1 条目)保持删库重建兜底
|
|
18
18
|
//
|
|
19
|
-
// **当前状态**:SCHEMA_VERSION =
|
|
19
|
+
// **当前状态**:SCHEMA_VERSION = 6;真实迁移 002(v1→v2:entities 表 CHECK 扩为 5 种 +
|
|
20
20
|
// sort_order 列,时间轴)、003(v2→v3:entities CHECK 扩 6 种含 timepoint +
|
|
21
|
-
// event.data.time_label 迁移为 timepoint 实体 + occurs_at 挂载关系,G2
|
|
21
|
+
// event.data.time_label 迁移为 timepoint 实体 + occurs_at 挂载关系,G2)、
|
|
22
|
+
// 006(v5→v6:对话历史出库——chat_messages 导出为 sessions/<id>.jsonl 后 DROP 表)。
|
|
22
23
|
// v0 库无 0→1 迁移条目,仍走删库重建兜底。
|
|
23
24
|
import migration002 from "./002_event_timeline.js";
|
|
24
25
|
import migration003 from "./003_timepoint.js";
|
|
25
26
|
import migration004 from "./004_setting_tags.js";
|
|
26
27
|
import migration005 from "./005_reference.js";
|
|
28
|
+
import migration006 from "./006_sessions_jsonl.js";
|
|
27
29
|
/** 全量迁移集(按 version 升序:002 时间轴事件、003 时间标签点实体化(G2)、
|
|
28
|
-
* 004 设定分类字段 tags(K2 修订)、005 参考资料 reference
|
|
29
|
-
export const MIGRATIONS = [
|
|
30
|
+
* 004 设定分类字段 tags(K2 修订)、005 参考资料 reference、006 对话历史出库) */
|
|
31
|
+
export const MIGRATIONS = [
|
|
32
|
+
migration002,
|
|
33
|
+
migration003,
|
|
34
|
+
migration004,
|
|
35
|
+
migration005,
|
|
36
|
+
migration006,
|
|
37
|
+
];
|
|
30
38
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA,WAAW;AACX,EAAE;AACF,yEAAyE;AACzE,uEAAuE;AACvE,yDAAyD;AACzD,uCAAuC;AACvC,EAAE;AACF,4EAA4E;AAC5E,8DAA8D;AAC9D,EAAE;AACF,8DAA8D;AAC9D,EAAE;AACF,kDAAkD;AAClD,wDAAwD;AACxD,uDAAuD;AACvD,sCAAsC;AACtC,sCAAsC;AACtC,EAAE;AACF,uEAAuE;AACvE,gEAAgE;AAChE,+DAA+D;AAC/D,2BAA2B;AAG3B,OAAO,YAAY,MAAM,yBAAyB,CAAC;AACnD,OAAO,YAAY,MAAM,oBAAoB,CAAC;AAC9C,OAAO,YAAY,MAAM,uBAAuB,CAAC;AACjD,OAAO,YAAY,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA,WAAW;AACX,EAAE;AACF,yEAAyE;AACzE,uEAAuE;AACvE,yDAAyD;AACzD,uCAAuC;AACvC,EAAE;AACF,4EAA4E;AAC5E,8DAA8D;AAC9D,EAAE;AACF,8DAA8D;AAC9D,EAAE;AACF,kDAAkD;AAClD,wDAAwD;AACxD,uDAAuD;AACvD,sCAAsC;AACtC,sCAAsC;AACtC,EAAE;AACF,uEAAuE;AACvE,gEAAgE;AAChE,+DAA+D;AAC/D,qEAAqE;AACrE,2BAA2B;AAG3B,OAAO,YAAY,MAAM,yBAAyB,CAAC;AACnD,OAAO,YAAY,MAAM,oBAAoB,CAAC;AAC9C,OAAO,YAAY,MAAM,uBAAuB,CAAC;AACjD,OAAO,YAAY,MAAM,oBAAoB,CAAC;AAC9C,OAAO,YAAY,MAAM,yBAAyB,CAAC;AAenD;2DAC2D;AAC3D,MAAM,CAAC,MAAM,UAAU,GAAyB;IAC9C,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,YAAY;CACb,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** 旧会话行(`chat_messages` 表行形态;tool_calls 已在调用方解析为数组) */
|
|
2
|
+
export interface LegacySessionRow {
|
|
3
|
+
id: string;
|
|
4
|
+
role: string;
|
|
5
|
+
content: string | null;
|
|
6
|
+
tool_calls: unknown[] | null;
|
|
7
|
+
tool_call_id: string | null;
|
|
8
|
+
created_at: string;
|
|
9
|
+
}
|
|
10
|
+
/** 旧 session_id 是否合法(迁移判定「可直接作文件名」) */
|
|
11
|
+
export declare function isValidLegacySessionId(id: string): boolean;
|
|
12
|
+
/** 旧会话目录绝对路径(`<projectRoot>/sessions`) */
|
|
13
|
+
export declare function legacySessionsDirPath(projectRoot: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* 整文件重写一个旧会话文件(header + 全部消息行)——**迁移 006 专用**。
|
|
16
|
+
*
|
|
17
|
+
* 幂等:输出仅由入参决定(不生成时间/随机 id)⇒ 迁移事务回滚后重跑结果一致
|
|
18
|
+
* (文件写不可回滚,靠「按表内容重写」收敛)。原子写(临时文件 + fsync + rename)。
|
|
19
|
+
* 空 rows 也写出仅 header 的文件(created_at 为空串)。
|
|
20
|
+
*
|
|
21
|
+
* @throws Error session_id 非法(调用方必须先经 isValidLegacySessionId 判定/改名)
|
|
22
|
+
*/
|
|
23
|
+
export declare function writeLegacySessionFile(projectRoot: string, sessionId: string, rows: readonly LegacySessionRow[]): void;
|
|
24
|
+
//# sourceMappingURL=legacy-sessions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legacy-sessions.d.ts","sourceRoot":"","sources":["../../src/migrations/legacy-sessions.ts"],"names":[],"mappings":"AAsBA,uDAAuD;AACvD,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,uCAAuC;AACvC,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED,0CAA0C;AAC1C,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEjE;AAqBD;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,SAAS,gBAAgB,EAAE,GAChC,IAAI,CAaN"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// 旧会话格式(v1 扁平 JSONL)导出辅助 —— **仅服务已发布的迁移 006**(对话历史出库)。
|
|
2
|
+
//
|
|
3
|
+
// 为什么留在这里而不是 db 的公开出口:新会话格式(pi session v3)的读写全部归 pi
|
|
4
|
+
// `SessionManager`(见 docs/db/schema.md「sessions/*.jsonl」),本仓生产链路**不再读写**
|
|
5
|
+
// 旧格式;只有迁移 006 需要把 `chat_messages` 表按旧格式落盘一次(已发布迁移不可改语义)。
|
|
6
|
+
//
|
|
7
|
+
// 因此:本模块不进 db 包入口导出、不提供读接口(读断言由测试自行用 node:fs 完成)。
|
|
8
|
+
// 旧格式行:header(`type/version/id/created_at`)+ 消息行(`type=message`)。
|
|
9
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { writeTextAtomic } from "../storage/atomic.js";
|
|
12
|
+
/** 会话目录名(项目目录内) */
|
|
13
|
+
const LEGACY_SESSIONS_DIR_NAME = "sessions";
|
|
14
|
+
/** 旧会话行格式版本(header.version 不为该值 → 老读侧整文件跳过) */
|
|
15
|
+
const LEGACY_SESSION_FILE_VERSION = 1;
|
|
16
|
+
/** 旧 session_id 硬约束(同时是文件名校验——客户端传入值不得含路径分隔符/`..`) */
|
|
17
|
+
const LEGACY_SESSION_ID_PATTERN = /^sess_[A-Za-z0-9_-]{1,64}$/;
|
|
18
|
+
/** 旧 session_id 是否合法(迁移判定「可直接作文件名」) */
|
|
19
|
+
export function isValidLegacySessionId(id) {
|
|
20
|
+
return LEGACY_SESSION_ID_PATTERN.test(id);
|
|
21
|
+
}
|
|
22
|
+
/** 旧会话目录绝对路径(`<projectRoot>/sessions`) */
|
|
23
|
+
export function legacySessionsDirPath(projectRoot) {
|
|
24
|
+
return join(projectRoot, LEGACY_SESSIONS_DIR_NAME);
|
|
25
|
+
}
|
|
26
|
+
/** 旧会话文件绝对路径(路径只由已校验的 id 拼出) */
|
|
27
|
+
function legacySessionFilePath(projectRoot, sessionId) {
|
|
28
|
+
return join(legacySessionsDirPath(projectRoot), `${sessionId}.jsonl`);
|
|
29
|
+
}
|
|
30
|
+
/** 消息行序列化(可选字段仅非空时写入,与旧读写实现逐字节一致) */
|
|
31
|
+
function serializeMessageLine(row) {
|
|
32
|
+
const line = {
|
|
33
|
+
type: "message",
|
|
34
|
+
id: row.id,
|
|
35
|
+
role: row.role,
|
|
36
|
+
content: row.content ?? null,
|
|
37
|
+
created_at: row.created_at,
|
|
38
|
+
};
|
|
39
|
+
if (row.tool_calls != null)
|
|
40
|
+
line.tool_calls = row.tool_calls;
|
|
41
|
+
if (row.tool_call_id != null)
|
|
42
|
+
line.tool_call_id = row.tool_call_id;
|
|
43
|
+
return `${JSON.stringify(line)}\n`;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 整文件重写一个旧会话文件(header + 全部消息行)——**迁移 006 专用**。
|
|
47
|
+
*
|
|
48
|
+
* 幂等:输出仅由入参决定(不生成时间/随机 id)⇒ 迁移事务回滚后重跑结果一致
|
|
49
|
+
* (文件写不可回滚,靠「按表内容重写」收敛)。原子写(临时文件 + fsync + rename)。
|
|
50
|
+
* 空 rows 也写出仅 header 的文件(created_at 为空串)。
|
|
51
|
+
*
|
|
52
|
+
* @throws Error session_id 非法(调用方必须先经 isValidLegacySessionId 判定/改名)
|
|
53
|
+
*/
|
|
54
|
+
export function writeLegacySessionFile(projectRoot, sessionId, rows) {
|
|
55
|
+
if (!isValidLegacySessionId(sessionId)) {
|
|
56
|
+
throw new Error(`旧会话 id 非法(应为 ^sess_[A-Za-z0-9_-]{1,64}$):${sessionId}`);
|
|
57
|
+
}
|
|
58
|
+
const dir = legacySessionsDirPath(projectRoot);
|
|
59
|
+
if (!existsSync(dir))
|
|
60
|
+
mkdirSync(dir, { recursive: true });
|
|
61
|
+
const header = `${JSON.stringify({
|
|
62
|
+
type: "session",
|
|
63
|
+
version: LEGACY_SESSION_FILE_VERSION,
|
|
64
|
+
id: sessionId,
|
|
65
|
+
created_at: rows[0]?.created_at ?? "",
|
|
66
|
+
})}\n`;
|
|
67
|
+
writeTextAtomic(legacySessionFilePath(projectRoot, sessionId), header + rows.map(serializeMessageLine).join(""));
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=legacy-sessions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legacy-sessions.js","sourceRoot":"","sources":["../../src/migrations/legacy-sessions.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,EAAE;AACF,oDAAoD;AACpD,yEAAyE;AACzE,0DAA0D;AAC1D,EAAE;AACF,kDAAkD;AAClD,kEAAkE;AAElE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,mBAAmB;AACnB,MAAM,wBAAwB,GAAG,UAAU,CAAC;AAE5C,+CAA+C;AAC/C,MAAM,2BAA2B,GAAG,CAAC,CAAC;AAEtC,sDAAsD;AACtD,MAAM,yBAAyB,GAAG,4BAA4B,CAAC;AAY/D,uCAAuC;AACvC,MAAM,UAAU,sBAAsB,CAAC,EAAU;IAC/C,OAAO,yBAAyB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC5C,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,qBAAqB,CAAC,WAAmB;IACvD,OAAO,IAAI,CAAC,WAAW,EAAE,wBAAwB,CAAC,CAAC;AACrD,CAAC;AAED,gCAAgC;AAChC,SAAS,qBAAqB,CAAC,WAAmB,EAAE,SAAiB;IACnE,OAAO,IAAI,CAAC,qBAAqB,CAAC,WAAW,CAAC,EAAE,GAAG,SAAS,QAAQ,CAAC,CAAC;AACxE,CAAC;AAED,qCAAqC;AACrC,SAAS,oBAAoB,CAAC,GAAqB;IACjD,MAAM,IAAI,GAA4B;QACpC,IAAI,EAAE,SAAS;QACf,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,IAAI;QAC5B,UAAU,EAAE,GAAG,CAAC,UAAU;KAC3B,CAAC;IACF,IAAI,GAAG,CAAC,UAAU,IAAI,IAAI;QAAE,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC,UAAU,CAAC;IAC7D,IAAI,GAAG,CAAC,YAAY,IAAI,IAAI;QAAE,IAAI,CAAC,YAAY,GAAG,GAAG,CAAC,YAAY,CAAC;IACnE,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;AACrC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CACpC,WAAmB,EACnB,SAAiB,EACjB,IAAiC;IAEjC,IAAI,CAAC,sBAAsB,CAAC,SAAS,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,4CAA4C,SAAS,EAAE,CAAC,CAAC;IAC3E,CAAC;IACD,MAAM,GAAG,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;IAC/C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC;QAC/B,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,2BAA2B;QACpC,EAAE,EAAE,SAAS;QACb,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,UAAU,IAAI,EAAE;KACtC,CAAC,IAAI,CAAC;IACP,eAAe,CAAC,qBAAqB,CAAC,WAAW,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;AACnH,CAAC"}
|
package/dist/queries/delta.js
CHANGED
|
@@ -54,7 +54,7 @@ function rowToDeltaRow(row) {
|
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
56
56
|
* changes 列解析防御:非法 JSON / 非数组 → [](单条坏行不打挂整表查询;
|
|
57
|
-
* 与 entity.ts parseDataColumn
|
|
57
|
+
* 与 entity.ts parseDataColumn 防御风格一致)
|
|
58
58
|
*/
|
|
59
59
|
function parseChanges(value) {
|
|
60
60
|
if (typeof value !== "string")
|
package/dist/queries/entity.js
CHANGED
|
@@ -142,7 +142,7 @@ export function rowToEntityRow(row) {
|
|
|
142
142
|
/**
|
|
143
143
|
* 解析 data 列(oracle 审核建议 1:JSON.parse 防御):
|
|
144
144
|
* 非法 JSON(手改库/异常写入)时返回 {} 而非抛错——否则 listEntities 整表查询
|
|
145
|
-
* 会被单条坏行打挂;与
|
|
145
|
+
* 会被单条坏行打挂;与 sessions.ts 的 JSON 列防御风格一致。坏行以空 data 呈现,
|
|
146
146
|
* 其余行正常返回;修复入口为回收站清理/手动修正(不在本层静默写回)。
|
|
147
147
|
*/
|
|
148
148
|
function parseDataColumn(value) {
|
|
@@ -73,17 +73,21 @@ export declare function hasMigrationPath(fromVersion: number, targetVersion: num
|
|
|
73
73
|
* `data.db.v{from}.{YYYYMMDDHHmmssZ}.bak`(时间戳命名,不覆盖旧备份)——
|
|
74
74
|
* 失败重试时现场保留(每次调用生成新时间戳快照,旧快照不删)
|
|
75
75
|
* 2. **逐个执行** (fromVersion, targetVersion] 区间内按 version 升序的迁移:
|
|
76
|
-
* 每个迁移一个事务(`db.transaction`):`up(db)` + `setUserVersion(m.version)`
|
|
76
|
+
* 每个迁移一个事务(`db.transaction`):`up(db, ctx)` + `setUserVersion(m.version)`
|
|
77
77
|
* **原子提交**——「迁移成功 ⇒ 版本已写入;失败 ⇒ 版本未变」;失败时该迁移整体
|
|
78
|
-
* 回滚(含 DDL
|
|
78
|
+
* 回滚(含 DDL 与已写数据)、后续迁移不执行、异常向上传播(快照保留供重试)。
|
|
79
|
+
* ※ 写文件类迁移(006 会话导出)的文件写入**不在事务回滚范围内**——靠幂等收敛
|
|
80
|
+
* (按表内容重写),详见迁移文件注释。
|
|
79
81
|
*
|
|
80
82
|
* @param opts.dbPath data.db 绝对路径(提供则迁移前快照;缺省跳过快照——纯逻辑调用)
|
|
83
|
+
* @param opts.projectRoot 项目根目录(写文件类迁移用;缺省空串 → 该类迁移自行报错)
|
|
81
84
|
* @returns 实际执行的迁移(升序)与快照路径(未快照为 null)
|
|
82
85
|
*/
|
|
83
86
|
export declare function runMigrations(db: Db, opts?: {
|
|
84
87
|
migrations?: readonly Migration[];
|
|
85
88
|
targetVersion?: number;
|
|
86
89
|
dbPath?: string;
|
|
90
|
+
projectRoot?: string;
|
|
87
91
|
}): {
|
|
88
92
|
applied: Migration[];
|
|
89
93
|
snapshot: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration.d.ts","sourceRoot":"","sources":["../../src/queries/migration.ts"],"names":[],"mappings":"AAmBA,OAAO,EAA+B,KAAK,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAGxE,OAAO,EAAc,KAAK,SAAS,
|
|
1
|
+
{"version":3,"file":"migration.d.ts","sourceRoot":"","sources":["../../src/queries/migration.ts"],"names":[],"mappings":"AAmBA,OAAO,EAA+B,KAAK,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAGxE,OAAO,EAAc,KAAK,SAAS,EAAyB,MAAM,wBAAwB,CAAC;AAE3F,mDAAmD;AACnD,eAAO,MAAM,iBAAiB,YAAY,CAAC;AAE3C,gCAAgC;AAChC,MAAM,WAAW,eAAe;IAC/B,gBAAgB;IACf,OAAO,EAAE,OAAO,CAAC;IAClB,6CAA6C;IAC5C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACpB,wCAAwC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACtB,qCAAqC;IACpC,SAAS,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACvE,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,mDAAmD;AACnD,MAAM,WAAW,aAAa;IAC7B,gCAAgC;IAC/B,EAAE,EAAE,EAAE,CAAC;IACP,MAAM,EAAE,eAAe,CAAC;CACzB;AAED;;;;;;;;;;GAUG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC5C,wCAAwC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAC1B,gBAAgB;IACf,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBACb,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAM7C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,EAAE,EACN,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,IAAI,GAAE;IAAE,UAAU,CAAC,EAAE,SAAS,SAAS,EAAE,CAAA;CAAO,GAC/C,aAAa,CAqCf;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,UAAU,GAAE,SAAS,SAAS,EAAe,GAC5C,OAAO,CAOT;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAC3B,EAAE,EAAE,EAAE,EACN,IAAI,GAAE;IACJ,UAAU,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACjB,GACL;IAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CA0BnD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAK1E;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,EAAE,GAAG,IAAI,CAE1C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,aAAa,CA2B5G;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAIvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMhF"}
|
|
@@ -77,6 +77,7 @@ export function ensureSchemaCompatible(db, dir, dbPath, opts = {}) {
|
|
|
77
77
|
migrations: opts.migrations ?? MIGRATIONS,
|
|
78
78
|
targetVersion: SCHEMA_VERSION,
|
|
79
79
|
dbPath,
|
|
80
|
+
projectRoot: dir, // 写文件类迁移(006 会话导出)需要项目目录
|
|
80
81
|
});
|
|
81
82
|
return {
|
|
82
83
|
db,
|
|
@@ -122,11 +123,14 @@ export function hasMigrationPath(fromVersion, targetVersion, migrations = MIGRAT
|
|
|
122
123
|
* `data.db.v{from}.{YYYYMMDDHHmmssZ}.bak`(时间戳命名,不覆盖旧备份)——
|
|
123
124
|
* 失败重试时现场保留(每次调用生成新时间戳快照,旧快照不删)
|
|
124
125
|
* 2. **逐个执行** (fromVersion, targetVersion] 区间内按 version 升序的迁移:
|
|
125
|
-
* 每个迁移一个事务(`db.transaction`):`up(db)` + `setUserVersion(m.version)`
|
|
126
|
+
* 每个迁移一个事务(`db.transaction`):`up(db, ctx)` + `setUserVersion(m.version)`
|
|
126
127
|
* **原子提交**——「迁移成功 ⇒ 版本已写入;失败 ⇒ 版本未变」;失败时该迁移整体
|
|
127
|
-
* 回滚(含 DDL
|
|
128
|
+
* 回滚(含 DDL 与已写数据)、后续迁移不执行、异常向上传播(快照保留供重试)。
|
|
129
|
+
* ※ 写文件类迁移(006 会话导出)的文件写入**不在事务回滚范围内**——靠幂等收敛
|
|
130
|
+
* (按表内容重写),详见迁移文件注释。
|
|
128
131
|
*
|
|
129
132
|
* @param opts.dbPath data.db 绝对路径(提供则迁移前快照;缺省跳过快照——纯逻辑调用)
|
|
133
|
+
* @param opts.projectRoot 项目根目录(写文件类迁移用;缺省空串 → 该类迁移自行报错)
|
|
130
134
|
* @returns 实际执行的迁移(升序)与快照路径(未快照为 null)
|
|
131
135
|
*/
|
|
132
136
|
export function runMigrations(db, opts = {}) {
|
|
@@ -144,10 +148,11 @@ export function runMigrations(db, opts = {}) {
|
|
|
144
148
|
snapshot = snapshotDbFile(opts.dbPath, fromVersion);
|
|
145
149
|
}
|
|
146
150
|
// 2. 逐个执行(每迁移一个事务,up + setUserVersion 原子)
|
|
151
|
+
const ctx = { projectRoot: opts.projectRoot ?? "" };
|
|
147
152
|
const applied = [];
|
|
148
153
|
for (const m of pending) {
|
|
149
154
|
db.transaction(() => {
|
|
150
|
-
m.up(db);
|
|
155
|
+
m.up(db, ctx);
|
|
151
156
|
setUserVersion(db, m.version);
|
|
152
157
|
})();
|
|
153
158
|
applied.push(m);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migration.js","sourceRoot":"","sources":["../../src/queries/migration.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,EAAE;AACF,uBAAuB;AACvB,iDAAiD;AACjD,2CAA2C;AAC3C,0DAA0D;AAC1D,6CAA6C;AAC7C,gEAAgE;AAChE,+CAA+C;AAC/C,4CAA4C;AAC5C,kEAAkE;AAClE,6DAA6D;AAC7D,wFAAwF;AACxF,kCAAkC;AAClC,wCAAwC;AAExC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAW,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"migration.js","sourceRoot":"","sources":["../../src/queries/migration.ts"],"names":[],"mappings":"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"}
|
package/dist/queries/relation.js
CHANGED
|
@@ -293,7 +293,7 @@ export function createRelation(db, input, outlineDir) {
|
|
|
293
293
|
throw new RelationError("RELATION_EXISTS", `关系已存在: ${input.sourceId} → ${input.targetId} (${input.relationType})`);
|
|
294
294
|
}
|
|
295
295
|
const now = nowIso();
|
|
296
|
-
// 关系 id:id 约定未列 rel- 前缀(运行时对象为 prop_/
|
|
296
|
+
// 关系 id:id 约定未列 rel- 前缀(运行时对象为 prop_/call_/会话 id),
|
|
297
297
|
// 用 generateId("rel-") 保证唯一(前缀 + nanoid,与实体/节点同构)
|
|
298
298
|
const row = {
|
|
299
299
|
id: generateId("rel-"),
|
package/dist/schema.d.ts
CHANGED
|
@@ -8,8 +8,10 @@ import type Database from "better-sqlite3";
|
|
|
8
8
|
* timepoint 实体 + occurs_at 挂载关系(从 event.data 移除 time_label)。
|
|
9
9
|
* v3 → v4(K2 修订,2026-08):**无 DDL**——仅 entities.data JSON 数据迁移
|
|
10
10
|
* (setting 的旧 rules 分类值复制到 data.tags 并移除 rules,migrations/004_setting_tags.ts)。
|
|
11
|
+
* v5 → v6(对话历史出库):chat_messages 导出为 `sessions/<id>.jsonl` 后 DROP 表
|
|
12
|
+
* (migrations/006_sessions_jsonl.ts;会话从此不入库)。
|
|
11
13
|
*/
|
|
12
|
-
export declare const SCHEMA_VERSION =
|
|
14
|
+
export declare const SCHEMA_VERSION = 6;
|
|
13
15
|
/**
|
|
14
16
|
* 建表:执行全部 DDL(CREATE TABLE/INDEX IF NOT EXISTS,定义于 tables.ts),幂等,可重复调用。
|
|
15
17
|
* 打开数据库后由 connection.openDatabase 自动调用。
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAG3C
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAG3C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,IAAI,CAAC;AAEhC;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAExD;AAED,2DAA2D;AAC3D,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,MAAM,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAK3E"}
|
package/dist/schema.js
CHANGED
|
@@ -13,8 +13,10 @@ import { CREATE_TABLES_SQL } from "./tables.js";
|
|
|
13
13
|
* timepoint 实体 + occurs_at 挂载关系(从 event.data 移除 time_label)。
|
|
14
14
|
* v3 → v4(K2 修订,2026-08):**无 DDL**——仅 entities.data JSON 数据迁移
|
|
15
15
|
* (setting 的旧 rules 分类值复制到 data.tags 并移除 rules,migrations/004_setting_tags.ts)。
|
|
16
|
+
* v5 → v6(对话历史出库):chat_messages 导出为 `sessions/<id>.jsonl` 后 DROP 表
|
|
17
|
+
* (migrations/006_sessions_jsonl.ts;会话从此不入库)。
|
|
16
18
|
*/
|
|
17
|
-
export const SCHEMA_VERSION =
|
|
19
|
+
export const SCHEMA_VERSION = 6; // +会话 JSONL(006_sessions_jsonl.ts 导出后 DROP 表)
|
|
18
20
|
/**
|
|
19
21
|
* 建表:执行全部 DDL(CREATE TABLE/INDEX IF NOT EXISTS,定义于 tables.ts),幂等,可重复调用。
|
|
20
22
|
* 打开数据库后由 connection.openDatabase 自动调用。
|
package/dist/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,EAAE;AACF,kDAAkD;AAClD,oCAAoC;AACpC,kFAAkF;AAGlF,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,EAAE;AACF,kDAAkD;AAClD,oCAAoC;AACpC,kFAAkF;AAGlF,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,CAAC,8CAA8C;AAE/E;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,EAAqB;IAChD,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;AAC7B,CAAC;AAED,2DAA2D;AAC3D,MAAM,UAAU,cAAc,CAAC,EAAqB;IAClD,OAAO,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAW,CAAC;AAC/D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,EAAqB,EAAE,OAAe;IACnE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,oCAAoC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACzE,CAAC;IACD,EAAE,CAAC,MAAM,CAAC,kBAAkB,OAAO,EAAE,CAAC,CAAC;AACzC,CAAC"}
|
package/dist/tables.d.ts
CHANGED
|
@@ -550,169 +550,10 @@ export declare const deltaRecords: import("drizzle-orm/sqlite-core").SQLiteTable
|
|
|
550
550
|
};
|
|
551
551
|
dialect: "sqlite";
|
|
552
552
|
}>;
|
|
553
|
-
/** chat_messages
|
|
554
|
-
export declare const chatMessages: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
|
|
555
|
-
name: "chat_messages";
|
|
556
|
-
schema: undefined;
|
|
557
|
-
columns: {
|
|
558
|
-
id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
559
|
-
name: "id";
|
|
560
|
-
tableName: "chat_messages";
|
|
561
|
-
dataType: "string";
|
|
562
|
-
columnType: "SQLiteText";
|
|
563
|
-
data: string;
|
|
564
|
-
driverParam: string;
|
|
565
|
-
notNull: true;
|
|
566
|
-
hasDefault: false;
|
|
567
|
-
isPrimaryKey: true;
|
|
568
|
-
isAutoincrement: false;
|
|
569
|
-
hasRuntimeDefault: false;
|
|
570
|
-
enumValues: [string, ...string[]];
|
|
571
|
-
baseColumn: never;
|
|
572
|
-
identity: undefined;
|
|
573
|
-
generated: undefined;
|
|
574
|
-
}, {}, {
|
|
575
|
-
length: number | undefined;
|
|
576
|
-
}>;
|
|
577
|
-
session_id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
578
|
-
name: "session_id";
|
|
579
|
-
tableName: "chat_messages";
|
|
580
|
-
dataType: "string";
|
|
581
|
-
columnType: "SQLiteText";
|
|
582
|
-
data: string;
|
|
583
|
-
driverParam: string;
|
|
584
|
-
notNull: true;
|
|
585
|
-
hasDefault: false;
|
|
586
|
-
isPrimaryKey: false;
|
|
587
|
-
isAutoincrement: false;
|
|
588
|
-
hasRuntimeDefault: false;
|
|
589
|
-
enumValues: [string, ...string[]];
|
|
590
|
-
baseColumn: never;
|
|
591
|
-
identity: undefined;
|
|
592
|
-
generated: undefined;
|
|
593
|
-
}, {}, {
|
|
594
|
-
length: number | undefined;
|
|
595
|
-
}>;
|
|
596
|
-
project_id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
597
|
-
name: "project_id";
|
|
598
|
-
tableName: "chat_messages";
|
|
599
|
-
dataType: "string";
|
|
600
|
-
columnType: "SQLiteText";
|
|
601
|
-
data: string;
|
|
602
|
-
driverParam: string;
|
|
603
|
-
notNull: true;
|
|
604
|
-
hasDefault: false;
|
|
605
|
-
isPrimaryKey: false;
|
|
606
|
-
isAutoincrement: false;
|
|
607
|
-
hasRuntimeDefault: false;
|
|
608
|
-
enumValues: [string, ...string[]];
|
|
609
|
-
baseColumn: never;
|
|
610
|
-
identity: undefined;
|
|
611
|
-
generated: undefined;
|
|
612
|
-
}, {}, {
|
|
613
|
-
length: number | undefined;
|
|
614
|
-
}>;
|
|
615
|
-
role: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
616
|
-
name: "role";
|
|
617
|
-
tableName: "chat_messages";
|
|
618
|
-
dataType: "string";
|
|
619
|
-
columnType: "SQLiteText";
|
|
620
|
-
data: string;
|
|
621
|
-
driverParam: string;
|
|
622
|
-
notNull: true;
|
|
623
|
-
hasDefault: false;
|
|
624
|
-
isPrimaryKey: false;
|
|
625
|
-
isAutoincrement: false;
|
|
626
|
-
hasRuntimeDefault: false;
|
|
627
|
-
enumValues: [string, ...string[]];
|
|
628
|
-
baseColumn: never;
|
|
629
|
-
identity: undefined;
|
|
630
|
-
generated: undefined;
|
|
631
|
-
}, {}, {
|
|
632
|
-
length: number | undefined;
|
|
633
|
-
}>;
|
|
634
|
-
content: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
635
|
-
name: "content";
|
|
636
|
-
tableName: "chat_messages";
|
|
637
|
-
dataType: "string";
|
|
638
|
-
columnType: "SQLiteText";
|
|
639
|
-
data: string;
|
|
640
|
-
driverParam: string;
|
|
641
|
-
notNull: false;
|
|
642
|
-
hasDefault: false;
|
|
643
|
-
isPrimaryKey: false;
|
|
644
|
-
isAutoincrement: false;
|
|
645
|
-
hasRuntimeDefault: false;
|
|
646
|
-
enumValues: [string, ...string[]];
|
|
647
|
-
baseColumn: never;
|
|
648
|
-
identity: undefined;
|
|
649
|
-
generated: undefined;
|
|
650
|
-
}, {}, {
|
|
651
|
-
length: number | undefined;
|
|
652
|
-
}>;
|
|
653
|
-
tool_calls: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
654
|
-
name: "tool_calls";
|
|
655
|
-
tableName: "chat_messages";
|
|
656
|
-
dataType: "string";
|
|
657
|
-
columnType: "SQLiteText";
|
|
658
|
-
data: string;
|
|
659
|
-
driverParam: string;
|
|
660
|
-
notNull: false;
|
|
661
|
-
hasDefault: false;
|
|
662
|
-
isPrimaryKey: false;
|
|
663
|
-
isAutoincrement: false;
|
|
664
|
-
hasRuntimeDefault: false;
|
|
665
|
-
enumValues: [string, ...string[]];
|
|
666
|
-
baseColumn: never;
|
|
667
|
-
identity: undefined;
|
|
668
|
-
generated: undefined;
|
|
669
|
-
}, {}, {
|
|
670
|
-
length: number | undefined;
|
|
671
|
-
}>;
|
|
672
|
-
tool_call_id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
673
|
-
name: "tool_call_id";
|
|
674
|
-
tableName: "chat_messages";
|
|
675
|
-
dataType: "string";
|
|
676
|
-
columnType: "SQLiteText";
|
|
677
|
-
data: string;
|
|
678
|
-
driverParam: string;
|
|
679
|
-
notNull: false;
|
|
680
|
-
hasDefault: false;
|
|
681
|
-
isPrimaryKey: false;
|
|
682
|
-
isAutoincrement: false;
|
|
683
|
-
hasRuntimeDefault: false;
|
|
684
|
-
enumValues: [string, ...string[]];
|
|
685
|
-
baseColumn: never;
|
|
686
|
-
identity: undefined;
|
|
687
|
-
generated: undefined;
|
|
688
|
-
}, {}, {
|
|
689
|
-
length: number | undefined;
|
|
690
|
-
}>;
|
|
691
|
-
created_at: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
692
|
-
name: "created_at";
|
|
693
|
-
tableName: "chat_messages";
|
|
694
|
-
dataType: "string";
|
|
695
|
-
columnType: "SQLiteText";
|
|
696
|
-
data: string;
|
|
697
|
-
driverParam: string;
|
|
698
|
-
notNull: true;
|
|
699
|
-
hasDefault: false;
|
|
700
|
-
isPrimaryKey: false;
|
|
701
|
-
isAutoincrement: false;
|
|
702
|
-
hasRuntimeDefault: false;
|
|
703
|
-
enumValues: [string, ...string[]];
|
|
704
|
-
baseColumn: never;
|
|
705
|
-
identity: undefined;
|
|
706
|
-
generated: undefined;
|
|
707
|
-
}, {}, {
|
|
708
|
-
length: number | undefined;
|
|
709
|
-
}>;
|
|
710
|
-
};
|
|
711
|
-
dialect: "sqlite";
|
|
712
|
-
}>;
|
|
553
|
+
/** chat_messages:已删除(迁移 006:对话历史出库为 `sessions/*.jsonl`,表已 DROP) */
|
|
713
554
|
/**
|
|
714
|
-
*
|
|
555
|
+
* 三张业务表 + 索引的建表 SQL(幂等:CREATE TABLE/INDEX IF NOT EXISTS)——建表执行的事实来源。
|
|
715
556
|
* 与上方 sqliteTable 定义必须同步(schema.test.ts 对齐断言覆盖)。
|
|
716
557
|
*/
|
|
717
|
-
export declare const CREATE_TABLES_SQL = "\n-- entities\uFF1A\u5B9E\u4F53\u8868\uFF08\u4EBA\u7269/\u8BBE\u5B9A/\u5730\u70B9/\u4F0F\u7B14/\u65F6\u95F4\u8F74\u4E8B\u4EF6/\u65F6\u95F4\u6807\u7B7E\u70B9\uFF09\nCREATE TABLE IF NOT EXISTS entities (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL CHECK(type IN ('character', 'setting', 'location', 'hook', 'event', 'timepoint', 'reference')),\n name TEXT NOT NULL,\n data TEXT NOT NULL DEFAULT '{}', -- JSON: \u5404\u7C7B\u578B\u7684\u4E13\u5C5E\u5B57\u6BB5\n sort_order INTEGER, -- \u65F6\u95F4\u8F74\u7EBF\u6027\u5E8F\uFF08G2 \u4FEE\u8BA2\uFF09\uFF1Aevent \u4E0E timepoint \u5404\u7C7B\u578B\u5185\u7EBF\u6027\uFF08\u5404\u81EA 0..n-1\uFF09\uFF0C\u5176\u4F59\u7C7B\u578B\u6052\u4E3A NULL\n created_at TEXT NOT NULL, -- ISO 8601\uFF0C\u5E94\u7528\u5C42\u5199\u5165\n updated_at TEXT NOT NULL, -- ISO 8601\uFF0C\u5E94\u7528\u5C42\u5199\u5165\uFF08\u63D0\u6848\u5FEB\u7167\u6BD4\u5BF9\uFF09\n deleted_at TEXT -- \u8F6F\u5220\u6807\u8BB0\uFF0CNULL \u8868\u793A\u672A\u5220\u9664\uFF1B\u975E NULL \u65F6\u8BE5\u5B9E\u4F53\u8FDB\u5165\u56DE\u6536\u7AD9\uFF0C\u672C\u4F53\u4FDD\u7559\u53EF\u8FD8\u539F\n);\n\n-- relation_records\uFF1A\u901A\u7528\u5173\u7CFB\u8868\nCREATE TABLE IF NOT EXISTS relation_records (\n id TEXT PRIMARY KEY,\n source_type TEXT NOT NULL, -- \u7AEF\u70B9\u7C7B\u578B\uFF1A\u5B9E\u4F53 'character'|'setting'|'location'|'hook'|'event'\uFF0C\u5927\u7EB2\u8282\u70B9 'outline_node'\n source_id TEXT NOT NULL,\n target_type TEXT NOT NULL,\n target_id TEXT NOT NULL,\n relation_type TEXT NOT NULL,\n metadata TEXT, -- JSON \u6269\u5C55\u5143\u6570\u636E\n created_at TEXT NOT NULL, -- ISO 8601\uFF0C\u5E94\u7528\u5C42\u5199\u5165\n updated_at TEXT NOT NULL, -- ISO 8601\uFF0C\u5E94\u7528\u5C42\u5199\u5165\uFF08\u63D0\u6848\u5FEB\u7167\u6BD4\u5BF9\uFF1B\u8F6F\u5220/\u8FD8\u539F\u4EA6\u66F4\u65B0\uFF09\n deleted_at TEXT -- \u7EA7\u8054\u8F6F\u5220\u6807\u8BB0\uFF1A\u4EC5\u5B9E\u4F53/\u8282\u70B9\u7EA7\u8054\u5220\u9664\u65F6\u5199\u5165\uFF1B\n -- \u624B\u52A8\u5220\u9664\u5173\u7CFB = \u7269\u7406\u5220\uFF08\u4E0D\u7F6E deleted_at\uFF0C\u4E0D\u8FDB\u5165\u56DE\u6536\u7AD9\uFF09\n);\n\n-- \u7D22\u5F15\uFF08\u8865\uFF09\uFF1Ak \u8DF3\u904D\u5386\u4E0E\u9AD8\u9891\u5173\u7CFB\u67E5\u8BE2\uFF08\u90E8\u5206\u7D22\u5F15\uFF0C\u4EC5\u8986\u76D6\u672A\u8F6F\u5220\u884C\uFF09\nCREATE INDEX IF NOT EXISTS idx_relation_source ON relation_records(source_id) WHERE deleted_at IS NULL;\nCREATE INDEX IF NOT EXISTS idx_relation_target ON relation_records(target_id) WHERE deleted_at IS NULL;\nCREATE INDEX IF NOT EXISTS idx_relation_type ON relation_records(relation_type) WHERE deleted_at IS NULL;\n\n-- delta_records\uFF1A\u5C5E\u6027\u53D8\u66F4\u8868\nCREATE TABLE IF NOT EXISTS delta_records (\n id TEXT PRIMARY KEY,\n node_id TEXT NOT NULL, -- \u89E6\u53D1\u53D8\u66F4\u7684\u5927\u7EB2\u8282\u70B9\n target_type TEXT NOT NULL,\n target_id TEXT NOT NULL,\n changes TEXT NOT NULL, -- JSON: [{field, op, from?, to?, value?}]\n description TEXT NOT NULL, -- \u4EBA\u7C7B\u53EF\u8BFB\u63CF\u8FF0\n \"order\" INTEGER NOT NULL DEFAULT 0, -- \u540C\u4E00\u8282\u70B9\u5185\u591A\u4E2A Delta \u7684\u6392\u5E8F\uFF08\u5168\u5C40\u5355\u8C03\u9012\u589E\uFF0C\u670D\u52A1\u7AEF\u751F\u6210\uFF09\n created_at TEXT NOT NULL, -- ISO 8601\uFF0C\u5E94\u7528\u5C42\u5199\u5165\n updated_at TEXT NOT NULL, -- ISO 8601\uFF0C\u5E94\u7528\u5C42\u5199\u5165\uFF08\u63D0\u6848\u5FEB\u7167\u6BD4\u5BF9\uFF09\n deleted_at TEXT -- \u7EA7\u8054\u8F6F\u5220\u6807\u8BB0\uFF1A\u4EC5\u5B9E\u4F53/\u8282\u70B9\u7EA7\u8054\u5220\u9664\u65F6\u5199\u5165\u3002\n -- \u53EF\u89C1\u6027\u8054\u52A8\u89E6\u53D1\u8282\u70B9\u4E0E\u76EE\u6807\u5B9E\u4F53\uFF1A\u4EFB\u4E00\u7AEF\u8F6F\u5220\u5373\u4E0D\u53EF\u89C1\n);\n
|
|
558
|
+
export declare const CREATE_TABLES_SQL = "\n-- entities\uFF1A\u5B9E\u4F53\u8868\uFF08\u4EBA\u7269/\u8BBE\u5B9A/\u5730\u70B9/\u4F0F\u7B14/\u65F6\u95F4\u8F74\u4E8B\u4EF6/\u65F6\u95F4\u6807\u7B7E\u70B9\uFF09\nCREATE TABLE IF NOT EXISTS entities (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL CHECK(type IN ('character', 'setting', 'location', 'hook', 'event', 'timepoint', 'reference')),\n name TEXT NOT NULL,\n data TEXT NOT NULL DEFAULT '{}', -- JSON: \u5404\u7C7B\u578B\u7684\u4E13\u5C5E\u5B57\u6BB5\n sort_order INTEGER, -- \u65F6\u95F4\u8F74\u7EBF\u6027\u5E8F\uFF08G2 \u4FEE\u8BA2\uFF09\uFF1Aevent \u4E0E timepoint \u5404\u7C7B\u578B\u5185\u7EBF\u6027\uFF08\u5404\u81EA 0..n-1\uFF09\uFF0C\u5176\u4F59\u7C7B\u578B\u6052\u4E3A NULL\n created_at TEXT NOT NULL, -- ISO 8601\uFF0C\u5E94\u7528\u5C42\u5199\u5165\n updated_at TEXT NOT NULL, -- ISO 8601\uFF0C\u5E94\u7528\u5C42\u5199\u5165\uFF08\u63D0\u6848\u5FEB\u7167\u6BD4\u5BF9\uFF09\n deleted_at TEXT -- \u8F6F\u5220\u6807\u8BB0\uFF0CNULL \u8868\u793A\u672A\u5220\u9664\uFF1B\u975E NULL \u65F6\u8BE5\u5B9E\u4F53\u8FDB\u5165\u56DE\u6536\u7AD9\uFF0C\u672C\u4F53\u4FDD\u7559\u53EF\u8FD8\u539F\n);\n\n-- relation_records\uFF1A\u901A\u7528\u5173\u7CFB\u8868\nCREATE TABLE IF NOT EXISTS relation_records (\n id TEXT PRIMARY KEY,\n source_type TEXT NOT NULL, -- \u7AEF\u70B9\u7C7B\u578B\uFF1A\u5B9E\u4F53 'character'|'setting'|'location'|'hook'|'event'\uFF0C\u5927\u7EB2\u8282\u70B9 'outline_node'\n source_id TEXT NOT NULL,\n target_type TEXT NOT NULL,\n target_id TEXT NOT NULL,\n relation_type TEXT NOT NULL,\n metadata TEXT, -- JSON \u6269\u5C55\u5143\u6570\u636E\n created_at TEXT NOT NULL, -- ISO 8601\uFF0C\u5E94\u7528\u5C42\u5199\u5165\n updated_at TEXT NOT NULL, -- ISO 8601\uFF0C\u5E94\u7528\u5C42\u5199\u5165\uFF08\u63D0\u6848\u5FEB\u7167\u6BD4\u5BF9\uFF1B\u8F6F\u5220/\u8FD8\u539F\u4EA6\u66F4\u65B0\uFF09\n deleted_at TEXT -- \u7EA7\u8054\u8F6F\u5220\u6807\u8BB0\uFF1A\u4EC5\u5B9E\u4F53/\u8282\u70B9\u7EA7\u8054\u5220\u9664\u65F6\u5199\u5165\uFF1B\n -- \u624B\u52A8\u5220\u9664\u5173\u7CFB = \u7269\u7406\u5220\uFF08\u4E0D\u7F6E deleted_at\uFF0C\u4E0D\u8FDB\u5165\u56DE\u6536\u7AD9\uFF09\n);\n\n-- \u7D22\u5F15\uFF08\u8865\uFF09\uFF1Ak \u8DF3\u904D\u5386\u4E0E\u9AD8\u9891\u5173\u7CFB\u67E5\u8BE2\uFF08\u90E8\u5206\u7D22\u5F15\uFF0C\u4EC5\u8986\u76D6\u672A\u8F6F\u5220\u884C\uFF09\nCREATE INDEX IF NOT EXISTS idx_relation_source ON relation_records(source_id) WHERE deleted_at IS NULL;\nCREATE INDEX IF NOT EXISTS idx_relation_target ON relation_records(target_id) WHERE deleted_at IS NULL;\nCREATE INDEX IF NOT EXISTS idx_relation_type ON relation_records(relation_type) WHERE deleted_at IS NULL;\n\n-- delta_records\uFF1A\u5C5E\u6027\u53D8\u66F4\u8868\nCREATE TABLE IF NOT EXISTS delta_records (\n id TEXT PRIMARY KEY,\n node_id TEXT NOT NULL, -- \u89E6\u53D1\u53D8\u66F4\u7684\u5927\u7EB2\u8282\u70B9\n target_type TEXT NOT NULL,\n target_id TEXT NOT NULL,\n changes TEXT NOT NULL, -- JSON: [{field, op, from?, to?, value?}]\n description TEXT NOT NULL, -- \u4EBA\u7C7B\u53EF\u8BFB\u63CF\u8FF0\n \"order\" INTEGER NOT NULL DEFAULT 0, -- \u540C\u4E00\u8282\u70B9\u5185\u591A\u4E2A Delta \u7684\u6392\u5E8F\uFF08\u5168\u5C40\u5355\u8C03\u9012\u589E\uFF0C\u670D\u52A1\u7AEF\u751F\u6210\uFF09\n created_at TEXT NOT NULL, -- ISO 8601\uFF0C\u5E94\u7528\u5C42\u5199\u5165\n updated_at TEXT NOT NULL, -- ISO 8601\uFF0C\u5E94\u7528\u5C42\u5199\u5165\uFF08\u63D0\u6848\u5FEB\u7167\u6BD4\u5BF9\uFF09\n deleted_at TEXT -- \u7EA7\u8054\u8F6F\u5220\u6807\u8BB0\uFF1A\u4EC5\u5B9E\u4F53/\u8282\u70B9\u7EA7\u8054\u5220\u9664\u65F6\u5199\u5165\u3002\n -- \u53EF\u89C1\u6027\u8054\u52A8\u89E6\u53D1\u8282\u70B9\u4E0E\u76EE\u6807\u5B9E\u4F53\uFF1A\u4EFB\u4E00\u7AEF\u8F6F\u5220\u5373\u4E0D\u53EF\u89C1\n);\n";
|
|
718
559
|
//# sourceMappingURL=tables.d.ts.map
|
package/dist/tables.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../src/tables.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../src/tables.ts"],"names":[],"mappings":"AAiBA,iDAAiD;AACjD,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoBpB,CAAC;AAEF,8DAA8D;AAC9D,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoB3B,CAAC;AAEF,6DAA6D;AAC7D,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAcxB,CAAC;AAEF,mEAAmE;AAEnE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,u+HA+C7B,CAAC"}
|
package/dist/tables.js
CHANGED
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
// 由 schema.test.ts「DDL 与定义对齐」断言锁住(列名/类型/notNull/主键机械比对)。
|
|
6
6
|
//
|
|
7
7
|
// 边界:
|
|
8
|
-
// - JSON 列(data/changes/metadata
|
|
8
|
+
// - JSON 列(data/changes/metadata)一律 **text 模式**,不用 drizzle `mode:'json'`——
|
|
9
9
|
// drizzle 的 json mode 对坏 JSON 直接 JSON.parse 抛错,会打挂整表查询;
|
|
10
10
|
// 防御在 queries 的 parseDataColumn 行映射层(坏行返回 {})。
|
|
11
|
+
// - 对话历史**不在库内**(项目目录 `sessions/*.jsonl`,见 sessions.ts / docs/db/schema.md)。
|
|
11
12
|
// - 标识符由 drizzle escapeName 双引号包裹输出(`"order"` 关键字列安全,无需特殊转义)。
|
|
12
13
|
// - CHECK 约束与部分索引两处都声明(drizzle 侧供对齐核对;真实建表以 DDL 常量为准)。
|
|
13
14
|
import { sql } from "drizzle-orm";
|
|
@@ -58,22 +59,9 @@ export const deltaRecords = sqliteTable("delta_records", {
|
|
|
58
59
|
updated_at: text("updated_at").notNull(),
|
|
59
60
|
deleted_at: text("deleted_at"),
|
|
60
61
|
});
|
|
61
|
-
/** chat_messages
|
|
62
|
-
export const chatMessages = sqliteTable("chat_messages", {
|
|
63
|
-
id: text("id").primaryKey(),
|
|
64
|
-
session_id: text("session_id").notNull(),
|
|
65
|
-
project_id: text("project_id").notNull(), // 会话按项目隔离
|
|
66
|
-
role: text("role").notNull(),
|
|
67
|
-
content: text("content"),
|
|
68
|
-
tool_calls: text("tool_calls"), // JSON(text 模式)
|
|
69
|
-
tool_call_id: text("tool_call_id"),
|
|
70
|
-
created_at: text("created_at").notNull(),
|
|
71
|
-
}, (t) => [
|
|
72
|
-
check("chat_messages_role_check", sql `${t.role} IN ('user', 'assistant', 'tool')`),
|
|
73
|
-
index("idx_chat_session").on(t.session_id, t.created_at),
|
|
74
|
-
]);
|
|
62
|
+
/** chat_messages:已删除(迁移 006:对话历史出库为 `sessions/*.jsonl`,表已 DROP) */
|
|
75
63
|
/**
|
|
76
|
-
*
|
|
64
|
+
* 三张业务表 + 索引的建表 SQL(幂等:CREATE TABLE/INDEX IF NOT EXISTS)——建表执行的事实来源。
|
|
77
65
|
* 与上方 sqliteTable 定义必须同步(schema.test.ts 对齐断言覆盖)。
|
|
78
66
|
*/
|
|
79
67
|
export const CREATE_TABLES_SQL = `
|
|
@@ -123,19 +111,5 @@ CREATE TABLE IF NOT EXISTS delta_records (
|
|
|
123
111
|
deleted_at TEXT -- 级联软删标记:仅实体/节点级联删除时写入。
|
|
124
112
|
-- 可见性联动触发节点与目标实体:任一端软删即不可见
|
|
125
113
|
);
|
|
126
|
-
|
|
127
|
-
-- chat_messages:对话历史表(与 data.db 同库)
|
|
128
|
-
CREATE TABLE IF NOT EXISTS chat_messages (
|
|
129
|
-
id TEXT PRIMARY KEY,
|
|
130
|
-
session_id TEXT NOT NULL,
|
|
131
|
-
project_id TEXT NOT NULL, -- 会话按项目隔离
|
|
132
|
-
role TEXT NOT NULL CHECK(role IN ('user','assistant','tool')),
|
|
133
|
-
content TEXT,
|
|
134
|
-
tool_calls TEXT, -- JSON: 助手消息的工具调用数组
|
|
135
|
-
tool_call_id TEXT, -- tool 消息关联的 assistant 工具调用 id
|
|
136
|
-
created_at TEXT NOT NULL -- ISO 8601,应用层写入
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
CREATE INDEX IF NOT EXISTS idx_chat_session ON chat_messages(session_id, created_at);
|
|
140
114
|
`;
|
|
141
115
|
//# sourceMappingURL=tables.js.map
|
package/dist/tables.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tables.js","sourceRoot":"","sources":["../src/tables.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,EAAE;AACF,iEAAiE;AACjE,oEAAoE;AACpE,yDAAyD;AACzD,EAAE;AACF,MAAM;AACN,
|
|
1
|
+
{"version":3,"file":"tables.js","sourceRoot":"","sources":["../src/tables.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@whispering233/ai-editor-db",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.33",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "数据库层:better-sqlite3 连接 / 建表 / 查询",
|
|
6
6
|
"type": "module",
|
|
@@ -26,10 +26,9 @@
|
|
|
26
26
|
"postpack": "node ../../scripts/restore-package-json.mjs"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@whispering233/ai-editor-shared": "0.0.
|
|
29
|
+
"@whispering233/ai-editor-shared": "0.0.33",
|
|
30
30
|
"better-sqlite3": "^13.0.0",
|
|
31
|
-
"drizzle-orm": "0.45.2"
|
|
32
|
-
"nanoid": "^5.1.0"
|
|
31
|
+
"drizzle-orm": "0.45.2"
|
|
33
32
|
},
|
|
34
33
|
"devDependencies": {
|
|
35
34
|
"@types/better-sqlite3": "^7.6.13",
|
package/dist/queries/chat.d.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { type ChatMessage, type ChatMessageRow, type ChatRole, type ChatSessionSummary } from "@whispering233/ai-editor-shared";
|
|
2
|
-
import type { Db } from "../connection.js";
|
|
3
|
-
/** 会话列表 lastMessage 截断长度(仅要求「截断」,长度为本实现约定,未入文档) */
|
|
4
|
-
export declare const SESSION_LAST_MESSAGE_MAX_LEN = 50;
|
|
5
|
-
/**
|
|
6
|
-
* 插入一条对话消息。
|
|
7
|
-
*
|
|
8
|
-
* id 生成约定:chat_messages 表 id 无文档前缀(id 约定仅覆盖
|
|
9
|
-
* char-/set-/loc-/hook-、sc-/ch-/vol-、proj- 与运行时 prop_/sess_/call_),
|
|
10
|
-
* 省略 id 时直接用 nanoid 生成(与消息 id 前缀空缺保持一致)。
|
|
11
|
-
*
|
|
12
|
-
* 存储形态转换:row.tool_calls 按 shared 类型为「解析后的数组」,
|
|
13
|
-
* 本函数负责 JSON.stringify 落库(列存 TEXT);created_at 必须由调用方
|
|
14
|
-
* 提供 ISO 8601 字符串(应用层写入)。
|
|
15
|
-
*/
|
|
16
|
-
export declare function insertChatMessage(db: Db, row: Pick<ChatMessageRow, "session_id" | "project_id" | "role" | "created_at"> & Partial<Pick<ChatMessageRow, "id" | "content" | "tool_calls" | "tool_call_id">>): void;
|
|
17
|
-
/**
|
|
18
|
-
* 会话归属迁移(B2.2 审核 P1-1):
|
|
19
|
-
* 跨项目恢复(备份包内 project_id ≠ 当前项目 id)后,chat_messages 的 project_id
|
|
20
|
-
* 从旧 id 批量迁移为当前项目 id——「保留 id 保会话」的理由在跨项目场景同样成立:
|
|
21
|
-
* 不迁移则恢复后聊天面板静默为空、旧会话行成孤儿数据(会话按 project_id 隔离)。
|
|
22
|
-
*
|
|
23
|
-
* @param fromProjectId 备份包内 project.json 的 id(旧归属)
|
|
24
|
-
* @param toProjectId 当前项目 id(迁移目标;调用方保证两者不等)
|
|
25
|
-
* @returns 受影响行数(0 = 无该旧 id 的消息;重复执行幂等——第二次起返回 0)
|
|
26
|
-
*/
|
|
27
|
-
export declare function migrateChatMessagesProject(db: Db, fromProjectId: string, toProjectId: string): number;
|
|
28
|
-
/**
|
|
29
|
-
* 会话列表(GET /api/v1/chat/sessions):
|
|
30
|
-
* - 按 project_id 隔离(会话按项目隔离)
|
|
31
|
-
* - 仅返回含消息的会话;updatedAt = 该会话最后一条消息的 created_at
|
|
32
|
-
* - 按最后活动时间(updatedAt)倒序,同时间戳按 session_id 升序保证稳定
|
|
33
|
-
* - lastMessage = 最后一条消息的 content 截断(无 content 时为空串)
|
|
34
|
-
*/
|
|
35
|
-
export declare function listSessions(db: Db, projectId: string): ChatSessionSummary[];
|
|
36
|
-
/**
|
|
37
|
-
* 解析 chat_messages.tool_calls 的 JSON 文本列为数组(ChatMessage.toolCalls)。
|
|
38
|
-
* 防御性处理:NULL / 非法 JSON / 非数组一律返回 null(视为无工具调用)。
|
|
39
|
-
*/
|
|
40
|
-
export declare function parseToolCalls(json: string | null): unknown[] | null;
|
|
41
|
-
/**
|
|
42
|
-
* 消息历史(GET /api/v1/chat/sessions/:id/messages):
|
|
43
|
-
* - 按 project_id 隔离
|
|
44
|
-
* - 按 created_at 升序,同时间戳按 rowid(插入序)升序保证稳定
|
|
45
|
-
* - 存储形态 → API 形态:tool_calls JSON 解析、tool_call_id → toolCallId、snake_case → camelCase
|
|
46
|
-
*/
|
|
47
|
-
export declare function listMessages(db: Db, sessionId: string, projectId: string): ChatMessage[];
|
|
48
|
-
/**
|
|
49
|
-
* 消息历史原始行(S7.6 续聊重建专用):
|
|
50
|
-
* - listMessages 输出 API 形态(camelCase,供 GET /messages);本函数输出**存储形态**
|
|
51
|
-
* (snake_case ChatMessageRow,tool_calls 已解析为数组)——直供 agent 包
|
|
52
|
-
* loadHistory/restoreSession(成对重组),避免 server 层做 API→存储形态反映射。
|
|
53
|
-
* - 查询语义与 listMessages 一致:按 project_id 隔离、created_at 升序 + rowid 稳定序。
|
|
54
|
-
*/
|
|
55
|
-
export declare function listMessageRows(db: Db, sessionId: string, projectId: string): ChatMessageRow[];
|
|
56
|
-
/** 成对重组后喂给 LLM 的历史消息形态(DeepSeek 要求 tool_call 与 tool 结果严格配对) */
|
|
57
|
-
export interface ReassembledChatMessage {
|
|
58
|
-
role: ChatRole;
|
|
59
|
-
content?: string | null;
|
|
60
|
-
/** assistant 消息的工具调用数组(仅当该组全部调用均成对成功时保留) */
|
|
61
|
-
toolCalls?: unknown[];
|
|
62
|
-
/** tool 消息关联的调用 id(与所属 assistant 消息 tool_calls[].id 配对) */
|
|
63
|
-
toolCallId?: string | null;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* 成对重组历史消息(assistant.tool_calls[].id ↔ tool.tool_call_id 成对重组喂回模型)。
|
|
67
|
-
*
|
|
68
|
-
* 用途:服务重启后凭 session_id 重建「继续上次对话」上下文,或会话级滑动窗口裁剪前的重组。
|
|
69
|
-
* 注意:本函数是纯函数,输入为 ChatMessageRow(tool_calls 已按 shared 类型为解析后的数组,
|
|
70
|
-
* 由调用方先经 parseToolCalls / 直接构造)。
|
|
71
|
-
*
|
|
72
|
-
* 算法:
|
|
73
|
-
* 1. 按 created_at 升序稳定排序(同时间戳保持输入顺序)
|
|
74
|
-
* 2. 第一遍收集 tool 消息:tool_call_id → content(同一 id 多条结果取最先到达者,防御性处理)
|
|
75
|
-
* 3. 第二遍顺序扫描:
|
|
76
|
-
* - user 消息:原样保留
|
|
77
|
-
* - assistant 消息(无 tool_calls):原样保留
|
|
78
|
-
* - assistant 消息(有 tool_calls):逐个调用校验——任一调用缺 id 或缺对应 tool 结果
|
|
79
|
-
* 即判定为孤儿半对,该 assistant 消息与其工具结果**整组丢弃**(
|
|
80
|
-
* 孤儿半对整对丢弃,不返回 409 也不部分保留);全部配对成功则输出 assistant 消息,
|
|
81
|
-
* 工具结果按 tool_calls 数组顺序紧随其后(tool 消息保留 toolCallId 供模型配对)
|
|
82
|
-
* - tool 消息:不单独输出——未被任何保留的 assistant 消息引用的即孤儿,整对丢弃
|
|
83
|
-
*/
|
|
84
|
-
export declare function reassembleMessages(rows: ChatMessageRow[]): ReassembledChatMessage[];
|
|
85
|
-
//# sourceMappingURL=chat.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/queries/chat.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAY,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,KAAK,QAAQ,EAAE,KAAK,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAC1I,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAI3C,mDAAmD;AACnD,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAE/C;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,EAAE,EACN,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,YAAY,GAAG,YAAY,GAAG,MAAM,GAAG,YAAY,CAAC,GAC5E,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,GAAG,SAAS,GAAG,YAAY,GAAG,cAAc,CAAC,CAAC,GAChF,IAAI,CAcN;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,EAAE,EAAE,EAAE,EAAE,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAOrG;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAqC5E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,EAAE,GAAG,IAAI,CASpE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,EAAE,CAoCxF;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,cAAc,EAAE,CAmC9F;AAED,+DAA+D;AAC/D,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,4CAA4C;IAC3C,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;IACvB,2DAA2D;IAC1D,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,sBAAsB,EAAE,CAwCnF"}
|
package/dist/queries/chat.js
DELETED
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
// @whispering233/ai-editor-db 对话历史数据层查询(T2.3)
|
|
2
|
-
//
|
|
3
|
-
// (chat/sessions 会话列表、chat/sessions/:id/messages 消息历史)。
|
|
4
|
-
// 时间约定:created_at 统一 ISO 8601 字符串、由应用层写入,本模块不生成时间。
|
|
5
|
-
// 注意:本模块只处理 chat_messages 表,不涉及会话元数据——会话列表信息(createdAt/updatedAt/
|
|
6
|
-
// messageCount/lastMessage)全部由消息行实时聚合得出,无独立会话表。
|
|
7
|
-
//
|
|
8
|
-
//:**查询经 queryDb 走 drizzle 构建器**(混合风格 4A)。
|
|
9
|
-
// 语义逐句对照旧实现:
|
|
10
|
-
// - INSERT 命名参数 ↔ insert(chatMessages).values({...})(tool_calls JSON.stringify 落库不变)
|
|
11
|
-
// - UPDATE ... SET project_id ↔ update.set({ project_id }).where(eq(...))
|
|
12
|
-
// - WHERE session_id = ? AND project_id = ? ↔ and(eq,eq);ORDER BY created_at ASC, rowid ASC ↔
|
|
13
|
-
// orderBy(chatMessages.created_at, sql`rowid ASC`)(rowid 为 SQLite 内部列,经 sql 模板原样透传)
|
|
14
|
-
// - listSessions 为相关子查询 + GROUP BY 聚合 + rowid 排序的复合 SQL,**保留 sql 模板**表达
|
|
15
|
-
// (builder 强行翻译需三层嵌套别名,违背 4A「需要 SQL 技巧就原生,不强行翻译」);
|
|
16
|
-
// projectId 两次注入均走 sql 模板参数绑定(安全,防注入)
|
|
17
|
-
// - parseToolCalls / reassembleMessages 为纯函数,不涉及 SQL,未改动
|
|
18
|
-
// - JSON 列 tool_calls 保持 text 模式:drizzle 行读出来是 string,解析防御仍在 parseToolCalls
|
|
19
|
-
import { nanoid } from "nanoid";
|
|
20
|
-
import { and, eq, sql } from "drizzle-orm";
|
|
21
|
-
import { truncate } from "@whispering233/ai-editor-shared";
|
|
22
|
-
import { queryDb } from "../query-db.js";
|
|
23
|
-
import { chatMessages } from "../tables.js";
|
|
24
|
-
/** 会话列表 lastMessage 截断长度(仅要求「截断」,长度为本实现约定,未入文档) */
|
|
25
|
-
export const SESSION_LAST_MESSAGE_MAX_LEN = 50;
|
|
26
|
-
/**
|
|
27
|
-
* 插入一条对话消息。
|
|
28
|
-
*
|
|
29
|
-
* id 生成约定:chat_messages 表 id 无文档前缀(id 约定仅覆盖
|
|
30
|
-
* char-/set-/loc-/hook-、sc-/ch-/vol-、proj- 与运行时 prop_/sess_/call_),
|
|
31
|
-
* 省略 id 时直接用 nanoid 生成(与消息 id 前缀空缺保持一致)。
|
|
32
|
-
*
|
|
33
|
-
* 存储形态转换:row.tool_calls 按 shared 类型为「解析后的数组」,
|
|
34
|
-
* 本函数负责 JSON.stringify 落库(列存 TEXT);created_at 必须由调用方
|
|
35
|
-
* 提供 ISO 8601 字符串(应用层写入)。
|
|
36
|
-
*/
|
|
37
|
-
export function insertChatMessage(db, row) {
|
|
38
|
-
queryDb(db)
|
|
39
|
-
.insert(chatMessages)
|
|
40
|
-
.values({
|
|
41
|
-
id: row.id ?? nanoid(),
|
|
42
|
-
session_id: row.session_id,
|
|
43
|
-
project_id: row.project_id,
|
|
44
|
-
role: row.role,
|
|
45
|
-
content: row.content ?? null,
|
|
46
|
-
tool_calls: row.tool_calls == null ? null : JSON.stringify(row.tool_calls),
|
|
47
|
-
tool_call_id: row.tool_call_id ?? null,
|
|
48
|
-
created_at: row.created_at,
|
|
49
|
-
})
|
|
50
|
-
.run();
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* 会话归属迁移(B2.2 审核 P1-1):
|
|
54
|
-
* 跨项目恢复(备份包内 project_id ≠ 当前项目 id)后,chat_messages 的 project_id
|
|
55
|
-
* 从旧 id 批量迁移为当前项目 id——「保留 id 保会话」的理由在跨项目场景同样成立:
|
|
56
|
-
* 不迁移则恢复后聊天面板静默为空、旧会话行成孤儿数据(会话按 project_id 隔离)。
|
|
57
|
-
*
|
|
58
|
-
* @param fromProjectId 备份包内 project.json 的 id(旧归属)
|
|
59
|
-
* @param toProjectId 当前项目 id(迁移目标;调用方保证两者不等)
|
|
60
|
-
* @returns 受影响行数(0 = 无该旧 id 的消息;重复执行幂等——第二次起返回 0)
|
|
61
|
-
*/
|
|
62
|
-
export function migrateChatMessagesProject(db, fromProjectId, toProjectId) {
|
|
63
|
-
const info = queryDb(db)
|
|
64
|
-
.update(chatMessages)
|
|
65
|
-
.set({ project_id: toProjectId })
|
|
66
|
-
.where(eq(chatMessages.project_id, fromProjectId))
|
|
67
|
-
.run();
|
|
68
|
-
return info.changes;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* 会话列表(GET /api/v1/chat/sessions):
|
|
72
|
-
* - 按 project_id 隔离(会话按项目隔离)
|
|
73
|
-
* - 仅返回含消息的会话;updatedAt = 该会话最后一条消息的 created_at
|
|
74
|
-
* - 按最后活动时间(updatedAt)倒序,同时间戳按 session_id 升序保证稳定
|
|
75
|
-
* - lastMessage = 最后一条消息的 content 截断(无 content 时为空串)
|
|
76
|
-
*/
|
|
77
|
-
export function listSessions(db, projectId) {
|
|
78
|
-
// 相关子查询(取每会话最后一条消息)+ GROUP BY 聚合(count/min/max)的复合 SQL:
|
|
79
|
-
// sql 模板表达(4A 混合风格),projectId 两次注入走参数绑定
|
|
80
|
-
const rows = queryDb(db).all(sql `
|
|
81
|
-
SELECT
|
|
82
|
-
t.session_id AS id,
|
|
83
|
-
t.message_count,
|
|
84
|
-
t.created_at,
|
|
85
|
-
t.updated_at,
|
|
86
|
-
(SELECT lm.content FROM chat_messages lm
|
|
87
|
-
WHERE lm.session_id = t.session_id AND lm.project_id = ${projectId}
|
|
88
|
-
ORDER BY lm.created_at DESC, lm.rowid DESC LIMIT 1) AS last_content
|
|
89
|
-
FROM (
|
|
90
|
-
SELECT session_id,
|
|
91
|
-
COUNT(*) AS message_count,
|
|
92
|
-
MIN(created_at) AS created_at,
|
|
93
|
-
MAX(created_at) AS updated_at
|
|
94
|
-
FROM chat_messages
|
|
95
|
-
WHERE project_id = ${projectId}
|
|
96
|
-
GROUP BY session_id
|
|
97
|
-
) t
|
|
98
|
-
ORDER BY t.updated_at DESC, t.session_id ASC
|
|
99
|
-
`);
|
|
100
|
-
return rows.map((r) => ({
|
|
101
|
-
id: r.id,
|
|
102
|
-
lastMessage: truncate(r.last_content ?? "", SESSION_LAST_MESSAGE_MAX_LEN),
|
|
103
|
-
messageCount: r.message_count,
|
|
104
|
-
createdAt: r.created_at,
|
|
105
|
-
updatedAt: r.updated_at,
|
|
106
|
-
}));
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* 解析 chat_messages.tool_calls 的 JSON 文本列为数组(ChatMessage.toolCalls)。
|
|
110
|
-
* 防御性处理:NULL / 非法 JSON / 非数组一律返回 null(视为无工具调用)。
|
|
111
|
-
*/
|
|
112
|
-
export function parseToolCalls(json) {
|
|
113
|
-
if (json === null)
|
|
114
|
-
return null;
|
|
115
|
-
try {
|
|
116
|
-
const parsed = JSON.parse(json);
|
|
117
|
-
return Array.isArray(parsed) ? parsed : null;
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
// 非法 JSON:按无工具调用处理,不向上抛(查询层不阻断会话恢复)
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* 消息历史(GET /api/v1/chat/sessions/:id/messages):
|
|
126
|
-
* - 按 project_id 隔离
|
|
127
|
-
* - 按 created_at 升序,同时间戳按 rowid(插入序)升序保证稳定
|
|
128
|
-
* - 存储形态 → API 形态:tool_calls JSON 解析、tool_call_id → toolCallId、snake_case → camelCase
|
|
129
|
-
*/
|
|
130
|
-
export function listMessages(db, sessionId, projectId) {
|
|
131
|
-
const rows = queryDb(db)
|
|
132
|
-
.select({
|
|
133
|
-
id: chatMessages.id,
|
|
134
|
-
session_id: chatMessages.session_id,
|
|
135
|
-
project_id: chatMessages.project_id,
|
|
136
|
-
role: chatMessages.role,
|
|
137
|
-
content: chatMessages.content,
|
|
138
|
-
tool_calls: chatMessages.tool_calls,
|
|
139
|
-
tool_call_id: chatMessages.tool_call_id,
|
|
140
|
-
created_at: chatMessages.created_at,
|
|
141
|
-
})
|
|
142
|
-
.from(chatMessages)
|
|
143
|
-
.where(and(eq(chatMessages.session_id, sessionId), eq(chatMessages.project_id, projectId)))
|
|
144
|
-
.orderBy(chatMessages.created_at, sql `rowid ASC`)
|
|
145
|
-
.all();
|
|
146
|
-
return rows.map((r) => ({
|
|
147
|
-
id: r.id,
|
|
148
|
-
sessionId: r.session_id,
|
|
149
|
-
projectId: r.project_id,
|
|
150
|
-
role: r.role,
|
|
151
|
-
content: r.content,
|
|
152
|
-
toolCalls: parseToolCalls(r.tool_calls) ?? undefined,
|
|
153
|
-
toolCallId: r.tool_call_id,
|
|
154
|
-
createdAt: r.created_at,
|
|
155
|
-
}));
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* 消息历史原始行(S7.6 续聊重建专用):
|
|
159
|
-
* - listMessages 输出 API 形态(camelCase,供 GET /messages);本函数输出**存储形态**
|
|
160
|
-
* (snake_case ChatMessageRow,tool_calls 已解析为数组)——直供 agent 包
|
|
161
|
-
* loadHistory/restoreSession(成对重组),避免 server 层做 API→存储形态反映射。
|
|
162
|
-
* - 查询语义与 listMessages 一致:按 project_id 隔离、created_at 升序 + rowid 稳定序。
|
|
163
|
-
*/
|
|
164
|
-
export function listMessageRows(db, sessionId, projectId) {
|
|
165
|
-
const rows = queryDb(db)
|
|
166
|
-
.select({
|
|
167
|
-
id: chatMessages.id,
|
|
168
|
-
session_id: chatMessages.session_id,
|
|
169
|
-
project_id: chatMessages.project_id,
|
|
170
|
-
role: chatMessages.role,
|
|
171
|
-
content: chatMessages.content,
|
|
172
|
-
tool_calls: chatMessages.tool_calls,
|
|
173
|
-
tool_call_id: chatMessages.tool_call_id,
|
|
174
|
-
created_at: chatMessages.created_at,
|
|
175
|
-
})
|
|
176
|
-
.from(chatMessages)
|
|
177
|
-
.where(and(eq(chatMessages.session_id, sessionId), eq(chatMessages.project_id, projectId)))
|
|
178
|
-
.orderBy(chatMessages.created_at, sql `rowid ASC`)
|
|
179
|
-
.all();
|
|
180
|
-
return rows.map((r) => ({
|
|
181
|
-
id: r.id,
|
|
182
|
-
session_id: r.session_id,
|
|
183
|
-
project_id: r.project_id,
|
|
184
|
-
role: r.role,
|
|
185
|
-
content: r.content,
|
|
186
|
-
tool_calls: parseToolCalls(r.tool_calls), // TEXT 列 → 解析后的数组(NULL/损坏 → null)
|
|
187
|
-
tool_call_id: r.tool_call_id,
|
|
188
|
-
created_at: r.created_at,
|
|
189
|
-
}));
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* 成对重组历史消息(assistant.tool_calls[].id ↔ tool.tool_call_id 成对重组喂回模型)。
|
|
193
|
-
*
|
|
194
|
-
* 用途:服务重启后凭 session_id 重建「继续上次对话」上下文,或会话级滑动窗口裁剪前的重组。
|
|
195
|
-
* 注意:本函数是纯函数,输入为 ChatMessageRow(tool_calls 已按 shared 类型为解析后的数组,
|
|
196
|
-
* 由调用方先经 parseToolCalls / 直接构造)。
|
|
197
|
-
*
|
|
198
|
-
* 算法:
|
|
199
|
-
* 1. 按 created_at 升序稳定排序(同时间戳保持输入顺序)
|
|
200
|
-
* 2. 第一遍收集 tool 消息:tool_call_id → content(同一 id 多条结果取最先到达者,防御性处理)
|
|
201
|
-
* 3. 第二遍顺序扫描:
|
|
202
|
-
* - user 消息:原样保留
|
|
203
|
-
* - assistant 消息(无 tool_calls):原样保留
|
|
204
|
-
* - assistant 消息(有 tool_calls):逐个调用校验——任一调用缺 id 或缺对应 tool 结果
|
|
205
|
-
* 即判定为孤儿半对,该 assistant 消息与其工具结果**整组丢弃**(
|
|
206
|
-
* 孤儿半对整对丢弃,不返回 409 也不部分保留);全部配对成功则输出 assistant 消息,
|
|
207
|
-
* 工具结果按 tool_calls 数组顺序紧随其后(tool 消息保留 toolCallId 供模型配对)
|
|
208
|
-
* - tool 消息:不单独输出——未被任何保留的 assistant 消息引用的即孤儿,整对丢弃
|
|
209
|
-
*/
|
|
210
|
-
export function reassembleMessages(rows) {
|
|
211
|
-
// 1. 按 created_at 升序稳定排序
|
|
212
|
-
const sorted = [...rows].sort((a, b) => a.created_at.localeCompare(b.created_at));
|
|
213
|
-
// 2. 收集 tool 消息:tool_call_id → content
|
|
214
|
-
const toolResults = new Map();
|
|
215
|
-
for (const r of sorted) {
|
|
216
|
-
if (r.role === "tool" && r.tool_call_id !== null && !toolResults.has(r.tool_call_id)) {
|
|
217
|
-
toolResults.set(r.tool_call_id, r.content);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
// 3. 顺序扫描重组
|
|
221
|
-
const out = [];
|
|
222
|
-
for (const r of sorted) {
|
|
223
|
-
if (r.role === "user") {
|
|
224
|
-
out.push({ role: "user", content: r.content });
|
|
225
|
-
continue;
|
|
226
|
-
}
|
|
227
|
-
if (r.role === "assistant") {
|
|
228
|
-
const calls = r.tool_calls ?? [];
|
|
229
|
-
if (calls.length === 0) {
|
|
230
|
-
out.push({ role: "assistant", content: r.content });
|
|
231
|
-
continue;
|
|
232
|
-
}
|
|
233
|
-
const callIds = calls.map((c) => c.id);
|
|
234
|
-
// 任一调用缺 id 或缺对应结果 → 整组丢弃
|
|
235
|
-
const allPaired = callIds.every((cid) => typeof cid === "string" && toolResults.has(cid));
|
|
236
|
-
if (!allPaired)
|
|
237
|
-
continue;
|
|
238
|
-
out.push({ role: "assistant", content: r.content, toolCalls: calls });
|
|
239
|
-
for (const cid of callIds) {
|
|
240
|
-
out.push({ role: "tool", toolCallId: cid, content: toolResults.get(cid) });
|
|
241
|
-
}
|
|
242
|
-
continue;
|
|
243
|
-
}
|
|
244
|
-
// role === "tool":仅作为上面对应的结果被引用;孤儿在此自然丢弃
|
|
245
|
-
}
|
|
246
|
-
return out;
|
|
247
|
-
}
|
|
248
|
-
//# sourceMappingURL=chat.js.map
|
package/dist/queries/chat.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../../src/queries/chat.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,EAAE;AACF,wDAAwD;AACxD,mDAAmD;AACnD,kEAAkE;AAClE,gDAAgD;AAChD,EAAE;AACF,0CAA0C;AAC1C,aAAa;AACb,qFAAqF;AACrF,0EAA0E;AAC1E,8FAA8F;AAC9F,oFAAoF;AACpF,wEAAwE;AACxE,mDAAmD;AACnD,sCAAsC;AACtC,yDAAyD;AACzD,4EAA4E;AAE5E,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAiF,MAAM,iCAAiC,CAAC;AAE1I,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,mDAAmD;AACnD,MAAM,CAAC,MAAM,4BAA4B,GAAG,EAAE,CAAC;AAE/C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB,CAC/B,EAAM,EACN,GACiF;IAEjF,OAAO,CAAC,EAAE,CAAC;SACR,MAAM,CAAC,YAAY,CAAC;SACpB,MAAM,CAAC;QACN,EAAE,EAAE,GAAG,CAAC,EAAE,IAAI,MAAM,EAAE;QACtB,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,IAAI;QAC5B,UAAU,EAAE,GAAG,CAAC,UAAU,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC;QAC1E,YAAY,EAAE,GAAG,CAAC,YAAY,IAAI,IAAI;QACtC,UAAU,EAAE,GAAG,CAAC,UAAU;KAC3B,CAAC;SACD,GAAG,EAAE,CAAC;AACX,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,0BAA0B,CAAC,EAAM,EAAE,aAAqB,EAAE,WAAmB;IAC3F,MAAM,IAAI,GAAG,OAAO,CAAC,EAAE,CAAC;SACrB,MAAM,CAAC,YAAY,CAAC;SACpB,GAAG,CAAC,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;SAChC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;SACjD,GAAG,EAAE,CAAC;IACT,OAAO,IAAI,CAAC,OAAO,CAAC;AACtB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,EAAM,EAAE,SAAiB;IACrD,wDAAwD;IACxD,wCAAwC;IACvC,MAAM,IAAI,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAA;;;;;;;iEAO+B,SAAS;;;;;;;;2BAQ/C,SAAS;;;;GAIjC,CAMC,CAAC;IAEH,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtB,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,YAAY,IAAI,EAAE,EAAE,4BAA4B,CAAC;QACzE,YAAY,EAAE,CAAC,CAAC,aAAa;QAC7B,SAAS,EAAE,CAAC,CAAC,UAAU;QACvB,SAAS,EAAE,CAAC,CAAC,UAAU;KACxB,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAmB;IAChD,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACV,oCAAoC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,EAAM,EAAE,SAAiB,EAAE,SAAiB;IACvE,MAAM,IAAI,GAAG,OAAO,CAAC,EAAE,CAAC;SACrB,MAAM,CAAC;QACN,EAAE,EAAE,YAAY,CAAC,EAAE;QACnB,UAAU,EAAE,YAAY,CAAC,UAAU;QACnC,UAAU,EAAE,YAAY,CAAC,UAAU;QACnC,IAAI,EAAE,YAAY,CAAC,IAAI;QACvB,OAAO,EAAE,YAAY,CAAC,OAAO;QAC7B,UAAU,EAAE,YAAY,CAAC,UAAU;QACnC,YAAY,EAAE,YAAY,CAAC,YAAY;QACvC,UAAU,EAAE,YAAY,CAAC,UAAU;KACpC,CAAC;SACD,IAAI,CAAC,YAAY,CAAC;SAClB,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC;SAC1F,OAAO,CAAC,YAAY,CAAC,UAAU,EAAE,GAAG,CAAA,WAAW,CAAC;SAChD,GAAG,EASJ,CAAC;IAEH,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtB,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,SAAS,EAAE,CAAC,CAAC,UAAU;QACvB,SAAS,EAAE,CAAC,CAAC,UAAU;QACvB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,SAAS;QACpD,UAAU,EAAE,CAAC,CAAC,YAAY;QAC1B,SAAS,EAAE,CAAC,CAAC,UAAU;KACxB,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,EAAM,EAAE,SAAiB,EAAE,SAAiB;IAC1E,MAAM,IAAI,GAAG,OAAO,CAAC,EAAE,CAAC;SACrB,MAAM,CAAC;QACN,EAAE,EAAE,YAAY,CAAC,EAAE;QACnB,UAAU,EAAE,YAAY,CAAC,UAAU;QACnC,UAAU,EAAE,YAAY,CAAC,UAAU;QACnC,IAAI,EAAE,YAAY,CAAC,IAAI;QACvB,OAAO,EAAE,YAAY,CAAC,OAAO;QAC7B,UAAU,EAAE,YAAY,CAAC,UAAU;QACnC,YAAY,EAAE,YAAY,CAAC,YAAY;QACvC,UAAU,EAAE,YAAY,CAAC,UAAU;KACpC,CAAC;SACD,IAAI,CAAC,YAAY,CAAC;SAClB,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC;SAC1F,OAAO,CAAC,YAAY,CAAC,UAAU,EAAE,GAAG,CAAA,WAAW,CAAC;SAChD,GAAG,EASJ,CAAC;IACH,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACtB,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,UAAU,EAAE,CAAC,CAAC,UAAU;QACxB,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,UAAU,EAAE,cAAc,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,kCAAkC;QAC5E,YAAY,EAAE,CAAC,CAAC,YAAY;QAC5B,UAAU,EAAE,CAAC,CAAC,UAAU;KACzB,CAAC,CAAC,CAAC;AACN,CAAC;AAYD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAsB;IACxD,yBAAyB;IACxB,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IAEnF,uCAAuC;IACtC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAyB,CAAC;IACrD,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,YAAY,KAAK,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC;YACrF,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;IAEF,YAAY;IACX,MAAM,GAAG,GAA6B,EAAE,CAAC;IACzC,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACtB,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YAC/C,SAAS;QACX,CAAC;QACD,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YAC3B,MAAM,KAAK,GAAG,CAAC,CAAC,UAAU,IAAI,EAAE,CAAC;YACjC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;gBACpD,SAAS;YACX,CAAC;YACD,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAE,CAAsB,CAAC,EAAE,CAAC,CAAC;YAClE,yBAAyB;YACpB,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAC7B,CAAC,GAAG,EAAiB,EAAE,CAAC,OAAO,GAAG,KAAK,QAAQ,IAAI,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CACxE,CAAC;YACF,IAAI,CAAC,SAAS;gBAAE,SAAS;YACzB,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;YACtE,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;gBAC1B,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YAC7E,CAAC;YACD,SAAS;QACX,CAAC;QACJ,yCAAyC;IACxC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|