@sema-agent/server 7.95.0 → 7.95.1

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.
@@ -11,10 +11,16 @@
11
11
  * · `absent` —— 行不在(`reapCtx` 按设计在 run 终态后删掉它),**或**读腿抛了一个**不是坏体**的错
12
12
  * (连接 / 超时 / 权限 / 店抖动):证不出它在就当它不在,**这是刻意保守**([ref] 方向锚:
13
13
  * 拒绝可重试,搁浅不可)。
14
- * · `corrupt` —— 读腿抛了**坏体类**错(属主谓词 `isJsonColumnBodyProblem`:`not-json` / `malformed`)。
14
+ * · `corrupt` —— 店给了它的**拒绝契约**(`StoreCorruptError`,`kind:"corrupt"` + 坐标)。
15
15
  * **不折成缺席**:两者的运维动作完全不同(前者去查那一行,后者什么都不用做)。
16
16
  * ⚠️ `not-text`(投影漂 = 我方的码病)**刻意不进这一态**,原样上抛、响亮成 500 —— 不许伪装成
17
17
  * 「数据坏了」,更不许折成缺席(那会让一次代码漂看起来像「这条会话没有上下文」)。
18
+ *
19
+ * 🔴 **按「店的拒绝形」分类,不按「列错误类」**(S-608,7.95.1:这一句此前反了,整整一版)。修前本口
20
+ * 认的是属主谓词 `isJsonColumnBodyProblem`(= 一只 `SqlJsonColumnError`),而 SQL 两孪生的 `refusing()`
21
+ * 早把列错误**折**成了一只普通 `Error` ⇒ 折后的错落进下面那条「店抖动 ⇒ 当缺席」臂,坏体与缺席在
22
+ * 用户面上仍然同答 409 —— 正是 S-577 第二半要修的那个病,只是它当时只在 local 车道上修成了(那一侧
23
+ * 恰好没折码,把列错误漏了出来 = 巧合式正确)。两条车道现在同一份拒绝契约,本口只认它。
18
24
  */
19
25
  import type { CheckpointCtx } from "./route-ctx.js";
