@morlay/session-rdb 0.0.13 → 0.0.15

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.
@@ -35,8 +35,21 @@ declare function remapShadowedRange(range: {
35
35
  start: number;
36
36
  end: number;
37
37
  };
38
+ declare function remapShadowedSeqs(seqs: readonly number[], seqMap: ReadonlyMap<number, number>): number[];
38
39
  declare function rowToEvent(row: EventRow, seqMap: ReadonlyMap<number, number>): SessionEvent;
39
40
  declare function buildSeqMap(rows: readonly Pick<EventRow, "fSequence" | "fOriginalSeq">[]): Map<number, number>;
41
+ /**
42
+ * 读取时重计算 replace 的 sourceEventSeqs(sourceEventSeqs 不落库)。
43
+ *
44
+ * 优先采用紧邻 metering 事件(compaction/summary | compaction/prune)的
45
+ * shadowedSeqs:它是压缩事务落库的**权威被遮蔽节点列表**(range 只是首尾
46
+ * 边界对,压缩竞态下可能漏掉并发落地的节点——range 数值扫描会漏掉这些
47
+ * 节点,使上游 assertProvenance 报 missing)。shadowedSeqs 已由 rowToEvent
48
+ * 重映射到稠密坐标,与 replace 的 surfaceOp range 同空间。
49
+ *
50
+ * 无紧邻 metering 事件时回退到 range 数值扫描(历史数据 / 非压缩 replace,
51
+ * 如 tool-result pruner 的旧样式),保证既有行为不变。
52
+ */
40
53
  declare function recomputeReplaceProvenance(events: SessionEvent[]): void;
41
54
  declare function findSurfaceRepairs(events: readonly SessionEvent[]): {
42
55
  degradeToAppend: Set<number>;
@@ -61,4 +74,4 @@ declare function scanRows(rows: readonly EventRow[], base?: number, seqMap?: Rea
61
74
  };
62
75
  declare function toJsonlArtifact(meta: SessionHeader, inheritedEventCount: number, events: readonly SessionEvent[]): string;
63
76
  //#endregion
64
- export { SESSION_IMPORT_PATH, SESSION_LOG_ARTIFACT_FILENAME, buildSeqMap, expandProvenanceFromStorage, findSurfaceRepairs, orphanInboxSpliceSeqs, parseImportZip, parseJsonlArtifact, persistImport, recomputeReplaceProvenance, registerSessionImport, remapShadowedRange, remapSurfaceOp, repairOrphanInboxSplices, repairSurfaceOps, rowToEvent, rowToMeta, scanRows, sessionConflictRow, sessionInsertRow, toJsonlArtifact };
77
+ export { SESSION_IMPORT_PATH, SESSION_LOG_ARTIFACT_FILENAME, buildSeqMap, expandProvenanceFromStorage, findSurfaceRepairs, orphanInboxSpliceSeqs, parseImportZip, parseJsonlArtifact, persistImport, recomputeReplaceProvenance, registerSessionImport, remapShadowedRange, remapShadowedSeqs, remapSurfaceOp, repairOrphanInboxSplices, repairSurfaceOps, rowToEvent, rowToMeta, scanRows, sessionConflictRow, sessionInsertRow, toJsonlArtifact };
package/dist/artifact.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { A as toJsonlArtifact, C as repairOrphanInboxSplices, D as scanRows, E as rowToMeta, O as sessionConflictRow, S as remapSurfaceOp, T as rowToEvent, _ as buildSeqMap, b as recomputeReplaceProvenance, k as sessionInsertRow, v as findSurfaceRepairs, w as repairSurfaceOps, x as remapShadowedRange, y as orphanInboxSpliceSeqs } from "./schema-B-8G4lWc.mjs";
2
- import { a as parseJsonlArtifact, i as parseImportZip, n as SESSION_LOG_ARTIFACT_FILENAME, o as persistImport, r as expandProvenanceFromStorage, s as registerSessionImport, t as SESSION_IMPORT_PATH } from "./import-D2vUnUZe.mjs";
3
- export { SESSION_IMPORT_PATH, SESSION_LOG_ARTIFACT_FILENAME, buildSeqMap, expandProvenanceFromStorage, findSurfaceRepairs, orphanInboxSpliceSeqs, parseImportZip, parseJsonlArtifact, persistImport, recomputeReplaceProvenance, registerSessionImport, remapShadowedRange, remapSurfaceOp, repairOrphanInboxSplices, repairSurfaceOps, rowToEvent, rowToMeta, scanRows, sessionConflictRow, sessionInsertRow, toJsonlArtifact };
1
+ import { A as sessionInsertRow, C as remapSurfaceOp, D as rowToMeta, E as rowToEvent, O as scanRows, S as remapShadowedSeqs, T as repairSurfaceOps, _ as buildSeqMap, b as recomputeReplaceProvenance, j as toJsonlArtifact, k as sessionConflictRow, v as findSurfaceRepairs, w as repairOrphanInboxSplices, x as remapShadowedRange, y as orphanInboxSpliceSeqs } from "./schema-vwzwEXNE.mjs";
2
+ import { a as parseJsonlArtifact, i as parseImportZip, n as SESSION_LOG_ARTIFACT_FILENAME, o as persistImport, r as expandProvenanceFromStorage, s as registerSessionImport, t as SESSION_IMPORT_PATH } from "./import-DFed8DBt.mjs";
3
+ export { SESSION_IMPORT_PATH, SESSION_LOG_ARTIFACT_FILENAME, buildSeqMap, expandProvenanceFromStorage, findSurfaceRepairs, orphanInboxSpliceSeqs, parseImportZip, parseJsonlArtifact, persistImport, recomputeReplaceProvenance, registerSessionImport, remapShadowedRange, remapShadowedSeqs, remapSurfaceOp, repairOrphanInboxSplices, repairSurfaceOps, rowToEvent, rowToMeta, scanRows, sessionConflictRow, sessionInsertRow, toJsonlArtifact };
@@ -1,4 +1,4 @@
1
- import { E as rowToMeta, l as isPersistedEvent } from "./schema-B-8G4lWc.mjs";
1
+ import { D as rowToMeta, l as isPersistedEvent } from "./schema-vwzwEXNE.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { SESSION_FORMAT_VERSION, SessionLogOffset, decodeSeqRanges, decodeStorageRecord } from "@deepseek-ai/dsh-session";
4
4
  import { SessionBranch, SessionBranchError, balanceRewindPrefix, buildTimeline } from "@morlay/session-branch";
package/dist/import.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { a as parseJsonlArtifact, i as parseImportZip, n as SESSION_LOG_ARTIFACT_FILENAME, o as persistImport, r as expandProvenanceFromStorage, s as registerSessionImport, t as SESSION_IMPORT_PATH } from "./import-D2vUnUZe.mjs";
1
+ import { a as parseJsonlArtifact, i as parseImportZip, n as SESSION_LOG_ARTIFACT_FILENAME, o as persistImport, r as expandProvenanceFromStorage, s as registerSessionImport, t as SESSION_IMPORT_PATH } from "./import-DFed8DBt.mjs";
2
2
  export { SESSION_IMPORT_PATH, SESSION_LOG_ARTIFACT_FILENAME, expandProvenanceFromStorage, parseImportZip, parseJsonlArtifact, persistImport, registerSessionImport };
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { i as WriteGuard, r as createTablesSql, t as SqliteBackend } from "./sqlite-DIXY-dFZ.mjs";
2
- import { A as toJsonlArtifact, C as repairOrphanInboxSplices, D as scanRows, E as rowToMeta, O as sessionConflictRow, _ as buildSeqMap, a as SESSION_PERSISTENCE_SQLITE_APPLICATION_ID, b as recomputeReplaceProvenance, g as toPostgresSchema, i as SCHEMA_VERSION, k as sessionInsertRow, l as isPersistedEvent, m as postgresTableDefs, n as EPHEMERAL_EVENT_TYPES, o as eventDimensions, r as EVENT_ENCODING, t as DEFAULT_BUSY_TIMEOUT_MS, w as repairSurfaceOps } from "./schema-B-8G4lWc.mjs";
3
- import { c as SessionBranchRdb, l as SessionBranchRdbProvider, s as registerSessionImport, u as locateTurnEnd } from "./import-D2vUnUZe.mjs";
1
+ import { i as WriteGuard, r as createTablesSql, t as SqliteBackend } from "./sqlite-CG_Qcx5C.mjs";
2
+ import { A as sessionInsertRow, D as rowToMeta, O as scanRows, T as repairSurfaceOps, _ as buildSeqMap, a as SESSION_PERSISTENCE_SQLITE_APPLICATION_ID, b as recomputeReplaceProvenance, g as toPostgresSchema, i as SCHEMA_VERSION, j as toJsonlArtifact, k as sessionConflictRow, l as isPersistedEvent, m as postgresTableDefs, n as EPHEMERAL_EVENT_TYPES, o as eventDimensions, r as EVENT_ENCODING, t as DEFAULT_BUSY_TIMEOUT_MS, w as repairOrphanInboxSplices } from "./schema-vwzwEXNE.mjs";
3
+ import { c as SessionBranchRdb, l as SessionBranchRdbProvider, s as registerSessionImport, u as locateTurnEnd } from "./import-DFed8DBt.mjs";
4
4
  import z from "@deepseek-ai/schemastery";
5
5
  import { randomUUID } from "node:crypto";
6
6
  import { Pool } from "pg";
@@ -59,6 +59,12 @@ function remapShadowedRange(range, remap) {
59
59
  end: remap(range.end)
60
60
  };
61
61
  }
62
+ function remapShadowedSeqs(seqs, seqMap) {
63
+ return seqs.flatMap((seq) => {
64
+ const dense = seqMap.get(seq);
65
+ return dense === void 0 ? [] : [dense];
66
+ });
67
+ }
62
68
  function rowToEvent(row, seqMap) {
63
69
  const remap = (seq) => seqMap.get(seq) ?? seq;
64
70
  const surfaceOp = row.fSurfaceOp !== null ? remapSurfaceOp(JSON.parse(row.fSurfaceOp), remap) : void 0;
@@ -66,6 +72,7 @@ function rowToEvent(row, seqMap) {
66
72
  if (row.fType === "compaction/summary" || row.fType === "compaction/prune") {
67
73
  const metering = data;
68
74
  if (metering.shadowedRange !== void 0) metering.shadowedRange = remapShadowedRange(metering.shadowedRange, remap);
75
+ if (metering.shadowedSeqs !== void 0) metering.shadowedSeqs = remapShadowedSeqs(metering.shadowedSeqs, seqMap);
69
76
  }
70
77
  return {
71
78
  type: row.fType,
@@ -85,12 +92,31 @@ const SURFACE_EVENT_TYPES = /* @__PURE__ */ new Set([
85
92
  "assistant/message",
86
93
  "tool/result"
87
94
  ]);
95
+ const METERING_EVENT_TYPES = /* @__PURE__ */ new Set(["compaction/summary", "compaction/prune"]);
96
+ /**
97
+ * 读取时重计算 replace 的 sourceEventSeqs(sourceEventSeqs 不落库)。
98
+ *
99
+ * 优先采用紧邻 metering 事件(compaction/summary | compaction/prune)的
100
+ * shadowedSeqs:它是压缩事务落库的**权威被遮蔽节点列表**(range 只是首尾
101
+ * 边界对,压缩竞态下可能漏掉并发落地的节点——range 数值扫描会漏掉这些
102
+ * 节点,使上游 assertProvenance 报 missing)。shadowedSeqs 已由 rowToEvent
103
+ * 重映射到稠密坐标,与 replace 的 surfaceOp range 同空间。
104
+ *
105
+ * 无紧邻 metering 事件时回退到 range 数值扫描(历史数据 / 非压缩 replace,
106
+ * 如 tool-result pruner 的旧样式),保证既有行为不变。
107
+ */
88
108
  function recomputeReplaceProvenance(events) {
89
- for (const event of events) {
90
- const raw = event;
109
+ for (let i = 0; i < events.length; i++) {
110
+ const raw = events[i];
91
111
  const op = raw.surfaceOp;
92
112
  if (typeof op !== "object" || op === null || op.op !== "replace") continue;
93
113
  const { start, end } = op;
114
+ const metering = i > 0 ? events[i - 1] : void 0;
115
+ const meteringData = metering !== void 0 && METERING_EVENT_TYPES.has(metering.type) ? metering.data : void 0;
116
+ if (meteringData?.shadowedSeqs !== void 0) {
117
+ raw.sourceEventSeqs = meteringData.shadowedSeqs;
118
+ continue;
119
+ }
94
120
  const refs = [];
95
121
  for (const candidate of events) if (candidate.seq >= start && candidate.seq <= end && SURFACE_EVENT_TYPES.has(candidate.type)) refs.push(candidate.seq);
96
122
  raw.sourceEventSeqs = refs;
@@ -849,4 +875,4 @@ function eventDimensions(event) {
849
875
  }
850
876
  }
851
877
  //#endregion
852
- export { toJsonlArtifact as A, repairOrphanInboxSplices as C, scanRows as D, rowToMeta as E, sessionConflictRow as O, remapSurfaceOp as S, rowToEvent as T, buildSeqMap as _, SESSION_PERSISTENCE_SQLITE_APPLICATION_ID as a, recomputeReplaceProvenance as b, isEphemeralType as c, tPersistenceState as d, tSessionEvents as f, toPostgresSchema as g, sqliteTableDefs as h, SCHEMA_VERSION as i, sessionInsertRow as k, isPersistedEvent as l, postgresTableDefs as m, EPHEMERAL_EVENT_TYPES as n, eventDimensions as o, tSessions as p, EVENT_ENCODING as r, eventKind as s, DEFAULT_BUSY_TIMEOUT_MS as t, tEvents as u, findSurfaceRepairs as v, repairSurfaceOps as w, remapShadowedRange as x, orphanInboxSpliceSeqs as y };
878
+ export { sessionInsertRow as A, remapSurfaceOp as C, rowToMeta as D, rowToEvent as E, scanRows as O, remapShadowedSeqs as S, repairSurfaceOps as T, buildSeqMap as _, SESSION_PERSISTENCE_SQLITE_APPLICATION_ID as a, recomputeReplaceProvenance as b, isEphemeralType as c, tPersistenceState as d, tSessionEvents as f, toPostgresSchema as g, sqliteTableDefs as h, SCHEMA_VERSION as i, toJsonlArtifact as j, sessionConflictRow as k, isPersistedEvent as l, postgresTableDefs as m, EPHEMERAL_EVENT_TYPES as n, eventDimensions as o, tSessions as p, EVENT_ENCODING as r, eventKind as s, DEFAULT_BUSY_TIMEOUT_MS as t, tEvents as u, findSurfaceRepairs as v, repairOrphanInboxSplices as w, remapShadowedRange as x, orphanInboxSpliceSeqs as y };
@@ -1,4 +1,4 @@
1
- import { O as sessionConflictRow, a as SESSION_PERSISTENCE_SQLITE_APPLICATION_ID, d as tPersistenceState, f as tSessionEvents, h as sqliteTableDefs, k as sessionInsertRow, o as eventDimensions, p as tSessions, t as DEFAULT_BUSY_TIMEOUT_MS, u as tEvents } from "./schema-B-8G4lWc.mjs";
1
+ import { A as sessionInsertRow, a as SESSION_PERSISTENCE_SQLITE_APPLICATION_ID, d as tPersistenceState, f as tSessionEvents, h as sqliteTableDefs, k as sessionConflictRow, o as eventDimensions, p as tSessions, t as DEFAULT_BUSY_TIMEOUT_MS, u as tEvents } from "./schema-vwzwEXNE.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { and, desc, eq, gte, sql } from "drizzle-orm";
4
4
  import { statSync } from "node:fs";
package/dist/storage.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { i as WriteGuard, n as openDatabase, t as SqliteBackend } from "./sqlite-DIXY-dFZ.mjs";
2
- import { a as SESSION_PERSISTENCE_SQLITE_APPLICATION_ID, c as isEphemeralType, d as tPersistenceState, f as tSessionEvents, i as SCHEMA_VERSION, l as isPersistedEvent, n as EPHEMERAL_EVENT_TYPES, o as eventDimensions, p as tSessions, r as EVENT_ENCODING, s as eventKind, t as DEFAULT_BUSY_TIMEOUT_MS, u as tEvents } from "./schema-B-8G4lWc.mjs";
1
+ import { i as WriteGuard, n as openDatabase, t as SqliteBackend } from "./sqlite-CG_Qcx5C.mjs";
2
+ import { a as SESSION_PERSISTENCE_SQLITE_APPLICATION_ID, c as isEphemeralType, d as tPersistenceState, f as tSessionEvents, i as SCHEMA_VERSION, l as isPersistedEvent, n as EPHEMERAL_EVENT_TYPES, o as eventDimensions, p as tSessions, r as EVENT_ENCODING, s as eventKind, t as DEFAULT_BUSY_TIMEOUT_MS, u as tEvents } from "./schema-vwzwEXNE.mjs";
3
3
  export { DEFAULT_BUSY_TIMEOUT_MS, EPHEMERAL_EVENT_TYPES, EVENT_ENCODING, SCHEMA_VERSION, SESSION_PERSISTENCE_SQLITE_APPLICATION_ID, SqliteBackend, WriteGuard, eventDimensions, eventKind, isEphemeralType, isPersistedEvent, openDatabase, tEvents, tPersistenceState, tSessionEvents, tSessions };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morlay/session-rdb",
3
- "version": "0.0.13",
3
+ "version": "0.0.15",
4
4
  "description": "RDB durable session backend for DeepSeek Harness: implements both session-persistence and session-branch (rewind / retry / fork) providers.",
5
5
  "keywords": [
6
6
  "branch",
package/src/log.ts CHANGED
@@ -90,6 +90,19 @@ export function remapShadowedRange(
90
90
  return { start: remap(range.start), end: remap(range.end) };
91
91
  }
92
92
 
93
+ export function remapShadowedSeqs(
94
+ seqs: readonly number[],
95
+ seqMap: ReadonlyMap<number, number>,
96
+ ): number[] {
97
+ // 未持久化的引用(分支裁剪等)直接丢弃:恒等回退会留下陈旧的上游 seq,
98
+ // 稠密坐标通常被压缩,它可能大于 replace 事件的稠密 seq,上游
99
+ // assertProvenance 会以 "must reference earlier events" 拒绝整个会话。
100
+ return seqs.flatMap((seq) => {
101
+ const dense = seqMap.get(seq);
102
+ return dense === undefined ? [] : [dense];
103
+ });
104
+ }
105
+
93
106
  export function rowToEvent(row: EventRow, seqMap: ReadonlyMap<number, number>): SessionEvent {
94
107
  const remap = (seq: number): number => seqMap.get(seq) ?? seq;
95
108
  const surfaceOp =
@@ -97,12 +110,21 @@ export function rowToEvent(row: EventRow, seqMap: ReadonlyMap<number, number>):
97
110
  ? remapSurfaceOp(JSON.parse(row.fSurfaceOp) as SurfaceOp, remap)
98
111
  : undefined;
99
112
  const data = JSON.parse(row.fData) as SessionEvent["data"];
100
- // compaction 事件的 shadowedRange 是插件合并字段,经结构化视图重映射。
113
+ // compaction 事件的 shadowedRange / shadowedSeqs 是插件合并字段,经结构化
114
+ // 视图重映射到稠密坐标。shadowedSeqs 是权威的被遮蔽节点列表(range 只是
115
+ // 首尾边界对,压缩竞态下可能漏掉并发落地的节点),replace 的 provenance
116
+ // 重计算依赖它,必须与 range 同空间。
101
117
  if (row.fType === "compaction/summary" || row.fType === "compaction/prune") {
102
- const metering = data as unknown as { shadowedRange?: { start: number; end: number } };
118
+ const metering = data as unknown as {
119
+ shadowedRange?: { start: number; end: number };
120
+ shadowedSeqs?: number[];
121
+ };
103
122
  if (metering.shadowedRange !== undefined) {
104
123
  metering.shadowedRange = remapShadowedRange(metering.shadowedRange, remap);
105
124
  }
125
+ if (metering.shadowedSeqs !== undefined) {
126
+ metering.shadowedSeqs = remapShadowedSeqs(metering.shadowedSeqs, seqMap);
127
+ }
106
128
  }
107
129
  return {
108
130
  type: row.fType as SessionEvent["type"],
@@ -125,14 +147,38 @@ export function buildSeqMap(
125
147
 
126
148
  const SURFACE_EVENT_TYPES = new Set(["user/message", "assistant/message", "tool/result"]);
127
149
 
150
+ const METERING_EVENT_TYPES = new Set(["compaction/summary", "compaction/prune"]);
151
+
152
+ /**
153
+ * 读取时重计算 replace 的 sourceEventSeqs(sourceEventSeqs 不落库)。
154
+ *
155
+ * 优先采用紧邻 metering 事件(compaction/summary | compaction/prune)的
156
+ * shadowedSeqs:它是压缩事务落库的**权威被遮蔽节点列表**(range 只是首尾
157
+ * 边界对,压缩竞态下可能漏掉并发落地的节点——range 数值扫描会漏掉这些
158
+ * 节点,使上游 assertProvenance 报 missing)。shadowedSeqs 已由 rowToEvent
159
+ * 重映射到稠密坐标,与 replace 的 surfaceOp range 同空间。
160
+ *
161
+ * 无紧邻 metering 事件时回退到 range 数值扫描(历史数据 / 非压缩 replace,
162
+ * 如 tool-result pruner 的旧样式),保证既有行为不变。
163
+ */
128
164
  export function recomputeReplaceProvenance(events: SessionEvent[]): void {
129
- for (const event of events) {
165
+ for (let i = 0; i < events.length; i++) {
166
+ const event = events[i]!;
130
167
  const raw = event as SessionEvent & { surfaceOp?: unknown; sourceEventSeqs?: number[] };
131
168
  const op = raw.surfaceOp;
132
169
  if (typeof op !== "object" || op === null || (op as { op?: string }).op !== "replace") {
133
170
  continue;
134
171
  }
135
172
  const { start, end } = op as { start: number; end: number };
173
+ const metering = i > 0 ? events[i - 1] : undefined;
174
+ const meteringData =
175
+ metering !== undefined && METERING_EVENT_TYPES.has(metering.type)
176
+ ? (metering.data as unknown as { shadowedSeqs?: number[] })
177
+ : undefined;
178
+ if (meteringData?.shadowedSeqs !== undefined) {
179
+ raw.sourceEventSeqs = meteringData.shadowedSeqs;
180
+ continue;
181
+ }
136
182
  const refs: number[] = [];
137
183
  for (const candidate of events) {
138
184
  if (