@morlay/session-rdb 0.0.13 → 0.0.14

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[], remap: (seq: 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-BfPVmr1X.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-B-tf5vQn.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-BfPVmr1X.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-B-tf5vQn.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-BUWnS0so.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-BfPVmr1X.mjs";
3
+ import { c as SessionBranchRdb, l as SessionBranchRdbProvider, s as registerSessionImport, u as locateTurnEnd } from "./import-B-tf5vQn.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,9 @@ function remapShadowedRange(range, remap) {
59
59
  end: remap(range.end)
60
60
  };
61
61
  }
62
+ function remapShadowedSeqs(seqs, remap) {
63
+ return seqs.map(remap);
64
+ }
62
65
  function rowToEvent(row, seqMap) {
63
66
  const remap = (seq) => seqMap.get(seq) ?? seq;
64
67
  const surfaceOp = row.fSurfaceOp !== null ? remapSurfaceOp(JSON.parse(row.fSurfaceOp), remap) : void 0;
@@ -66,6 +69,7 @@ function rowToEvent(row, seqMap) {
66
69
  if (row.fType === "compaction/summary" || row.fType === "compaction/prune") {
67
70
  const metering = data;
68
71
  if (metering.shadowedRange !== void 0) metering.shadowedRange = remapShadowedRange(metering.shadowedRange, remap);
72
+ if (metering.shadowedSeqs !== void 0) metering.shadowedSeqs = remapShadowedSeqs(metering.shadowedSeqs, remap);
69
73
  }
70
74
  return {
71
75
  type: row.fType,
@@ -85,12 +89,31 @@ const SURFACE_EVENT_TYPES = /* @__PURE__ */ new Set([
85
89
  "assistant/message",
86
90
  "tool/result"
87
91
  ]);
92
+ const METERING_EVENT_TYPES = /* @__PURE__ */ new Set(["compaction/summary", "compaction/prune"]);
93
+ /**
94
+ * 读取时重计算 replace 的 sourceEventSeqs(sourceEventSeqs 不落库)。
95
+ *
96
+ * 优先采用紧邻 metering 事件(compaction/summary | compaction/prune)的
97
+ * shadowedSeqs:它是压缩事务落库的**权威被遮蔽节点列表**(range 只是首尾
98
+ * 边界对,压缩竞态下可能漏掉并发落地的节点——range 数值扫描会漏掉这些
99
+ * 节点,使上游 assertProvenance 报 missing)。shadowedSeqs 已由 rowToEvent
100
+ * 重映射到稠密坐标,与 replace 的 surfaceOp range 同空间。
101
+ *
102
+ * 无紧邻 metering 事件时回退到 range 数值扫描(历史数据 / 非压缩 replace,
103
+ * 如 tool-result pruner 的旧样式),保证既有行为不变。
104
+ */
88
105
  function recomputeReplaceProvenance(events) {
89
- for (const event of events) {
90
- const raw = event;
106
+ for (let i = 0; i < events.length; i++) {
107
+ const raw = events[i];
91
108
  const op = raw.surfaceOp;
92
109
  if (typeof op !== "object" || op === null || op.op !== "replace") continue;
93
110
  const { start, end } = op;
111
+ const metering = i > 0 ? events[i - 1] : void 0;
112
+ const meteringData = metering !== void 0 && METERING_EVENT_TYPES.has(metering.type) ? metering.data : void 0;
113
+ if (meteringData?.shadowedSeqs !== void 0) {
114
+ raw.sourceEventSeqs = meteringData.shadowedSeqs;
115
+ continue;
116
+ }
94
117
  const refs = [];
95
118
  for (const candidate of events) if (candidate.seq >= start && candidate.seq <= end && SURFACE_EVENT_TYPES.has(candidate.type)) refs.push(candidate.seq);
96
119
  raw.sourceEventSeqs = refs;
@@ -849,4 +872,4 @@ function eventDimensions(event) {
849
872
  }
850
873
  }
851
874
  //#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 };
875
+ 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-BfPVmr1X.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-BUWnS0so.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-BfPVmr1X.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.14",
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,13 @@ 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
+ remap: (seq: number) => number,
96
+ ): number[] {
97
+ return seqs.map(remap);
98
+ }
99
+
93
100
  export function rowToEvent(row: EventRow, seqMap: ReadonlyMap<number, number>): SessionEvent {
94
101
  const remap = (seq: number): number => seqMap.get(seq) ?? seq;
95
102
  const surfaceOp =
@@ -97,12 +104,21 @@ export function rowToEvent(row: EventRow, seqMap: ReadonlyMap<number, number>):
97
104
  ? remapSurfaceOp(JSON.parse(row.fSurfaceOp) as SurfaceOp, remap)
98
105
  : undefined;
99
106
  const data = JSON.parse(row.fData) as SessionEvent["data"];
100
- // compaction 事件的 shadowedRange 是插件合并字段,经结构化视图重映射。
107
+ // compaction 事件的 shadowedRange / shadowedSeqs 是插件合并字段,经结构化
108
+ // 视图重映射到稠密坐标。shadowedSeqs 是权威的被遮蔽节点列表(range 只是
109
+ // 首尾边界对,压缩竞态下可能漏掉并发落地的节点),replace 的 provenance
110
+ // 重计算依赖它,必须与 range 同空间。
101
111
  if (row.fType === "compaction/summary" || row.fType === "compaction/prune") {
102
- const metering = data as unknown as { shadowedRange?: { start: number; end: number } };
112
+ const metering = data as unknown as {
113
+ shadowedRange?: { start: number; end: number };
114
+ shadowedSeqs?: number[];
115
+ };
103
116
  if (metering.shadowedRange !== undefined) {
104
117
  metering.shadowedRange = remapShadowedRange(metering.shadowedRange, remap);
105
118
  }
119
+ if (metering.shadowedSeqs !== undefined) {
120
+ metering.shadowedSeqs = remapShadowedSeqs(metering.shadowedSeqs, remap);
121
+ }
106
122
  }
107
123
  return {
108
124
  type: row.fType as SessionEvent["type"],
@@ -125,14 +141,38 @@ export function buildSeqMap(
125
141
 
126
142
  const SURFACE_EVENT_TYPES = new Set(["user/message", "assistant/message", "tool/result"]);
127
143
 
144
+ const METERING_EVENT_TYPES = new Set(["compaction/summary", "compaction/prune"]);
145
+
146
+ /**
147
+ * 读取时重计算 replace 的 sourceEventSeqs(sourceEventSeqs 不落库)。
148
+ *
149
+ * 优先采用紧邻 metering 事件(compaction/summary | compaction/prune)的
150
+ * shadowedSeqs:它是压缩事务落库的**权威被遮蔽节点列表**(range 只是首尾
151
+ * 边界对,压缩竞态下可能漏掉并发落地的节点——range 数值扫描会漏掉这些
152
+ * 节点,使上游 assertProvenance 报 missing)。shadowedSeqs 已由 rowToEvent
153
+ * 重映射到稠密坐标,与 replace 的 surfaceOp range 同空间。
154
+ *
155
+ * 无紧邻 metering 事件时回退到 range 数值扫描(历史数据 / 非压缩 replace,
156
+ * 如 tool-result pruner 的旧样式),保证既有行为不变。
157
+ */
128
158
  export function recomputeReplaceProvenance(events: SessionEvent[]): void {
129
- for (const event of events) {
159
+ for (let i = 0; i < events.length; i++) {
160
+ const event = events[i]!;
130
161
  const raw = event as SessionEvent & { surfaceOp?: unknown; sourceEventSeqs?: number[] };
131
162
  const op = raw.surfaceOp;
132
163
  if (typeof op !== "object" || op === null || (op as { op?: string }).op !== "replace") {
133
164
  continue;
134
165
  }
135
166
  const { start, end } = op as { start: number; end: number };
167
+ const metering = i > 0 ? events[i - 1] : undefined;
168
+ const meteringData =
169
+ metering !== undefined && METERING_EVENT_TYPES.has(metering.type)
170
+ ? (metering.data as unknown as { shadowedSeqs?: number[] })
171
+ : undefined;
172
+ if (meteringData?.shadowedSeqs !== undefined) {
173
+ raw.sourceEventSeqs = meteringData.shadowedSeqs;
174
+ continue;
175
+ }
136
176
  const refs: number[] = [];
137
177
  for (const candidate of events) {
138
178
  if (