@sema-agent/server 7.144.0 → 7.145.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/MIGRATION.md +129 -0
- package/README.md +3 -0
- package/README.zh-CN.md +1 -0
- package/USAGE.md +16 -8
- package/dist/audit.d.ts +2 -21
- package/dist/audit.js +0 -9
- package/dist/boot/session-faces.d.ts +1 -1
- package/dist/boot/session-faces.js +3 -3
- package/dist/boot/stage-09-leader.d.ts +1 -1
- package/dist/boot/stage-10-http-server.d.ts +1 -1
- package/dist/boot/stage-10-http-server.js +2 -2
- package/dist/config-d-migration.d.ts +3 -2
- package/dist/fleet/fleet-bus.d.ts +13 -3
- package/dist/fleet/fleet-bus.js +17 -3
- package/dist/http/admission.js +2 -1
- package/dist/http/checkpoint-ctx.d.ts +1 -0
- package/dist/http/checkpoint-ctx.js +1 -0
- package/dist/http/durable-run-leg.js +7 -6
- package/dist/http/resume-legs.js +5 -3
- package/dist/http/route-ctx.d.ts +15 -0
- package/dist/http/routes/approvals-assistant.d.ts +20 -1
- package/dist/http/routes/approvals-assistant.js +27 -6
- package/dist/http/routes/rules.d.ts +1 -1
- package/dist/http/routes/rules.js +5 -2
- package/dist/http/routes/session-sync.js +12 -0
- package/dist/http/routes/tasks.js +12 -9
- package/dist/http/run-meta.d.ts +14 -7
- package/dist/http/run-meta.js +5 -2
- package/dist/http/server.d.ts +13 -5
- package/dist/http/server.js +3 -0
- package/dist/http/submit-ctx.d.ts +12 -0
- package/dist/http/submit-ctx.js +23 -0
- package/dist/observability/fail-open.d.ts +15 -3
- package/dist/observability/fail-open.js +15 -3
- package/dist/plugins/approval-ask-store-file.d.ts +16 -2
- package/dist/plugins/approval-ask-store-file.js +21 -1
- package/dist/plugins/approval-ask-store-memory.d.ts +8 -2
- package/dist/plugins/approval-ask-store-memory.js +11 -8
- package/dist/plugins/caching-session-store.d.ts +3 -0
- package/dist/plugins/caching-session-store.js +3 -0
- package/dist/plugins/checkpoint-store-sql.d.ts +29 -11
- package/dist/plugins/checkpoint-store-sql.js +38 -16
- package/dist/plugins/file-run-store.d.ts +13 -1
- package/dist/plugins/file-run-store.js +11 -1
- package/dist/plugins/local-checkpoint-store.d.ts +14 -3
- package/dist/plugins/local-checkpoint-store.js +23 -5
- package/dist/plugins/local-session-store.d.ts +52 -16
- package/dist/plugins/local-session-store.js +47 -14
- package/dist/plugins/memory-run-store.d.ts +2 -0
- package/dist/plugins/memory-run-store.js +6 -0
- package/dist/plugins/one-time-migrations.js +2 -2
- package/dist/plugins/run-store-sql.d.ts +4 -0
- package/dist/plugins/run-store-sql.js +3 -0
- package/dist/plugins/session-store.d.ts +1 -1
- package/dist/prompt-screen.d.ts +21 -0
- package/dist/prompt-screen.js +8 -0
- package/dist/rules-consent.d.ts +63 -51
- package/dist/rules-consent.js +6 -10
- package/dist/run-acceptance.d.ts +40 -10
- package/dist/run-acceptance.js +38 -5
- package/dist/runs.js +1 -1
- package/dist/security.d.ts +10 -3
- package/dist/session-agent.d.ts +39 -0
- package/dist/session-agent.js +16 -0
- package/dist/session-error-wire.d.ts +4 -4
- package/dist/trace/model-segment.d.ts +3 -2
- package/dist/trace/model-segment.js +16 -8
- package/dist/trace/redact.d.ts +66 -2
- package/dist/trace/redact.js +220 -101
- package/dist/trace/self-minted-wire.d.ts +73 -1
- package/dist/trace/self-minted-wire.js +27 -0
- package/package.json +2 -2
- package/wire-changes/server@7.145.0.json +146 -0
package/dist/http/submit-ctx.js
CHANGED
|
@@ -3,6 +3,17 @@ import { releaseClaimedRun } from "./claimed-run.js";
|
|
|
3
3
|
import { buildCheckpointCtx, CheckpointCtxUnsealableError } from "./checkpoint-ctx.js";
|
|
4
4
|
import { refusalBody } from "./refusal-body.js";
|
|
5
5
|
import { limitFactsUnmeasured } from "../limit-refusal.js";
|
|
6
|
+
import { serviceVersion } from "../service-version.js";
|
|
7
|
+
import { recordFailOpen } from "../observability/fail-open.js";
|
|
8
|
+
import { DEFAULT_MODEL_REF } from "../model-select.js";
|
|
9
|
+
const WITHHELD_OBJECTIVE = "";
|
|
10
|
+
export function objectiveWithheldIn(ctx) {
|
|
11
|
+
return ctx.promptScreened === true;
|
|
12
|
+
}
|
|
13
|
+
function withheldBody(body, picked) {
|
|
14
|
+
const pin = typeof body.model !== "string" && typeof picked === "string" && picked !== DEFAULT_MODEL_REF ? { model: picked } : {};
|
|
15
|
+
return { ...body, objective: WITHHELD_OBJECTIVE, ...pin };
|
|
16
|
+
}
|
|
6
17
|
function unsealableRefusal(e) {
|
|
7
18
|
return {
|
|
8
19
|
status: 409,
|
|
@@ -53,4 +64,16 @@ export async function persistSubmitCtx(store, deps, claim, sessionId, fields) {
|
|
|
53
64
|
return rowTooLargeRefusal(tooLarge);
|
|
54
65
|
}
|
|
55
66
|
}
|
|
67
|
+
export async function persistSubmissionCtx(store, deps, claim, prepared) {
|
|
68
|
+
const sessionId = prepared.spec.sessionId;
|
|
69
|
+
const screened = prepared.acceptance.objectiveAtSubmit === undefined;
|
|
70
|
+
if (screened && prepared.verify !== undefined)
|
|
71
|
+
recordFailOpen("server.objective-delivery.verify-replay-copy", `session=${sessionId}`);
|
|
72
|
+
return persistSubmitCtx(store, deps, claim, sessionId, {
|
|
73
|
+
body: screened && prepared.verify === undefined ? withheldBody(prepared.body, prepared.spec.model) : prepared.body,
|
|
74
|
+
memoryScope: prepared.auth?.memoryScope,
|
|
75
|
+
...(screened || prepared.bodyFromScreenedCtx === true ? { promptScreened: true } : {}),
|
|
76
|
+
writerVersion: serviceVersion(),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
56
79
|
//# sourceMappingURL=submit-ctx.js.map
|
|
@@ -21,7 +21,7 @@ export type FailOpenEntry = {
|
|
|
21
21
|
export declare const FAIL_OPEN_TAGS: {
|
|
22
22
|
readonly "server.approval-ask-journal.compaction-failed": {
|
|
23
23
|
readonly cls: "F";
|
|
24
|
-
readonly note: "S-384(`plugins/approval-ask-store-file.ts` 的 `write()`
|
|
24
|
+
readonly note: "S-384(`plugins/approval-ask-store-file.ts` 的 `write()` 尾巴,与起服重放到删行记录时的那一次压实 —— 同一只 `compactOrRecord`;local 车道的 File 形 ask 账):账本**压实**(整本重写成行快照)失败 —— 盘满 / 只读挂载 / `.tmp` 不可写。压实是**纯管家动作**,走到它的时候这一次写**已经 append+fsync 落盘、也已经翻进内存** ⇒ 把它的 I/O 错误抛给调用方 = 把一次已生效的人类批准报成失败(客户端重试会拿到 `ask_not_pending`),正是本店存在的理由被反过来用。所以吞 + 留痕:账本继续长(core 的 `AppendLog` 在 `closeForSwap` 之后惰性重开,一次瞬时错误不会把它变成砖),下一次写再试压实(删行记录引起的压实失败,位 `removalRecordsOnDisk` 留着:下一次任何写、或下一次起服再试;起服那一次失败同样只留痕、不拒启)。放行的最坏后果 = 账本不再收缩(盘占用按写入量线性增长),**语义零影响**(行与转移一个不差)。计数非零 = 这台机器的数据根写不动了,运维要去看盘。";
|
|
25
25
|
};
|
|
26
26
|
readonly "server.approval-ask-journal.corrupt-record-skipped": {
|
|
27
27
|
readonly cls: "F";
|
|
@@ -201,7 +201,11 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
201
201
|
};
|
|
202
202
|
readonly "server.approvals.park-row-blob-unreadable": {
|
|
203
203
|
readonly cls: "F";
|
|
204
|
-
readonly note: "S-700(`plugins/checkpoint-store-sql.ts` 的 `readParkRowActionCell`):运维待批队列(`GET /v1/approvals` / `/stream`)的某一行,`checkpoint` 列里寄存行的 **`pendingAction` 子树**(NP-3 / 7.100.0 起引擎内只取这一棵、整只快照不出库;只为 park 行事实 `requiresRealApproval` / `ruleOffersAbsence` / `mandated`(S-663 二段,core #1079)/ `mandate`(S-721)/ `origin`(S-755)而读 —— 事实位的真源是 `approval-card.ts` 的 `ParkRowFacts`)**在场却不是一只对象**(JSON `null` 字面 / 串 / 数 —— 手改 / 迁移脚本改过)。子树**缺席**(SQL NULL:成员不在,或整只文档不是对象 —— 后者 `get()` 照旧拒;MariaDB 文本承载上的非 JSON 字节同样答 SQL NULL)⇒ 事实缺席、不计数。放行的最坏后果 = **这一行**的 park 行事实(`ParkRowFacts` 各位)缺席(展示/分诊材料;core 定性 display metadata,resume 的带子读 gate 自己的位),队列其余部分照常 —— 而这一行本来就 resume 不了(`get()` 对同一格拒)。不放行的代价是整只 `rows.map` 抛出 ⇒ 一行坏 blob 打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远空),与同一 `rows.map` 里 `rule_suggestions` / `risk_descriptor` 两格同族同判。故 F 类;tag 与那两格**分开计**(哪一格在坏要读得出来)。计数非零 = 这一行的 decide 同样读不动(`get()` 对同一格拒),去按 session 查那一行。 7.144.0 起同 tag
|
|
204
|
+
readonly note: "S-700(`plugins/checkpoint-store-sql.ts` 的 `readParkRowActionCell`):运维待批队列(`GET /v1/approvals` / `/stream`)的某一行,`checkpoint` 列里寄存行的 **`pendingAction` 子树**(NP-3 / 7.100.0 起引擎内只取这一棵、整只快照不出库;只为 park 行事实 `requiresRealApproval` / `ruleOffersAbsence` / `mandated`(S-663 二段,core #1079)/ `mandate`(S-721)/ `origin`(S-755)而读 —— 事实位的真源是 `approval-card.ts` 的 `ParkRowFacts`)**在场却不是一只对象**(JSON `null` 字面 / 串 / 数 —— 手改 / 迁移脚本改过)。子树**缺席**(SQL NULL:成员不在,或整只文档不是对象 —— 后者 `get()` 照旧拒;MariaDB 文本承载上的非 JSON 字节同样答 SQL NULL)⇒ 事实缺席、不计数。放行的最坏后果 = **这一行**的 park 行事实(`ParkRowFacts` 各位)缺席(展示/分诊材料;core 定性 display metadata,resume 的带子读 gate 自己的位),队列其余部分照常 —— 而这一行本来就 resume 不了(`get()` 对同一格拒)。不放行的代价是整只 `rows.map` 抛出 ⇒ 一行坏 blob 打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远空),与同一 `rows.map` 里 `rule_suggestions` / `risk_descriptor` 两格同族同判。故 F 类;tag 与那两格**分开计**(哪一格在坏要读得出来)。计数非零 = 这一行的 decide 同样读不动(`get()` 对同一格拒),去按 session 查那一行。 7.144.0 起同 tag 另有一座:停驻行的**整行读**(`readStoredRow`;按快照摘要记忆,未命中才读)—— 运维队列行的判决 `rowVerdict` 与技能卡两位、7.145.0 起还有收件箱 / 任务视图 `listByScope` 的行投影,同经这一只读法、同一失败方向(行级隔离):整行解不出 ⇒ 判官对读回的原样值作答(恒 `damaged`),行照列、带判决;体是对象但 core 摘要读不动(`{}` / 缺 `state` 等手改行)⇒ 判官对这只对象作答;两形都只丢摘要派生的位(队列行的技能卡两位 / 收件箱行的摘要键,收件箱行退回列组事实),逐次计、不进记忆,一行坏体在子树与整行两读上各计一次。修前收件箱对同一行整张拒(500 / 503)。 (`member=gate` 那第二座随 core 7.55.0 #1468 P1 阶段二退役:队列行的卡与到期分法改读 `approval_card` / `gate_kind` 两列 —— 写口与 M29 回填同一只派生落下的格 —— 本读面不再读 blob 的 `gate` 子树。)(S-2076 的 `readParkedSkillCardFacts` 两座已由上面那次 `projectStoredRow` 整行读取代,树上零命中。)";
|
|
205
|
+
};
|
|
206
|
+
readonly "server.approvals.queue-row-agent-unreadable": {
|
|
207
|
+
readonly cls: "F";
|
|
208
|
+
readonly note: "S-3000(`routes/approvals-assistant.ts` 的 `sessionAgentsOf`):运维待批队列(`GET /v1/approvals` 的 `pending[]` 与 `/v1/approvals/stream` 的 `pending` 帧)每一只停驻行带它那只会话在委派树里的位置 `agent`(会话视图的同名键,唯一读口 `session-agent.ts` 的 `readSessionAgent` = 会话店的 `placementOf` 一读)。某一只会话那一读抛(店瞬断 / 超时 / local 后端的会话存档行头读不出)⇒ **这只会话的行**缺 `agent` 键,列表其余照常。放行的最坏后果 = 这一行这一次没有委派归属线索:壳的归属判官认不到它(行照列、人从队列照样决得了 —— 与本键存在之前同形);`agent` 不是授权事实(队列行本就只列调用方 scope 的卡,决断口另有属主门)。不放行的代价是一只读不出的会话打掉整张待批列表(订阅流上每一拍都是读失败)。故 F 类;必须留痕,否则「这一次没读成」与「这不是一只 placed 委派子代」在 wire 上同形。detail = 会话 id 与经出口座的错误句。计数按次(订阅流每一拍各计一次)。";
|
|
205
209
|
};
|
|
206
210
|
readonly "server.approvals.durable-row-checkpoint-id-missing": {
|
|
207
211
|
readonly cls: "F";
|
|
@@ -373,7 +377,7 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
373
377
|
};
|
|
374
378
|
readonly "server.run-terminal.redact-threw": {
|
|
375
379
|
readonly cls: "F";
|
|
376
|
-
readonly note: "三个发射座(errorMessage 终态文案 / 诊断文本 diag / 工件 artifact,probe 前缀判别;普查 56 行;补丁头 patch-head 那座随 7.83.0 / S-113 的 redactPatchHead 一并退役)+ 出口座(S-485 / RH-1:`trace/exit-seat.ts` 的 `seatText`,probe 前缀 `seat=<座表行>`;trace 帧自由文本位上抛 ⇒ 这一位换同一枚占位、投影不抛)+ 活面放行窗(`trace/model-segment.ts` 的 `createLiveModelText`,probe 前缀 `live-hold`:逐块放行口抛 ⇒ 这一拍不放、字节留在窗里;段末整段读法抛 ⇒
|
|
380
|
+
readonly note: "三个发射座(errorMessage 终态文案 / 诊断文本 diag / 工件 artifact,probe 前缀判别;普查 56 行;补丁头 patch-head 那座随 7.83.0 / S-113 的 redactPatchHead 一并退役)+ 出口座(S-485 / RH-1:`trace/exit-seat.ts` 的 `seatText`,probe 前缀 `seat=<座表行>`;trace 帧自由文本位上抛 ⇒ 这一位换同一枚占位、投影不抛)+ 活面放行窗(`trace/model-segment.ts` 的 `createLiveModelText`,probe 前缀 `live-hold`:逐块放行口抛 ⇒ 这一拍不放、字节留在窗里;段末整段读法抛 ⇒ 余下的字节整段换同一枚占位;块界上扫描窗前移的认证扫描抛 ⇒ 窗不动,只是没省,交出的字节一个不变):S-105 最终轮 [medium]:终态 errorMessage/blockedReason/remoteEnvFailures[].message 脱敏抛出(RangeError:关键字/前缀臂那一形由 S-219 的 atLeast 铸点闭掉;JSON 秘密字段臂的转义对循环与 PEM 栅栏臂的标签交替循环由 S-265 换成手写线性扫描器闭掉 —— 已知形全部闭掉,本臂留作结构性兜底,计数恒零是证据、不归零即新形)⇒ 落固定占位 «redacted:oversized»——绝不落原文(安全轴 fail-closed),也绝不阻断终态翻转与 claim 释放(可用性)。方向刻意:一条超大错误体不得把 run 卡在 running。";
|
|
377
381
|
};
|
|
378
382
|
readonly "server.run-terminal.provider-echo-shaped": {
|
|
379
383
|
readonly cls: "F";
|
|
@@ -439,6 +443,14 @@ export declare const FAIL_OPEN_TAGS: {
|
|
|
439
443
|
readonly cls: "F";
|
|
440
444
|
readonly note: "S-1874 / S-2137(`routes/approvals-assistant.ts` 的 `GET /v1/assistant/inbox`):每行的 `objective` = 这道门所属 run 的目标预览 —— 按 run id 直读那一行的 `objectivePreview` 列(写入时已脱敏 + 截断;`GET /v1/assistant/tasks` 读同一列)。门 → run 是按键直读:门所在会话的认领持有者 / 最近一条腿,委派子代的门读派它的那条 run(停驻子代行上的 `parentTaskId`,行经判别器 `findParkedAgentForCheckpoint` 取)。某一行这几次直读失败(店抖动 / 那一行 run 的 `result` 格读不出 —— SQL `getRun` 对坏格刻意抛)⇒ **这一行**的 `objective` 落 `null`,列表其余照常。放行的最坏后果 = inbox 上这一张卡这一次不显示它属于哪个任务(纯展示位;不参与任何决裁、续跑或门判)。不放行的代价是一行 run 读不出就打掉整个用户的待批列表(人看不到要批的卡)。故 F 类;必须留痕,否则「这一次没读成」与「这道门本来就没有 run」同形。detail = 那一行的会话 id 与错误句。前身(7.97.0–7.135.x):同一格读停驻上下文、坏体落 null 的那一只 tag(objective-ctx-corrupt),随 S-1874 退役(inbox 不再读续跑上下文)。";
|
|
441
445
|
};
|
|
446
|
+
readonly "server.objective-delivery.face-fill-failed": {
|
|
447
|
+
readonly cls: "F";
|
|
448
|
+
readonly note: "`run-acceptance.ts` 的补写口(腿上装着 prompt 屏时,本服务存 / 转目标的面在提交时不写,引擎交付之后以交付的那一份补写):某一张面的补写抛错 —— `run-preview`(run 行预览列的写一次补写,店抖动 / 行已被收割删除)、`fleet-name`(进程内 fleet 行的名;不会抛,列在闭集里是为了词全)。放行的最坏后果 = 那张面保持提交时的**空**:预览缺席(列表 / 收件箱 / fleet 行不显示这条 run 是什么任务)—— 少写,不是写错:不会写出屏前原文,不参与任何门判、续跑决裁或模型输入。不放行的代价是一次展示面的写失败打断这条正在跑的 run。故 F 类;必须留痕,否则「补写失败」与「屏改写过、本来就没有可写的那一份」同形。detail = `face=<面> err=<错误句,经运维日志出口座>`。";
|
|
449
|
+
};
|
|
450
|
+
readonly "server.objective-delivery.verify-replay-copy": {
|
|
451
|
+
readonly cls: "P-DEBT";
|
|
452
|
+
readonly note: "`submit-ctx.ts` 的 `persistSubmissionCtx`:腿上装着 prompt 屏、且这次提交是 verify 形(只回结果的腿:宿主看不到受理帧,分不出屏改没改,也看不到有没有交付)⇒ 续跑输入照旧存请求原文的目标,不按「交付的那一份」写。理由:verify 形停驻后的续跑把续跑输入里的目标交给引擎的验证器(`resumeWithVerification` 的 `objective`),留空 = 验证器拿不到任务陈述;引擎自己的首跑验证器同样读请求原文。方向明知不对(屏丢弃 / 抛错 / 改写过的目标原文仍在 checkpoint ctx 里,直到这一行被收割),待引擎把交付的那一份交给宿主(含只回结果的腿)、验证器改读交付的目标之后修掉。每一次这样的提交记一次;计数非零 = 这台部署有「prompt 屏 + verify 形」的提交,它们的续跑输入里有屏前原文。detail = `session=<会话 id>`。";
|
|
453
|
+
};
|
|
442
454
|
readonly "server.run-store.corrupt-record-skipped": {
|
|
443
455
|
readonly cls: "F";
|
|
444
456
|
readonly note: "S-582(`plugins/file-run-store.ts`,local 车道的 File 形 run 店;四个盘面同一只座):盘上一条记录读不出形 —— 一行 `42` / 缺本店必填键的对象 / 中间的半截 JSON(撕尾不走本 tag,那是崩溃的正常落盘形)⇒ 跳过它继续。前三个盘面各自的最坏后果:`run.json` 坏 ⇒ 那条 run 读成**缺席**(`GET /v1/runs/:id` 404,listRuns 少一行;它若占着 claim,EEXIST 路按「行不在」判残骸接管);`events.jsonl` 一行坏 ⇒ 回放少一个事件(其余照读、按 seq 去重不变);claim 记录坏 ⇒ 走既有的撕裂 claim 路(有非终局行就收编,否则隔离出 activeDir)。第四个盘面 `run-blobs/<会话>.jsonl`(`wiring_manifest` 行容器段的块账本,`plugins/wiring-section-codec.ts`)同一只座、两个检测点:① 重放时一条块记录形不合(`digest` 不是串 / `body` 不是容器)⇒ 跳过;② 读口(`getEvents`)装回时清单行引用的块不在本会话账本里(块缺:账本行被删 / 被跳过、断电丢尾、删会话时 run 目录删不掉留下的僵尸 run)⇒ 那一帧这一次不交出(永不交出缺段的清单),每个 `(taskId, seq)` 每进程计一次,`maxSeq` / `retainedFrom` 照旧按存位;明细 `kind=section-ref taskId= seq= sections=<段:摘要,…>`。块账本整份读不出(权限 / 挂载)不走本 tag,与 `events.jsonl` 读不出同一条路(strict 拒启,否则原样抛)。都是**可用性**损失,不开任何门 ⇒ F。修前三面全零披露(店自己的 `typeof … continue` 与 core 的静默跳过);计数让「这台机器的 run 账在腐」在遥测里显形。⚠️ `RUN_STORE_STRICT_HYDRATE=on` 的射程是「文件读不出」,不含「文件里有坏行」—— strict 档下坏行照走本 tag(未改 strict 语义,见普查表第 96 行)。";
|
|
@@ -3,7 +3,7 @@ import { createLogger } from "./logger.js";
|
|
|
3
3
|
export const FAIL_OPEN_TAGS = {
|
|
4
4
|
"server.approval-ask-journal.compaction-failed": {
|
|
5
5
|
cls: "F",
|
|
6
|
-
note: "S-384(`plugins/approval-ask-store-file.ts` 的 `write()`
|
|
6
|
+
note: "S-384(`plugins/approval-ask-store-file.ts` 的 `write()` 尾巴,与起服重放到删行记录时的那一次压实 —— 同一只 `compactOrRecord`;local 车道的 File 形 ask 账):账本**压实**(整本重写成行快照)失败 —— 盘满 / 只读挂载 / `.tmp` 不可写。压实是**纯管家动作**,走到它的时候这一次写**已经 append+fsync 落盘、也已经翻进内存** ⇒ 把它的 I/O 错误抛给调用方 = 把一次已生效的人类批准报成失败(客户端重试会拿到 `ask_not_pending`),正是本店存在的理由被反过来用。所以吞 + 留痕:账本继续长(core 的 `AppendLog` 在 `closeForSwap` 之后惰性重开,一次瞬时错误不会把它变成砖),下一次写再试压实(删行记录引起的压实失败,位 `removalRecordsOnDisk` 留着:下一次任何写、或下一次起服再试;起服那一次失败同样只留痕、不拒启)。放行的最坏后果 = 账本不再收缩(盘占用按写入量线性增长),**语义零影响**(行与转移一个不差)。计数非零 = 这台机器的数据根写不动了,运维要去看盘。",
|
|
7
7
|
},
|
|
8
8
|
"server.approval-ask-journal.corrupt-record-skipped": {
|
|
9
9
|
cls: "F",
|
|
@@ -183,7 +183,11 @@ export const FAIL_OPEN_TAGS = {
|
|
|
183
183
|
},
|
|
184
184
|
"server.approvals.park-row-blob-unreadable": {
|
|
185
185
|
cls: "F",
|
|
186
|
-
note: "S-700(`plugins/checkpoint-store-sql.ts` 的 `readParkRowActionCell`):运维待批队列(`GET /v1/approvals` / `/stream`)的某一行,`checkpoint` 列里寄存行的 **`pendingAction` 子树**(NP-3 / 7.100.0 起引擎内只取这一棵、整只快照不出库;只为 park 行事实 `requiresRealApproval` / `ruleOffersAbsence` / `mandated`(S-663 二段,core #1079)/ `mandate`(S-721)/ `origin`(S-755)而读 —— 事实位的真源是 `approval-card.ts` 的 `ParkRowFacts`)**在场却不是一只对象**(JSON `null` 字面 / 串 / 数 —— 手改 / 迁移脚本改过)。子树**缺席**(SQL NULL:成员不在,或整只文档不是对象 —— 后者 `get()` 照旧拒;MariaDB 文本承载上的非 JSON 字节同样答 SQL NULL)⇒ 事实缺席、不计数。放行的最坏后果 = **这一行**的 park 行事实(`ParkRowFacts` 各位)缺席(展示/分诊材料;core 定性 display metadata,resume 的带子读 gate 自己的位),队列其余部分照常 —— 而这一行本来就 resume 不了(`get()` 对同一格拒)。不放行的代价是整只 `rows.map` 抛出 ⇒ 一行坏 blob 打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远空),与同一 `rows.map` 里 `rule_suggestions` / `risk_descriptor` 两格同族同判。故 F 类;tag 与那两格**分开计**(哪一格在坏要读得出来)。计数非零 = 这一行的 decide 同样读不动(`get()` 对同一格拒),去按 session 查那一行。 7.144.0 起同 tag
|
|
186
|
+
note: "S-700(`plugins/checkpoint-store-sql.ts` 的 `readParkRowActionCell`):运维待批队列(`GET /v1/approvals` / `/stream`)的某一行,`checkpoint` 列里寄存行的 **`pendingAction` 子树**(NP-3 / 7.100.0 起引擎内只取这一棵、整只快照不出库;只为 park 行事实 `requiresRealApproval` / `ruleOffersAbsence` / `mandated`(S-663 二段,core #1079)/ `mandate`(S-721)/ `origin`(S-755)而读 —— 事实位的真源是 `approval-card.ts` 的 `ParkRowFacts`)**在场却不是一只对象**(JSON `null` 字面 / 串 / 数 —— 手改 / 迁移脚本改过)。子树**缺席**(SQL NULL:成员不在,或整只文档不是对象 —— 后者 `get()` 照旧拒;MariaDB 文本承载上的非 JSON 字节同样答 SQL NULL)⇒ 事实缺席、不计数。放行的最坏后果 = **这一行**的 park 行事实(`ParkRowFacts` 各位)缺席(展示/分诊材料;core 定性 display metadata,resume 的带子读 gate 自己的位),队列其余部分照常 —— 而这一行本来就 resume 不了(`get()` 对同一格拒)。不放行的代价是整只 `rows.map` 抛出 ⇒ 一行坏 blob 打掉整个租户的队列(列表 500、SSE 心跳照常而队列永远空),与同一 `rows.map` 里 `rule_suggestions` / `risk_descriptor` 两格同族同判。故 F 类;tag 与那两格**分开计**(哪一格在坏要读得出来)。计数非零 = 这一行的 decide 同样读不动(`get()` 对同一格拒),去按 session 查那一行。 7.144.0 起同 tag 另有一座:停驻行的**整行读**(`readStoredRow`;按快照摘要记忆,未命中才读)—— 运维队列行的判决 `rowVerdict` 与技能卡两位、7.145.0 起还有收件箱 / 任务视图 `listByScope` 的行投影,同经这一只读法、同一失败方向(行级隔离):整行解不出 ⇒ 判官对读回的原样值作答(恒 `damaged`),行照列、带判决;体是对象但 core 摘要读不动(`{}` / 缺 `state` 等手改行)⇒ 判官对这只对象作答;两形都只丢摘要派生的位(队列行的技能卡两位 / 收件箱行的摘要键,收件箱行退回列组事实),逐次计、不进记忆,一行坏体在子树与整行两读上各计一次。修前收件箱对同一行整张拒(500 / 503)。 (`member=gate` 那第二座随 core 7.55.0 #1468 P1 阶段二退役:队列行的卡与到期分法改读 `approval_card` / `gate_kind` 两列 —— 写口与 M29 回填同一只派生落下的格 —— 本读面不再读 blob 的 `gate` 子树。)(S-2076 的 `readParkedSkillCardFacts` 两座已由上面那次 `projectStoredRow` 整行读取代,树上零命中。)",
|
|
187
|
+
},
|
|
188
|
+
"server.approvals.queue-row-agent-unreadable": {
|
|
189
|
+
cls: "F",
|
|
190
|
+
note: "S-3000(`routes/approvals-assistant.ts` 的 `sessionAgentsOf`):运维待批队列(`GET /v1/approvals` 的 `pending[]` 与 `/v1/approvals/stream` 的 `pending` 帧)每一只停驻行带它那只会话在委派树里的位置 `agent`(会话视图的同名键,唯一读口 `session-agent.ts` 的 `readSessionAgent` = 会话店的 `placementOf` 一读)。某一只会话那一读抛(店瞬断 / 超时 / local 后端的会话存档行头读不出)⇒ **这只会话的行**缺 `agent` 键,列表其余照常。放行的最坏后果 = 这一行这一次没有委派归属线索:壳的归属判官认不到它(行照列、人从队列照样决得了 —— 与本键存在之前同形);`agent` 不是授权事实(队列行本就只列调用方 scope 的卡,决断口另有属主门)。不放行的代价是一只读不出的会话打掉整张待批列表(订阅流上每一拍都是读失败)。故 F 类;必须留痕,否则「这一次没读成」与「这不是一只 placed 委派子代」在 wire 上同形。detail = 会话 id 与经出口座的错误句。计数按次(订阅流每一拍各计一次)。",
|
|
187
191
|
},
|
|
188
192
|
"server.approvals.durable-row-checkpoint-id-missing": {
|
|
189
193
|
cls: "F",
|
|
@@ -355,7 +359,7 @@ export const FAIL_OPEN_TAGS = {
|
|
|
355
359
|
},
|
|
356
360
|
"server.run-terminal.redact-threw": {
|
|
357
361
|
cls: "F",
|
|
358
|
-
note: "三个发射座(errorMessage 终态文案 / 诊断文本 diag / 工件 artifact,probe 前缀判别;普查 56 行;补丁头 patch-head 那座随 7.83.0 / S-113 的 redactPatchHead 一并退役)+ 出口座(S-485 / RH-1:`trace/exit-seat.ts` 的 `seatText`,probe 前缀 `seat=<座表行>`;trace 帧自由文本位上抛 ⇒ 这一位换同一枚占位、投影不抛)+ 活面放行窗(`trace/model-segment.ts` 的 `createLiveModelText`,probe 前缀 `live-hold`:逐块放行口抛 ⇒ 这一拍不放、字节留在窗里;段末整段读法抛 ⇒
|
|
362
|
+
note: "三个发射座(errorMessage 终态文案 / 诊断文本 diag / 工件 artifact,probe 前缀判别;普查 56 行;补丁头 patch-head 那座随 7.83.0 / S-113 的 redactPatchHead 一并退役)+ 出口座(S-485 / RH-1:`trace/exit-seat.ts` 的 `seatText`,probe 前缀 `seat=<座表行>`;trace 帧自由文本位上抛 ⇒ 这一位换同一枚占位、投影不抛)+ 活面放行窗(`trace/model-segment.ts` 的 `createLiveModelText`,probe 前缀 `live-hold`:逐块放行口抛 ⇒ 这一拍不放、字节留在窗里;段末整段读法抛 ⇒ 余下的字节整段换同一枚占位;块界上扫描窗前移的认证扫描抛 ⇒ 窗不动,只是没省,交出的字节一个不变):S-105 最终轮 [medium]:终态 errorMessage/blockedReason/remoteEnvFailures[].message 脱敏抛出(RangeError:关键字/前缀臂那一形由 S-219 的 atLeast 铸点闭掉;JSON 秘密字段臂的转义对循环与 PEM 栅栏臂的标签交替循环由 S-265 换成手写线性扫描器闭掉 —— 已知形全部闭掉,本臂留作结构性兜底,计数恒零是证据、不归零即新形)⇒ 落固定占位 «redacted:oversized»——绝不落原文(安全轴 fail-closed),也绝不阻断终态翻转与 claim 释放(可用性)。方向刻意:一条超大错误体不得把 run 卡在 running。",
|
|
359
363
|
},
|
|
360
364
|
"server.run-terminal.provider-echo-shaped": {
|
|
361
365
|
cls: "F",
|
|
@@ -397,6 +401,14 @@ export const FAIL_OPEN_TAGS = {
|
|
|
397
401
|
cls: "F",
|
|
398
402
|
note: "S-1874 / S-2137(`routes/approvals-assistant.ts` 的 `GET /v1/assistant/inbox`):每行的 `objective` = 这道门所属 run 的目标预览 —— 按 run id 直读那一行的 `objectivePreview` 列(写入时已脱敏 + 截断;`GET /v1/assistant/tasks` 读同一列)。门 → run 是按键直读:门所在会话的认领持有者 / 最近一条腿,委派子代的门读派它的那条 run(停驻子代行上的 `parentTaskId`,行经判别器 `findParkedAgentForCheckpoint` 取)。某一行这几次直读失败(店抖动 / 那一行 run 的 `result` 格读不出 —— SQL `getRun` 对坏格刻意抛)⇒ **这一行**的 `objective` 落 `null`,列表其余照常。放行的最坏后果 = inbox 上这一张卡这一次不显示它属于哪个任务(纯展示位;不参与任何决裁、续跑或门判)。不放行的代价是一行 run 读不出就打掉整个用户的待批列表(人看不到要批的卡)。故 F 类;必须留痕,否则「这一次没读成」与「这道门本来就没有 run」同形。detail = 那一行的会话 id 与错误句。前身(7.97.0–7.135.x):同一格读停驻上下文、坏体落 null 的那一只 tag(objective-ctx-corrupt),随 S-1874 退役(inbox 不再读续跑上下文)。",
|
|
399
403
|
},
|
|
404
|
+
"server.objective-delivery.face-fill-failed": {
|
|
405
|
+
cls: "F",
|
|
406
|
+
note: "`run-acceptance.ts` 的补写口(腿上装着 prompt 屏时,本服务存 / 转目标的面在提交时不写,引擎交付之后以交付的那一份补写):某一张面的补写抛错 —— `run-preview`(run 行预览列的写一次补写,店抖动 / 行已被收割删除)、`fleet-name`(进程内 fleet 行的名;不会抛,列在闭集里是为了词全)。放行的最坏后果 = 那张面保持提交时的**空**:预览缺席(列表 / 收件箱 / fleet 行不显示这条 run 是什么任务)—— 少写,不是写错:不会写出屏前原文,不参与任何门判、续跑决裁或模型输入。不放行的代价是一次展示面的写失败打断这条正在跑的 run。故 F 类;必须留痕,否则「补写失败」与「屏改写过、本来就没有可写的那一份」同形。detail = `face=<面> err=<错误句,经运维日志出口座>`。",
|
|
407
|
+
},
|
|
408
|
+
"server.objective-delivery.verify-replay-copy": {
|
|
409
|
+
cls: "P-DEBT",
|
|
410
|
+
note: "`submit-ctx.ts` 的 `persistSubmissionCtx`:腿上装着 prompt 屏、且这次提交是 verify 形(只回结果的腿:宿主看不到受理帧,分不出屏改没改,也看不到有没有交付)⇒ 续跑输入照旧存请求原文的目标,不按「交付的那一份」写。理由:verify 形停驻后的续跑把续跑输入里的目标交给引擎的验证器(`resumeWithVerification` 的 `objective`),留空 = 验证器拿不到任务陈述;引擎自己的首跑验证器同样读请求原文。方向明知不对(屏丢弃 / 抛错 / 改写过的目标原文仍在 checkpoint ctx 里,直到这一行被收割),待引擎把交付的那一份交给宿主(含只回结果的腿)、验证器改读交付的目标之后修掉。每一次这样的提交记一次;计数非零 = 这台部署有「prompt 屏 + verify 形」的提交,它们的续跑输入里有屏前原文。detail = `session=<会话 id>`。",
|
|
411
|
+
},
|
|
400
412
|
"server.run-store.corrupt-record-skipped": {
|
|
401
413
|
cls: "F",
|
|
402
414
|
note: "S-582(`plugins/file-run-store.ts`,local 车道的 File 形 run 店;四个盘面同一只座):盘上一条记录读不出形 —— 一行 `42` / 缺本店必填键的对象 / 中间的半截 JSON(撕尾不走本 tag,那是崩溃的正常落盘形)⇒ 跳过它继续。前三个盘面各自的最坏后果:`run.json` 坏 ⇒ 那条 run 读成**缺席**(`GET /v1/runs/:id` 404,listRuns 少一行;它若占着 claim,EEXIST 路按「行不在」判残骸接管);`events.jsonl` 一行坏 ⇒ 回放少一个事件(其余照读、按 seq 去重不变);claim 记录坏 ⇒ 走既有的撕裂 claim 路(有非终局行就收编,否则隔离出 activeDir)。第四个盘面 `run-blobs/<会话>.jsonl`(`wiring_manifest` 行容器段的块账本,`plugins/wiring-section-codec.ts`)同一只座、两个检测点:① 重放时一条块记录形不合(`digest` 不是串 / `body` 不是容器)⇒ 跳过;② 读口(`getEvents`)装回时清单行引用的块不在本会话账本里(块缺:账本行被删 / 被跳过、断电丢尾、删会话时 run 目录删不掉留下的僵尸 run)⇒ 那一帧这一次不交出(永不交出缺段的清单),每个 `(taskId, seq)` 每进程计一次,`maxSeq` / `retainedFrom` 照旧按存位;明细 `kind=section-ref taskId= seq= sections=<段:摘要,…>`。块账本整份读不出(权限 / 挂载)不走本 tag,与 `events.jsonl` 读不出同一条路(strict 拒启,否则原样抛)。都是**可用性**损失,不开任何门 ⇒ F。修前三面全零披露(店自己的 `typeof … continue` 与 core 的静默跳过);计数让「这台机器的 run 账在腐」在遥测里显形。⚠️ `RUN_STORE_STRICT_HYDRATE=on` 的射程是「文件读不出」,不含「文件里有坏行」—— strict 档下坏行照走本 tag(未改 strict 语义,见普查表第 96 行)。",
|
|
@@ -130,6 +130,11 @@ export declare class FileApprovalAskStore implements ApprovalAskStore {
|
|
|
130
130
|
private journalV1Records;
|
|
131
131
|
/** 盘上还有 v1 信封记录(随 M34):重放到 v1 记录时置位,任何一次压实(整本重写成 v2)清位 —— 已被压实过的账本不再为升级重写第二次。 */
|
|
132
132
|
private journalV1OnDisk;
|
|
133
|
+
/**
|
|
134
|
+
* 盘上还有删行记录({@link ROW_REMOVAL_OPS}):写下一条 / 重放到一条时置位,任何一次压实(整本重写成快照)清位 —— 与 {@link journalV1OnDisk} 同形。
|
|
135
|
+
* 置位期间压实到期(顶注「压实」段的起因 ②):同一拍压实;压实失败 ⇒ 位留着,下一次写(或下一次起服)再试。
|
|
136
|
+
*/
|
|
137
|
+
private removalRecordsOnDisk;
|
|
133
138
|
/** 本次启动隔离出去的原件路径(无坏字节 ⇒ `undefined`)。 */
|
|
134
139
|
readonly corruptQuarantinePath: string | undefined;
|
|
135
140
|
/**
|
|
@@ -159,11 +164,16 @@ export declare class FileApprovalAskStore implements ApprovalAskStore {
|
|
|
159
164
|
* 代价如实:不可编码的值(BigInt / 循环引用)在本形上**抛**,而 InMemory 形收得下 —— 那与 SQL 孪生
|
|
160
165
|
* 同向(它也存不下),是**介质事实**,写进顶注的边界段。
|
|
161
166
|
*
|
|
162
|
-
* 🔴 **压实失败不许落到调用方头上**(替补对抗复审 F3 [medium],实测后修):走到
|
|
167
|
+
* 🔴 **压实失败不许落到调用方头上**(替补对抗复审 F3 [medium],实测后修):走到 {@link compactOrRecord} 时这
|
|
163
168
|
* 一次写**已经 fsync 落盘、也已经翻进内存**,把一次纯管家动作的 I/O 错误抛给调用方 = 把一次已生效的
|
|
164
169
|
* 人类批准报成失败(客户端重试会拿到 `ask_not_pending`)。压实是 F 类兜底:失败留痕、下一次写再试。
|
|
170
|
+
* 两个起因同一条:删行记录引起的压实失败,位({@link removalRecordsOnDisk})留着,下一次**任何**写(与下一次起服)再试 ——
|
|
171
|
+
* 不是等下一次真删了行的调用。三只删行动词的内核体全是同步前缀,所以走到这里时被删的行已经不在内存里,快照里没有它们。
|
|
165
172
|
*/
|
|
166
173
|
private write;
|
|
174
|
+
/** 压实到期就压实;失败 ⇒ 登记过的 F 类兜底(计数 + 留痕),不上抛 —— `write()` 尾段与起服各调一次,同一句话。 */
|
|
175
|
+
private compactOrRecord;
|
|
176
|
+
/** 压实是否到期(顶注「压实」段两个起因)。到期 ⇒ 压实;失败原样抛给 {@link compactOrRecord}。 */
|
|
167
177
|
private maybeCompact;
|
|
168
178
|
/** 整本重写成行快照 + 原子替换。失败 ⇒ 原账本原样留着、`records` 不动(下一次写再试),日志由
|
|
169
179
|
* core 的 [ref] 惰性重开接住 —— 一次瞬时 I/O 错误绝不把这条日志变成砖。 */
|
|
@@ -193,8 +203,12 @@ export declare class FileApprovalAskStore implements ApprovalAskStore {
|
|
|
193
203
|
abortBatch(batchId: string): Promise<string[]>;
|
|
194
204
|
deferReconcile(askId: string, expectedState: AskState, expectedRev: number, nowMs: number): Promise<boolean>;
|
|
195
205
|
resolveProvisional(askId: string, from: AskState, to: AskState, patch: AskTransitionPatch): Promise<boolean>;
|
|
206
|
+
/** 删一个 run 名下的全部问行与批行。与两只留存动词同律:内核的纯读选择为空 ⇒ 不落账本、不压实;否则「先落账、再翻内存」并同拍压实(顶注「压实」段起因 ②)。 */
|
|
196
207
|
deleteByTask(taskId: string): Promise<void>;
|
|
197
|
-
/**
|
|
208
|
+
/**
|
|
209
|
+
* 留存律的问行半场(谓词在内核,见接口注)。空的一次(内核的纯读选择为空)不落账本、不碰内存、不压实;否则照常「先落账、再翻内存」,
|
|
210
|
+
* 并在 `write()` 尾段同一拍压实(顶注「压实」段起因 ②)—— 选择非空 ⇒ 内核同步前缀里必删 ≥ 1 行,被删行的字节不留在盘上。
|
|
211
|
+
*/
|
|
198
212
|
reapSettledAsks(cutoffMs: number, limit: number, keepAnchors: ReadonlySet<string>): Promise<number>;
|
|
199
213
|
/** 留存律的批行半场(同上)。 */
|
|
200
214
|
reapEmptyBatches(cutoffMs: number, limit: number): Promise<number>;
|
|
@@ -28,6 +28,7 @@ const JOURNAL_OPS = [
|
|
|
28
28
|
];
|
|
29
29
|
const JOURNAL_OP_WORDS = new Set(JOURNAL_OPS);
|
|
30
30
|
export const JOURNAL_OP_TABLE_GATE = true;
|
|
31
|
+
const ROW_REMOVAL_OPS = new Set(["reapSettledAsks", "reapEmptyBatches", "deleteByTask"]);
|
|
31
32
|
const JOURNAL_V1_CLAIM_INTENTS = ["expire", "cancel"];
|
|
32
33
|
const JOURNAL_V1_CLAIM_INTENT_WORDS = new Set(JOURNAL_V1_CLAIM_INTENTS);
|
|
33
34
|
const JOURNAL_ENVELOPE = 2;
|
|
@@ -94,6 +95,7 @@ export class FileApprovalAskStore {
|
|
|
94
95
|
corruptLineCount = 0;
|
|
95
96
|
journalV1Records = 0;
|
|
96
97
|
journalV1OnDisk = false;
|
|
98
|
+
removalRecordsOnDisk = false;
|
|
97
99
|
corruptQuarantinePath;
|
|
98
100
|
constructor(root) {
|
|
99
101
|
const dir = join(root, APPROVAL_ASK_DIR);
|
|
@@ -117,6 +119,9 @@ export class FileApprovalAskStore {
|
|
|
117
119
|
`and rewrote the live journal from the replayed state. A dropped record is a LOST approval-ask transition: inspect the quarantined file.`);
|
|
118
120
|
this.compact();
|
|
119
121
|
}
|
|
122
|
+
else if (this.removalRecordsOnDisk) {
|
|
123
|
+
this.compactOrRecord();
|
|
124
|
+
}
|
|
120
125
|
}
|
|
121
126
|
clockRead() {
|
|
122
127
|
const pinned = this.pinnedNow;
|
|
@@ -136,6 +141,8 @@ export class FileApprovalAskStore {
|
|
|
136
141
|
const onDisk = { v: JOURNAL_ENVELOPE, k: "op", t, ...callToDisk(encoded) };
|
|
137
142
|
this.logFor().append(onDisk, true);
|
|
138
143
|
this.records += 1;
|
|
144
|
+
if (ROW_REMOVAL_OPS.has(call.op))
|
|
145
|
+
this.removalRecordsOnDisk = true;
|
|
139
146
|
this.pinnedNow = t;
|
|
140
147
|
let out;
|
|
141
148
|
try {
|
|
@@ -144,15 +151,22 @@ export class FileApprovalAskStore {
|
|
|
144
151
|
finally {
|
|
145
152
|
this.pinnedNow = null;
|
|
146
153
|
}
|
|
154
|
+
this.compactOrRecord();
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
compactOrRecord() {
|
|
147
158
|
try {
|
|
148
159
|
this.maybeCompact();
|
|
149
160
|
}
|
|
150
161
|
catch (e) {
|
|
151
162
|
recordFailOpen("server.approval-ask-journal.compaction-failed", `path=${this.path} err=${e instanceof Error ? e.message : String(e)}`);
|
|
152
163
|
}
|
|
153
|
-
return out;
|
|
154
164
|
}
|
|
155
165
|
maybeCompact() {
|
|
166
|
+
if (this.removalRecordsOnDisk) {
|
|
167
|
+
this.compact();
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
156
170
|
if (this.records < ASK_JOURNAL_COMPACT_MIN_RECORDS)
|
|
157
171
|
return;
|
|
158
172
|
const snapshot = this.core.exportRows();
|
|
@@ -168,6 +182,7 @@ export class FileApprovalAskStore {
|
|
|
168
182
|
this.log?.closeForSwap();
|
|
169
183
|
atomicWriteFile(this.tmpDir, this.path, lines.join(""));
|
|
170
184
|
this.journalV1OnDisk = false;
|
|
185
|
+
this.removalRecordsOnDisk = false;
|
|
171
186
|
this.records = lines.length;
|
|
172
187
|
}
|
|
173
188
|
replayRecord(disk) {
|
|
@@ -203,6 +218,8 @@ export class FileApprovalAskStore {
|
|
|
203
218
|
refuse("unknown record kind");
|
|
204
219
|
if (typeof probe.op !== "string" || !JOURNAL_OP_WORDS.has(probe.op))
|
|
205
220
|
refuse("verb outside this build's closed set (a DOWNGRADE: a newer server wrote it)");
|
|
221
|
+
if (typeof probe.op === "string" && ROW_REMOVAL_OPS.has(probe.op))
|
|
222
|
+
this.removalRecordsOnDisk = true;
|
|
206
223
|
if (typeof probe.t !== "number" || !Number.isFinite(probe.t))
|
|
207
224
|
refuse("call record without a clock reading");
|
|
208
225
|
if (probe.op === "claimTerminal" && !JOURNAL_V1_CLAIM_INTENT_WORDS.has(String(raw.intent)))
|
|
@@ -301,6 +318,9 @@ export class FileApprovalAskStore {
|
|
|
301
318
|
return this.write({ op: "resolveProvisional", askId, from, to, patch }, (c) => this.core.resolveProvisional(c.askId, c.from, c.to, c.patch));
|
|
302
319
|
}
|
|
303
320
|
async deleteByTask(taskId) {
|
|
321
|
+
const plan = this.core.planDeleteByTask(taskId);
|
|
322
|
+
if (plan.askIds.length === 0 && plan.batchIds.length === 0)
|
|
323
|
+
return;
|
|
304
324
|
return this.write({ op: "deleteByTask", taskId }, (c) => this.core.deleteByTask(c.taskId));
|
|
305
325
|
}
|
|
306
326
|
async reapSettledAsks(cutoffMs, limit, keepAnchors) {
|
|
@@ -16,8 +16,9 @@
|
|
|
16
16
|
* · {@link InMemoryApprovalAskStore.exportRows} / {@link InMemoryApprovalAskStore.importRows} —— 压实用的
|
|
17
17
|
* 整本快照口(纯搬行,不经任何谓词)。压实要把「一串调用」折成「一串终局行」,没有这一对就只能在
|
|
18
18
|
* File 形里另写一套「哪些行还算数」的判据 —— 那才是第二份手抄。
|
|
19
|
-
* · {@link InMemoryApprovalAskStore.planReapSettledAsks} / {@link InMemoryApprovalAskStore.planReapEmptyBatches}
|
|
20
|
-
*
|
|
19
|
+
* · {@link InMemoryApprovalAskStore.planReapSettledAsks} / {@link InMemoryApprovalAskStore.planReapEmptyBatches} /
|
|
20
|
+
* {@link InMemoryApprovalAskStore.planDeleteByTask} —— 三只删行动词的**纯读选择**(删哪些行)。File 形据它判「这一次有没有东西可删」,空的一次不落账本;
|
|
21
|
+
* 删除本身仍是接口动词,与选择共用这一只。
|
|
21
22
|
* · {@link InMemoryApprovalAskStore.peekAsk} —— 一行的**同步**点读(纯读,零判定)。File 形在写口的同步前缀里据它判「这次决议在不在
|
|
22
23
|
* 属主域内」(出域的一次不落账本)、重放时据它喂行自己的属主([ref],理由在 File 形的 `decideAsk`)。
|
|
23
24
|
*
|
|
@@ -118,6 +119,11 @@ export declare class InMemoryApprovalAskStore implements ApprovalAskStore {
|
|
|
118
119
|
getByIdempotencyKey(taskId: string, key: string): Promise<AskRow | null>;
|
|
119
120
|
resolveProvisional(askId: string, from: AskState, to: AskState, patch: AskTransitionPatch): Promise<boolean>;
|
|
120
121
|
deleteByTask(taskId: string): Promise<void>;
|
|
122
|
+
/** {@link deleteByTask} 会删哪些行(纯读,零写;选择与删除共用这一只,没有第二份谓词)。 */
|
|
123
|
+
planDeleteByTask(taskId: string): {
|
|
124
|
+
readonly askIds: readonly string[];
|
|
125
|
+
readonly batchIds: readonly string[];
|
|
126
|
+
};
|
|
121
127
|
getBatch(batchId: string): Promise<BatchRow | null>;
|
|
122
128
|
/**
|
|
123
129
|
* 留存律的问行半场(谓词见接口注,与 SQL twin 的 `DELETE … LIMIT` 同答)。方法体全同步(零 `await`)⇒ File 形把它当一次落账的调用
|
|
@@ -365,14 +365,17 @@ export class InMemoryApprovalAskStore {
|
|
|
365
365
|
return true;
|
|
366
366
|
}
|
|
367
367
|
async deleteByTask(taskId) {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
368
|
+
const plan = this.planDeleteByTask(taskId);
|
|
369
|
+
for (const id of plan.askIds)
|
|
370
|
+
this.asks.delete(id);
|
|
371
|
+
for (const id of plan.batchIds)
|
|
372
|
+
this.batches.delete(id);
|
|
373
|
+
}
|
|
374
|
+
planDeleteByTask(taskId) {
|
|
375
|
+
return {
|
|
376
|
+
askIds: [...this.asks.values()].filter((r) => r.taskId === taskId).map((r) => r.askId),
|
|
377
|
+
batchIds: [...this.batches.values()].filter((b) => b.taskId === taskId).map((b) => b.batchId),
|
|
378
|
+
};
|
|
376
379
|
}
|
|
377
380
|
async getBatch(batchId) {
|
|
378
381
|
const b = this.batches.get(batchId);
|
|
@@ -116,6 +116,9 @@ export declare class CachingSessionStore implements SessionStore {
|
|
|
116
116
|
* 裸店、不经本包装,但 security.ts 的 OwnerAwareSessionStore 契约列了它 —— 经 sessionStorage 消费的
|
|
117
117
|
* 未来调用方不该静默 no-op,与 beginImportStaging 同批补齐(同一类白名单缺口)。 */
|
|
118
118
|
readonly sweepStagingSessions?: () => Promise<number>;
|
|
119
|
+
/** 带戳存档的早拒口(local 店独有;`OwnerAwareSessionStore.assertArchiveAnonymous`),读持久存档头、不经暖缓存。
|
|
120
|
+
* 🔴 必须转发(与 staging 族同一条教训):默认部署把真店包进本类,会话同步第一阶段的路由读到的是包装层 —— 不转发 = 这一口在默认部署上静默不发(第一阶段仍答通用冲突),没有任何一处报错。 */
|
|
121
|
+
readonly assertArchiveAnonymous?: (sessionId: string) => Promise<void>;
|
|
119
122
|
/** 2c session-sync (§7/§8) — delegated IDEMPOTENT replace; like importEntries it writes durable state under
|
|
120
123
|
* `sessionId`, so a stale warm handle for that id is evicted on success (next acquire re-wakes from the new log). */
|
|
121
124
|
readonly replaceEntries?: (sessionId: string, owner: string | null, entries: SessionTreeEntry[]) => Promise<void>;
|
|
@@ -29,6 +29,7 @@ export class CachingSessionStore {
|
|
|
29
29
|
importEntries;
|
|
30
30
|
beginImportStaging;
|
|
31
31
|
sweepStagingSessions;
|
|
32
|
+
assertArchiveAnonymous;
|
|
32
33
|
replaceEntries;
|
|
33
34
|
retention;
|
|
34
35
|
placements;
|
|
@@ -85,6 +86,8 @@ export class CachingSessionStore {
|
|
|
85
86
|
this.listEntryIds = ownerAware.listEntryIds.bind(inner);
|
|
86
87
|
if (ownerAware.exportEntriesStream)
|
|
87
88
|
this.exportEntriesStream = ownerAware.exportEntriesStream.bind(inner);
|
|
89
|
+
if (ownerAware.assertArchiveAnonymous)
|
|
90
|
+
this.assertArchiveAnonymous = ownerAware.assertArchiveAnonymous.bind(inner);
|
|
88
91
|
this.forget = (sessionId) => {
|
|
89
92
|
this.evict(sessionId);
|
|
90
93
|
return inner.forget?.(sessionId);
|
|
@@ -178,8 +178,10 @@ export interface PendingCheckpoint extends ParkRowFacts, SkillCardFacts {
|
|
|
178
178
|
* 这一行**本引擎续不续得了** —— core 判官 `judgeCheckpointRow` 对这一行盘上字节的判决(core 7.62.0 [ref];与助手收件箱行的 `rowVerdict`、
|
|
179
179
|
* 续跑在第一级读到的拒同一个读者、同一答),逐字投、本服务不重算不筛。判官要整行:SQL 孪生按 (库内算的 `checkpoint` 文本 sha256, 引擎构建)
|
|
180
180
|
* 记忆({@link StoredRowProjectionMemo}),未命中才按令牌批读整行;LOCAL 孪生取 core 店摘要上的同一位。
|
|
181
|
-
*
|
|
182
|
-
*
|
|
181
|
+
* 盘上字节读不成一只检查点的行(体是 `{}`、缺 `state`、JSON 字面 `null` / 标量 / 数组 / 非 JSON)同样判:判官对任何值都答,这种行答 `damaged`,
|
|
182
|
+
* 行照列(计 `server.approvals.park-row-blob-unreadable`)—— 一行坏字节只坏它自己那一行,见 {@link SqlCheckpointStore.readStoredRow}。
|
|
183
|
+
* **在场 ⇔ 这一次列表判过这一行**:缺席只在 SQL 孪生上出现 —— 两次读之间行已离开待决,或两次读之间行的字节变了(判过的是另一份,
|
|
184
|
+
* 不拼到这一行上;下一次列表即带)。缺席**不是** `ok`。非 `ok` 的行照列(人要看到它停在哪)。
|
|
183
185
|
*/
|
|
184
186
|
rowVerdict?: CheckpointRowVerdict;
|
|
185
187
|
}
|
|
@@ -444,7 +446,7 @@ export type CandidateIdentity = {
|
|
|
444
446
|
* 收敛器读口的**身份判词**(两只孪生同一只;`findCheckpointCandidatesForAsk` 的 SQL / LOCAL 两形都只经这里):先问 core 判官 `judgeCheckpointRow`
|
|
445
447
|
* (core 7.62.0 [ref],[ref])—— 判 `lacking`(更新的引擎能续它、本引擎缺读法:格式版本超上限,或要求表点名了本引擎不认的读法词)⇒ `{ readable: false }`:
|
|
446
448
|
* 本副本读不准这一行,交给收敛器当「不确定」,留给能续它的副本去绑(修前 SQL 孪生按 `version` 列比 `MAX_SUPPORTED_CHECKPOINT_VERSION`,
|
|
447
|
-
* 射程只有版本一轴,LOCAL 孪生没有这一臂)。`damaged`
|
|
449
|
+
* 射程只有版本一轴,LOCAL 孪生没有这一臂)。`damaged` **不是**这一臂:本引擎不续这些字节(别的副本续不续,引擎没有说、本服务不替它们断言),身份读得出就照常判 —— 绑上之后续跑由判官给那句固定的拒,
|
|
448
450
|
* 比让 ask 停在 PARKING 诚实。其余三态见 {@link pendingActionToolCallId}。
|
|
449
451
|
*/
|
|
450
452
|
export declare function candidateIdentityOf(cp: Checkpoint): CandidateIdentity;
|
|
@@ -725,22 +727,38 @@ export declare class SqlCheckpointStore implements CheckpointStore, KindlessBatc
|
|
|
725
727
|
* 交回的事实只许出自主读读到的同一份字节(一行一个字节版本;修前按令牌拼,B 的判决与清单配 A 的行),这一行本次不带判决与清单(与「离开待决」同一种缺席),
|
|
726
728
|
* 下一次列表主读读到新字节即命中记忆。
|
|
727
729
|
*
|
|
728
|
-
* 失败方向
|
|
729
|
-
*
|
|
730
|
-
* (
|
|
731
|
-
* `not-text`(读腿漏了投影)与摘要格形不对原样抛(码坏)。授予绑定那一臂(摘要 / 缺席词)不在这里:由 {@link parkRowFacts} 读子树投出,本读口失败时它照在。
|
|
730
|
+
* 失败方向 = 本店停驻行读者的那一条律({@link SqlCheckpointStore.readStoredRow}):摘要读不动的行(字节解不出 / 体是对象但 core 的摘要读不动)
|
|
731
|
+
* 判决照给、清单与成因词缺席,计 `server.approvals.park-row-blob-unreadable`,不进记忆(下一次列表重读、再计)。行已离开待决(两次读之间被决 / 过期)⇒ 不在返回里。
|
|
732
|
+
* `not-text`(读腿漏了投影)与摘要格形不对原样抛(码坏)。授予绑定那一臂(摘要 / 缺席词)不在这里:由 {@link parkRowFacts} 读子树投出。
|
|
732
733
|
*/
|
|
733
734
|
private storedRowProjections;
|
|
734
|
-
/** 一行整行读回 → 它的 {@link StoredRowProjection}(
|
|
735
|
+
/** 一行整行读回 → 它的 {@link StoredRowProjection}(摘要读得动的入记忆;失败方向见 {@link SqlCheckpointStore.readStoredRow})。 */
|
|
735
736
|
private projectStoredRow;
|
|
737
|
+
/**
|
|
738
|
+
* 🔴 **本店停驻行读者的那一条律:一行的字节只坏它自己那一行**(运维队列 / 订阅流的整行读与收件箱 / 任务视图的 `listByScope` 同经这里)。
|
|
739
|
+
*
|
|
740
|
+
* 一行停驻行的整行读 = `checkpoint` 列那一份字节 → core 判官 `judgeCheckpointRow` 的判决 + (读得动时)core 的摘要 `summarizeCheckpoint`。
|
|
741
|
+
* 判决**恒在**:判官对任何值都答(core:pure and total),字节读不成一只检查点(JSON 字面 `null` / 标量 / 数组 / 非 JSON)⇒ 交给它的是这一格**原样的文本**
|
|
742
|
+
* (不是本店编的一行),它答 `damaged`;体是对象而摘要读不动(`{}`、缺 `state` / `pendingAction` / `gate` 的手改行)⇒ 判官对这只对象答 ——
|
|
743
|
+
* 🚧 判官与摘要对「缺 `pendingAction`」不同答(摘要抛、判官答 `ok`:core 判官不查这一位),这一形的行照列而判决是 `ok`;本店不另判(判官的缺口归 core)。
|
|
744
|
+
* 摘要读不动的两种都计 `server.approvals.park-row-blob-unreadable`(每次列表都计)、不进记忆;**不抛** —— 修前收件箱 / 任务视图对这种行整张拒
|
|
745
|
+
* (`{}` / 缺 `state` 落 503「请重试」,坏行不修同路径恒失败),运维队列对字节解不出的行不带判决:同一张坏行两个读者两个方向。
|
|
746
|
+
* 读腿漂(`not-text`:SELECT 没投影这一列)与摘要格形不对照旧原样抛 —— 那是码坏,不是一行数据坏。
|
|
747
|
+
*
|
|
748
|
+
* 判决按 (这份字节, 引擎构建) 记忆({@link StoredRowProjectionMemo}):命中交给摘要不重判;摘要读得动的那一份由调用方 / 这里记下。
|
|
749
|
+
*/
|
|
750
|
+
private readStoredRow;
|
|
736
751
|
/**
|
|
737
752
|
* [ref] #2 (scheduler seam, core 1.105): a scope's PENDING checkpoints as lightweight
|
|
738
753
|
* {@link CheckpointSummary} rows in ONE query — the cross-task triage view a supervisor scheduler needs
|
|
739
754
|
* (gate kind + risk severity + budget spent + deadline per suspended task), no N+1 `get`s. The projection is
|
|
740
755
|
* core's shared {@link summarizeCheckpoint} run over the persisted blob (the `checkpoint` column = the same full
|
|
741
|
-
* {@link Checkpoint} `get()` parses), so
|
|
742
|
-
* created_at_ms ASC; callers (the inbox/scheduler) sort by severity.
|
|
743
|
-
*
|
|
756
|
+
* {@link Checkpoint} `get()` parses), so a readable row stays byte-identical to core's InMemory/Pg/File impls. Order =
|
|
757
|
+
* created_at_ms ASC; callers (the inbox/scheduler) sort by severity.
|
|
758
|
+
*
|
|
759
|
+
* 一行读不出摘要(字节解不出 / 体是对象而摘要读不动)⇒ 这一行**照列**,判决由判官给(缺 `pendingAction` 那一形判官答 `ok`,见 {@link SqlCheckpointStore.readStoredRow}),其余几位取本店在字节**之外**握着的事实:
|
|
760
|
+
* 令牌 / 会话 / scope / 建行时刻 / 截止 / 门型(`gate_kind` 列,与运维队列同一只判官 {@link queueRowGateKind}:读不出 ⇒ 不列、计数)/
|
|
761
|
+
* `checkpointId` 成员(是串才带;入 wire 前过信任边界由路由判)。失败方向与运维队列同一条律({@link SqlCheckpointStore.readStoredRow})。
|
|
744
762
|
*
|
|
745
763
|
* LIMIT bounds the fan-out (review w16yqkkxv): a triage view never needs more than a few — 500 is a generous
|
|
746
764
|
* ceiling that still protects memory/latency if a scope ever accumulates pathologically many pending gates.
|
|
@@ -153,6 +153,9 @@ const readRiskDescriptorBody = createObjectColumnReader();
|
|
|
153
153
|
function decodeCheckpointColumn(cell, where) {
|
|
154
154
|
return decodeJsonColumn(cell, readCheckpointBlobBody, `checkpoint.checkpoint ${where}`);
|
|
155
155
|
}
|
|
156
|
+
function verdictOfUnreadableBody(cell) {
|
|
157
|
+
return judgeCheckpointRow(cell);
|
|
158
|
+
}
|
|
156
159
|
function decodeOutcomeColumn(cell, where) {
|
|
157
160
|
return decodeNullableJsonColumn(cell, readResumeOutcomeBody, `checkpoint.outcome ${where}`);
|
|
158
161
|
}
|
|
@@ -653,47 +656,66 @@ export class SqlCheckpointStore {
|
|
|
653
656
|
const token = String(r.token);
|
|
654
657
|
const sha256 = readJsonColumnTextSha256(r[CHECKPOINT_SHA256_AS], "checkpoint.checkpoint_sha256 (whole-row read)");
|
|
655
658
|
const projection = this.projectStoredRow(r, sha256);
|
|
656
|
-
if (
|
|
659
|
+
if (sha256 === expected.get(token))
|
|
657
660
|
out.set(token, projection);
|
|
658
661
|
}
|
|
659
662
|
}
|
|
660
663
|
return out;
|
|
661
664
|
}
|
|
662
665
|
projectStoredRow(r, sha256) {
|
|
666
|
+
const read = this.readStoredRow(r, "(operator queue row, whole-row read)", sha256);
|
|
667
|
+
return read.summary === undefined ? { rowVerdict: read.rowVerdict, skillCardFacts: {} } : { rowVerdict: read.rowVerdict, skillCardFacts: skillCardFacts(read.summary) };
|
|
668
|
+
}
|
|
669
|
+
readStoredRow(r, where, sha256, discloseFor) {
|
|
670
|
+
const memo = this.storedRowMemo.get(sha256);
|
|
663
671
|
let cp;
|
|
664
672
|
try {
|
|
665
|
-
cp = decodeCheckpointColumn(r.checkpoint,
|
|
673
|
+
cp = decodeCheckpointColumn(r.checkpoint, where);
|
|
666
674
|
}
|
|
667
675
|
catch (e) {
|
|
668
676
|
if (!isJsonColumnBodyProblem(e))
|
|
669
677
|
throw e;
|
|
670
678
|
recordFailOpen("server.approvals.park-row-blob-unreadable");
|
|
671
|
-
return
|
|
679
|
+
return { rowVerdict: verdictOfUnreadableBody(r.checkpoint) };
|
|
672
680
|
}
|
|
673
681
|
let summary;
|
|
674
682
|
try {
|
|
675
|
-
summary = summarizeCheckpoint(cp);
|
|
683
|
+
summary = summarizeCheckpoint(cp, discloseFor?.(cp), memo?.rowVerdict);
|
|
676
684
|
}
|
|
677
685
|
catch {
|
|
678
686
|
recordFailOpen("server.approvals.park-row-blob-unreadable");
|
|
679
|
-
return { rowVerdict: judgeCheckpointRow(cp)
|
|
687
|
+
return { rowVerdict: judgeCheckpointRow(cp) };
|
|
680
688
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
return
|
|
689
|
+
if (memo === undefined)
|
|
690
|
+
this.storedRowMemo.set(sha256, { rowVerdict: summary.rowVerdict, skillCardFacts: skillCardFacts(summary) });
|
|
691
|
+
return { rowVerdict: summary.rowVerdict, summary };
|
|
684
692
|
}
|
|
685
693
|
async listByScope(scope) {
|
|
686
|
-
const
|
|
694
|
+
const d = this.db.dialect;
|
|
695
|
+
const { rows } = await this.db.query(`SELECT ${jsonColumnText(d, "checkpoint")}, ${jsonColumnTextSha256(d, "checkpoint", CHECKPOINT_SHA256_AS)}, ${jsonColumnText(d, "checkpoint", PARK_ROW_CHECKPOINT_ID)}, ` +
|
|
696
|
+
`token, session_id, scope, created_at_ms, deadline, gate_kind FROM checkpoint WHERE status = 'pending' AND scope = ${this.q("?", "$1")} ORDER BY created_at_ms ASC LIMIT 500`, [scope]);
|
|
687
697
|
const out = [];
|
|
688
698
|
for (const r of rows) {
|
|
689
699
|
const sha256 = readJsonColumnTextSha256(r[CHECKPOINT_SHA256_AS], `checkpoint.checkpoint_sha256 in the ${scope} scheduler view`);
|
|
690
|
-
const
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
700
|
+
const read = this.readStoredRow(r, `in the ${scope} scheduler view`, sha256, (cp) => (sentence) => recordFailOpen("server.checkpoint.inbox-risk-axes-damaged", withoutCheckpointToken(sentence, cp.token)));
|
|
701
|
+
if (read.summary !== undefined) {
|
|
702
|
+
out.push(read.summary);
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
const gateKind = queueRowGateKind(r.gate_kind);
|
|
706
|
+
if (gateKind === undefined)
|
|
707
|
+
continue;
|
|
708
|
+
const checkpointId = readCheckpointIdCell(r[PARK_ROW_CHECKPOINT_ID.as]);
|
|
709
|
+
out.push({
|
|
710
|
+
token: String(r.token),
|
|
711
|
+
rowVerdict: read.rowVerdict,
|
|
712
|
+
...(typeof checkpointId === "string" ? { checkpointId } : {}),
|
|
713
|
+
sessionId: String(r.session_id),
|
|
714
|
+
scope: String(r.scope),
|
|
715
|
+
gateKind,
|
|
716
|
+
createdAt: Number(r.created_at_ms),
|
|
717
|
+
...(r.deadline == null ? {} : { deadline: Number(r.deadline) }),
|
|
718
|
+
});
|
|
697
719
|
}
|
|
698
720
|
return out;
|
|
699
721
|
}
|
|
@@ -118,7 +118,13 @@ export declare class FileRunStore implements InProcessClaimFence {
|
|
|
118
118
|
*/
|
|
119
119
|
private refuseIfUnreadable;
|
|
120
120
|
private hydrate;
|
|
121
|
-
/**
|
|
121
|
+
/** run.json 的整文件原子替换 —— {@link persist} 的落盘那一半,不碰索引。 */
|
|
122
|
+
private writeRunFile;
|
|
123
|
+
/** Commit a RunRow to disk (whole-file atomic replace) AND the index — the single write path for every status change.
|
|
124
|
+
* 落盘在前、`runs.set` 在后:落盘抛 ⇒ 索引原样。要「全有或全无」的写口因此不在索引里那只行上原地先改再调 —— 原地改过的行,落盘一抛就是「内存已改、盘上没改」。
|
|
125
|
+
* 两种不原地的写法:对**副本**调本口(索引里换成副本:`releaseIdemKey` / `bindIdemBodyFingerprint` / `takeOverClaim`);或副本只走 {@link writeRunFile},
|
|
126
|
+
* 落成之后把改动**合回索引里的同一只行**(`fillObjectivePreview`)——后一种用在会撞进收割腿 await 窗的写口上:`reapSuspended` 等收割腿在 probe 的 await 之间手里持着索引里的行对象,
|
|
127
|
+
* await 之后的复核读的就是这只对象,索引里的行被换成副本,它们就看不见之后的认领 / 取消了。 */
|
|
122
128
|
private persist;
|
|
123
129
|
private eventLogFor;
|
|
124
130
|
/** Create a run iff the session has no active run (the task_active unique-key claim) AND no submission key of this
|
|
@@ -136,6 +142,12 @@ export declare class FileRunStore implements InProcessClaimFence {
|
|
|
136
142
|
* Synchronous from the key check to the index write ⇒ two same-key creates in this process (different sessions,
|
|
137
143
|
* different claim locks) cannot both pass the check: the loser observes the winner's row. */
|
|
138
144
|
private commitNewRun;
|
|
145
|
+
/** run 行预览的写一次补写(SQL 孪生同义:列仍是 null 才写,建行写过 / 已补过的不动;行不在 ⇒ 无事)。
|
|
146
|
+
* **副本判改 → 落盘 → 合回**:先把改动落在副本上写盘,落盘抛 ⇒ 内存里的行一字未动(预览仍是 null)、盘上也原样,下一次补写照常再试;
|
|
147
|
+
* 落成之后再把预览合回索引里的**同一只**行对象(不换对象:跨 await 持着行的收割腿复核读的是手上这只对象)。
|
|
148
|
+
* 原地先改再落盘,抛的那一拍就把内存和盘劈成两半(内存带预览、盘上 null),且「列已有值」的早退让这一分叉再也补不回来。
|
|
149
|
+
* 本体同步执行到底,中间没有 await ⇒ 副本落盘与合回之间没有别的写者插进来。 */
|
|
150
|
+
fillObjectivePreview(taskId: string, preview: string): Promise<void>;
|
|
139
151
|
requestCancel(taskId: string, owner: string | null): Promise<boolean>;
|
|
140
152
|
isCancelRequested(taskId: string, owner: string | null): Promise<boolean>;
|
|
141
153
|
requestPreempt(taskId: string, owner: string | null): Promise<boolean>;
|