20
26
  export type ResumeCtxRead = {
@@ -1,12 +1,12 @@
1
- import { isJsonColumnBodyProblem, SqlJsonColumnError } from "../plugins/sql-json-column.js";
1
+ import { SqlJsonColumnError, StoreCorruptError } from "../plugins/sql-json-column.js";
2
2
  export async function readResumeCtx(load) {
3
3
  try {
4
4
  const ctx = await load;
5
5
  return ctx == null ? { kind: "absent" } : { kind: "ok", ctx };
6
6
  }
7
7
  catch (e) {
8
- if (isJsonColumnBodyProblem(e))
9
- return { kind: "corrupt", where: e.where };
8
+ if (e instanceof StoreCorruptError)
9
+ return { kind: e.kind, where: e.where };
10
10
  if (e instanceof SqlJsonColumnError)
11
11
  throw e;
12
12
  return { kind: "absent" };
@@ -262,7 +262,7 @@ function mapAskRow(r) {
262
262
  return mapAskRowBody(r, askId);
263
263
  }
264
264
  catch (e) {
265
- throw foldJsonColumn(e, (m) => new Error(`approval-ask-store: ${m} — refusing rather than serve an ask row nobody can read`));
265
+ throw foldJsonColumn(e, (m) => `approval-ask-store: ${m} — refusing rather than serve an ask row nobody can read`);
266
266
  }
267
267
  }
268
268
  function mapAskRowBody(r, askId) {
@@ -15,7 +15,7 @@ function readRecordColumn(v, handle) {
15
15
  return decodeJsonColumn(v, readAgentRecordBody, `${BACKGROUND_AGENT_TABLE}.record_json for handle ${handle}`);
16
16
  }
17
17
  catch (e) {
18
- throw foldJsonColumn(e, (m) => new Error(`BackgroundAgentStore: ${m} — refusing rather than serve a hollow agent record`));
18
+ throw foldJsonColumn(e, (m) => `BackgroundAgentStore: ${m} — refusing rather than serve a hollow agent record`);
19
19
  }
20
20
  }
21
21
  function readUsageColumn(v, handle) {
@@ -23,7 +23,7 @@ function readUsageColumn(v, handle) {
23
23
  return decodeNullableJsonColumn(v, readUsageBody, `${BACKGROUND_AGENT_TABLE}.usage_json for handle ${handle}`);
24
24
  }
25
25
  catch (e) {
26
- throw foldJsonColumn(e, (m) => new Error(`BackgroundAgentStore: ${m} — refusing rather than report a usage figure nobody can read`));
26
+ throw foldJsonColumn(e, (m) => `BackgroundAgentStore: ${m} — refusing rather than report a usage figure nobody can read`);
27
27
  }
28
28
  }
29
29
  const PROJ_COLS = "scope, owner, session_scoped, session_id, parent_session_id, root_session_id, session_anchor, name, agent_type, status, spawned_at_ms, updated_at_ms, settled_at_ms, usage_json, rev, parked_checkpoint_token, park_claim_id, admitted_org_scopes, admitted_org_write_scope, record_json";
@@ -137,7 +137,7 @@ function refusing(what, read) {
137
137
  return read();
138
138
  }
139
139
  catch (e) {
140
- throw foldJsonColumn(e, (m) => new Error(`SqlCheckpointStore: ${m} — refusing rather than serve ${what}`));
140
+ throw foldJsonColumn(e, (m) => `SqlCheckpointStore: ${m} — refusing rather than serve ${what}`);
141
141
  }
142
142
  }
143
143
  function readRuleOffersCell(cell) {
@@ -314,7 +314,7 @@ function readAuditDetail(raw, id) {
314
314
  return decodeJsonColumn(raw, readJsonObjectColumn, `${DEVICE_AUDIT_TABLE}.detail for audit row ${id}`);
315
315
  }
316
316
  catch (e) {
317
- throw foldJsonColumn(e, (m) => new Error(`device-store: ${m} — refusing rather than serve an audit row whose detail nobody can read`));
317
+ throw foldJsonColumn(e, (m) => `device-store: ${m} — refusing rather than serve an audit row whose detail nobody can read`);
318
318
  }
319
319
  }
320
320
  function ph(dialect, n) {
@@ -130,7 +130,7 @@ function refusingBakeRow(read) {
130
130
  return read();
131
131
  }
132
132
  catch (e) {
133
- throw foldJsonColumn(e, (m) => new Error(`image-bake-store: ${m} — refusing rather than serve a bake row nobody can read`));
133
+ throw foldJsonColumn(e, (m) => `image-bake-store: ${m} — refusing rather than serve a bake row nobody can read`);
134
134
  }
135
135
  }
136
136
  export const IMAGE_BAKE_INDEXES = [
@@ -89,7 +89,7 @@ function refusingImageRow(read) {
89
89
  return read();
90
90
  }
91
91
  catch (e) {
92
- throw foldJsonColumn(e, (m) => new Error(`image-index: ${m} — refusing rather than serve an image row nobody can read`));
92
+ throw foldJsonColumn(e, (m) => `image-index: ${m} — refusing rather than serve an image row nobody can read`);
93
93
  }
94
94
  }
95
95
  export const IMAGE_INDEX_INDEXES = [
@@ -67,7 +67,7 @@ function readResultColumn(text, id) {
67
67
  return decodeNullableJsonColumn(text, readNonNullJsonValueColumn, `${LEADER_RUN_TABLE}.result for leader run ${id}`);
68
68
  }
69
69
  catch (e) {
70
- throw foldJsonColumn(e, (m) => new Error(`leader_run: ${m} — refusing rather than serve this run as if it had finished empty-handed`));
70
+ throw foldJsonColumn(e, (m) => `leader_run: ${m} — refusing rather than serve this run as if it had finished empty-handed`);
71
71
  }
72
72
  }
73
73
  function statusOf(raw, id) {
@@ -7,7 +7,7 @@ import { isApprovalGateKind } from "../tool-approval.js";
7
7
  import { FileCheckpointStore, atomicWriteFile, executionVerdict, sanitizePathComponent, } from "@sema-agent/core";
8
8
  import { approvalDecisionOfWinner, approvalPayloadFingerprint, boundedRuleOffers, boundedToolInput, pendingActionToolCallId, pendingRowPrecedes, TERMINAL_BACKSTOP_MS, TERMINAL_GRACE_MS } from "./checkpoint-store-sql.js";
9
9
  import { quarantineUnreadableSidecar } from "./local-sidecar-quarantine.js";
10
- import { createZodColumnReader, decodeJsonColumn, readStringArrayColumn } from "./sql-json-column.js";
10
+ import { createZodColumnReader, decodeJsonColumn, foldJsonColumn, readStringArrayColumn } from "./sql-json-column.js";
11
11
  import { UNMANAGED_RETENTION } from "./retention-store-sql.js";
12
12
  import { assertJsonColumnWritable, createObjectColumnReader } from "./sql-json-column.js";
13
13
  function terminalAtOf(cp) {
@@ -36,6 +36,14 @@ function createCtxRecordReader() {
36
36
  return { value: { ...envelope.value, ctx: payload.value } };
37
37
  };
38
38
  }
39
+ function refusingCtx(sessionId, read) {
40
+ try {
41
+ return read();
42
+ }
43
+ catch (e) {
44
+ throw foldJsonColumn(e, (m) => `LocalCheckpointStore: ${m} — refusing rather than serve a resume context nobody can read (session ${JSON.stringify(sessionId)})`);
45
+ }
46
+ }
39
47
  export class LocalCheckpointStore {
40
48
  retention = UNMANAGED_RETENTION;
41
49
  durability = "durable";
@@ -316,7 +324,7 @@ export class LocalCheckpointStore {
316
324
  const p = join(this.ctxDir, ctxFileOf(sessionId));
317
325
  if (!existsSync(p))
318
326
  return null;
319
- const rec = decodeJsonColumn(readFileSync(p, "utf8"), createCtxRecordReader(), `checkpoint-ctx ${ctxFileOf(sessionId)} for session ${JSON.stringify(sessionId)}`);
327
+ const rec = refusingCtx(sessionId, () => decodeJsonColumn(readFileSync(p, "utf8"), createCtxRecordReader(), `checkpoint-ctx ${ctxFileOf(sessionId)} for session ${JSON.stringify(sessionId)}`));
320
328
  return rec.sessionId === sessionId ? rec.ctx : null;
321
329
  }
322
330
  async reapCtx(cutoff) {
@@ -2,7 +2,7 @@ import { MAILBOX_INVALID_PEER_META_CODE, MAILBOX_TOMBSTONED_RECIPIENT_CODE, Mail
2
2
  import { pgHasUnstorable, pgSanitizeText, PgUnstorableError } from "./pg-safe-json.js";
3
3
  import { ensureIndexes } from "./ensure-index.js";
4
4
  import { escapeLike } from "./sql-escape.js";
5
- import { decodeNullableJsonColumn, foldJsonColumn, jsonColumnText, readStringArrayColumn } from "./sql-json-column.js";
5
+ import { decodeNullableJsonColumn, foldJsonColumn, isJsonColumnBodyProblem, jsonColumnText, readStringArrayColumn } from "./sql-json-column.js";
6
6
  export const MAILBOX_TABLE = "mailbox";
7
7
  export const MAILBOX_MSG_TABLE = "mailbox_message";
8
8
  export const MAILBOX_TOMBSTONE_TABLE = "mailbox_tombstone";
@@ -52,7 +52,7 @@ function decodeHopChain(raw, seq) {
52
52
  return decodeNullableJsonColumn(raw, readStringArrayColumn, `${MAILBOX_MSG_TABLE}.hop_chain for seq ${String(seq)}`);
53
53
  }
54
54
  catch (e) {
55
- throw foldJsonColumn(e, (m) => new Error(`MailboxStore: ${m} — refusing the lease rather than let a message skip peer admission (an absent chain makes core's judgePeerAdmission not run at all)`));
55
+ throw foldJsonColumn(e, (m) => `MailboxStore: ${m} — refusing the lease rather than let a message skip peer admission (an absent chain makes core's judgePeerAdmission not run at all)`);
56
56
  }
57
57
  }
58
58
  const PEER_META_MAX_BYTES = 60_000;
@@ -73,7 +73,9 @@ function decodePeerMeta(raw, seq) {
73
73
  catch (e) {
74
74
  if (e instanceof MailboxStoreError)
75
75
  throw e;
76
- throw foldJsonColumn(e, (m) => new MailboxStoreError(MAILBOX_INVALID_PEER_META_CODE, `MailboxStore: ${m} — refusing the lease rather than read a corrupt typed record as "no metadata"`));
76
+ if (!isJsonColumnBodyProblem(e))
77
+ throw e;
78
+ throw new MailboxStoreError(MAILBOX_INVALID_PEER_META_CODE, `MailboxStore: ${e.message} — refusing the lease rather than read a corrupt typed record as "no metadata"`);
77
79
  }
78
80
  }
79
81
  const readPeerMetaColumn = (body) => {
@@ -51,7 +51,7 @@ function readFrontmatter(cell, id) {
51
51
  return decodeJsonColumn(cell, readFrontmatterColumn, `${PG_MEMORY_ENGINE_TABLES.entry}.frontmatter for entry ${String(id)}`);
52
52
  }
53
53
  catch (e) {
54
- 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`));
54
+ throw foldJsonColumn(e, (m) => `memory-engine: ${m} — refusing rather than serve a note whose frontmatter (the origin/exposure law carrier) nobody can read`);
55
55
  }
56
56
  }
57
57
  function readEmbedding(cell, id) {
@@ -48,7 +48,7 @@ function readFrontmatter(cell, id) {
48
48
  return decodeJsonColumn(cell, readFrontmatterColumn, `${TIDB_MEMORY_ENGINE_TABLES.entry}.frontmatter for entry ${String(id)}`);
49
49
  }
50
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`));
51
+ throw foldJsonColumn(e, (m) => `memory-engine: ${m} — refusing rather than serve a note whose frontmatter (the origin/exposure law carrier) nobody can read`);
52
52
  }
53
53
  }
54
54
  export class TiDBMemoryEngineBackend {
@@ -34,7 +34,7 @@ export function refusingMemorySyncRow(read) {
34
34
  return read();
35
35
  }
36
36
  catch (e) {
37
- throw foldJsonColumn(e, (m) => new Error(`memory-sync store: ${m} — refusing rather than serve a sync row nobody can read`));
37
+ throw foldJsonColumn(e, (m) => `memory-sync store: ${m} — refusing rather than serve a sync row nobody can read`);
38
38
  }
39
39
  }
40
40
  function toMs(v) {
@@ -346,7 +346,7 @@ async function refusingUnreadableColumns(what, fn) {
346
346
  return await fn();
347
347
  }
348
348
  catch (e) {
349
- throw foldJsonColumn(e, (m) => new Error(`${m} — the ${what} does not carry a readable shape; refusing the whole of it rather than report a partial one`));
349
+ throw foldJsonColumn(e, (m) => `${m} — the ${what} does not carry a readable shape; refusing the whole of it rather than report a partial one`);
350
350
  }
351
351
  }
352
352
  class SqlDurableRulePartition {
@@ -728,7 +728,7 @@ export class SqlRuleImportTicketStore {
728
728
  outcome = decodeJsonColumn(row.redeemedOutcome, readJsonObjectColumn, `${PERMISSION_RULE_TICKET_TABLE}.redeemed_outcome for ticket ${ticketId}`);
729
729
  }
730
730
  catch (e) {
731
- throw foldJsonColumn(e, (m) => new Error(`${m} — refusing rather than read a settled ticket as still open (which re-runs a redemption that already ran)`));
731
+ throw foldJsonColumn(e, (m) => `${m} — refusing rather than read a settled ticket as still open (which re-runs a redemption that already ran)`);
732
732
  }
733
733
  return { state: "settled", outcome };
734
734
  }
@@ -22,7 +22,7 @@ function refusing(what, read) {
22
22
  return read();
23
23
  }
24
24
  catch (e) {
25
- throw foldJsonColumn(e, (m) => new Error(`SqlRunStore: ${m} — refusing rather than ${what}`));
25
+ throw foldJsonColumn(e, (m) => `SqlRunStore: ${m} — refusing rather than ${what}`);
26
26
  }
27
27
  }
28
28
  function usageResultBody(v) {
@@ -14,7 +14,7 @@ export function decodeSessionEventPayload(cell, sessionId, rowId) {
14
14
  return decodeJsonColumn(cell, readSessionEventPayload, `session_event.payload for session ${sessionId} row ${String(rowId)}`);
15
15
  }
16
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`));
17
+ throw foldJsonColumn(e, (m) => `session storage: ${m} — refusing rather than wake a session from a tree with a node nobody can read`);
18
18
  }
19
19
  }
20
20
  //# sourceMappingURL=session-event-payload.js.map
@@ -1,7 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { loosenReasons, SessionPolicyError, normalizeRules, stripRev } from "@sema-agent/core";
3
3
  import { readSessionPermissionRules, describeSessionPermissionRulesProblem } from "../session-policy-wire.js";
4
- import { decodeJsonColumn, foldJsonColumn, jsonColumnText } from "./sql-json-column.js";
4
+ import { decodeJsonColumn, isJsonColumnBodyProblem, jsonColumnText } from "./sql-json-column.js";
5
5
  import { pgHasUnstorable } from "./pg-safe-json.js";
6
6
  import { mysqlDriver, pgDriver } from "./sql-driver.js";
7
7
  import { isDeadlockError, isDupKeyError } from "./sql-errors.js";
@@ -17,7 +17,9 @@ function readRulesBody(text, where) {
17
17
  return decodeJsonColumn(text, readSessionRulesBody, where);
18
18
  }
19
19
  catch (e) {
20
- throw foldJsonColumn(e, (m) => new SessionPolicyError("corrupt", `${m} — an unreadable rules body served as an EMPTY rule set would silently drop every tightening this session carries`));
20
+ if (!isJsonColumnBodyProblem(e))
21
+ throw e;
22
+ throw new SessionPolicyError("corrupt", `${e.message} — an unreadable rules body served as an EMPTY rule set would silently drop every tightening this session carries`);
21
23
  }
22
24
  }
23
25
  function rowToStored(r, where) {
@@ -85,8 +85,17 @@ export declare function jsonColumnTextExpr(dialect: SqlDialect, column: string):
85
85
  export declare const SQL_JSON_COLUMN_PROBLEMS: readonly ["not-text", "not-json", "malformed"];
86
86
  export type SqlJsonColumnProblem = (typeof SQL_JSON_COLUMN_PROBLEMS)[number];
87
87
  /**
88
- * 店层码,**不上 wire**:各店在自己的错误映射里把它折成该店既有的 corrupt 类码(见 {@link foldJsonColumn})。
89
- * 终局对外码经 7.92.2 的词法门,永不裸穿。
88
+ * **体**的两形(`not-text` 之外的全部)—— 「数据坏了」这一族。类型从闭集派生,不手抄。
89
+ * 存在的理由:{@link StoreCorruptError} 要在**类型面**说出「我携带的种别恒非 `not-text`」。
90
+ */
91
+ export type SqlJsonColumnBodyProblem = Exclude<SqlJsonColumnProblem, "not-text">;
92
+ /**
93
+ * 店层码,**不上 wire**:店层折码口 {@link foldJsonColumn} 把它折成 {@link StoreCorruptError}
94
+ * (有自己闭集契约词的店折成自己的码)。终局对外码经 7.92.2 的词法门,永不裸穿。
95
+ *
96
+ * 🔴 **它只许在 `not-text` 那一形上穿过店的边界**(S-608):`not-text` 是读腿自己的缺陷(投影漂),
97
+ * 按 {@link foldJsonColumn} 的规则不折、原样上抛;而体的两形一旦穿出去,上层就得**按列错误类**分类,
98
+ * 那正是 S-608 的病 —— 读口按列错误分类、店按店拒绝形折码,两层各说各话,坏体于是落进「店抖动」臂。
90
99
  */
91
100
  export declare class SqlJsonColumnError extends Error {
92
101
  readonly problem: SqlJsonColumnProblem;
@@ -133,13 +142,47 @@ export declare function decodeJsonColumn<T>(text: unknown, read: JsonColumnReade
133
142
  */
134
143
  export declare function decodeNullableJsonColumn<T>(cell: unknown, read: JsonColumnReader<T>, where: string): T | undefined;
135
144
  /**
136
- * 店层折码的**唯一规则**(`catch (e) { throw foldJsonColumn(e, (m) => new XError("corrupt", m)); }`)。
145
+ * **「这一格耐久载荷读不出来」的店层拒绝**(整仓唯一一只)—— {@link foldJsonColumn} 的**恒定产物**。
146
+ *
147
+ * 🔴 为什么它必须是一只**具名类**而不是 `new Error(…)`(S-608 真案,7.95.1):折码的产物是**店交给
148
+ * 上层的契约**。折成裸 `Error` 之后,「这一行确定性地读不出来」与「店抖了一下」在上层**没有任何判别位**,
149
+ * 而两者的处置完全相反(前者要人去修那一行,后者重试 / 保守当缺席)。S-577 给续跑上下文修的正是这条
150
+ * 分家,可它在 SQL 车道上**结构不可达**整整一版:读口按**列错误类**分类,而店早把列错误折成了裸
151
+ * `Error` ⇒ 坏体一路掉进「店抖动 ⇒ 当缺席」臂,用户拿到的还是那个 409(local 车道答对,只是因为它
152
+ * 根本没折码、把列错误漏了出来 —— 巧合式正确,不是同一份契约)。
153
+ * ⇒ 读口只认**店的拒绝契约**,而店的拒绝契约只有这一只。
154
+ *
155
+ * 🔴 {@link kind} 逐字就是消费点那只联合里的那个词(`http/resume-ctx-read.ts` 的 `ResumeCtxRead`):
156
+ * 店说 `corrupt`,读口原样转述,中间**没有第二张翻译表**。
157
+ *
158
+ * 有**自己的闭集契约词**的店(`session-policy` 的 `SessionPolicyError("corrupt")`、`mailbox` 的
159
+ * `MailboxStoreError`)不经本折码口 —— 它们的消费点按**那个码**判,本类的判别位对它们是多余的第二份。
160
+ * 那两处用属主谓词 {@link isJsonColumnBodyProblem} 自己写那一句(判别仍只有一个写者)。
161
+ */
162
+ export declare class StoreCorruptError extends Error {
163
+ /** 消费点的判别词(与 `ResumeCtxRead` 的那一态同字)。 */
164
+ readonly kind: "corrupt";
165
+ /** 体的种别(类型面即保证恒非 `not-text` —— 那一形不折,见 {@link foldJsonColumn})。 */
166
+ readonly problem: SqlJsonColumnBodyProblem;
167
+ /** 出事的坐标(`<table>.<column> for <identity>`,由 {@link SqlJsonColumnError.where} 原样带过来)——
168
+ * 进 wire 的 `where` 字段,人按它去找那一行。 */
169
+ readonly where: string;
170
+ constructor(cause: SqlJsonColumnError & {
171
+ readonly problem: SqlJsonColumnBodyProblem;
172
+ }, sentence: string);
173
+ }
174
+ /**
175
+ * 店层折码的**唯一规则**(`catch (e) { throw foldJsonColumn(e, (m) => \`XStore: ${m} — refusing …\`); }`)。
176
+ *
177
+ * 🔴 回调交出来的是**一句话**,不是一只错误(S-608 按类改):产物恒是 {@link StoreCorruptError},于是
178
+ * 「折成一只没有判别位的错」在**构造上**写不出来。此前这一格的签名是 `(message) => Error`,21 个调用点
179
+ * 里 19 个折成了裸 `Error` —— 每一个都是一枚等着下一个消费方来踩的 S-608。
137
180
  *
138
181
  * 🔴 `not-text` **不折**:它是读腿自己的缺陷(投影漏了),折成「这一行坏了」会把一次代码漂伪装成一次
139
- * 数据事故 —— 这条判别只写在这里一处,五个店不各写一遍(各写一遍的下场是某天有一处把程序错也折了)。
140
- * 非本模块的错误原样透出(连接 / 超时 / 权限本来就该照原样上抛)。
182
+ * 数据事故 —— 这条判别只写在这里一处(与它的容忍侧孪生 {@link isJsonColumnBodyProblem} 共用),
183
+ * 各店不各写一遍。非本模块的错误原样透出(连接 / 超时 / 权限本来就该照原样上抛)。
141
184
  */
142
- export declare function foldJsonColumn(e: unknown, corrupt: (message: string) => Error): Error;
185
+ export declare function foldJsonColumn(e: unknown, refusing: (message: string) => string): Error;
143
186
  /**
144
187
  * 折码的**容忍侧孪生**(与 {@link foldJsonColumn} 同一条判别,只写这一处):这一次失败是不是
145
188
  * **数据体**的问题(`not-json` / `malformed`)。
@@ -149,7 +192,9 @@ export declare function foldJsonColumn(e: unknown, corrupt: (message: string) =>
149
192
  * 调用点原样上抛:一条读腿漏了投影、或库连不上,绝不许被一条「这一格坏了,跳过它」的容忍臂吞掉 ——
150
193
  * 那会让一次代码漂 / 一次故障伪装成「几行数据坏了」,而两者的处置完全不同。
151
194
  */
152
- export declare function isJsonColumnBodyProblem(e: unknown): e is SqlJsonColumnError;
195
+ export declare function isJsonColumnBodyProblem(e: unknown): e is SqlJsonColumnError & {
196
+ readonly problem: SqlJsonColumnBodyProblem;
197
+ };
153
198
  /**
154
199
  * **写腿的形闸** —— 一个值**序列化之后**还是不是这一列读得回来的形。判据与读腿**共用同一只形读者**,
155
200
  * 所以「写得进」与「读得出」在构造上是同一句话。
@@ -49,9 +49,20 @@ export function decodeJsonColumn(text, read, where) {
49
49
  export function decodeNullableJsonColumn(cell, read, where) {
50
50
  return cell === null ? undefined : decodeJsonColumn(cell, read, where);
51
51
  }
52
- export function foldJsonColumn(e, corrupt) {
53
- if (e instanceof SqlJsonColumnError && e.problem !== "not-text")
54
- return corrupt(e.message);
52
+ export class StoreCorruptError extends Error {
53
+ kind = "corrupt";
54
+ problem;
55
+ where;
56
+ constructor(cause, sentence) {
57
+ super(sentence, { cause });
58
+ this.name = "StoreCorruptError";
59
+ this.problem = cause.problem;
60
+ this.where = cause.where;
61
+ }
62
+ }
63
+ export function foldJsonColumn(e, refusing) {
64
+ if (isJsonColumnBodyProblem(e))
65
+ return new StoreCorruptError(e, refusing(e.message));
55
66
  return e instanceof Error ? e : new Error(String(e));
56
67
  }
57
68
  export function isJsonColumnBodyProblem(e) {
@@ -27,7 +27,7 @@ function parseItem(v, where) {
27
27
  return decodeJsonColumn(v, readTaskListItemBody, `${TASK_LIST_ITEM_TABLE}.item_json ${where}`);
28
28
  }
29
29
  catch (e) {
30
- throw foldJsonColumn(e, (m) => new Error(`TaskListStore: ${m} — refusing rather than hand the model a row nobody can read as a task`));
30
+ throw foldJsonColumn(e, (m) => `TaskListStore: ${m} — refusing rather than hand the model a row nobody can read as a task`);
31
31
  }
32
32
  }
33
33
  function assertListKeyBytes(listKey) {
@@ -18,7 +18,7 @@ function parseRecord(raw, key) {
18
18
  return decodeJsonColumn(raw, readUsageWindowRecordBody, `${USAGE_WINDOW_TABLE}.record for key "${key}"`);
19
19
  }
20
20
  catch (e) {
21
- throw foldJsonColumn(e, (m) => new Error(`${m} — refusing to mis-count a governance window`));
21
+ throw foldJsonColumn(e, (m) => `${m} — refusing to mis-count a governance window`);
22
22
  }
23
23
  }
24
24
  function validateUsageWindowRecord(v) {
@@ -9,7 +9,7 @@ function decodeJournalResult(cell, where) {
9
9
  return decodeJsonColumn(cell, readJournalRowBody, where);
10
10
  }
11
11
  catch (e) {
12
- throw foldJsonColumn(e, (m) => new Error(`workflow journal: ${m} — refusing rather than replay a journal entry nobody can read`));
12
+ throw foldJsonColumn(e, (m) => `workflow journal: ${m} — refusing rather than replay a journal entry nobody can read`);
13
13
  }
14
14
  }
15
15
  export class SqlWorkflowJournalStore {
@@ -46,7 +46,7 @@ function readRunColumn(v, id) {
46
46
  return decodeJsonColumn(v, readWorkflowRunBody, `workflow_run.run for run ${id}`);
47
47
  }
48
48
  catch (e) {
49
- throw foldJsonColumn(e, (m) => new Error(`SqlWorkflowRunStore: ${m} — refusing rather than resume a hollow run`));
49
+ throw foldJsonColumn(e, (m) => `SqlWorkflowRunStore: ${m} — refusing rather than resume a hollow run`);
50
50
  }
51
51
  }
52
52
  export class SqlWorkflowRunStore {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "7.95.0",
3
+ "version": "7.95.1",
4
4
  "description": "Sema Server — the server/API implementation layer for Sema, wiring core, registry, model providers, and cloud agent execution. Built on @sema-agent/core.",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",