@morlay/session-rdb 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -166
- package/dist/artifact.d.mts +2 -2
- package/dist/artifact.mjs +3 -2
- package/dist/{branch-D4nVzQQM.mjs → branch-IQii6Muq.mjs} +1 -8
- package/dist/deletion.d.mts +1 -2
- package/dist/deletion.mjs +2 -1
- package/dist/export.d.mts +1 -3
- package/dist/export.mjs +1 -4
- package/dist/gc.d.mts +1 -3
- package/dist/gc.mjs +1 -3
- package/dist/import.d.mts +1 -2
- package/dist/import.mjs +2 -7
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +4 -3
- package/dist/{log-BWVFYAh5.mjs → log-D5C8-TGs.mjs} +0 -52
- package/dist/{log-CUjKQbOR.d.mts → log-tZMKFRKi.d.mts} +17 -168
- package/dist/routes-DGBlBE_I.mjs +10 -0
- package/dist/{sqlite-SCiSnwaz.mjs → sqlite-CMMSn5kE.mjs} +24 -62
- package/dist/{src-BZ1tffd5.mjs → src-dsd2H70D.mjs} +59 -97
- package/dist/storage.d.mts +2 -22
- package/dist/storage.mjs +1 -1
- package/dist/testing.d.mts +1 -1
- package/dist/testing.mjs +1 -1
- package/dist/usage.d.mts +1 -1
- package/dist/usage.mjs +1 -17
- package/package.json +19 -19
- package/src/backend.ts +35 -54
- package/src/branch.ts +3 -5
- package/src/deletion.ts +2 -1
- package/src/entities/v3/event-usage.ts +3 -6
- package/src/entities/v3/session-counts.ts +3 -5
- package/src/entities/v3/session-usage.ts +3 -5
- package/src/export.ts +5 -4
- package/src/gc.ts +4 -3
- package/src/import.ts +5 -6
- package/src/index.ts +47 -45
- package/src/legacy.ts +3 -5
- package/src/log.ts +24 -53
- package/src/postgres.ts +70 -61
- package/src/routes.ts +16 -0
- package/src/rows.ts +11 -15
- package/src/session-query.ts +3 -7
- package/src/sqlite.ts +70 -58
- package/src/storage-takeover/projection-cache.ts +6 -8
- package/src/storage-takeover/repository.ts +2 -2
- package/src/storage-takeover/types.ts +1 -1
- package/src/usage.ts +22 -34
- package/src/write-guard.ts +1 -1
package/README.md
CHANGED
|
@@ -1,182 +1,33 @@
|
|
|
1
1
|
# @morlay/session-rdb
|
|
2
2
|
|
|
3
|
-
RDB
|
|
4
|
-
`
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
会话持久化与分支回退的 RDB 后端(SQLite / PostgreSQL):提供 `ctx.sessionPersistence`(上游 `SessionHandle`
|
|
4
|
+
模型:`create` / `open` / `flush` / `stat` / `list`)与 `ctx.sessionBranch`(`rewind` / `retry` / `fork` 的
|
|
5
|
+
provider 面),并接管 `ctx.sessionQuery`(全文检索 disabled)与 `$DSH_HOME/storages` 的两个域
|
|
6
|
+
(workspace、投影 checkpoint)。
|
|
7
7
|
|
|
8
|
-
##
|
|
8
|
+
## 用法
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
配置就是这一行的 config(聚合层 `@morlay/better-session` 的 `session-rdb` 行、profile patch,或设置页改的
|
|
11
|
+
那份 entry config):
|
|
12
12
|
|
|
13
13
|
```yaml
|
|
14
14
|
- id: session-rdb
|
|
15
15
|
name: "@morlay/session-rdb"
|
|
16
16
|
config:
|
|
17
17
|
type: sqlite
|
|
18
|
-
# path
|
|
19
|
-
#
|
|
18
|
+
# 省略 path 时回落 bundle patch 的默认($DSH_HOME/sessions/sessions.sqlite,由 patch 的 !!js 表达式求值);
|
|
19
|
+
# 自定义路径请用绝对路径字符串。
|
|
20
20
|
path: /absolute/path/to/sessions.sqlite
|
|
21
21
|
journalMode: wal
|
|
22
22
|
busyTimeout: 5000
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
PostgreSQL 同理:`type: postgres` + `connectionString
|
|
25
|
+
PostgreSQL 同理:`type: postgres` + `connectionString`(可加 `schema`,默认 `public`)。改配置由 Loader 重挂
|
|
26
|
+
这一行生效(换后端 = 换库,旧数据留在旧后端);字段、默认值与设置页行为见
|
|
27
|
+
[设计总览](.agents/designs/20260917-设计总览.md) 的「配置门面」。
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
```ts
|
|
34
|
-
type Config =
|
|
35
|
-
| {
|
|
36
|
-
type: "sqlite";
|
|
37
|
-
/** SQLite 数据库文件路径;`:memory:` 用于测试。 */
|
|
38
|
-
path: string;
|
|
39
|
-
/** journal_mode:`wal`(默认)/ `delete` / `truncate` / `persist`。 */
|
|
40
|
-
journalMode?: "wal" | "delete" | "truncate" | "persist";
|
|
41
|
-
/** 写锁竞争等待毫秒数(默认 5000)。 */
|
|
42
|
-
busyTimeout?: number;
|
|
43
|
-
/** 投影 checkpoint 的写入节奏(`writeEveryEvents` / `writeIntervalMs`)。 */
|
|
44
|
-
projectionCache?: ProjectionCacheOptions;
|
|
45
|
-
}
|
|
46
|
-
| {
|
|
47
|
-
type: "postgres";
|
|
48
|
-
/** node-postgres 连接串;首次打开自动建表并写入 store 身份。 */
|
|
49
|
-
connectionString: string;
|
|
50
|
-
/** 目标 schema(默认 public,必须已存在)。 */
|
|
51
|
-
schema?: string;
|
|
52
|
-
/** 投影 checkpoint 的写入节奏(`writeEveryEvents` / `writeIntervalMs`)。 */
|
|
53
|
-
projectionCache?: ProjectionCacheOptions;
|
|
54
|
-
};
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
### 设置页上的这一行
|
|
58
|
-
|
|
59
|
-
`Config` **整段标了 `.volatile()`**(判别式 union 只能整段标:schemastery 不许在 union 分支内部标 volatile),
|
|
60
|
-
所以 host 会描述这一行,插件管理页里按 `type` 展开当前后端的字段——与上面 YAML 里的字段一一对应。
|
|
61
|
-
|
|
62
|
-
- **切后端**:`type` 是各分支的判别标签,页面上那一行给的是一个**切换控件**(`"sqlite"` ↔ `"postgres"`);选另一支
|
|
63
|
-
就换成那一套字段(`path` ↔ `connectionString` / `schema`),页面结构与 host 侧校验的选支依据同一份。
|
|
64
|
-
- **生效方式**:settings 写完 → Loader 重装这一行(`reconcileProfilePatches`)→ 用新 config 重新建连接。所以这里
|
|
65
|
-
装配时取一次快照(`this.config = config.get()`),没有运行期重连逻辑;换后端 = 换库,旧数据留在旧后端。
|
|
66
|
-
- 默认值都在 schema 上(`journalMode: wal`、`busyTimeout`、`schema: public`、`projectionCache` 的节流),页面上
|
|
67
|
-
清空即回到这些默认。
|
|
68
|
-
|
|
69
|
-
## 分支能力(session-branch 闭环)
|
|
70
|
-
|
|
71
|
-
除 `ctx.sessionPersistence` 外,本包还实现 `@morlay/session-branch` 的 provider
|
|
72
|
-
抽象并**随插件自动注册 `ctx.sessionBranch`**(`SessionBranchRdb`),在不修改上游
|
|
73
|
-
代码的前提下提供 `rewind / retry / fork` 的持久化闭环(原语:`forkFrom` 纯 append
|
|
74
|
-
派生、`rewind` 后端事务截断)——语义、坐标论证与已知限制见
|
|
75
|
-
[分支能力](.agents/designs/20260917-分支能力.md)。
|
|
76
|
-
|
|
77
|
-
上层编排(edit / reroll / retry / rewind / fork 完整功能)由
|
|
78
|
-
`@morlay/ui-conversation-message-actions` 提供,或直接在 `ctx.sessionBranch` /
|
|
79
|
-
`ctx.sessionEditor` 之上编程。
|
|
80
|
-
|
|
81
|
-
## 会话删除(仅已归档)
|
|
82
|
-
|
|
83
|
-
delete 面由本包自持:`deleteSession(id)` 只允许删除**已归档**会话(未归档报
|
|
84
|
-
`SESSION_NOT_ARCHIVED`),live(有打开的 handle 或未 materialize)报 `SESSION_LIVE`。
|
|
85
|
-
删除只落该会话的行(桥接行、workspace 归属行、投影 checkpoint、会话行)——被删会话的事件行
|
|
86
|
-
成为**孤儿**留在 `t_events`,不影响删除后的可见性,回收见下节。
|
|
87
|
-
|
|
88
|
-
web 模式经 `POST /api/session.delete`(body `{ sessionId }`)暴露,状态映射:
|
|
89
|
-
200 已删除 / 404 不存在 / 409 未归档或 live。决策与边界见
|
|
90
|
-
[ADR-会话删除仅限已归档且硬删](.agents/adrs/20260917-会话删除仅限已归档且硬删.md) 与
|
|
91
|
-
[ADR-删除不再清孤儿与vacuum独立成gc通道](.agents/adrs/20260918-删除不再清孤儿与vacuum独立成gc通道.md)。
|
|
92
|
-
|
|
93
|
-
## 会话列表(两条路)
|
|
94
|
-
|
|
95
|
-
| 用途 | 接缝 | 内容 |
|
|
96
|
-
| ---------------------- | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
97
|
-
| 官方 UI(上游 `ui-*`) | `session/list` → `ctx.sessionQuery.listSessions`(本包接管,**默认排除已归档**) | 未归档的可见语料,标题走投影(`projections.title`) |
|
|
98
|
-
| 我们自己的管理面 | `POST /api/session.rows`(本包路由) | **完整语料(含归档)** + 标题 + 最后活动时间 + 工作区归属,**搜索 / 子代理过滤 / 分页都在后端** |
|
|
99
|
-
|
|
100
|
-
排除归档是本部署的策略:归档是「收起来」的会话,不该混进日常列表与搜索。管理动作(取消归档 / 删除 /
|
|
101
|
-
导入)需要完整集合,所以另给一条自有路由,两条路不混。决策与取舍见
|
|
102
|
-
[ADR-会话列表两条路](.agents/adrs/20260922-会话列表两条路.md)。
|
|
103
|
-
|
|
104
|
-
`session.rows` 的请求体是 `{ query?, includeSubagents?, page?, pageSize? }`(`pageSize` 默认 20、上限 200),
|
|
105
|
-
响应是 `{ items, total, page, pageSize }`——**搜索与分页都在后端**:前端分页等于每次拉全量,数据一多就崩。
|
|
106
|
-
|
|
107
|
-
排序键是会话行的 **`f_last_event_at`**(由写路径维护、老数据由迁移一次性回填、没有事件的会话回落
|
|
108
|
-
`f_created_at`)。早先这里对每行算一次「该会话最后事件时间」的相关子查询:真实库 243 行要 **4.2s**,
|
|
109
|
-
加 `LIMIT` 也救不了(排序要求全表先算出来);物化之后同一条分页查询是 **0.2ms**。
|
|
110
|
-
|
|
111
|
-
## 导出
|
|
112
|
-
|
|
113
|
-
`POST /api/session.export`(body `{ sessionId }`)直接把会话日志打成 zip **响应体**:
|
|
114
|
-
`content-type: application/zip` + `Content-Disposition` 文件名,包内 artifact 与导入通道读的
|
|
115
|
-
是同一份(`readRaw` 的 `session.vN.jsonl`)。会话不存在 404,body 非法 400。只读,不限归档状态。
|
|
116
|
-
|
|
117
|
-
## 孤儿回收与 VACUUM(GC)
|
|
118
|
-
|
|
119
|
-
`POST /api/session.gc` 一次做完四件事:让所有运行中的 agent 退场
|
|
120
|
-
(`ctx.agents.list()` 逐个 cancel 并等 `whenIdle`)→ `collectOrphanSessions()` 回收**父已不存在的
|
|
121
|
-
subagent 会话**(`origin = 'subagent'`、父不在表里;有 open handle / pending 的跳过)→
|
|
122
|
-
`collectOrphans()` 回收已无桥接行引用的事件行 → `vacuum()`(SQLite `VACUUM`、Postgres
|
|
123
|
-
`VACUUM ANALYZE`,均不得在事务内执行)。顺序上先删会话再清事件行,被删会话独占的事件行才刚成为孤儿。
|
|
124
|
-
响应 `{ orphanSessions, orphanEvents, stoppedAgents, vacuumed }`。
|
|
125
|
-
|
|
126
|
-
为什么独立成通道(而不是删除时顺带做):实测删除的成本大头就是那句全库孤儿清理
|
|
127
|
-
(1 万行会话:545ms → 加孤儿清理后 2.3s),且它无法让库文件变小。代价是删除后库体积不降,
|
|
128
|
-
直到执行 GC;入口在「对话管理」页,执行期间阻塞界面。
|
|
129
|
-
|
|
130
|
-
## 用量统计
|
|
131
|
-
|
|
132
|
-
`POST /api/session.usage` 一次回报三份数据:`totals`(含 `subagent` / `human` 拆分)、按
|
|
133
|
-
天 × provider/model × subagent 的桶、按会话的行(活动计数:轮次 / 步骤 / 用户输入 / 工具调用)。
|
|
134
|
-
|
|
135
|
-
统计读侧只读三张**可销毁重建的派生表**,读侧维度(会话 / 模型 / 本地日 / 是否被引用 / 是否子代理)
|
|
136
|
-
全部在写入时物化,查询不做 `EXISTS`、也不回连事件表:
|
|
137
|
-
|
|
138
|
-
- `t_event_usage`——一条 `assistant/message` 事件行一行(去重口径的总量与「天 × 模型」桶读它);
|
|
139
|
-
- `t_session_usage`——会话 × 本地日 × 模型的 token 汇总(按会话的行读它,含 fork 继承前缀);
|
|
140
|
-
- `t_session_counts`——会话 × 本地日的四项活动计数。
|
|
141
|
-
|
|
142
|
-
写入时在事务内落用量行、事务后**旁路累加**两张汇总表;rewind / fork / 删除会话之后按会话重算并全量重算
|
|
143
|
-
引用标记;表为空(首次 / 迁移后 / 手动清空)在打开库时全量回填——迁移对这三张表先删后建。真实库实测
|
|
144
|
-
单次统计从 **1.6s → ~140ms → ~30ms**(口径不变,旧口径逐项交叉核对一致),升级后首次打开付一次回填
|
|
145
|
-
(2.4s / 3.09 万用量行,此后不再发生)。口径与取舍见
|
|
146
|
-
[ADR-统计衍生表物化归属与汇总](.agents/adrs/20260923-统计衍生表物化归属与汇总.md)。
|
|
147
|
-
|
|
148
|
-
## 查询接管(`ctx.sessionQuery`)
|
|
149
|
-
|
|
150
|
-
官方 `session-query-sqlite` 由 `@morlay/better-session` 的 patch 禁用,本包注册 `ctx.sessionQuery`:
|
|
151
|
-
精确读 / 过滤 / 血缘复用上游 `SessionQueryEngine` 基类,**全文检索恒 disabled**
|
|
152
|
-
(抛 `SESSION_QUERY_SEARCH_DISABLED`,不引入派生索引库)。决策见
|
|
153
|
-
[ADR-接管会话查询服务所有权](.agents/adrs/20260917-接管会话查询服务所有权.md)。
|
|
154
|
-
|
|
155
|
-
## storages 接管(workspace 与投影缓存)
|
|
156
|
-
|
|
157
|
-
`$DSH_HOME/storages` 不再产生文件:官方 `storage-json` 与
|
|
158
|
-
`session-projection-cache` 由 `@morlay/better-session` 的 patch 禁用,数据落本包的
|
|
159
|
-
语义专用表——workspace 域经本包注册的 `rdb` KV 后端,投影 checkpoint 经本包的
|
|
160
|
-
`ctx.sessionProjectionCache` 服务。表结构见
|
|
161
|
-
[表结构](.agents/designs/20260917-表结构.md),决策见
|
|
162
|
-
[ADR-接管storages到rdb语义表](.agents/adrs/20260917-接管storages到rdb语义表.md)。
|
|
163
|
-
|
|
164
|
-
写节流参数(默认 200 / 5000,与官方 base 装配一致)同样写在行 config 里:
|
|
165
|
-
|
|
166
|
-
```yaml
|
|
167
|
-
- id: session-rdb
|
|
168
|
-
name: "@morlay/session-rdb"
|
|
169
|
-
config:
|
|
170
|
-
type: sqlite
|
|
171
|
-
path: /absolute/path/to/sessions.sqlite
|
|
172
|
-
projectionCache:
|
|
173
|
-
writeEveryEvents: 200
|
|
174
|
-
writeIntervalMs: 5000
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
旧 `storages` JSON 的导入是**包内 API**(先停掉 dsh,旧文件保留不删)——没有 CLI
|
|
178
|
-
入口、也不在 `exports` 里:
|
|
179
|
-
|
|
180
|
-
```ts
|
|
181
|
-
await importStorages(repository, { dshHome }); // 用法见 src/import-storages.ts
|
|
182
|
-
```
|
|
29
|
+
管理面的 HTTP 通道统一挂在 `/api/morlay/v1/session` 前缀下(均为 `POST`,body `{ sessionId }` 或列表请求
|
|
30
|
+
`{ query?, includeSubagents?, page?, pageSize? }`):`/rows`(完整语料,含归档)、`/export`、`/import`、
|
|
31
|
+
`/delete`、`/gc`、`/usage`(前缀与上游分家的理由见
|
|
32
|
+
[ADR-管理面HTTP路由前缀与上游分家](.agents/adrs/20260929-管理面HTTP路由前缀与上游分家.md))。旧
|
|
33
|
+
`$DSH_HOME/storages` JSON 的导入是包内 API `importStorages`(`src/import-storages.ts`,不在包出口里)。
|
package/dist/artifact.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { C as usageRowOf, S as toJsonlArtifact, _ as sealOwnEvents, a as normalizeToCurrentShape, b as syncMeteringRanges, c as renameLegacyPtcEvents, d as repairReadView, f as repairRequestHeaders, g as scanRows, h as rowToMeta, i as needsShapeAdoption, l as repairAssistantSettlement, m as rowToEvent, n as OWN_EVENT_TYPES, o as orphanInboxSpliceSeqs, p as repairSurfaceOps, r as findSurfaceRepairs, s as recomputeReplaceProvenance, t as EventUsageRow, u as repairOrphanInboxSplices, v as sessionConflictRow, x as titleOfEventData, y as sessionInsertRow } from "./log-
|
|
2
|
-
import {
|
|
1
|
+
import { C as usageRowOf, Pt as SESSION_IMPORT_PATH, S as toJsonlArtifact, _ as sealOwnEvents, a as normalizeToCurrentShape, b as syncMeteringRanges, c as renameLegacyPtcEvents, d as repairReadView, f as repairRequestHeaders, g as scanRows, h as rowToMeta, i as needsShapeAdoption, l as repairAssistantSettlement, m as rowToEvent, n as OWN_EVENT_TYPES, o as orphanInboxSpliceSeqs, p as repairSurfaceOps, r as findSurfaceRepairs, s as recomputeReplaceProvenance, t as EventUsageRow, u as repairOrphanInboxSplices, v as sessionConflictRow, x as titleOfEventData, y as sessionInsertRow } from "./log-tZMKFRKi.mjs";
|
|
2
|
+
import { SESSION_LOG_ARTIFACT_FILENAME, parseImportZip, parseJsonlArtifact, persistImport, registerSessionImport } from "./import.mjs";
|
|
3
3
|
export { EventUsageRow, OWN_EVENT_TYPES, SESSION_IMPORT_PATH, SESSION_LOG_ARTIFACT_FILENAME, findSurfaceRepairs, needsShapeAdoption, normalizeToCurrentShape, orphanInboxSpliceSeqs, parseImportZip, parseJsonlArtifact, persistImport, recomputeReplaceProvenance, registerSessionImport, renameLegacyPtcEvents, repairAssistantSettlement, repairOrphanInboxSplices, repairReadView, repairRequestHeaders, repairSurfaceOps, rowToEvent, rowToMeta, scanRows, sealOwnEvents, sessionConflictRow, sessionInsertRow, syncMeteringRanges, titleOfEventData, toJsonlArtifact, usageRowOf };
|
package/dist/artifact.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { i as SESSION_IMPORT_PATH } from "./routes-DGBlBE_I.mjs";
|
|
2
|
+
import { S as usageRowOf, _ as sessionConflictRow, a as orphanInboxSpliceSeqs, b as titleOfEventData, c as repairAssistantSettlement, d as repairRequestHeaders, f as repairSurfaceOps, g as sealOwnEvents, h as scanRows, i as normalizeToCurrentShape, l as repairOrphanInboxSplices, m as rowToMeta, n as findSurfaceRepairs, o as recomputeReplaceProvenance, p as rowToEvent, r as needsShapeAdoption, s as renameLegacyPtcEvents, t as OWN_EVENT_TYPES, u as repairReadView, v as sessionInsertRow, x as toJsonlArtifact, y as syncMeteringRanges } from "./log-D5C8-TGs.mjs";
|
|
3
|
+
import { SESSION_LOG_ARTIFACT_FILENAME, parseImportZip, parseJsonlArtifact, persistImport, registerSessionImport } from "./import.mjs";
|
|
3
4
|
export { OWN_EVENT_TYPES, SESSION_IMPORT_PATH, SESSION_LOG_ARTIFACT_FILENAME, findSurfaceRepairs, needsShapeAdoption, normalizeToCurrentShape, orphanInboxSpliceSeqs, parseImportZip, parseJsonlArtifact, persistImport, recomputeReplaceProvenance, registerSessionImport, renameLegacyPtcEvents, repairAssistantSettlement, repairOrphanInboxSplices, repairReadView, repairRequestHeaders, repairSurfaceOps, rowToEvent, rowToMeta, scanRows, sealOwnEvents, sessionConflictRow, sessionInsertRow, syncMeteringRanges, titleOfEventData, toJsonlArtifact, usageRowOf };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as repairRequestHeaders, h as scanRows, i as normalizeToCurrentShape, m as rowToMeta } from "./log-
|
|
1
|
+
import { d as repairRequestHeaders, h as scanRows, i as normalizeToCurrentShape, m as rowToMeta } from "./log-D5C8-TGs.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { SESSION_FORMAT_VERSION, SessionLogOffset } from "@deepseek-ai/dsh-session";
|
|
4
4
|
import { createSessionFormatCatalogWithChildren } from "@deepseek-ai/dsh-session-format-catalog";
|
|
@@ -35,11 +35,6 @@ function physicalEvent(row) {
|
|
|
35
35
|
...row.fSurfaceOp !== null ? { surfaceOp: JSON.parse(row.fSurfaceOp) } : {}
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
-
/**
|
|
39
|
-
* 读旧格式用的 catalog:v3→v4 这条边要求显式子会话证据(上游 0.1.7 起),本仓库声明
|
|
40
|
-
* **无子会话证据**——v3 的目录事实由父会话自己在成功路径上写成 `subagent/catalog` 事件,
|
|
41
|
-
* 迁移的 child evidence 只是补偿入口;我们没有更权威的子集合来源可给(见 ADR-跟随上游session-format-v4)。
|
|
42
|
-
*/
|
|
43
38
|
const restoreCatalog = createSessionFormatCatalogWithChildren([]);
|
|
44
39
|
function convertLegacyRows(row, eventRows) {
|
|
45
40
|
const restore = restoreCatalog.createRestore(physicalHeader(row), {
|
|
@@ -217,7 +212,6 @@ var SessionBranchRdbProvider = class {
|
|
|
217
212
|
internals.dropReuseEventIds(childId);
|
|
218
213
|
throw error;
|
|
219
214
|
}
|
|
220
|
-
await this.persistence.rebuildSessionStats(childId);
|
|
221
215
|
return childId;
|
|
222
216
|
}
|
|
223
217
|
async rewind(id, toBoundary, signal) {
|
|
@@ -290,7 +284,6 @@ var SessionBranchRdbProvider = class {
|
|
|
290
284
|
headSeq: keepLength - 1,
|
|
291
285
|
snapshotEvents: () => []
|
|
292
286
|
});
|
|
293
|
-
await this.persistence.rebuildSessionStats(id);
|
|
294
287
|
return {
|
|
295
288
|
header: rowToMeta(row),
|
|
296
289
|
revision: await internals.readStoredRevision(id)
|
package/dist/deletion.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { et as SessionPersistenceRdb, jt as SESSION_DELETE_PATH } from "./log-tZMKFRKi.mjs";
|
|
2
2
|
import { Context } from "@deepseek-ai/cordis";
|
|
3
3
|
//#region src/deletion.d.ts
|
|
4
|
-
declare const SESSION_DELETE_PATH = "/api/session.delete";
|
|
5
4
|
declare function registerSessionDeletion(ctx: Context, persistence: SessionPersistenceRdb): void;
|
|
6
5
|
//#endregion
|
|
7
6
|
export { SESSION_DELETE_PATH, registerSessionDeletion };
|
package/dist/deletion.mjs
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { t as SESSION_DELETE_PATH } from "./routes-DGBlBE_I.mjs";
|
|
2
|
+
import { r as registerSessionDeletion } from "./src-dsd2H70D.mjs";
|
|
2
3
|
export { SESSION_DELETE_PATH, registerSessionDeletion };
|
package/dist/export.d.mts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Mt as SESSION_EXPORT_PATH, et as SessionPersistenceRdb } from "./log-tZMKFRKi.mjs";
|
|
2
2
|
import { Context } from "@deepseek-ai/cordis";
|
|
3
3
|
//#region src/export.d.ts
|
|
4
|
-
declare const SESSION_EXPORT_PATH = "/api/session.export";
|
|
5
|
-
/** 导出通道:直接把会话日志打成 zip 响应体,与导入通道读同一份 artifact。 */
|
|
6
4
|
declare function registerSessionExport(ctx: Context, persistence: SessionPersistenceRdb): void;
|
|
7
5
|
//#endregion
|
|
8
6
|
export { SESSION_EXPORT_PATH, registerSessionExport };
|
package/dist/export.mjs
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
+
import { n as SESSION_EXPORT_PATH } from "./routes-DGBlBE_I.mjs";
|
|
1
2
|
import { strToU8, zip } from "fflate";
|
|
2
3
|
//#region src/export.ts
|
|
3
|
-
const SESSION_EXPORT_PATH = "/api/session.export";
|
|
4
|
-
/** 文件名只保留安全字符:会话 id 来自请求体,会写进 Content-Disposition。 */
|
|
5
4
|
function safeFilename(sessionId) {
|
|
6
5
|
return sessionId.replace(/[^A-Za-z0-9._-]/gu, "_");
|
|
7
6
|
}
|
|
8
|
-
/** fflate 只给回调式异步 API(同步变体被 node/no-sync 禁止)。 */
|
|
9
7
|
function zipBytes(files) {
|
|
10
8
|
return new Promise((resolve, reject) => {
|
|
11
9
|
zip(files, (error, data) => {
|
|
@@ -14,7 +12,6 @@ function zipBytes(files) {
|
|
|
14
12
|
});
|
|
15
13
|
});
|
|
16
14
|
}
|
|
17
|
-
/** 导出通道:直接把会话日志打成 zip 响应体,与导入通道读同一份 artifact。 */
|
|
18
15
|
function registerSessionExport(ctx, persistence) {
|
|
19
16
|
ctx.inject(["webServer", "connection"], (webCtx) => {
|
|
20
17
|
const webServer = webCtx.webServer;
|
package/dist/gc.d.mts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Nt as SESSION_GC_PATH, et as SessionPersistenceRdb } from "./log-tZMKFRKi.mjs";
|
|
2
2
|
import { Context } from "@deepseek-ai/cordis";
|
|
3
3
|
//#region src/gc.d.ts
|
|
4
|
-
declare const SESSION_GC_PATH = "/api/session.gc";
|
|
5
|
-
/** GC 通道:停 agent → 回收孤儿事件行 → VACUUM,一次请求完成。 */
|
|
6
4
|
declare function registerSessionGc(ctx: Context, persistence: SessionPersistenceRdb): void;
|
|
7
5
|
//#endregion
|
|
8
6
|
export { SESSION_GC_PATH, registerSessionGc };
|
package/dist/gc.mjs
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
+
import { r as SESSION_GC_PATH } from "./routes-DGBlBE_I.mjs";
|
|
1
2
|
//#region src/gc.ts
|
|
2
|
-
const SESSION_GC_PATH = "/api/session.gc";
|
|
3
|
-
/** 让所有运行中的 agent 先退场:GC 要重写事件表,运行中的写路径必须停下来。 */
|
|
4
3
|
async function stopRunningAgents(ctx) {
|
|
5
4
|
const running = ctx.get("agents")?.list() ?? [];
|
|
6
5
|
for (const agent of running) agent.cancel?.({ kind: "user" }, { keepInbox: true });
|
|
7
6
|
await Promise.all(running.map((agent) => agent.whenIdle()));
|
|
8
7
|
return running.length;
|
|
9
8
|
}
|
|
10
|
-
/** GC 通道:停 agent → 回收孤儿事件行 → VACUUM,一次请求完成。 */
|
|
11
9
|
function registerSessionGc(ctx, persistence) {
|
|
12
10
|
ctx.inject(["webServer", "connection"], (webCtx) => {
|
|
13
11
|
const webServer = webCtx.webServer;
|
package/dist/import.d.mts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Pt as SESSION_IMPORT_PATH, et as SessionPersistenceRdb } from "./log-tZMKFRKi.mjs";
|
|
2
2
|
import { SessionStorageMetadata } from "@deepseek-ai/dsh-session-persistence";
|
|
3
3
|
import { Session, SessionEvent, SessionId } from "@deepseek-ai/dsh-session";
|
|
4
4
|
import { Context } from "@deepseek-ai/cordis";
|
|
5
5
|
//#region src/import.d.ts
|
|
6
6
|
declare const SESSION_LOG_ARTIFACT_FILENAME = "session.jsonl";
|
|
7
|
-
declare const SESSION_IMPORT_PATH = "/api/session.import";
|
|
8
7
|
declare function parseJsonlArtifact(content: string): SessionStorageMetadata & {
|
|
9
8
|
events: SessionEvent[];
|
|
10
9
|
};
|
package/dist/import.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { i as
|
|
1
|
+
import { i as SESSION_IMPORT_PATH } from "./routes-DGBlBE_I.mjs";
|
|
2
|
+
import { i as replaceLiveSessionLog, l as restoreCatalog } from "./branch-IQii6Muq.mjs";
|
|
2
3
|
import { randomUUID } from "node:crypto";
|
|
3
4
|
import { SESSION_FORMAT_VERSION, SessionLogOffset } from "@deepseek-ai/dsh-session";
|
|
4
5
|
import { parseSessionFormatLogFilename } from "@deepseek-ai/dsh-session-format";
|
|
@@ -6,7 +7,6 @@ import { balanceRewindPrefix } from "@morlay/session-branch";
|
|
|
6
7
|
import { unzip } from "fflate";
|
|
7
8
|
//#region src/import.ts
|
|
8
9
|
const SESSION_LOG_ARTIFACT_FILENAME = "session.jsonl";
|
|
9
|
-
const SESSION_IMPORT_PATH = "/api/session.import";
|
|
10
10
|
const MAX_IMPORT_ZIP_BYTES = 67108864;
|
|
11
11
|
function parseJsonlArtifact(content) {
|
|
12
12
|
const lines = content.split("\n");
|
|
@@ -67,11 +67,6 @@ function parseJsonlArtifact(content) {
|
|
|
67
67
|
events
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
|
-
/**
|
|
71
|
-
* fflate 的 `unzip` 是回调式异步 API(node 侧内部走 worker_threads,浏览器侧走 Web Worker),
|
|
72
|
-
* 这里桥成 Promise。它与 `unzipSync` 的差异只在同步/异步:非法 zip 走的是同一段校验代码,
|
|
73
|
-
* 拒绝的错误对象与同步版一致,因此上层文案无需区分。
|
|
74
|
-
*/
|
|
75
70
|
function unzipAsync(data) {
|
|
76
71
|
return new Promise((resolve, reject) => {
|
|
77
72
|
unzip(data, (error, entries) => {
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as SessionDeletionErrorCode, Ft as SESSION_ROWS_PATH, Q as SessionDeletionError, X as ProjectionCacheOptions, Y as Config, Z as ResolvedConfig, et as SessionPersistenceRdb, it as locateTurnEnd, nt as SessionBranchRdb, ot as SessionRowsItem, pt as SCHEMA_VERSION, rt as SessionBranchRdbProvider, st as SessionRowsValue, tt as SessionPersistenceRdbInternals } from "./log-tZMKFRKi.mjs";
|
|
2
2
|
export { Config, ProjectionCacheOptions, ResolvedConfig, SCHEMA_VERSION, SESSION_ROWS_PATH, SessionBranchRdb, SessionBranchRdbProvider, SessionDeletionError, SessionDeletionErrorCode, SessionPersistenceRdb, SessionPersistenceRdb as default, SessionPersistenceRdbInternals, type SessionRowsItem, type SessionRowsValue, locateTurnEnd };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { o as SCHEMA_VERSION } from "./sqlite-
|
|
2
|
-
import {
|
|
3
|
-
import { n as
|
|
1
|
+
import { o as SCHEMA_VERSION } from "./sqlite-CMMSn5kE.mjs";
|
|
2
|
+
import { a as SESSION_ROWS_PATH } from "./routes-DGBlBE_I.mjs";
|
|
3
|
+
import { n as SessionPersistenceRdb, t as SessionDeletionError } from "./src-dsd2H70D.mjs";
|
|
4
|
+
import { n as SessionBranchRdbProvider, r as locateTurnEnd, t as SessionBranchRdb } from "./branch-IQii6Muq.mjs";
|
|
4
5
|
export { SCHEMA_VERSION, SESSION_ROWS_PATH, SessionBranchRdb, SessionBranchRdbProvider, SessionDeletionError, SessionPersistenceRdb, SessionPersistenceRdb as default, locateTurnEnd };
|
|
@@ -84,17 +84,11 @@ const SURFACE_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
84
84
|
"tool/result"
|
|
85
85
|
]);
|
|
86
86
|
const METERING_EVENT_TYPES = /* @__PURE__ */ new Set(["compaction/summary", "compaction/prune"]);
|
|
87
|
-
/**
|
|
88
|
-
* 升序的 surface 事件 seq 索引:读视图修复里的溯源(replace 的 `sourceEventSeqs` 与
|
|
89
|
-
* metering 的 `shadowedSeqs`)共用这一份,避免每个 replace 各扫一遍全量事件。
|
|
90
|
-
* 前提是输入按 seq 有序(`readLog` 与 live snapshot 都如此)。
|
|
91
|
-
*/
|
|
92
87
|
function surfaceSeqIndex(events) {
|
|
93
88
|
const seqs = [];
|
|
94
89
|
for (const event of events) if (SURFACE_EVENT_TYPES.has(event.type)) seqs.push(event.seq);
|
|
95
90
|
return seqs;
|
|
96
91
|
}
|
|
97
|
-
/** `[startSeq, endSeq]` 内的 surface seq(含端点),二分定位后切片。 */
|
|
98
92
|
function surfaceSeqsInRange(seqs, startSeq, endSeq) {
|
|
99
93
|
let low = 0;
|
|
100
94
|
let high = seqs.length;
|
|
@@ -346,20 +340,6 @@ function renameLegacyPtcEvents(events) {
|
|
|
346
340
|
}
|
|
347
341
|
}
|
|
348
342
|
}
|
|
349
|
-
/**
|
|
350
|
-
* 把**旧代(v3 及更早)的消息形状**归一到当前格式(v4):迁移链(严格)拒绝那些形状时,读路径仍要
|
|
351
|
-
* 能把它们读出来——只动字段与标记,不改坐标、不改内容。
|
|
352
|
-
*
|
|
353
|
-
* 三件事:
|
|
354
|
-
* - `system/message` 的 source:`{ kind: 'plugin', plugin: '@deepseek-ai/dsh-system-prompt' }` → `{ kind: 'system-prompt' }`
|
|
355
|
-
* (v4 起 system 消息只认这个 kind);
|
|
356
|
-
* - `tool/result` 的消息:v3 是 user 角色、结果块包在 `content[0]`;v4 是 `role: 'tool'` + 顶层
|
|
357
|
-
* `toolCallId` + `content` 就是结果块;
|
|
358
|
-
* - **本仓库自造、上游不认识的事件类型**(见 `OWN_EVENT_TYPES`:历史遗留的与我们当前在写的)标
|
|
359
|
-
* `ignorable: true`——v4 的校验对未知类型只接受可忽略信封。
|
|
360
|
-
*
|
|
361
|
-
* 只认这张白名单:**其它未知类型保持 fail loud**(那是「数据来自更新的 harness」的信号,不能吞掉)。
|
|
362
|
-
*/
|
|
363
343
|
function normalizeToCurrentShape(events) {
|
|
364
344
|
for (const event of events) {
|
|
365
345
|
const type = event.type;
|
|
@@ -405,33 +385,10 @@ function liftToolResultMessage(message) {
|
|
|
405
385
|
callId
|
|
406
386
|
};
|
|
407
387
|
}
|
|
408
|
-
/**
|
|
409
|
-
* 落库前给**本仓库自造的事件类型**补上 `ignorable: true` 信封。
|
|
410
|
-
*
|
|
411
|
-
* 上游的持久化校验(`validateStoredEvents`)只放行「已知类型」或「带可忽略信封的未知类型」,我们的类型
|
|
412
|
-
* 按构造就在已知表之外——不补的话连**写入**都会被拒(`appendBatch` 里那道校验)。上游 `Session.append`
|
|
413
|
-
* 没有承载这个信封的口子(第三个参数只服务 surface 事件),所以这一笔由持久化层补:canonical log 里的
|
|
414
|
-
* 形状不变,落库的那一份带上信封。
|
|
415
|
-
* @param events - 即将落库的批次(`materializeAppendBatch` 的 JSON 快照副本,可写)。
|
|
416
|
-
*/
|
|
417
388
|
function sealOwnEvents(events) {
|
|
418
389
|
for (const event of events) if (OWN_EVENT_TYPES.has(event.type)) event.ignorable = true;
|
|
419
390
|
}
|
|
420
|
-
/**
|
|
421
|
-
* 本仓库自造、上游不认识的事件类型:读的时候标 `ignorable: true`,v4 的持久化校验才肯跳过它们
|
|
422
|
-
* (`validateStoredEvents` 只放行「已知类型」或「带可忽略信封的未知类型」)。
|
|
423
|
-
*
|
|
424
|
-
* 两类都在这张表里:
|
|
425
|
-
* - **历史遗留**:写过、后来连同机制一起删除的(`session-branch/version`);
|
|
426
|
-
* - **当前在写**:本仓库的插件自己声明的事件类型(`session-mode/selected`)——上游的
|
|
427
|
-
* `KNOWN_SESSION_EVENT_TYPES` 由上游仓库的声明生成,外部插件的事件按构造就在它之外。
|
|
428
|
-
*
|
|
429
|
-
* 新增自造事件类型时**必须**登记到这里,否则读路径 fail loud(真回归:`session-mode/selected` 漏登记,
|
|
430
|
-
* 会话打不开)。**不要**用「所有未知类型」代替(那会掩盖更新版本的未知事件——那种必须拒读)。
|
|
431
|
-
*/
|
|
432
391
|
const OWN_EVENT_TYPES = /* @__PURE__ */ new Set(["session-branch/version", "session-mode/selected"]);
|
|
433
|
-
/** 这批事件是否需要按当前形状归一(旧代消息形状,或尚未标 `ignorable` 的自造事件类型)——写路径曾把回退
|
|
434
|
-
* 视图的结果以当前版本号落库,所以**版本号不可信**,读之前要按内容判一次。 */
|
|
435
392
|
function needsShapeAdoption(events) {
|
|
436
393
|
for (const event of events) {
|
|
437
394
|
const type = event.type;
|
|
@@ -573,15 +530,6 @@ function numericField(value) {
|
|
|
573
530
|
function textField(value) {
|
|
574
531
|
return typeof value === "string" && value !== "" ? value : null;
|
|
575
532
|
}
|
|
576
|
-
/**
|
|
577
|
-
* 一条事件行的用量(只有带 `usage` 的 `assistant/message` 才有):写路径据此记录
|
|
578
|
-
* `t_event_usage`,统计因此不必逐行解析 JSON。两种 `f_data` 结构(带信封 / 老格式)都认。
|
|
579
|
-
* 读侧维度一起物化:本地日由事件时间算,`f_referenced` 直接 1(这一批事件行与桥接行同事务,
|
|
580
|
-
* 提交即被引用),`f_subagent` 取写入它的会话是否子代理。
|
|
581
|
-
* @param event - 待写入的事件行。
|
|
582
|
-
* @param subagent - 写入它的会话是否 `origin === 'subagent'`。
|
|
583
|
-
* @returns 用量行,或该事件没有用量时的 undefined。
|
|
584
|
-
*/
|
|
585
533
|
function usageRowOf(event, subagent) {
|
|
586
534
|
if (event.fType !== "assistant/message") return void 0;
|
|
587
535
|
let parsed;
|