@sema-agent/server 7.110.0 → 7.112.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 +16 -0
- package/dist/approval-ask-audit-store.d.ts +38 -25
- package/dist/approval-ask-audit-store.js +26 -59
- package/dist/bench/s1/run-firm.d.ts +11 -0
- package/dist/bench/s1/run-firm.js +47 -32
- package/dist/boot/coordinators.js +1 -1
- package/dist/boot/session-faces.d.ts +4 -0
- package/dist/boot/session-faces.js +3 -1
- package/dist/boot/stage-07-capability-layer.d.ts +1 -0
- package/dist/boot/stage-07-capability-layer.js +4 -3
- package/dist/boot/stage-08-reapers.d.ts +1 -0
- package/dist/boot/stage-09-leader.d.ts +1 -0
- package/dist/boot/stage-09-leader.js +2 -1
- package/dist/boot/stage-10-http-server.d.ts +1 -0
- package/dist/boot/workflow-orchestration.js +1 -1
- package/dist/hooks/hook-runner.d.ts +5 -0
- package/dist/hooks/hook-runner.js +45 -1
- package/dist/http/admission.js +6 -1
- package/dist/http/resume-legs.d.ts +6 -15
- package/dist/http/resume-legs.js +33 -14
- package/dist/http/route-ctx.d.ts +28 -11
- package/dist/http/routes/approvals-assistant.js +1 -1
- package/dist/http/routes/runs.js +17 -7
- package/dist/http/routes/tasks.d.ts +3 -2
- package/dist/http/routes/tasks.js +45 -51
- package/dist/http/server.d.ts +1 -1
- package/dist/http/server.js +1 -2
- package/dist/index.d.ts +1 -1
- package/dist/instruction-selection.d.ts +34 -0
- package/dist/instruction-selection.js +23 -0
- package/dist/observability/corrupt-read-seat.d.ts +18 -0
- package/dist/observability/corrupt-read-seat.js +18 -0
- package/dist/observability/fail-open.d.ts +20 -4
- package/dist/observability/fail-open.js +17 -4
- package/dist/orchestration/workflow-agent-session-index.d.ts +12 -23
- package/dist/orchestration/workflow-agent-session-index.js +11 -50
- package/dist/orchestration/workflow-notify-journal.d.ts +11 -8
- package/dist/orchestration/workflow-notify-journal.js +20 -36
- package/dist/plugins/file-outcome-sink.d.ts +4 -1
- package/dist/plugins/file-outcome-sink.js +9 -3
- package/dist/plugins/file-run-store.js +3 -3
- package/dist/plugins/retention-store-sql.d.ts +1 -1
- package/dist/plugins/retention-store-sql.js +2 -0
- package/dist/plugins/store-backend.js +1 -0
- package/dist/plugins/store-contracts.d.ts +59 -9
- package/dist/plugins/store-contracts.js +10 -0
- package/dist/plugins/user-input-ledger-file.d.ts +4 -2
- package/dist/plugins/user-input-ledger-file.js +11 -1
- package/dist/plugins/user-input-ledger-sql.d.ts +14 -4
- package/dist/plugins/user-input-ledger-sql.js +46 -18
- package/dist/project-memory.d.ts +10 -2
- package/dist/project-memory.js +5 -2
- package/dist/run-cancel-context.d.ts +59 -7
- package/dist/run-cancel-context.js +26 -0
- package/dist/runs.d.ts +7 -5
- package/dist/runs.js +22 -16
- package/dist/trace/ledger-sink.d.ts +4 -0
- package/dist/trace/ledger-sink.js +1 -1
- package/dist/trace/project.d.ts +11 -3
- package/dist/trace/project.js +6 -5
- package/dist/trace/self-minted-wire.d.ts +48 -0
- package/dist/trace/self-minted-wire.js +17 -0
- package/dist/trace/wire-projection-faces.d.ts +3 -3
- package/dist/trace/wire-projection-faces.js +4 -2
- package/dist/user-input-ledger.d.ts +101 -48
- package/dist/user-input-ledger.js +52 -35
- package/package.json +3 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type WorkflowRun } from "@sema-agent/core";
|
|
2
|
+
import type { Logger } from "../observability/logger.js";
|
|
2
3
|
/** 一条索引行:**子会话 id 是主键**(会话 id 全局唯一 ⇒ 读口天然是单行精确查,无分页假阴性)。 */
|
|
3
4
|
export interface WorkflowAgentSessionRow {
|
|
4
5
|
/** parked 的那只 workflow 子代自己的会话 id(= `WorkflowAgentRun.sessionId` = 那条 checkpoint 行的 `sessionId`;
|
|
@@ -101,38 +102,26 @@ export declare function readAgentSessionLedgerLine(v: unknown): WorkflowAgentSes
|
|
|
101
102
|
* 单机车道的索引(`FileWorkflowRunStore` / `InMemoryWorkflowRunStore` 的同室孪生)。
|
|
102
103
|
*
|
|
103
104
|
* `root` 在场 ⇒ 追加式 JSONL 账本(与姊妹 {@link import("./workflow-notify-journal.js").FileWorkflowNotifyJournalStore}
|
|
104
|
-
* 逐字同形:0o700 目录 + 0o600
|
|
105
|
+
* 逐字同形:0o700 目录 + 0o600 账本、按行重放),重启后 park 仍可 join;缺席 ⇒ 纯内存
|
|
105
106
|
* (`WORKFLOW_RUN_STORE=memory` 的显式易失车道,与它的 run 店同寿命)。
|
|
106
107
|
*
|
|
107
108
|
* 账本的一行 = **一次 `syncRun` 的整份行集**(按 runId 后写覆盖),所以重放即收敛,不需要压缩腿。
|
|
109
|
+
*
|
|
110
|
+
* 追加 / 重放走本仓 JSONL 店的**共用口**(S-977;此前 ctor 里自写 `truncateTornTail` + 手写 `split("\n") + JSON.parse` 重放):
|
|
111
|
+
* 追加 = core `AppendLog`(惰性打开、撕尾在**打开与每次追加前**修 —— 修前只在 boot 修,运行期半写后的下一次成功写仍会被粘进残尾),
|
|
112
|
+
* 重放 = `replayJsonlSkipping`(登记座 `workflow-agent-session-index`:中间坏行 —— 含半截 JSON 与形不对的整行 —— 跳过 + 逐条披露 + F tag;
|
|
113
|
+
* 撕尾不计、不披露)。盘上字节 / 0o600 账本 / 0o700 目录与修前逐字同,升级零迁移。
|
|
108
114
|
*/
|
|
109
115
|
export declare class LocalWorkflowAgentSessionIndex implements WorkflowAgentSessionIndex {
|
|
110
116
|
private readonly fsyncEnabled;
|
|
111
|
-
/** 坏账本行的**响亮**出口(可选;缺席 ⇒ 那一行照样不入索引,只是没人听见)。形与 workflow 店族的
|
|
112
|
-
* `onWarn` 逐字同源(`workflow-completion-inbox.ts` 的那一只),不另立第二种披露通道。 */
|
|
113
|
-
private readonly onWarn?;
|
|
114
117
|
private readonly ledgerPath;
|
|
115
118
|
private readonly byRun;
|
|
116
119
|
private readonly bySession;
|
|
117
|
-
private
|
|
120
|
+
private log;
|
|
118
121
|
constructor(root?: string, fsyncEnabled?: boolean,
|
|
119
|
-
/** 坏账本行的**响亮**出口(可选;缺席 ⇒
|
|
120
|
-
*
|
|
121
|
-
onWarn?:
|
|
122
|
-
/**
|
|
123
|
-
* 打开账本时把**撕裂尾**(崩在 append 中途、末尾没有换行的那截字节)截回最后一个完整行边界。
|
|
124
|
-
*
|
|
125
|
-
* 🔴 为什么必须在**任何 append 之前**做(codex 修复验证轮 [medium],故障注入复现):残尾留在盘上时,
|
|
126
|
-
* 下一次 `syncRun` 会紧贴着它落字节 ⇒ `<残尾><新记录>` 在 replay 里是**一行**,JSON 解析失败被整行丢掉。
|
|
127
|
-
* 丢的**不是**那截未提交的残尾(丢它是对的),而是紧随其后那次**成功重试**写下的行 —— 于是「写失败了
|
|
128
|
-
* 下一次写会重试救回来」这条本模块与 `syncAgentSessionIndex` 都在依赖的性质当场失效,park 索引静默消失。
|
|
129
|
-
* 截尾之后 append 永远从一个干净的行边界开始,重试才真的救得回来。
|
|
130
|
-
*
|
|
131
|
-
* 截掉的只可能是**未提交**的字节(一条记录的耐久性以它那个换行为界,与 notify journal 同一条崩溃序),
|
|
132
|
-
* 所以这里不是丢数据;但它是一次真实的崩溃留痕,**响亮**一次([ref]:安全轴不许静默)。
|
|
133
|
-
*/
|
|
134
|
-
private truncateTornTail;
|
|
135
|
-
private replay;
|
|
122
|
+
/** 坏账本行的**响亮**出口(可选;缺席 ⇒ 重放属主退回 `console.warn`,不退回沉默)。形与日志座 `Logger["warn"]` 逐字同源,
|
|
123
|
+
* 不另立第二种披露通道。 */
|
|
124
|
+
onWarn?: Logger["warn"]);
|
|
136
125
|
/** 内存两张表的收敛(重放与写路径共用一份逻辑)。 */
|
|
137
126
|
private apply;
|
|
138
127
|
syncRun(runId: string, rows: readonly WorkflowAgentSessionRow[]): Promise<void>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { existsSync, fsyncSync, mkdirSync, openSync, readFileSync, truncateSync, writeSync } from "node:fs";
|
|
2
1
|
import { join } from "node:path";
|
|
2
|
+
import { AppendLog, ensureDir } from "@sema-agent/core";
|
|
3
|
+
import { replayJsonlSkipping } from "../observability/corrupt-read-seat.js";
|
|
3
4
|
const nonEmptyString = (v) => typeof v === "string" && v !== "";
|
|
4
5
|
export function readRunParks(run) {
|
|
5
6
|
if (typeof run !== "object" || run === null)
|
|
@@ -72,64 +73,27 @@ function readAgentSessionRow(v) {
|
|
|
72
73
|
return undefined;
|
|
73
74
|
return { sessionId, runId, agentKey, scope };
|
|
74
75
|
}
|
|
76
|
+
const LEDGER_LINE_SHAPE = {
|
|
77
|
+
what: "a workflow agent-session index line {runId, rows[]}",
|
|
78
|
+
read: readAgentSessionLedgerLine,
|
|
79
|
+
};
|
|
75
80
|
export class LocalWorkflowAgentSessionIndex {
|
|
76
81
|
fsyncEnabled;
|
|
77
|
-
onWarn;
|
|
78
82
|
ledgerPath;
|
|
79
83
|
byRun = new Map();
|
|
80
84
|
bySession = new Map();
|
|
81
|
-
|
|
85
|
+
log;
|
|
82
86
|
constructor(root, fsyncEnabled = true, onWarn) {
|
|
83
87
|
this.fsyncEnabled = fsyncEnabled;
|
|
84
|
-
this.onWarn = onWarn;
|
|
85
88
|
if (root === undefined) {
|
|
86
89
|
this.ledgerPath = undefined;
|
|
87
90
|
return;
|
|
88
91
|
}
|
|
89
92
|
const dir = join(root, "workflow-agent-sessions");
|
|
90
|
-
|
|
93
|
+
ensureDir(dir);
|
|
91
94
|
this.ledgerPath = join(dir, "index.jsonl");
|
|
92
|
-
this.
|
|
93
|
-
this.replay();
|
|
94
|
-
}
|
|
95
|
-
truncateTornTail() {
|
|
96
|
-
const path = this.ledgerPath;
|
|
97
|
-
if (path === undefined || !existsSync(path))
|
|
98
|
-
return;
|
|
99
|
-
const bytes = readFileSync(path);
|
|
100
|
-
if (bytes.length === 0 || bytes[bytes.length - 1] === 0x0a)
|
|
101
|
-
return;
|
|
102
|
-
const keep = bytes.lastIndexOf(0x0a) + 1;
|
|
103
|
-
truncateSync(path, keep);
|
|
104
|
-
this.onWarn?.("workflow_agent_session_ledger_torn_tail_truncated", {
|
|
105
|
-
path,
|
|
106
|
-
droppedBytes: bytes.length - keep,
|
|
107
|
-
note: "the last append never finished (no trailing newline) — those uncommitted bytes were cut back to the last complete line so the NEXT append starts on a clean boundary. Without the cut, the retry that follows would be glued onto the broken suffix and dropped as one unparsable line, i.e. the retry — not the torn write — is what would be lost.",
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
replay() {
|
|
111
|
-
if (this.ledgerPath === undefined || !existsSync(this.ledgerPath))
|
|
112
|
-
return;
|
|
113
|
-
for (const line of readFileSync(this.ledgerPath, "utf8").split("\n")) {
|
|
114
|
-
if (!line.trim())
|
|
115
|
-
continue;
|
|
116
|
-
let rec;
|
|
117
|
-
try {
|
|
118
|
-
rec = JSON.parse(line);
|
|
119
|
-
}
|
|
120
|
-
catch {
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
const entry = readAgentSessionLedgerLine(rec);
|
|
124
|
-
if (entry === undefined) {
|
|
125
|
-
this.onWarn?.("workflow_agent_session_ledger_line_rejected", {
|
|
126
|
-
path: this.ledgerPath,
|
|
127
|
-
note: "a committed ledger line did not read back as {runId, rows[]} — that run keeps its previous snapshot; a /decide for a park it should have indexed falls back to the legacy lane (honest miss, never a mis-route)",
|
|
128
|
-
});
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
95
|
+
for (const entry of replayJsonlSkipping(this.ledgerPath, "workflow-agent-session-index", LEDGER_LINE_SHAPE, onWarn).records)
|
|
131
96
|
this.apply(entry.runId, entry.rows);
|
|
132
|
-
}
|
|
133
97
|
}
|
|
134
98
|
apply(runId, rows) {
|
|
135
99
|
for (const old of this.byRun.get(runId) ?? []) {
|
|
@@ -145,11 +109,8 @@ export class LocalWorkflowAgentSessionIndex {
|
|
|
145
109
|
}
|
|
146
110
|
async syncRun(runId, rows) {
|
|
147
111
|
if (this.ledgerPath !== undefined) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
writeSync(this.fd, `${JSON.stringify({ runId, rows })}\n`);
|
|
151
|
-
if (this.fsyncEnabled)
|
|
152
|
-
fsyncSync(this.fd);
|
|
112
|
+
this.log ??= new AppendLog(this.ledgerPath);
|
|
113
|
+
this.log.append({ runId, rows }, this.fsyncEnabled);
|
|
153
114
|
}
|
|
154
115
|
this.apply(runId, rows);
|
|
155
116
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type WorkflowRun, type WorkflowRunStore, type WorkflowRunStatus, type WorkflowRunSummary } from "@sema-agent/core";
|
|
2
2
|
import { type FleetEventBus } from "../fleet/fleet-bus.js";
|
|
3
3
|
import { type WorkflowAgentSessionIndex } from "./workflow-agent-session-index.js";
|
|
4
|
+
import type { Logger } from "../observability/logger.js";
|
|
4
5
|
/** A workflow completion notification's terminal payload — the bounded, redacted shape core's notifier seam
|
|
5
6
|
* carries, re-derivable from a durable {@link import("@sema-agent/core").WorkflowRun} on the recovery path. */
|
|
6
7
|
export interface WorkflowCompletionPayload {
|
|
@@ -201,6 +202,11 @@ export declare class WorkflowNotifyGate {
|
|
|
201
202
|
* appends, atomic single-line records. Single-instance / TOC-local: a cross-replica TiDB/PG port is a follow-on
|
|
202
203
|
* (the recovery sweep + gate are backend-agnostic).
|
|
203
204
|
*
|
|
205
|
+
* 追加 / 重放走本仓 JSONL 店的**共用口**(S-977;此前是第二 / 第三份私有实现):追加 = core `AppendLog`(惰性打开;单条 `writeSync`
|
|
206
|
+
* 循环写满、失败的写截回开写处;**撕尾在打开与每次追加前修**,所以崩在一次 append 中途之后的下一条成功写不会被粘进残尾),
|
|
207
|
+
* 重放 = `replayJsonlSkipping`(登记座 `workflow-notify-journal`:中间坏行跳过 + 逐条披露 + F tag;撕尾不计 —— 它是崩溃的正常落盘形;
|
|
208
|
+
* 末行 JSON 完整但缺换行的认下、下一次追加补换行)。盘上字节 / 0o600 账本 / 0o700 目录与修前逐字同,升级零迁移。
|
|
209
|
+
*
|
|
204
210
|
* The ledger only ever grows by one line per `record`/`ack`; an acked entry's line is not deleted (the replay
|
|
205
211
|
* folds it), so retention is a future compaction concern — the backlog `listPending` walks is bounded by
|
|
206
212
|
* IN-FLIGHT + un-swept runs, not history.
|
|
@@ -209,12 +215,9 @@ export declare class FileWorkflowNotifyJournalStore implements WorkflowNotifyJou
|
|
|
209
215
|
private readonly fsyncEnabled;
|
|
210
216
|
private readonly ledgerPath;
|
|
211
217
|
private readonly entries;
|
|
212
|
-
private
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
* (a crash mid-append) by dropping an unparsable trailing record — the same crash-safety FileWorkflowRunStore
|
|
216
|
-
* relies on (a partial append is simply not yet committed state). */
|
|
217
|
-
private replay;
|
|
218
|
+
private log;
|
|
219
|
+
/** @param warn 坏账本行的披露座(缺席 ⇒ 重放属主退回 `console.warn`,不退回沉默)。 */
|
|
220
|
+
constructor(root: string, fsyncEnabled?: boolean, warn?: Logger["warn"]);
|
|
218
221
|
/** Commit one entry: append the JSON line (fsync) THEN flip the in-memory map (crash-safe ordering — the
|
|
219
222
|
* durable record lands before the in-memory state the caller observes). */
|
|
220
223
|
private commit;
|
|
@@ -228,7 +231,7 @@ export declare class FileWorkflowNotifyJournalStore implements WorkflowNotifyJou
|
|
|
228
231
|
ack(runId: string, ackedAt: number): Promise<void>;
|
|
229
232
|
listPending(): Promise<WorkflowNotifyJournalEntry[]>;
|
|
230
233
|
get(runId: string): Promise<WorkflowNotifyJournalEntry | null>;
|
|
231
|
-
/** Release the append handle
|
|
234
|
+
/** Release the append handle — called on shutdown. core `AppendLog.close()` swallows its own `closeSync` error (never throws). */
|
|
232
235
|
close(): void;
|
|
233
236
|
/** Test/inspection: total entries (acked + pending). */
|
|
234
237
|
get size(): number;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { mkdirSync, readFileSync, existsSync, openSync, writeSync, fsyncSync, closeSync } from "node:fs";
|
|
2
1
|
import { join } from "node:path";
|
|
2
|
+
import { AppendLog, ensureDir } from "@sema-agent/core";
|
|
3
3
|
import { buildFleetWorkflowRow } from "../fleet/fleet-bus.js";
|
|
4
4
|
import { isTerminalWorkflowRowStatus, seedTerminalWorkflowRow } from "../fleet/fleet-terminal-window.js";
|
|
5
5
|
import { buildParkedAgentSessionRows, sameAgentSessionRows } from "./workflow-agent-session-index.js";
|
|
6
6
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
7
|
+
import { replayJsonlSkipping } from "../observability/corrupt-read-seat.js";
|
|
7
8
|
const SUMMARY_MAX = 500;
|
|
8
9
|
export function recoverySummary(status, error) {
|
|
9
10
|
if (status === "completed")
|
|
@@ -162,42 +163,31 @@ export class WorkflowNotifyGate {
|
|
|
162
163
|
return { scanned: pending.length, redelivered, stillRunning, abandoned };
|
|
163
164
|
}
|
|
164
165
|
}
|
|
166
|
+
const JOURNAL_ENTRY_SHAPE = {
|
|
167
|
+
what: "a notify-journal entry {runId: string, scope: string, acked: boolean}",
|
|
168
|
+
read(record) {
|
|
169
|
+
const { runId, scope, acked } = record;
|
|
170
|
+
if (typeof runId !== "string" || typeof scope !== "string" || typeof acked !== "boolean")
|
|
171
|
+
return undefined;
|
|
172
|
+
return record;
|
|
173
|
+
},
|
|
174
|
+
};
|
|
165
175
|
export class FileWorkflowNotifyJournalStore {
|
|
166
176
|
fsyncEnabled;
|
|
167
177
|
ledgerPath;
|
|
168
178
|
entries = new Map();
|
|
169
|
-
|
|
170
|
-
constructor(root, fsyncEnabled = true) {
|
|
179
|
+
log;
|
|
180
|
+
constructor(root, fsyncEnabled = true, warn) {
|
|
171
181
|
this.fsyncEnabled = fsyncEnabled;
|
|
172
182
|
const dir = join(root, "workflow-notify");
|
|
173
|
-
|
|
183
|
+
ensureDir(dir);
|
|
174
184
|
this.ledgerPath = join(dir, "journal.jsonl");
|
|
175
|
-
this.
|
|
176
|
-
|
|
177
|
-
replay() {
|
|
178
|
-
if (!existsSync(this.ledgerPath))
|
|
179
|
-
return;
|
|
180
|
-
const text = readFileSync(this.ledgerPath, "utf8");
|
|
181
|
-
for (const line of text.split("\n")) {
|
|
182
|
-
if (!line.trim())
|
|
183
|
-
continue;
|
|
184
|
-
let ev;
|
|
185
|
-
try {
|
|
186
|
-
ev = JSON.parse(line);
|
|
187
|
-
}
|
|
188
|
-
catch {
|
|
189
|
-
continue;
|
|
190
|
-
}
|
|
191
|
-
if (ev && typeof ev.runId === "string")
|
|
192
|
-
this.entries.set(ev.runId, ev);
|
|
193
|
-
}
|
|
185
|
+
for (const ev of replayJsonlSkipping(this.ledgerPath, "workflow-notify-journal", JOURNAL_ENTRY_SHAPE, warn).records)
|
|
186
|
+
this.entries.set(ev.runId, ev);
|
|
194
187
|
}
|
|
195
188
|
commit(entry) {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
writeSync(this.fd, `${JSON.stringify(entry)}\n`);
|
|
199
|
-
if (this.fsyncEnabled)
|
|
200
|
-
fsyncSync(this.fd);
|
|
189
|
+
this.log ??= new AppendLog(this.ledgerPath);
|
|
190
|
+
this.log.append(entry, this.fsyncEnabled);
|
|
201
191
|
this.entries.set(entry.runId, entry);
|
|
202
192
|
}
|
|
203
193
|
async record(input) {
|
|
@@ -226,14 +216,8 @@ export class FileWorkflowNotifyJournalStore {
|
|
|
226
216
|
return this.entries.get(runId) ?? null;
|
|
227
217
|
}
|
|
228
218
|
close() {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
closeSync(this.fd);
|
|
232
|
-
}
|
|
233
|
-
catch {
|
|
234
|
-
}
|
|
235
|
-
this.fd = undefined;
|
|
236
|
-
}
|
|
219
|
+
this.log?.close();
|
|
220
|
+
this.log = undefined;
|
|
237
221
|
}
|
|
238
222
|
get size() {
|
|
239
223
|
return this.entries.size;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type TaskOutcome as CoreTaskOutcome } from "@sema-agent/core";
|
|
2
2
|
/** The one write verb every outcome sink (TiDB/Pg ledger adapters + this File twin) exposes to main.ts,
|
|
3
3
|
* plus the OPTIONAL read aggregate backing `GET /v1/outcomes` (P2) — present on the SQL ledgers (their
|
|
4
4
|
* existing per-(signature, model) summary); absent here (the JSONL file IS the local dataset — a dashboard
|
|
@@ -18,7 +18,10 @@ export interface OutcomeSink {
|
|
|
18
18
|
}
|
|
19
19
|
export declare class FileOutcomeSink implements OutcomeSink {
|
|
20
20
|
private readonly path;
|
|
21
|
+
private log;
|
|
21
22
|
constructor(root: string);
|
|
22
23
|
recordCore(o: CoreTaskOutcome): Promise<void>;
|
|
24
|
+
/** 收追加口(`StoreBackend.close` 的优雅关闭腿;之后再写会重开)。core `AppendLog.close()` 自己吞 `closeSync` 的错,不抛。 */
|
|
25
|
+
dispose(): void;
|
|
23
26
|
}
|
|
24
27
|
//# sourceMappingURL=file-outcome-sink.d.ts.map
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
import { mkdirSync, appendFileSync } from "node:fs";
|
|
2
1
|
import { join } from "node:path";
|
|
2
|
+
import { AppendLog, ensureDir } from "@sema-agent/core";
|
|
3
3
|
export class FileOutcomeSink {
|
|
4
4
|
path;
|
|
5
|
+
log;
|
|
5
6
|
constructor(root) {
|
|
6
7
|
const dir = join(root, "outcomes");
|
|
7
|
-
|
|
8
|
+
ensureDir(dir);
|
|
8
9
|
this.path = join(dir, "outcomes.jsonl");
|
|
9
10
|
}
|
|
10
11
|
async recordCore(o) {
|
|
11
|
-
|
|
12
|
+
this.log ??= new AppendLog(this.path);
|
|
13
|
+
this.log.append({ at: Date.now(), ...o }, false);
|
|
14
|
+
}
|
|
15
|
+
dispose() {
|
|
16
|
+
this.log?.close();
|
|
17
|
+
this.log = undefined;
|
|
12
18
|
}
|
|
13
19
|
}
|
|
14
20
|
//# sourceMappingURL=file-outcome-sink.js.map
|
|
@@ -7,7 +7,7 @@ import { randomBytes } from "node:crypto";
|
|
|
7
7
|
import { AppendLog, atomicWriteFile, ensureDir, isProcessLive, processFingerprint, sanitizePathComponent, writeThenLink } from "@sema-agent/core";
|
|
8
8
|
import { recordFailOpen } from "../observability/fail-open.js";
|
|
9
9
|
import { replayJsonlSkipping } from "../observability/corrupt-read-seat.js";
|
|
10
|
-
import { RUN_STALLED_ERROR, StoreBootRefusalError } from "./store-contracts.js";
|
|
10
|
+
import { RUN_ORPHANED_ERROR, RUN_STALLED_ERROR, StoreBootRefusalError, buildRunOrphanedError } from "./store-contracts.js";
|
|
11
11
|
import { notifyRunTerminal } from "../observability/run-terminal.js";
|
|
12
12
|
import { migrateRetiredAskOrigin } from "./one-time-migrations.js";
|
|
13
13
|
const ownerEq = (a, b) => (a ?? null) === (b ?? null);
|
|
@@ -603,7 +603,7 @@ export class FileRunStore {
|
|
|
603
603
|
for (const r of orphans) {
|
|
604
604
|
r.status = "failed";
|
|
605
605
|
r.result = null;
|
|
606
|
-
r.error =
|
|
606
|
+
r.error = RUN_ORPHANED_ERROR;
|
|
607
607
|
r.updatedAt = new Date();
|
|
608
608
|
this.persist(r);
|
|
609
609
|
}
|
|
@@ -808,7 +808,7 @@ export class FileRunStore {
|
|
|
808
808
|
pidStillRunning: owner !== undefined && isProcessLive({ pid: owner.pid }),
|
|
809
809
|
note: "the claim's owner process is provably not the one that wrote it (fingerprint falsified, or the pid is gone) and the row has nothing left to resume — taking the claim over",
|
|
810
810
|
});
|
|
811
|
-
return { kind: "stale", reason:
|
|
811
|
+
return { kind: "stale", reason: buildRunOrphanedError(who), snapshot };
|
|
812
812
|
}
|
|
813
813
|
takeOverClaim(sessionId, reason, snapshot) {
|
|
814
814
|
const nowClaim = readClaimRecord(singleRecordOf(this.readRecords(this.activePath(sessionId), CLAIM_RECORD_SHAPE)), sessionId);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* core 把破坏性级联整个下沉成 store 方法(`expireCheckpoints` / `deleteExpiredSessions` /
|
|
9
9
|
* `deleteOrphanToolResults`),并点名 server 的 session-purge 顺序契约(E21,`boot/session-faces.ts` 的
|
|
10
10
|
* `purgeSession` 协调器)为参考实现。那条顺序契约横跨**十余张表**(run 账本 / checkpoint / tool_result /
|
|
11
|
-
* anchor / exemption / policy / attachment / snapshot / workflow inbox / session_meta+event),而设计稿
|
|
11
|
+
* anchor / exemption / policy / attachment / 用户输入受理账本 / snapshot / workflow inbox / session_meta+event),而设计稿
|
|
12
12
|
* §6 的 F5 又要求「审计行与删除同一个 DB 事务」—— 一个事务 ⇒ 一条连接 ⇒ **一个属主**。
|
|
13
13
|
*
|
|
14
14
|
* ⇒ 布局裁定:本能力**不能**挂在 session / checkpoint / tool-result 任何一只店上(每只店只拥有自己的表,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mysqlDriver, pgDriver } from "./sql-driver.js";
|
|
2
2
|
import { TASK_LIST_ITEM_TABLE, TASK_LIST_META_TABLE, taskListKeyFor } from "./task-list-store-sql.js";
|
|
3
|
+
import { USER_INPUT_ACCEPTANCE_TABLE } from "./user-input-ledger-sql.js";
|
|
3
4
|
import { ensureIndexes } from "./ensure-index.js";
|
|
4
5
|
export const MANAGED_RETENTION = "managed";
|
|
5
6
|
export const UNMANAGED_RETENTION = "none";
|
|
@@ -382,6 +383,7 @@ export class SqlRetentionStore {
|
|
|
382
383
|
["session_policy", "session_id"],
|
|
383
384
|
["session_policy_rev_mark", "session_id"],
|
|
384
385
|
...(this.opts.taskAttachmentTable ? [["task_attachment", "session_id"]] : []),
|
|
386
|
+
[USER_INPUT_ACCEPTANCE_TABLE, "session_id"],
|
|
385
387
|
["file_history_tracked", "scope"],
|
|
386
388
|
["file_history_version", "scope"],
|
|
387
389
|
["file_history_boundary", "scope"],
|
|
@@ -277,6 +277,7 @@ class LocalBackend {
|
|
|
277
277
|
this.approvalAskStore.dispose();
|
|
278
278
|
this.sendFileLedgerStore.dispose();
|
|
279
279
|
this.userInputLedgerStore.dispose();
|
|
280
|
+
this.outcomeSinkInst.dispose();
|
|
280
281
|
this.workflowJournalStore.dispose();
|
|
281
282
|
this.checkpointStoreInst?.close();
|
|
282
283
|
this.permissionRuleStoresInst?.dispose();
|
|
@@ -94,23 +94,73 @@ export interface RunCancelContext {
|
|
|
94
94
|
elapsedMs?: number;
|
|
95
95
|
}
|
|
96
96
|
/**
|
|
97
|
-
*
|
|
97
|
+
* 🔴 S-947(cli [ref] 第 3 件 / L-212)—— **server 为什么中止了这条 run**:取消终局的机读成因,闭集。
|
|
98
98
|
*
|
|
99
|
-
*
|
|
100
|
-
* (`
|
|
101
|
-
*
|
|
102
|
-
*
|
|
99
|
+
* 病:非 detach 流断连、`POST /v1/runs/:id/cancel`、断连时有 ask 但 park 宽限用尽 —— 三件事在终局上**逐字同形**
|
|
100
|
+
* (`failed` + `terminal.code:"cancelled"`),壳只能按 `errorMessage` 文案猜。
|
|
101
|
+
*
|
|
102
|
+
* 每个词都必须有一个**真站点**(没有站点的词不许进表;普查与站点坐标见契约 §5.0d):
|
|
103
|
+
* · `client_disconnected` —— 非 detach 的 `POST /v1/tasks/stream` 客户端断连,断连那一刻本 run 没有流内未决 ask
|
|
104
|
+
* (`http/routes/tasks.ts` 的断连臂,立即中止);
|
|
105
|
+
* · `ask_park_grace_exhausted` —— 同一断连臂,但断连那一刻有流内未决 ask、已按窗到期同路转 park,宽限计时器用尽时 park 仍没落成;
|
|
106
|
+
* · `cancel_verb` —— `POST /v1/runs/:id/cancel`:同副本快路、跨副本耐久取消旗的轮询(四条腿的心跳 / 登记追平),
|
|
107
|
+
* 以及 park 中的 run 被它直接终局化(那一形不经 abort,成因照样是这条动词)。
|
|
108
|
+
* **刻意不在表里**(普查过、没有真站点):抢占(`preempt` 是优雅让出,终局是 `paused`,不是取消)、有序关停
|
|
109
|
+
* (S-929 只把未决卡转 park,从不中止活 run;进程退出后的行由收割器落「持有者死亡」判决,那不是本仓铸的取消终局)、
|
|
110
|
+
* `interrupt`(引擎的 turn 动词,终局 `completed` + `haltedByUser`)、detach 断连(不中止)。
|
|
111
|
+
*
|
|
112
|
+
* 词表属主 = 本文件(与它所在的 {@link PersistedTaskResult} 同住);**单写者** = `run-cancel-context.ts` 的 `createRunAbort`
|
|
113
|
+
* (中止那一刻登记)与 `mintCancelledResult`(终局铸点读它),自铸 wire 值登记 = `trace/self-minted-wire.ts` 的 `abortCause`
|
|
114
|
+
* (`satisfies Record<AbortCause, …>`:加 / 删一个词而登记没跟 ⇒ tsc 红)。
|
|
115
|
+
*/
|
|
116
|
+
export declare const ABORT_CAUSES: readonly ["client_disconnected", "ask_park_grace_exhausted", "cancel_verb"];
|
|
117
|
+
export type AbortCause = (typeof ABORT_CAUSES)[number];
|
|
118
|
+
/**
|
|
119
|
+
* server 自铸、挂在终局结果上的**侧记键**全集(都描述「server 这一侧发生了什么」,core 那一侧无从知道)。
|
|
120
|
+
* 本型是这一族键的**唯一登记处**(`trace/core-keyset-guard.ts` 的辖域线指向这里;那边另有一道编译期钉:
|
|
121
|
+
* core 的 `TaskResult` 哪天长出同名键 ⇒ 当场红 —— 同一个键两个铸者是本型存在要防的那件事)。
|
|
122
|
+
* · `cancelContext`([ref])—— 取消现场快照(分诊材料:多久没动静、起跑多久);
|
|
123
|
+
* · `abortCause`(S-947)—— 这次取消是谁 / 什么发起的({@link AbortCause})。
|
|
124
|
+
* 两键**同一只铸点**(`mintCancelledResult`)、**同一条在场闸**(「只补空座」:终局就是被那次中止打断的那一条,
|
|
125
|
+
* 才挂;与中止赛跑跑完的终局、引擎自带码的失败一律原样立住、两键都不挂)。差别只在缺席纪律:快照一键都证不出 ⇒ 整只缺席;
|
|
126
|
+
* 成因在铸点那一刻恒已知 ⇒ 取消终局上恒在场。
|
|
103
127
|
*/
|
|
104
|
-
export
|
|
128
|
+
export interface PersistedResultSideKeys {
|
|
105
129
|
cancelContext?: RunCancelContext;
|
|
106
|
-
|
|
130
|
+
abortCause?: AbortCause;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* 落库/上 wire 的终局结果 = core 的 `TaskResult` **加上** server 自铸的可缺席侧记键({@link PersistedResultSideKeys})。
|
|
134
|
+
*
|
|
135
|
+
* additive 且可缺席,所以旧行、旧消费端逐字不受影响(`TaskResult` 仍可原样赋给本型)。⚠️ 这不是给「往 core 结果里塞私货」
|
|
136
|
+
* 开的口子:每加一个键都要能回答「为什么它属于 run 账本而不属于 core 的任务结果」—— 两键的答案都是它们描述的是
|
|
137
|
+
* **server 侧的取消现场**,core 那一侧根本不知道有人按了取消、谁按的。落点 = `result` JSON 列(三孪生整只存),**零新列**。
|
|
138
|
+
*/
|
|
139
|
+
export type PersistedTaskResult = TaskResult & PersistedResultSideKeys;
|
|
107
140
|
/**
|
|
108
141
|
* 🔴 run 店「持有它的实例丢了」那一判的**行上落款**(S-928 codex r1-F1 提为常量):三只 run 店的 `reapStale` 把心跳过期的 running 行翻成
|
|
109
|
-
* `failed` 并写下这一句。它是 run
|
|
110
|
-
* (
|
|
142
|
+
* `failed` 并写下这一句。它是 run 店死亡判决留在行上的证据之一(另一只 = {@link RUN_ORPHANED_ERROR})—— 用户输入受理账本的收敛判词
|
|
143
|
+
* (S-969 L8 起两车道同一只)据 {@link isHolderDeathVerdict} 在 reap **之后**仍能判出「写者死了」。改这一句 = 改那条判据;所以三个写者与那一个读者都只引用本常量。读时折叠成同一句话的 wire 展示站点
|
|
111
144
|
* (`routes/runs.ts` 的 stale 折叠、`routes/trace-usage.ts` 的 WORKER_DOWN 帧)刻意留字面量:它们由文案冻结门逐字钉着,且不参与任何判据。
|
|
112
145
|
*/
|
|
113
146
|
export declare const RUN_STALLED_ERROR = "run stalled (instance lost?)";
|
|
147
|
+
/**
|
|
148
|
+
* 🔴 File 车道「持有它的进程随重启没了」那一判的行上落款(S-969 L8 提为常量):boot 孤儿回收(`FileRunStore.reclaimOrphanedAtBoot`,
|
|
149
|
+
* 数据根 LOCK 作证前世进程已死 ⇒ 仍 running 的行无条件判孤儿)与运行期 claim 接管的 running 臂(`judgeClaim`,外来 + owner 已死 + 无可续)
|
|
150
|
+
* 都写这一句;接管臂在括号里附上前任持有者的进程指纹(取证用,形 = {@link buildRunOrphanedError})。与 {@link RUN_STALLED_ERROR} 同一种身份:
|
|
151
|
+
* run 店写在行上的**死亡判决**,读者 = {@link isHolderDeathVerdict}。
|
|
152
|
+
*/
|
|
153
|
+
export declare const RUN_ORPHANED_ERROR = "run orphaned by engine restart";
|
|
154
|
+
/** File 车道两个孤儿落款写口共用的那一句(`heldBy` = 接管臂的前任持有者描述;boot 回收臂不带)。 */
|
|
155
|
+
export declare function buildRunOrphanedError(heldBy?: string): string;
|
|
156
|
+
/**
|
|
157
|
+
* run 行上是不是一份**持有者死亡判决**:`failed`,且 `error` 是 run 店的两只死亡落款之一 —— {@link RUN_STALLED_ERROR}(三只 run 店的
|
|
158
|
+
* `reapStale`:心跳过期 ⇒ 实例丢了)或 {@link RUN_ORPHANED_ERROR}(File 车道:前世进程随重启没了;接管臂带括注)。判决只说「持有它的那个进程
|
|
159
|
+
* 死在这条腿收场之前」,不说是谁 —— 持有者是谁读行上的 `instanceId`(两只落款都不改它)。唯一读者 = 用户输入受理账本的收敛判词
|
|
160
|
+
* (`src/user-input-ledger.ts` `judgeOpen`);改任一句落款 = 改那条判据,所以写者与读者都只引用本模块的常量。
|
|
161
|
+
* 正常失败(别的 error)/ park 过期收割(`approval expired before decision`:腿 park 过)都**不是**死亡判决。
|
|
162
|
+
*/
|
|
163
|
+
export declare function isHolderDeathVerdict(run: Pick<RunRecord, "status" | "error">): boolean;
|
|
114
164
|
export interface RunRecord {
|
|
115
165
|
taskId: string;
|
|
116
166
|
sessionId: string;
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
export class StoreBootRefusalError extends Error {
|
|
2
2
|
}
|
|
3
|
+
export const ABORT_CAUSES = ["client_disconnected", "ask_park_grace_exhausted", "cancel_verb"];
|
|
3
4
|
export const RUN_STALLED_ERROR = "run stalled (instance lost?)";
|
|
5
|
+
export const RUN_ORPHANED_ERROR = "run orphaned by engine restart";
|
|
6
|
+
export function buildRunOrphanedError(heldBy) {
|
|
7
|
+
return heldBy === undefined ? RUN_ORPHANED_ERROR : `${RUN_ORPHANED_ERROR} (claim held by ${heldBy}, which is gone)`;
|
|
8
|
+
}
|
|
9
|
+
export function isHolderDeathVerdict(run) {
|
|
10
|
+
if (run.status !== "failed" || run.error === null)
|
|
11
|
+
return false;
|
|
12
|
+
return run.error === RUN_STALLED_ERROR || run.error === RUN_ORPHANED_ERROR || run.error.startsWith(`${RUN_ORPHANED_ERROR} (`);
|
|
13
|
+
}
|
|
4
14
|
export const RUN_STATUS_CLASS = {
|
|
5
15
|
running: "live",
|
|
6
16
|
completed: "succeeded",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Logger } from "../observability/logger.js";
|
|
2
|
-
import { UserInputLedgerFold, type NewUserInputAcceptance, type OpenScanCursor, type SettleHow, type UserInputAcceptance, type UserInputLedgerRecord, type UserInputLedgerStore } from "../user-input-ledger.js";
|
|
2
|
+
import { UserInputLedgerFold, type NewUserInputAcceptance, type OpenScanCursor, type OpenScanPage, type SettleHow, type UserInputAcceptance, type UserInputLedgerRecord, type UserInputLedgerStore } from "../user-input-ledger.js";
|
|
3
3
|
/** 账本文件名(取证按文件字节对账 —— 崩溃语义的真源是文件)。 */
|
|
4
4
|
export declare const USER_INPUT_LEDGER_FILE = "ledger.jsonl";
|
|
5
5
|
/** 纯内存孪生(测试 / 夹具)。 */
|
|
@@ -13,9 +13,11 @@ export declare class MemoryUserInputLedgerStore implements UserInputLedgerStore
|
|
|
13
13
|
settle(acceptId: string, how: SettleHow, atMs: number): Promise<boolean>;
|
|
14
14
|
converge(acceptId: string, atMs: number): Promise<boolean>;
|
|
15
15
|
announce(acceptId: string, taskId: string, atMs: number): Promise<boolean>;
|
|
16
|
-
listOpen(after?: OpenScanCursor, limit?: number): Promise<
|
|
16
|
+
listOpen(after?: OpenScanCursor, limit?: number): Promise<OpenScanPage>;
|
|
17
17
|
listCrashed(sessionId: string): Promise<UserInputAcceptance[]>;
|
|
18
18
|
listUnannounced(sessionId: string, limit: number): Promise<UserInputAcceptance[]>;
|
|
19
|
+
/** S-965:会话树删除 = 一行会话级墓碑(守卫:该会话此刻有行 ⇒ 落;否则零写、回 0)。写抛 ⇒ 原样抛(协调器据此不删会话本体)。 */
|
|
20
|
+
deleteBySession(sessionId: string): Promise<number>;
|
|
19
21
|
}
|
|
20
22
|
/** local 车道(File)孪生。 */
|
|
21
23
|
export declare class FileUserInputLedgerStore extends MemoryUserInputLedgerStore {
|
|
@@ -39,6 +39,10 @@ export class MemoryUserInputLedgerStore {
|
|
|
39
39
|
async listUnannounced(sessionId, limit) {
|
|
40
40
|
return this.fold.listUnannounced(sessionId, limit);
|
|
41
41
|
}
|
|
42
|
+
async deleteBySession(sessionId) {
|
|
43
|
+
const n = this.fold.sessionSize(sessionId);
|
|
44
|
+
return (await this.write({ kind: "erased", sessionId, at: Date.now() })) ? n : 0;
|
|
45
|
+
}
|
|
42
46
|
}
|
|
43
47
|
const RECORD_SHAPE = {
|
|
44
48
|
what: "user-input ledger record of an unknown kind or with a missing / out-of-vocabulary member",
|
|
@@ -46,8 +50,14 @@ const RECORD_SHAPE = {
|
|
|
46
50
|
const o = record;
|
|
47
51
|
const str = (k) => (typeof o[k] === "string" && o[k].length > 0 ? o[k] : undefined);
|
|
48
52
|
const at = typeof o.at === "number" && Number.isFinite(o.at) ? o.at : undefined;
|
|
53
|
+
if (at === undefined)
|
|
54
|
+
return undefined;
|
|
55
|
+
if (o.kind === "erased") {
|
|
56
|
+
const sessionId = str("sessionId");
|
|
57
|
+
return sessionId === undefined ? undefined : { kind: "erased", sessionId, at };
|
|
58
|
+
}
|
|
49
59
|
const acceptId = str("acceptId");
|
|
50
|
-
if (acceptId === undefined
|
|
60
|
+
if (acceptId === undefined)
|
|
51
61
|
return undefined;
|
|
52
62
|
switch (o.kind) {
|
|
53
63
|
case "accepted": {
|
|
@@ -13,13 +13,22 @@
|
|
|
13
13
|
* 无 owner / principal / scope 列(行随它挂靠的会话走 —— 收编形Ⅲ);`writer` 是受理进程的 `instanceId`(随进程消亡,不是租户轴)。
|
|
14
14
|
* 行里不存输入正文(只有身份与时刻)。
|
|
15
15
|
*
|
|
16
|
-
* ##
|
|
17
|
-
*
|
|
16
|
+
* ## 会话树删除(S-965)
|
|
17
|
+
* `deleteBySession` = `DELETE … WHERE session_id = ?`(真删行;File 孪生是一行会话级墓碑,同义)。两条删会话树的级联各有一臂:在线删除协调器
|
|
18
|
+
* 经本动词;托管留存的事务化孪生(`retention-store-sql.ts` `purgeSessionTrees` ④)在**自己的事务**里对本表发同一只谓词(`session_id` 单键,
|
|
19
|
+
* 多值集合按方言)—— 它不调店,理由见该文件顶注的布局裁定(一个事务 ⇒ 一条连接 ⇒ 一个属主)。谓词走 `(session_id, state, accepted_at_ms)` 索引的前缀。
|
|
20
|
+
*
|
|
21
|
+
* ## 读不出的行(S-969 L6):逐行跳过,游标按原始行推进
|
|
22
|
+
* 三只读口都按 `state` 列取行(`= 'open'` / `= 'converged'`),所以 `state` 词表外的行任何读口都取不到;被取到的行上读不出的列 —— `input_kind`
|
|
23
|
+
* 词表外(新版本写的输入族:滚动发布 / 回滚的混版窗)、收敛行缺 `state_at_ms` —— 由 {@link readRows} 逐行跳过 + 计 F 类 tag
|
|
24
|
+
* `server.user-input-ledger.corrupt-record-skipped`(File 孪生重放坏行的同一只登记座),其余行照交。修前一行读不出 ⇒ 整页抛:收敛断点不前进
|
|
25
|
+
* (其后的 open 行永不收敛)、同会话的通告与读面一并失效。`listOpen` 的续翻游标取**原始**末行的键(`accept_id` / `accepted_at_ms` 是 NOT NULL
|
|
26
|
+
* 键列,坏行也读得出),跳过的行照样推进;页里少了几行不等于扫到底(那由原始行数判)。SQL 文本 / DDL 不变。
|
|
18
27
|
*/
|
|
19
28
|
import type { Pool as MysqlPool } from "mysql2/promise";
|
|
20
29
|
import type { Pool as PgPool } from "pg";
|
|
21
30
|
import { type SqlDriver } from "./sql-driver.js";
|
|
22
|
-
import { type NewUserInputAcceptance, type OpenScanCursor, type SettleHow, type UserInputAcceptance, type UserInputLedgerStore } from "../user-input-ledger.js";
|
|
31
|
+
import { type NewUserInputAcceptance, type OpenScanCursor, type OpenScanPage, type SettleHow, type UserInputAcceptance, type UserInputLedgerStore } from "../user-input-ledger.js";
|
|
23
32
|
export declare const USER_INPUT_ACCEPTANCE_TABLE = "user_input_acceptance";
|
|
24
33
|
export declare class SqlUserInputLedgerStore implements UserInputLedgerStore {
|
|
25
34
|
private readonly db;
|
|
@@ -29,9 +38,10 @@ export declare class SqlUserInputLedgerStore implements UserInputLedgerStore {
|
|
|
29
38
|
settle(acceptId: string, how: SettleHow, atMs: number): Promise<boolean>;
|
|
30
39
|
converge(acceptId: string, atMs: number): Promise<boolean>;
|
|
31
40
|
announce(acceptId: string, taskId: string, atMs: number): Promise<boolean>;
|
|
32
|
-
listOpen(after?: OpenScanCursor, limit?: number): Promise<
|
|
41
|
+
listOpen(after?: OpenScanCursor, limit?: number): Promise<OpenScanPage>;
|
|
33
42
|
listCrashed(sessionId: string): Promise<UserInputAcceptance[]>;
|
|
34
43
|
listUnannounced(sessionId: string, limit: number): Promise<UserInputAcceptance[]>;
|
|
44
|
+
deleteBySession(sessionId: string): Promise<number>;
|
|
35
45
|
}
|
|
36
46
|
/** MySQL 协议腿的构造绑定。 */
|
|
37
47
|
export declare class TiDBUserInputLedgerStore extends SqlUserInputLedgerStore {
|