@sema-agent/server 7.93.7 → 7.95.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/USAGE.md +52 -0
- package/dist/boot/resolve-spec.js +14 -1
- package/dist/boot/stage-09-leader.js +1 -1
- package/dist/boot/stores.js +6 -3
- package/dist/boot/workflow-orchestration.js +5 -1
- package/dist/config-catalog.js +5 -3
- package/dist/config-center/types.d.ts +1 -1
- package/dist/config-invariants.d.ts +2 -2
- package/dist/config-invariants.js +17 -0
- package/dist/config-types.d.ts +44 -3
- package/dist/config-types.js +1 -0
- package/dist/config.js +24 -4
- package/dist/hosted-posture.d.ts +176 -0
- package/dist/hosted-posture.js +76 -0
- package/dist/http/resume-ctx-read.d.ts +40 -0
- package/dist/http/resume-ctx-read.js +25 -0
- package/dist/http/resume-legs.js +62 -42
- package/dist/http/routes/capabilities.js +3 -0
- package/dist/http/routes/images.js +12 -0
- package/dist/http/routes/runs.js +18 -4
- package/dist/http/routes/workflows.js +3 -2
- package/dist/http/wire-types.d.ts +13 -4
- package/dist/observability/corrupt-read-seat.d.ts +56 -0
- package/dist/observability/corrupt-read-seat.js +16 -0
- package/dist/observability/fail-open.d.ts +4 -0
- package/dist/observability/fail-open.js +4 -0
- package/dist/orchestration/workflow-journal-entry.d.ts +24 -6
- package/dist/orchestration/workflow-journal-entry.js +17 -10
- package/dist/plugins/approval-ask-store-sql.js +2 -2
- package/dist/plugins/checkpoint-store-sql.d.ts +16 -3
- package/dist/plugins/checkpoint-store-sql.js +7 -9
- package/dist/plugins/image-bake-store-sql.js +85 -13
- package/dist/plugins/image-index-sql.js +75 -12
- package/dist/plugins/local-checkpoint-store.d.ts +23 -2
- package/dist/plugins/local-checkpoint-store.js +40 -33
- package/dist/plugins/local-session-store.d.ts +14 -0
- package/dist/plugins/local-session-store.js +12 -7
- package/dist/plugins/local-sidecar-quarantine.d.ts +10 -0
- package/dist/plugins/local-sidecar-quarantine.js +15 -0
- package/dist/plugins/local-task-attachment-store.d.ts +1 -1
- package/dist/plugins/local-task-attachment-store.js +5 -2
- package/dist/plugins/mailbox-store-sql.d.ts +6 -3
- package/dist/plugins/mailbox-store-sql.js +45 -43
- package/dist/plugins/memory-embedder-fingerprint.js +8 -12
- package/dist/plugins/memory-engine-pg.js +34 -16
- package/dist/plugins/memory-engine-tidb.js +19 -12
- package/dist/plugins/memory-engine-vector-util.d.ts +26 -0
- package/dist/plugins/memory-engine-vector-util.js +3 -0
- package/dist/plugins/memory-sync-store-pg.d.ts +6 -0
- package/dist/plugins/memory-sync-store-pg.js +14 -10
- package/dist/plugins/memory-sync-store-tidb.js +7 -11
- package/dist/plugins/pg-session-storage.js +26 -28
- package/dist/plugins/session-event-payload.d.ts +37 -0
- package/dist/plugins/session-event-payload.js +20 -0
- package/dist/plugins/sql-json-column.d.ts +124 -22
- package/dist/plugins/sql-json-column.js +22 -1
- package/dist/plugins/sql-row-helpers.d.ts +10 -16
- package/dist/plugins/sql-row-helpers.js +0 -29
- package/dist/plugins/store-backend.js +10 -12
- package/dist/plugins/store-contracts.d.ts +4 -6
- package/dist/plugins/store-contracts.js +0 -60
- package/dist/plugins/task-attachment-store.d.ts +21 -12
- package/dist/plugins/task-attachment-store.js +12 -0
- package/dist/plugins/tidb-session-storage.d.ts +2 -2
- package/dist/plugins/tidb-session-storage.js +12 -11
- package/dist/plugins/tidb-session-store.d.ts +2 -2
- package/dist/plugins/tidb-session-store.js +20 -22
- package/dist/plugins/usage-window-store-sql.d.ts +9 -1
- package/dist/plugins/usage-window-store-sql.js +23 -9
- package/dist/plugins/workflow-journal-store-sql.js +16 -4
- package/dist/run-local.js +4 -10
- package/dist/usage-analytics.d.ts +14 -1
- package/dist/usage-analytics.js +11 -17
- package/package.json +2 -2
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { jaccardDistance, termSet } from "@sema-agent/core";
|
|
2
|
+
import { readFrontmatterColumn } from "./memory-engine-vector-util.js";
|
|
3
|
+
import { decodeJsonColumn, foldJsonColumn, jsonColumnText } from "./sql-json-column.js";
|
|
2
4
|
import { computeEntryRev, serializeEntryFile } from "@sema-agent/core";
|
|
3
5
|
import { pgHasUnstorable } from "./pg-safe-json.js";
|
|
4
6
|
import { assertMemoryScopeWidth, memoryEntryKeyWidthRefusal } from "./memory-key-guards.js";
|
|
@@ -41,8 +43,13 @@ function isPrimaryKeyDup(err) {
|
|
|
41
43
|
function inPlaceholders(count) {
|
|
42
44
|
return Array.from({ length: count }, () => "?").join(", ");
|
|
43
45
|
}
|
|
44
|
-
function
|
|
45
|
-
|
|
46
|
+
function readFrontmatter(cell, id) {
|
|
47
|
+
try {
|
|
48
|
+
return decodeJsonColumn(cell, readFrontmatterColumn, `${TIDB_MEMORY_ENGINE_TABLES.entry}.frontmatter for entry ${String(id)}`);
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
throw foldJsonColumn(e, (m) => new Error(`memory-engine: ${m} — refusing rather than serve a note whose frontmatter (the origin/exposure law carrier) nobody can read`));
|
|
52
|
+
}
|
|
46
53
|
}
|
|
47
54
|
export class TiDBMemoryEngineBackend {
|
|
48
55
|
pool;
|
|
@@ -72,7 +79,7 @@ export class TiDBMemoryEngineBackend {
|
|
|
72
79
|
return `${fm.name ?? slug} ${fm.description ?? ""} ${body}`;
|
|
73
80
|
}
|
|
74
81
|
headerFromRow(r) {
|
|
75
|
-
const fm =
|
|
82
|
+
const fm = readFrontmatter(r.frontmatter, r.id);
|
|
76
83
|
return {
|
|
77
84
|
id: r.id,
|
|
78
85
|
slug: r.slug,
|
|
@@ -92,7 +99,7 @@ export class TiDBMemoryEngineBackend {
|
|
|
92
99
|
id: r.id,
|
|
93
100
|
slug: r.slug,
|
|
94
101
|
scope: r.scope,
|
|
95
|
-
frontmatter:
|
|
102
|
+
frontmatter: readFrontmatter(r.frontmatter, r.id),
|
|
96
103
|
body: r.body,
|
|
97
104
|
rev: r.rev,
|
|
98
105
|
};
|
|
@@ -101,14 +108,14 @@ export class TiDBMemoryEngineBackend {
|
|
|
101
108
|
const uniq = [...new Set(scopes)];
|
|
102
109
|
if (uniq.length === 0)
|
|
103
110
|
return [];
|
|
104
|
-
const rows = await this.selectRows(`SELECT id, scope, slug, frontmatter, rev, mtime_ms, size_bytes FROM ${TIDB_MEMORY_ENGINE_TABLES.entry} WHERE scope IN (${inPlaceholders(uniq.length)}) ORDER BY id`, uniq);
|
|
111
|
+
const rows = await this.selectRows(`SELECT id, scope, slug, ${jsonColumnText("tidb", "frontmatter")}, rev, mtime_ms, size_bytes FROM ${TIDB_MEMORY_ENGINE_TABLES.entry} WHERE scope IN (${inPlaceholders(uniq.length)}) ORDER BY id`, uniq);
|
|
105
112
|
return rows.map((r) => this.headerFromRow(r));
|
|
106
113
|
}
|
|
107
114
|
async getByIds(ids) {
|
|
108
115
|
const uniq = [...new Set(ids)];
|
|
109
116
|
if (uniq.length === 0)
|
|
110
117
|
return [];
|
|
111
|
-
const rows = await this.selectRows(`SELECT id, scope, slug, frontmatter, body, rev, mtime_ms, size_bytes FROM ${TIDB_MEMORY_ENGINE_TABLES.entry} WHERE id IN (${inPlaceholders(uniq.length)}) ORDER BY id`, uniq);
|
|
118
|
+
const rows = await this.selectRows(`SELECT id, scope, slug, ${jsonColumnText("tidb", "frontmatter")}, body, rev, mtime_ms, size_bytes FROM ${TIDB_MEMORY_ENGINE_TABLES.entry} WHERE id IN (${inPlaceholders(uniq.length)}) ORDER BY id`, uniq);
|
|
112
119
|
return rows.map((r) => this.entryFromRow(r));
|
|
113
120
|
}
|
|
114
121
|
async search(query, scopes, opts) {
|
|
@@ -121,11 +128,11 @@ export class TiDBMemoryEngineBackend {
|
|
|
121
128
|
if (q.size === 0)
|
|
122
129
|
return [];
|
|
123
130
|
const uniq = [...new Set(scopes)];
|
|
124
|
-
const rows = await this.selectRows(`SELECT id, scope, slug, frontmatter, body, rev, mtime_ms, size_bytes FROM ${TIDB_MEMORY_ENGINE_TABLES.entry} WHERE scope IN (${inPlaceholders(uniq.length)})`, uniq);
|
|
131
|
+
const rows = await this.selectRows(`SELECT id, scope, slug, ${jsonColumnText("tidb", "frontmatter")}, body, rev, mtime_ms, size_bytes FROM ${TIDB_MEMORY_ENGINE_TABLES.entry} WHERE scope IN (${inPlaceholders(uniq.length)})`, uniq);
|
|
125
132
|
const scored = [];
|
|
126
133
|
for (const raw of rows) {
|
|
127
134
|
const r = raw;
|
|
128
|
-
const fm =
|
|
135
|
+
const fm = readFrontmatter(r.frontmatter, r.id);
|
|
129
136
|
const d = jaccardDistance(q, this.haystackOf(r.slug, fm, r.body));
|
|
130
137
|
if (d === null)
|
|
131
138
|
continue;
|
|
@@ -203,11 +210,11 @@ export class TiDBMemoryEngineBackend {
|
|
|
203
210
|
report.conflicts.push({ op: "add", id: entry.id, reason: "unstorable_bytes (utf8mb4 cannot store NUL/lone surrogates without mangling; strip them at the source — the store never rewrites content)" });
|
|
204
211
|
return;
|
|
205
212
|
}
|
|
206
|
-
const prior = await this.selectRows(`SELECT scope, slug, rev, frontmatter FROM ${T} WHERE id = ?`, [entry.id]);
|
|
213
|
+
const prior = await this.selectRows(`SELECT scope, slug, rev, ${jsonColumnText("tidb", "frontmatter")} FROM ${T} WHERE id = ?`, [entry.id]);
|
|
207
214
|
const priorRow = prior.length > 0 ? prior[0] : undefined;
|
|
208
215
|
let existsById = priorRow !== undefined;
|
|
209
216
|
{
|
|
210
|
-
const committedBaseFm = rememberBatchCommitted(originBaseline, entry.id, priorRow !== undefined ?
|
|
217
|
+
const committedBaseFm = rememberBatchCommitted(originBaseline, entry.id, priorRow !== undefined ? readFrontmatter(priorRow.frontmatter, entry.id) : undefined);
|
|
211
218
|
const refusal = originWhitewashRefusal("add", committedBaseFm, entry.frontmatter);
|
|
212
219
|
if (refusal !== undefined) {
|
|
213
220
|
report.conflicts.push({ op: "add", id: entry.id, reason: refusal });
|
|
@@ -288,7 +295,7 @@ export class TiDBMemoryEngineBackend {
|
|
|
288
295
|
slug = `${entry.slug}-${n}`;
|
|
289
296
|
}
|
|
290
297
|
}
|
|
291
|
-
const cur = await this.selectRows(`SELECT slug, rev, scope, frontmatter FROM ${T} WHERE id = ?`, [patch.id]);
|
|
298
|
+
const cur = await this.selectRows(`SELECT slug, rev, scope, ${jsonColumnText("tidb", "frontmatter")} FROM ${T} WHERE id = ?`, [patch.id]);
|
|
292
299
|
if (cur.length === 0) {
|
|
293
300
|
report.conflicts.push({ op: patch.op, id: patch.id, reason: "unknown id (entry not found in backend)" });
|
|
294
301
|
return;
|
|
@@ -296,7 +303,7 @@ export class TiDBMemoryEngineBackend {
|
|
|
296
303
|
const currentRev = String(cur[0].rev);
|
|
297
304
|
const currentSlug = String(cur[0].slug);
|
|
298
305
|
const currentScope = String(cur[0].scope);
|
|
299
|
-
const committedFm =
|
|
306
|
+
const committedFm = readFrontmatter(cur[0].frontmatter, patch.id);
|
|
300
307
|
const committedBaseFm = rememberBatchCommitted(originBaseline, patch.id, committedFm);
|
|
301
308
|
if (patch.op === "update" && patch.entry !== undefined) {
|
|
302
309
|
if (committedFm.provenance?.kind === "repo_file") {
|
|
@@ -1,2 +1,28 @@
|
|
|
1
|
+
/** memory-engine 方言实现共用的纯工具函数。三个距离/词集函数(termSet/jaccardDistance/cosineDistance)
|
|
2
|
+
* 在 core(1.268.0 起公开导出,单一语义源)——消费者直引 core([ref] 兼容面全清:此处转口已删)。
|
|
3
|
+
* `isUniqueViolation` 重新内联:它当年随 core 的 pg **示例适配器**导出面走,core 1.424 按 clay 包卫生令
|
|
4
|
+
* 把整个 examples 面退出 npm 包([ref] BREAKING)⇒ re-export 断源。原为逐字抄自 core 1.423
|
|
5
|
+
* `examples/adapters/pg-adapter.js`(纯谓词,无 byte-equal 契约约束);[ref] P1-① 后**码面判据**
|
|
6
|
+
* 改走属主 `sql-errors.ts`,只留 message 补充臂(理由在函数体内)。 */
|
|
7
|
+
import type { MemoryEntryFrontmatter } from "@sema-agent/core";
|
|
8
|
+
import { type JsonColumnReader } from "./sql-json-column.js";
|
|
1
9
|
export declare function isUniqueViolation(err: unknown): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* `agent_memory_engine_entry.frontmatter` 的**形读者**。
|
|
12
|
+
*
|
|
13
|
+
* 🔴 判据只到「一只 JSON 对象」为止:键表属主在 core(`MemoryEntryFrontmatter`),而 core 没有导出
|
|
14
|
+
* 验证器 —— 在这里手写一份等价 zod 就是设计稿明令禁止的第二份键集。这一条最小判据已经把坏体四形全关在
|
|
15
|
+
* 门外,而此前这里是**零判据**(裸 `JSON.parse` + `as MemoryEntryFrontmatter`)。
|
|
16
|
+
*
|
|
17
|
+
* 🔴 失败方向 = **拒**,而且这一列不是普通载荷:`frontmatter` 是 **origin/exposure 不可变律**的载体
|
|
18
|
+
* (`committedOriginOf` / `exposureBandOf` / `distilledCarriage` 全读它)。一行读不出形的 frontmatter 被
|
|
19
|
+
* 当成 `{}` 端出去 = 那条笔记的 external-origin 标记与曝光带**静默消失** —— 洗白检查(`originWhitewashRefusal`)
|
|
20
|
+
* 拿到的「已提交基线」于是是空的,一次真的洗白会被放过。
|
|
21
|
+
*/
|
|
22
|
+
export declare const readFrontmatterColumn: JsonColumnReader<MemoryEntryFrontmatter>;
|
|
23
|
+
/**
|
|
24
|
+
* `agent_memory_engine_entry.embedding` 的**形读者** = 本店既有的那一条判据(`Array.isArray`)。
|
|
25
|
+
* 维数判据仍在调用点(它要跟 embedder 的 `dimensions` 比,那是运行期事实,不是列的形)。
|
|
26
|
+
*/
|
|
27
|
+
export declare const readEmbeddingColumn: JsonColumnReader<unknown[]>;
|
|
2
28
|
//# sourceMappingURL=memory-engine-vector-util.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isPgUniqueViolation } from "./sql-errors.js";
|
|
2
|
+
import { createObjectColumnReader, readJsonArrayColumn } from "./sql-json-column.js";
|
|
2
3
|
export function isUniqueViolation(err) {
|
|
3
4
|
if (isPgUniqueViolation(err))
|
|
4
5
|
return true;
|
|
@@ -6,4 +7,6 @@ export function isUniqueViolation(err) {
|
|
|
6
7
|
return false;
|
|
7
8
|
return /duplicate key|unique constraint|already exists/i.test(String(err.message ?? ""));
|
|
8
9
|
}
|
|
10
|
+
export const readFrontmatterColumn = createObjectColumnReader();
|
|
11
|
+
export const readEmbeddingColumn = readJsonArrayColumn;
|
|
9
12
|
//# sourceMappingURL=memory-engine-vector-util.js.map
|
|
@@ -50,6 +50,12 @@ export interface MemorySyncStore {
|
|
|
50
50
|
export declare const MEMORY_SYNC_INDEXES: readonly IndexSpec[];
|
|
51
51
|
/** Idempotent DDL for the sync plane (PG dialect). Call once at startup. */
|
|
52
52
|
export declare function ensurePgMemorySyncSchema(query: PgQueryFn): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* 本店三只 JSON 承载列的**折码点**(唯一)—— 本店没有枚举式 corrupt 码,既有的「读不出来」口径就是
|
|
55
|
+
* 响亮抛(`claimLeaseMs` 守卫 / dup id 一律 fail-loud)⇒ 折成响亮裸 `Error`,点名 `<表>.<列>` + 行标识
|
|
56
|
+
* (S-550 车①② 裁定,沿用;不新铸码)。两个孪生共用本函数(MySQL 腿 import 它 —— 一条折码规则一个写者)。
|
|
57
|
+
*/
|
|
58
|
+
export declare function refusingMemorySyncRow<T>(read: () => T): T;
|
|
53
59
|
/** Options for {@link PgMemorySyncStore}. */
|
|
54
60
|
export interface PgMemorySyncStoreOptions {
|
|
55
61
|
/** How long a `claimDue` lease holds a row invisible before a crashed worker's claim self-heals.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ensureIndexes } from "./ensure-index.js";
|
|
2
2
|
import { pgSafeJsonStringify, pgProtocolJsonStringify } from "./pg-safe-json.js";
|
|
3
3
|
import { assertMemoryScopeWidth } from "./memory-key-guards.js";
|
|
4
|
+
import { decodeJsonColumn, decodeNullableJsonColumn, foldJsonColumn, jsonColumnText, readNonNullJsonValueColumn, readOpaqueJsonValueColumn, readStringRecordColumn, } from "./sql-json-column.js";
|
|
4
5
|
export const PG_MEMORY_SYNC_TABLES = {
|
|
5
6
|
syncCursor: "agent_memory_engine_sync_cursor",
|
|
6
7
|
pushQueue: "agent_memory_engine_push_queue",
|
|
@@ -28,10 +29,13 @@ export async function ensurePgMemorySyncSchema(query) {
|
|
|
28
29
|
)`);
|
|
29
30
|
await ensureIndexes(query, "pg", MEMORY_SYNC_INDEXES);
|
|
30
31
|
}
|
|
31
|
-
function
|
|
32
|
-
|
|
33
|
-
return
|
|
34
|
-
|
|
32
|
+
export function refusingMemorySyncRow(read) {
|
|
33
|
+
try {
|
|
34
|
+
return read();
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
throw foldJsonColumn(e, (m) => new Error(`memory-sync store: ${m} — refusing rather than serve a sync row nobody can read`));
|
|
38
|
+
}
|
|
35
39
|
}
|
|
36
40
|
function toMs(v) {
|
|
37
41
|
return typeof v === "number" ? v : Number(v);
|
|
@@ -48,14 +52,14 @@ export class PgMemorySyncStore {
|
|
|
48
52
|
this.claimLeaseMs = lease;
|
|
49
53
|
}
|
|
50
54
|
async getCursor(scope, peer) {
|
|
51
|
-
const res = await this.query(`SELECT base_revs, updated_at_ms FROM ${PG_MEMORY_SYNC_TABLES.syncCursor} WHERE scope = $1 AND peer = $2`, [scope, peer]);
|
|
55
|
+
const res = await this.query(`SELECT ${jsonColumnText("pg", "base_revs")}, updated_at_ms FROM ${PG_MEMORY_SYNC_TABLES.syncCursor} WHERE scope = $1 AND peer = $2`, [scope, peer]);
|
|
52
56
|
const row = res.rows[0];
|
|
53
57
|
if (!row)
|
|
54
58
|
return undefined;
|
|
55
59
|
return {
|
|
56
60
|
scope,
|
|
57
61
|
peer,
|
|
58
|
-
baseRevs:
|
|
62
|
+
baseRevs: refusingMemorySyncRow(() => decodeJsonColumn(row.base_revs, readStringRecordColumn, `${PG_MEMORY_SYNC_TABLES.syncCursor}.base_revs for scope ${scope} peer ${peer}`)),
|
|
59
63
|
updatedAtMs: toMs(row.updated_at_ms),
|
|
60
64
|
};
|
|
61
65
|
}
|
|
@@ -83,12 +87,12 @@ export class PgMemorySyncStore {
|
|
|
83
87
|
)
|
|
84
88
|
UPDATE ${PG_MEMORY_SYNC_TABLES.pushQueue} q SET next_attempt_at_ms = $3
|
|
85
89
|
FROM due WHERE q.id = due.id
|
|
86
|
-
RETURNING q.id, q.scope, q.entry_id,
|
|
90
|
+
RETURNING q.id, q.scope, q.entry_id, ${jsonColumnText("pg", "payload")}, q.attempts, q.last_error`, [nowMs, limit, leasedUntilMs]);
|
|
87
91
|
return res.rows.map((r) => ({
|
|
88
92
|
id: String(r.id),
|
|
89
93
|
scope: String(r.scope),
|
|
90
94
|
entryId: String(r.entry_id),
|
|
91
|
-
payload:
|
|
95
|
+
payload: refusingMemorySyncRow(() => decodeJsonColumn(r.payload, readOpaqueJsonValueColumn, `${PG_MEMORY_SYNC_TABLES.pushQueue}.payload for item ${String(r.id)}`)),
|
|
92
96
|
attempts: toMs(r.attempts),
|
|
93
97
|
lastError: r.last_error == null ? null : String(r.last_error),
|
|
94
98
|
leasedUntilMs,
|
|
@@ -138,7 +142,7 @@ export class PgMemoryHistoryStore {
|
|
|
138
142
|
ON CONFLICT (id, rev) DO NOTHING`, params);
|
|
139
143
|
}
|
|
140
144
|
async historyOf(id) {
|
|
141
|
-
const res = await this.query(`SELECT id, rev, prev_rev, op, scope, snapshot, written_at_ms
|
|
145
|
+
const res = await this.query(`SELECT id, rev, prev_rev, op, scope, ${jsonColumnText("pg", "snapshot")}, written_at_ms
|
|
142
146
|
FROM ${PG_MEMORY_SYNC_TABLES.history}
|
|
143
147
|
WHERE id = $1
|
|
144
148
|
ORDER BY written_at_ms ASC, rev ASC`, [id]);
|
|
@@ -156,7 +160,7 @@ export function historyRowFrom(r) {
|
|
|
156
160
|
prevRev: r.prev_rev == null ? null : String(r.prev_rev),
|
|
157
161
|
op: String(r.op),
|
|
158
162
|
scope: String(r.scope),
|
|
159
|
-
snapshot: r.snapshot
|
|
163
|
+
snapshot: refusingMemorySyncRow(() => decodeNullableJsonColumn(r.snapshot, readNonNullJsonValueColumn, `${PG_MEMORY_SYNC_TABLES.history}.snapshot for entry ${String(r.id)} rev ${String(r.rev)}`)) ?? null,
|
|
160
164
|
writtenAtMs: toMs(r.written_at_ms),
|
|
161
165
|
};
|
|
162
166
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { historyRowFrom } from "./memory-sync-store-pg.js";
|
|
1
|
+
import { historyRowFrom, refusingMemorySyncRow } from "./memory-sync-store-pg.js";
|
|
2
|
+
import { decodeJsonColumn, jsonColumnText, readOpaqueJsonValueColumn, readStringRecordColumn } from "./sql-json-column.js";
|
|
2
3
|
import { ensureIndexes } from "./ensure-index.js";
|
|
3
4
|
import { MEMORY_SYNC_INDEXES, MEMORY_HISTORY_INDEXES, PG_MEMORY_SYNC_TABLES } from "./memory-sync-store-pg.js";
|
|
4
5
|
import { assertMemoryScopeWidth } from "./memory-key-guards.js";
|
|
@@ -30,11 +31,6 @@ export async function ensureTiDBMemorySyncSchema(pool) {
|
|
|
30
31
|
) COLLATE utf8mb4_bin`);
|
|
31
32
|
await ensureIndexes((text, params) => pool.query(text, params), "mysql", MEMORY_SYNC_INDEXES);
|
|
32
33
|
}
|
|
33
|
-
function fromJson(v) {
|
|
34
|
-
if (typeof v === "string")
|
|
35
|
-
return JSON.parse(v);
|
|
36
|
-
return v;
|
|
37
|
-
}
|
|
38
34
|
function toMs(v) {
|
|
39
35
|
return typeof v === "number" ? v : Number(v);
|
|
40
36
|
}
|
|
@@ -50,14 +46,14 @@ export class TiDBMemorySyncStore {
|
|
|
50
46
|
this.claimLeaseMs = lease;
|
|
51
47
|
}
|
|
52
48
|
async getCursor(scope, peer) {
|
|
53
|
-
const [rows] = await this.pool.query(`SELECT base_revs, updated_at_ms FROM ${TIDB_MEMORY_SYNC_TABLES.syncCursor} WHERE scope = ? AND peer = ?`, [scope, peer]);
|
|
49
|
+
const [rows] = await this.pool.query(`SELECT ${jsonColumnText("tidb", "base_revs")}, updated_at_ms FROM ${TIDB_MEMORY_SYNC_TABLES.syncCursor} WHERE scope = ? AND peer = ?`, [scope, peer]);
|
|
54
50
|
const row = rows[0];
|
|
55
51
|
if (!row)
|
|
56
52
|
return undefined;
|
|
57
53
|
return {
|
|
58
54
|
scope,
|
|
59
55
|
peer,
|
|
60
|
-
baseRevs:
|
|
56
|
+
baseRevs: refusingMemorySyncRow(() => decodeJsonColumn(row.base_revs, readStringRecordColumn, `${TIDB_MEMORY_SYNC_TABLES.syncCursor}.base_revs for scope ${scope} peer ${peer}`)),
|
|
61
57
|
updatedAtMs: toMs(row.updated_at_ms),
|
|
62
58
|
};
|
|
63
59
|
}
|
|
@@ -76,7 +72,7 @@ export class TiDBMemorySyncStore {
|
|
|
76
72
|
if (limit <= 0)
|
|
77
73
|
return [];
|
|
78
74
|
const leasedUntilMs = nowMs + this.claimLeaseMs;
|
|
79
|
-
const [candidates] = await this.pool.query(`SELECT id, scope, entry_id, payload, attempts, next_attempt_at_ms, last_error
|
|
75
|
+
const [candidates] = await this.pool.query(`SELECT id, scope, entry_id, ${jsonColumnText("tidb", "payload")}, attempts, next_attempt_at_ms, last_error
|
|
80
76
|
FROM ${TIDB_MEMORY_SYNC_TABLES.pushQueue}
|
|
81
77
|
WHERE next_attempt_at_ms <= ?
|
|
82
78
|
ORDER BY next_attempt_at_ms, id
|
|
@@ -92,7 +88,7 @@ export class TiDBMemorySyncStore {
|
|
|
92
88
|
id: row.id,
|
|
93
89
|
scope: row.scope,
|
|
94
90
|
entryId: row.entry_id,
|
|
95
|
-
payload:
|
|
91
|
+
payload: refusingMemorySyncRow(() => decodeJsonColumn(row.payload, readOpaqueJsonValueColumn, `${TIDB_MEMORY_SYNC_TABLES.pushQueue}.payload for item ${row.id}`)),
|
|
96
92
|
attempts: toMs(row.attempts),
|
|
97
93
|
lastError: row.last_error == null ? null : String(row.last_error),
|
|
98
94
|
leasedUntilMs,
|
|
@@ -141,7 +137,7 @@ export class TiDBMemoryHistoryStore {
|
|
|
141
137
|
ON DUPLICATE KEY UPDATE id = id`, params);
|
|
142
138
|
}
|
|
143
139
|
async historyOf(id) {
|
|
144
|
-
const [rows] = await this.pool.query(`SELECT id, rev, prev_rev, op, scope, snapshot, written_at_ms
|
|
140
|
+
const [rows] = await this.pool.query(`SELECT id, rev, prev_rev, op, scope, ${jsonColumnText("tidb", "snapshot")}, written_at_ms
|
|
145
141
|
FROM ${TIDB_MEMORY_SYNC_TABLES.history}
|
|
146
142
|
WHERE id = ?
|
|
147
143
|
ORDER BY written_at_ms ASC, rev ASC`, [id]);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { emitLeafAdvance } from "../session-leaf-bus.js";
|
|
2
2
|
import { BaseSessionStorage, StoredSession, SessionError, uuidv7, validateEntriesForImport, leafIdAfterEntry, normalizePromptEpoch, } from "@sema-agent/core";
|
|
3
3
|
import { escapeLike } from "./sql-escape.js";
|
|
4
|
+
import { decodeSessionEventPayload } from "./session-event-payload.js";
|
|
5
|
+
import { jsonColumnText } from "./sql-json-column.js";
|
|
4
6
|
import { pgSafeJsonStringify } from "./pg-safe-json.js";
|
|
5
7
|
import { toIso } from "./sql-row-helpers.js";
|
|
6
8
|
import { assertPlacementAdmission, LIST_PLACED_BATCH, PLACEMENT_SELECT_COLUMNS, placedRowFromRow, placementFromRow, placementInsertValues, SQL_SESSION_PLACEMENTS, } from "./session-placement.js";
|
|
@@ -10,9 +12,6 @@ import { isPgUniqueViolation } from "./sql-errors.js";
|
|
|
10
12
|
import { MANAGED_RETENTION } from "./retention-store-sql.js";
|
|
11
13
|
import { ensureIndexes } from "./ensure-index.js";
|
|
12
14
|
const isDupKey = isPgUniqueViolation;
|
|
13
|
-
function parseEntry(p) {
|
|
14
|
-
return (typeof p === "string" ? JSON.parse(p) : p);
|
|
15
|
-
}
|
|
16
15
|
export const PG_SESSION_SCHEMA = [
|
|
17
16
|
`CREATE TABLE IF NOT EXISTS session_meta (
|
|
18
17
|
session_id VARCHAR(64) COLLATE "C" NOT NULL,
|
|
@@ -74,11 +73,11 @@ export class PgSessionStorage extends BaseSessionStorage {
|
|
|
74
73
|
return undefined;
|
|
75
74
|
let cursor = this.floorSeq;
|
|
76
75
|
for (;;) {
|
|
77
|
-
const res = await this.pool.query(
|
|
76
|
+
const res = await this.pool.query(`SELECT seq, ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 AND seq < $2 AND type IN ('prompt_epoch','compaction') ORDER BY seq DESC LIMIT 100`, [this.sessionId, cursor]);
|
|
78
77
|
if (res.rows.length === 0)
|
|
79
78
|
return undefined;
|
|
80
79
|
for (const r of res.rows) {
|
|
81
|
-
const e =
|
|
80
|
+
const e = decodeSessionEventPayload(r.payload, this.sessionId, r.seq);
|
|
82
81
|
const d = e.type === "prompt_epoch" ? normalizePromptEpoch(e) : normalizePromptEpoch(e.details?.promptEpoch);
|
|
83
82
|
if (d)
|
|
84
83
|
return d;
|
|
@@ -92,18 +91,18 @@ export class PgSessionStorage extends BaseSessionStorage {
|
|
|
92
91
|
if (!meta)
|
|
93
92
|
return null;
|
|
94
93
|
const { floorEntryId, floorSeq } = await PgSessionStorage.computeFloor(pool, sessionId);
|
|
95
|
-
const eventRes = await pool.query(
|
|
96
|
-
const entries = eventRes.rows.map((r) =>
|
|
94
|
+
const eventRes = await pool.query(`SELECT seq, ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 AND seq >= $2 ORDER BY seq ASC`, [sessionId, floorSeq]);
|
|
95
|
+
const entries = eventRes.rows.map((r) => decodeSessionEventPayload(r.payload, sessionId, r.seq));
|
|
97
96
|
const placement = placementFromRow(meta);
|
|
98
97
|
const metadata = { id: sessionId, createdAt: toIso(meta.created_at), ...(placement !== undefined ? { placement } : {}) };
|
|
99
98
|
return new PgSessionStorage(pool, sessionId, metadata, entries, meta.leaf_id ?? null, floorEntryId, floorSeq);
|
|
100
99
|
}
|
|
101
100
|
static async computeFloor(pool, sessionId) {
|
|
102
|
-
const compRes = await pool.query(
|
|
101
|
+
const compRes = await pool.query(`SELECT ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 AND type = 'compaction' ORDER BY seq DESC LIMIT 1`, [sessionId]);
|
|
103
102
|
if (!compRes.rows[0])
|
|
104
103
|
return { floorEntryId: null, floorSeq: 0 };
|
|
105
|
-
const comp =
|
|
106
|
-
const fk = comp.firstKeptEntryId;
|
|
104
|
+
const comp = decodeSessionEventPayload(compRes.rows[0].payload, sessionId, "latest compaction");
|
|
105
|
+
const fk = comp.type === "compaction" ? comp.firstKeptEntryId : undefined;
|
|
107
106
|
if (!fk)
|
|
108
107
|
return { floorEntryId: null, floorSeq: 0 };
|
|
109
108
|
const seqRes = await pool.query("SELECT seq FROM session_event WHERE session_id = $1 AND entry_id = $2", [sessionId, fk]);
|
|
@@ -115,11 +114,10 @@ export class PgSessionStorage extends BaseSessionStorage {
|
|
|
115
114
|
const inMem = await super.getEntry(id);
|
|
116
115
|
if (inMem)
|
|
117
116
|
return inMem;
|
|
118
|
-
const res = await this.pool.query(
|
|
119
|
-
|
|
120
|
-
if (p == null)
|
|
117
|
+
const res = await this.pool.query(`SELECT ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 AND entry_id = $2`, [this.sessionId, id]);
|
|
118
|
+
if (res.rows[0] === undefined)
|
|
121
119
|
return undefined;
|
|
122
|
-
return
|
|
120
|
+
return decodeSessionEventPayload(res.rows[0].payload, this.sessionId, id);
|
|
123
121
|
}
|
|
124
122
|
async appendEntry(entry, opts) {
|
|
125
123
|
await this.persist(entry, opts);
|
|
@@ -355,7 +353,7 @@ export class PgSessionStore {
|
|
|
355
353
|
if (!metaRes.rows[0])
|
|
356
354
|
return null;
|
|
357
355
|
const leafId = metaRes.rows[0].leaf_id ?? null;
|
|
358
|
-
const eventRes = await this.pool.query(
|
|
356
|
+
const eventRes = await this.pool.query(`SELECT entry_id, parent_id, type, ${jsonColumnText("pg", "payload")}, ts FROM session_event WHERE session_id = $1 ORDER BY seq ASC`, [sourceId]);
|
|
359
357
|
const rows = eventRes.rows;
|
|
360
358
|
const newId = uuidv7();
|
|
361
359
|
const now = new Date();
|
|
@@ -364,7 +362,7 @@ export class PgSessionStore {
|
|
|
364
362
|
await conn.query("BEGIN");
|
|
365
363
|
for (let seq = 0; seq < rows.length; seq++) {
|
|
366
364
|
const r = rows[seq];
|
|
367
|
-
const payload =
|
|
365
|
+
const payload = String(r.payload);
|
|
368
366
|
await conn.query("INSERT INTO session_event (session_id, seq, entry_id, parent_id, type, payload, ts) VALUES ($1,$2,$3,$4,$5,$6::jsonb,$7)", [newId, seq, r.entry_id, r.parent_id ?? null, r.type, payload, r.ts]);
|
|
369
367
|
}
|
|
370
368
|
await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq) VALUES ($1,$2,$3,$4,$5,$6)", [newId, owner, now, now, leafId, rows.length]);
|
|
@@ -387,8 +385,8 @@ export class PgSessionStore {
|
|
|
387
385
|
const metaRes = await this.pool.query("SELECT 1 FROM session_meta WHERE session_id = $1", [sessionId]);
|
|
388
386
|
if (metaRes.rows.length === 0)
|
|
389
387
|
return null;
|
|
390
|
-
const eventRes = await this.pool.query(
|
|
391
|
-
return eventRes.rows.map((r) =>
|
|
388
|
+
const eventRes = await this.pool.query(`SELECT seq, ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 ORDER BY seq ASC`, [sessionId]);
|
|
389
|
+
return eventRes.rows.map((r) => decodeSessionEventPayload(r.payload, sessionId, r.seq));
|
|
392
390
|
}
|
|
393
391
|
async listEntryIds(sessionId) {
|
|
394
392
|
const metaRes = await this.pool.query("SELECT 1 FROM session_meta WHERE session_id = $1", [sessionId]);
|
|
@@ -406,9 +404,9 @@ export class PgSessionStore {
|
|
|
406
404
|
async function* gen() {
|
|
407
405
|
let cursor = opts?.afterSeq ?? -1;
|
|
408
406
|
for (;;) {
|
|
409
|
-
const res = await pool.query(
|
|
407
|
+
const res = await pool.query(`SELECT seq, ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 AND seq > $2 ORDER BY seq ASC LIMIT $3`, [sessionId, cursor, batchSize]);
|
|
410
408
|
for (const r of res.rows)
|
|
411
|
-
yield
|
|
409
|
+
yield decodeSessionEventPayload(r.payload, sessionId, r.seq);
|
|
412
410
|
if (res.rows.length < batchSize)
|
|
413
411
|
return;
|
|
414
412
|
cursor = Number(res.rows[res.rows.length - 1].seq);
|
|
@@ -483,8 +481,8 @@ export class PgSessionStore {
|
|
|
483
481
|
return new PgStagingHandle(this.pool, realSessionId, stagingIdFor(realSessionId, token));
|
|
484
482
|
}
|
|
485
483
|
async readStagedEntries(stagingId) {
|
|
486
|
-
const res = await this.pool.query(
|
|
487
|
-
return res.rows.map((r) =>
|
|
484
|
+
const res = await this.pool.query(`SELECT seq, ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 ORDER BY seq ASC`, [stagingId]);
|
|
485
|
+
return res.rows.map((r) => decodeSessionEventPayload(r.payload, stagingId, r.seq));
|
|
488
486
|
}
|
|
489
487
|
async sweepStagingSessions() {
|
|
490
488
|
const cutoff = new Date(Date.now() - STAGING_GC_GRACE_MS);
|
|
@@ -579,11 +577,11 @@ class PgStagingHandle {
|
|
|
579
577
|
throw new SyncConflictError(this.realSessionId, rel);
|
|
580
578
|
}
|
|
581
579
|
if (rel.relation === "fast_forward" && opts?.resolution !== "overwrite-dst" && dstIds !== null && dstIds.length > 0) {
|
|
582
|
-
const dstFull = await conn.query(
|
|
583
|
-
const stagedFull = await conn.query(
|
|
580
|
+
const dstFull = await conn.query(`SELECT seq, ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 ORDER BY seq ASC`, [this.realSessionId]);
|
|
581
|
+
const stagedFull = await conn.query(`SELECT entry_id, ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 ORDER BY seq ASC`, [this.stagingId]);
|
|
584
582
|
const dstSet = new Set(dstIds);
|
|
585
|
-
const dstEntries = dstFull.rows.map((r) =>
|
|
586
|
-
const stagedShared = stagedFull.rows.filter((r) => dstSet.has(String(r.entry_id))).map((r) =>
|
|
583
|
+
const dstEntries = dstFull.rows.map((r) => decodeSessionEventPayload(r.payload, this.realSessionId, r.seq));
|
|
584
|
+
const stagedShared = stagedFull.rows.filter((r) => dstSet.has(String(r.entry_id))).map((r) => decodeSessionEventPayload(r.payload, this.stagingId, r.entry_id));
|
|
587
585
|
const diverged = fastForwardSharedContentDiverged(stagedShared, dstEntries);
|
|
588
586
|
if (diverged) {
|
|
589
587
|
await conn.query("ROLLBACK");
|
|
@@ -592,10 +590,10 @@ class PgStagingHandle {
|
|
|
592
590
|
}
|
|
593
591
|
await conn.query("DELETE FROM session_event WHERE session_id = $1", [this.realSessionId]);
|
|
594
592
|
await conn.query("UPDATE session_event SET session_id = $1 WHERE session_id = $2", [this.realSessionId, this.stagingId]);
|
|
595
|
-
const lastRes = await conn.query(
|
|
593
|
+
const lastRes = await conn.query(`SELECT ${jsonColumnText("pg", "payload")} FROM session_event WHERE session_id = $1 ORDER BY seq DESC LIMIT 1`, [this.realSessionId]);
|
|
596
594
|
let leafId = null;
|
|
597
595
|
if (lastRes.rows[0])
|
|
598
|
-
leafId = leafIdAfterEntry(
|
|
596
|
+
leafId = leafIdAfterEntry(decodeSessionEventPayload(lastRes.rows[0].payload, this.realSessionId, "last staged row"));
|
|
599
597
|
const now = new Date();
|
|
600
598
|
await conn.query("INSERT INTO session_meta (session_id, owner, created_at, updated_at, leaf_id, leaf_seq) VALUES ($1,$2,$3,$4,$5,$6) " +
|
|
601
599
|
"ON CONFLICT (session_id) DO UPDATE SET owner = EXCLUDED.owner, updated_at = EXCLUDED.updated_at, leaf_id = EXCLUDED.leaf_id, leaf_seq = EXCLUDED.leaf_seq", [this.realSessionId, owner, now, now, leafId, srcIds.length]);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* **`session_event.payload` 这一列的单一属主**(S-550 车③a)—— 投影铸点、解码点、形读者各一份,三个
|
|
3
|
+
* 会话存储腿(`tidb-session-storage.ts` / `pg-session-storage.ts` / `tidb-session-store.ts`)共读本文件。
|
|
4
|
+
*
|
|
5
|
+
* ## 为什么必须收成一处
|
|
6
|
+
* 这一列此前有 **10 个** 各自写一遍的读法(`typeof p === "string" ? JSON.parse(p) : p as SessionTreeEntry`),
|
|
7
|
+
* 分布在三个文件的 wake / getEntry / getEpochAnchor / computeFloor / export / fork / import / 收尾 leaf
|
|
8
|
+
* 计算上。那个三元式正是 S-550 要消灭的歧义本身:**从值本身分不出驱动解过没解过**,而这一列在 MySQL 侧是
|
|
9
|
+
* `JSON`、PG 侧是 `JSONB` —— 一台 MariaDB 部署上它以文本到达(驱动不自动解码),一条被迁移脚本多
|
|
10
|
+
* `JSON.stringify` 过一次的行则在真 JSON 列上「串就再解一次」地被读成好体。十份拷贝里只要有一份将来
|
|
11
|
+
* 忘了跟,那一条 wake 就会静默读出半棵树。
|
|
12
|
+
*
|
|
13
|
+
* ## 失败方向 = 拒
|
|
14
|
+
* 会话事件日志是 **wake 的全部依据**:一行读不出形的 payload 被当成 `null` / 空对象端出去 ⇒ 这只会话的
|
|
15
|
+
* 上下文树里多一个没有 `id`、没有 `type` 的节点,`leafIdAfterEntry` 与 `buildContext` 在它上面的行为未定;
|
|
16
|
+
* 静默跳过它则是**一段对话从历史里消失**(而模型会照着一棵少了一截的树继续说话)。两者都比一次响亮的
|
|
17
|
+
* wake 失败坏。本店没有枚举式 corrupt 码(`SessionError` 的词表 —— `conflict` 等 —— 是并发契约词,
|
|
18
|
+
* 不是读侧 corrupt 词)⇒ 折成响亮裸 `Error`,点名 `<表>.<列>` + 行标识(S-550 车①② 裁定,沿用)。
|
|
19
|
+
*/
|
|
20
|
+
import type { SessionTreeEntry } from "@sema-agent/core";
|
|
21
|
+
import { type JsonColumnReader } from "./sql-json-column.js";
|
|
22
|
+
/**
|
|
23
|
+
* 形读者:core 自己声明的 `SessionTreeEntryBase` 里那两把**不可省**的串键。
|
|
24
|
+
*
|
|
25
|
+
* 🚧 **如实声明判据的射程**:core 的 base 形是 `{type, id, parentId, timestamp}` 四键全必填,但 `parentId`
|
|
26
|
+
* 可为 `null`、而 `JSON.stringify` 会把一个 `undefined` 的键直接丢掉 —— 拿它当必填有把存量行判坏的风险。
|
|
27
|
+
* 所以判据取 `type` + `id` 两把 —— 它们是本仓读腿**自己**要用的那两把:`e.type === "prompt_epoch"`
|
|
28
|
+
* (epoch 锚点腿)与 core 的 `leafIdAfterEntry`(亲读 7.26.2 的 dist:
|
|
29
|
+
* `entry.type === "leaf" ? entry.targetId : entry.id`,两把都在里面)。而且它们进得了列就必定是串 ——
|
|
30
|
+
* 写腿把这两把各自**另外**落进一列 `NOT NULL`(`session_event.entry_id` / `.type`)。
|
|
31
|
+
* 这两条已经把坏体四形全关在门外;**不复刻 core 的变体键表**(那是设计稿明令禁止的第二份键集,
|
|
32
|
+
* core 也没有导出 `SessionTreeEntry` 的验证器 —— `validateEntriesForImport` 吃的是已经成形的对象)。
|
|
33
|
+
*/
|
|
34
|
+
export declare const readSessionEventPayload: JsonColumnReader<SessionTreeEntry>;
|
|
35
|
+
/** 一格 `session_event.payload`(已按文本投影)→ 一条事件。坏体 ⇒ 响亮拒(见文件头注)。 */
|
|
36
|
+
export declare function decodeSessionEventPayload(cell: unknown, sessionId: string, rowId: unknown): SessionTreeEntry;
|
|
37
|
+
//# sourceMappingURL=session-event-payload.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { decodeJsonColumn, foldJsonColumn } from "./sql-json-column.js";
|
|
2
|
+
export const readSessionEventPayload = (body) => {
|
|
3
|
+
if (typeof body !== "object" || body === null || Array.isArray(body))
|
|
4
|
+
return { problem: "expected a JSON object (a SessionTreeEntry)" };
|
|
5
|
+
const row = body;
|
|
6
|
+
if (typeof row.id !== "string")
|
|
7
|
+
return { problem: "the entry carries no string `id`" };
|
|
8
|
+
if (typeof row.type !== "string")
|
|
9
|
+
return { problem: "the entry carries no string `type`" };
|
|
10
|
+
return { value: body };
|
|
11
|
+
};
|
|
12
|
+
export function decodeSessionEventPayload(cell, sessionId, rowId) {
|
|
13
|
+
try {
|
|
14
|
+
return decodeJsonColumn(cell, readSessionEventPayload, `session_event.payload for session ${sessionId} row ${String(rowId)}`);
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
throw foldJsonColumn(e, (m) => new Error(`session storage: ${m} — refusing rather than wake a session from a tree with a node nobody can read`));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=session-event-payload.js.map
|