@wudao-org/server 0.0.0-stage → 0.4.21
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/LICENSE +21 -0
- package/README.md +4 -2
- package/dist/.tsbuildinfo +1 -0
- package/dist/index.d.ts +95 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1645 -0
- package/dist/index.js.map +1 -0
- package/dist/lanes.d.ts +52 -0
- package/dist/lanes.d.ts.map +1 -0
- package/dist/lanes.js +78 -0
- package/dist/lanes.js.map +1 -0
- package/dist/pet-turns.d.ts +6 -0
- package/dist/pet-turns.d.ts.map +1 -0
- package/dist/pet-turns.js +24 -0
- package/dist/pet-turns.js.map +1 -0
- package/dist/placeholder.d.ts +2 -0
- package/dist/placeholder.d.ts.map +1 -0
- package/dist/placeholder.js +48 -0
- package/dist/placeholder.js.map +1 -0
- package/dist/ports.d.ts +364 -0
- package/dist/ports.d.ts.map +1 -0
- package/dist/ports.js +8 -0
- package/dist/ports.js.map +1 -0
- package/dist/probe.d.ts +39 -0
- package/dist/probe.d.ts.map +1 -0
- package/dist/probe.js +96 -0
- package/dist/probe.js.map +1 -0
- package/dist/recovery.d.ts +199 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +424 -0
- package/dist/recovery.js.map +1 -0
- package/dist/vault.d.ts +23 -0
- package/dist/vault.d.ts.map +1 -0
- package/dist/vault.js +117 -0
- package/dist/vault.js.map +1 -0
- package/package.json +43 -3
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/** ω99-242 — the admission start interval (invariant 6: ≥2s). */
|
|
2
|
+
export declare const RECOVERY_INTERVAL_MS = 2000;
|
|
3
|
+
/** ω99-242 — the in-flight recovery ceiling (invariant 6: ≤2). */
|
|
4
|
+
export declare const RECOVERY_CONCURRENCY = 2;
|
|
5
|
+
/** ω99-242 — backlogs at or below this size are observed in full each tick;
|
|
6
|
+
* larger ones rotate two per tick so observation stays bounded and fair. */
|
|
7
|
+
export declare const RECOVERY_ROTATION_BOUND = 4;
|
|
8
|
+
/** One journal line. `at` is the coordinator clock (ms). */
|
|
9
|
+
export type RecoveryJournalEntry = {
|
|
10
|
+
readonly type: "admit";
|
|
11
|
+
readonly sessionId: string;
|
|
12
|
+
readonly at: number;
|
|
13
|
+
readonly user?: boolean;
|
|
14
|
+
} | {
|
|
15
|
+
readonly type: "checkpoint";
|
|
16
|
+
readonly sessionId: string;
|
|
17
|
+
readonly at: number;
|
|
18
|
+
} | {
|
|
19
|
+
readonly type: "claim";
|
|
20
|
+
readonly sessionId: string;
|
|
21
|
+
readonly at: number;
|
|
22
|
+
} | {
|
|
23
|
+
readonly type: "settle";
|
|
24
|
+
readonly sessionId: string;
|
|
25
|
+
readonly at: number;
|
|
26
|
+
readonly verifiedDone: number;
|
|
27
|
+
readonly remaining: number;
|
|
28
|
+
} | {
|
|
29
|
+
readonly type: "cancel";
|
|
30
|
+
readonly sessionId: string;
|
|
31
|
+
readonly at: number;
|
|
32
|
+
readonly reason: string;
|
|
33
|
+
};
|
|
34
|
+
/** The per-session fold of the journal (file order wins; see foldJournal). */
|
|
35
|
+
export interface FoldSessionState {
|
|
36
|
+
readonly sessionId: string;
|
|
37
|
+
/** The last admit/checkpoint/claim/settle still stands (no later cancel). */
|
|
38
|
+
tracked: boolean;
|
|
39
|
+
/** A shutdown checkpoint stands (the freeze intent is unconsumed). */
|
|
40
|
+
checkpointed: boolean;
|
|
41
|
+
/** A recovery claim stands without its settle — the lost-ack marker. */
|
|
42
|
+
claimPending: boolean;
|
|
43
|
+
/** Counts carried by the last settle (the web's X/Y source). */
|
|
44
|
+
verifiedDone?: number;
|
|
45
|
+
remaining?: number;
|
|
46
|
+
lastAt: number;
|
|
47
|
+
}
|
|
48
|
+
export interface RecoveryJournalFold {
|
|
49
|
+
readonly sessions: ReadonlyMap<string, FoldSessionState>;
|
|
50
|
+
/** Lines that yielded no entry — unparseable, empty, or ill-shaped. */
|
|
51
|
+
readonly skippedLines: number;
|
|
52
|
+
}
|
|
53
|
+
/** Fold journal lines (file order) into per-session state. Pure: same lines →
|
|
54
|
+
* same fold. Semantics per entry type:
|
|
55
|
+
* - admit — tracking intent (a run admission happened; `user` marks a manual
|
|
56
|
+
* one, which CONSUMES a standing checkpoint: the user's takeover supersedes
|
|
57
|
+
* the shutdown freeze);
|
|
58
|
+
* - checkpoint — the graceful-shutdown freeze stands;
|
|
59
|
+
* - claim — a recovery claim stands, superseding any checkpoint;
|
|
60
|
+
* - settle — the admission ACK: clears claim/checkpoint, keeps tracking,
|
|
61
|
+
* carries the reconciliation counts;
|
|
62
|
+
* - cancel — intent withdrawn (terminal / user-input / admission-failed):
|
|
63
|
+
* untracks until a LATER admit re-tracks (file order decides). */
|
|
64
|
+
export declare function foldRecoveryLines(lines: readonly string[]): RecoveryJournalFold;
|
|
65
|
+
export interface RecoveryJournal {
|
|
66
|
+
readonly path: string;
|
|
67
|
+
append(entry: RecoveryJournalEntry): Promise<void>;
|
|
68
|
+
fold(): Promise<RecoveryJournalFold>;
|
|
69
|
+
}
|
|
70
|
+
/** Open (or create) the coordinator journal at `path`. Appends only — the
|
|
71
|
+
* file's earlier bytes are never rewritten (append-only law). A missing
|
|
72
|
+
* directory is created on first append; a missing/unreadable file folds to
|
|
73
|
+
* the empty state (nothing to recover — an honest zero, not an error). */
|
|
74
|
+
export declare function createRecoveryJournal(path: string): RecoveryJournal;
|
|
75
|
+
/** One observation of a candidate session — the port's evidence bundle. */
|
|
76
|
+
export interface RecoveryObservation {
|
|
77
|
+
/** Another LIVE process holds the session's run lease → active (observe
|
|
78
|
+
* only, never re-send into a live engine). */
|
|
79
|
+
readonly leaseHeldByOther: boolean;
|
|
80
|
+
/** A permission ask is pending on the session → blocked (may unlock via a
|
|
81
|
+
* permission reply — invariant 7's exception). ω99-248: #8 — the evidence
|
|
82
|
+
* is now persistent for non-current sessions (the session's own
|
|
83
|
+
* permission_ask record) — the verdict survives restarts. */
|
|
84
|
+
readonly permissionPending: boolean;
|
|
85
|
+
/** The persisted pending ask's evidence (RECORD-sourced only: a
|
|
86
|
+
* cross-restart waiting-for-human marker for the web's morning line).
|
|
87
|
+
* Absent when the session is not record-blocked. Evidence, never
|
|
88
|
+
* authorization: nothing auto-allows off it. */
|
|
89
|
+
readonly pendingAsk?: {
|
|
90
|
+
readonly toolName: string;
|
|
91
|
+
readonly summary: string;
|
|
92
|
+
readonly question: string;
|
|
93
|
+
};
|
|
94
|
+
/** A delegated child task is unresolved (父等子) → blocked (the parent is
|
|
95
|
+
* not independently recovered). */
|
|
96
|
+
readonly childPending: boolean;
|
|
97
|
+
/** The same unfinished turn stands in the session record (half-turn
|
|
98
|
+
* evidence: orphaned tool calls, or a trailing user turn with no reply). */
|
|
99
|
+
readonly turnOpen: boolean;
|
|
100
|
+
/** The mission ledger is terminal (completed/abandoned/stale) → terminal. */
|
|
101
|
+
readonly missionTerminal: boolean;
|
|
102
|
+
/** The mission DAG topological rank of the session's bound task
|
|
103
|
+
* (Number.POSITIVE_INFINITY = mission-unbound). */
|
|
104
|
+
readonly dagRank: number;
|
|
105
|
+
}
|
|
106
|
+
export interface RecoveryClassification {
|
|
107
|
+
readonly state: "terminal" | "blocked" | "active" | "unfinished";
|
|
108
|
+
readonly recoverable: boolean;
|
|
109
|
+
}
|
|
110
|
+
/** Classify one observation. Rule order is the judgment discipline:
|
|
111
|
+
* 1. a standing claim without settle (lost ack) → blocked, never re-admitted
|
|
112
|
+
* — even across restarts (invariant 2; trumps every other signal);
|
|
113
|
+
* 2. another live process owns the session → active (observe only);
|
|
114
|
+
* 3. the mission ledger is terminal → terminal (untouched);
|
|
115
|
+
* 4. permission pending / delegated child unresolved → blocked;
|
|
116
|
+
* 5. our own shutdown checkpoint stands, or the same turn is open →
|
|
117
|
+
* unfinished (recoverable) — idle alone is NOT evidence (negative pin);
|
|
118
|
+
* 6. otherwise terminal. */
|
|
119
|
+
export declare function classifyRecovery(observation: RecoveryObservation, journal: {
|
|
120
|
+
readonly checkpointed: boolean;
|
|
121
|
+
readonly claimPending: boolean;
|
|
122
|
+
}): RecoveryClassification;
|
|
123
|
+
/** The admission ACK's counts — the web's reconciliation numbers (R6: counted
|
|
124
|
+
* from structured data by the cli projection, never fabricated here). */
|
|
125
|
+
export interface RecoverySettle {
|
|
126
|
+
readonly verifiedDone: number;
|
|
127
|
+
readonly remaining: number;
|
|
128
|
+
}
|
|
129
|
+
/** The driver-shaped surface this coordinator speaks to (structural — no CLI
|
|
130
|
+
* import, the pet-lane precedent). M24 声明(§二.10):port.admit 的注入发生
|
|
131
|
+
* 在 resume 边界=冷转预期(cold turn expected)——恢复注入必然重放历史前
|
|
132
|
+
* 缀之外的全新 user turn,前缀缓存不命中是本设计的已知代价,轮界时机补
|
|
133
|
+
* 偿(恢复只在 boot/间隔≥2s 的轮界发生,绝不在运行中打断)。 */
|
|
134
|
+
export interface RecoveryPort {
|
|
135
|
+
/** Boot-time extra candidates (mission in-flight sessions the disk scan
|
|
136
|
+
* names); merged with the journal-tracked set, deduped. */
|
|
137
|
+
bootCandidates(): Promise<readonly string[]>;
|
|
138
|
+
/** Observe one session; undefined = the session is gone/unreadable (the
|
|
139
|
+
* coordinator cancels its tracking — an honest drop, never a crash). */
|
|
140
|
+
observe(sessionId: string): Promise<RecoveryObservation | undefined>;
|
|
141
|
+
/** Resume the session and inject the verify-first recovery prompt (the
|
|
142
|
+
* NORMAL permission pipeline — invariant 8; this coordinator never
|
|
143
|
+
* bypasses anything). Resolves with the reconciliation counts; a
|
|
144
|
+
* rejection means the admission did not land (no settle is written). */
|
|
145
|
+
admit(sessionId: string): Promise<RecoverySettle>;
|
|
146
|
+
/** The driver's current session id (manual-input invalidation target). */
|
|
147
|
+
currentSession(): string | undefined;
|
|
148
|
+
/** Is a run in flight? (The admission gate — never interrupt a live run.) */
|
|
149
|
+
busy(): boolean;
|
|
150
|
+
}
|
|
151
|
+
/** One row of the status surface (the recoveryStatus op's reply). */
|
|
152
|
+
export interface RecoveryStatusRow {
|
|
153
|
+
readonly sessionId: string;
|
|
154
|
+
readonly state: "blocked" | "active" | "recovering" | "unfinished" | "terminal" | "tracked";
|
|
155
|
+
readonly verifiedDone?: number;
|
|
156
|
+
readonly remaining?: number;
|
|
157
|
+
/** ω99-248 — the waiting-for-human ask's evidence when the row is
|
|
158
|
+
* permission-blocked off the persistent record (the web's morning line
|
|
159
|
+
* 「昨夜停在这里等你授权」; withdrawn once the record settles). */
|
|
160
|
+
readonly permissionAsk?: {
|
|
161
|
+
readonly toolName: string;
|
|
162
|
+
readonly summary: string;
|
|
163
|
+
readonly question: string;
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
export interface RecoveryCoordinatorOptions {
|
|
167
|
+
readonly journalPath: string;
|
|
168
|
+
readonly port: RecoveryPort;
|
|
169
|
+
/** Clock seam (tests inject a controlled one; default Date.now). */
|
|
170
|
+
readonly now?: () => number;
|
|
171
|
+
/** Admission start interval (default RECOVERY_INTERVAL_MS). */
|
|
172
|
+
readonly intervalMs?: number;
|
|
173
|
+
/** In-flight ceiling (default RECOVERY_CONCURRENCY). */
|
|
174
|
+
readonly concurrency?: number;
|
|
175
|
+
/** Observation rotation bound (default RECOVERY_ROTATION_BOUND). */
|
|
176
|
+
readonly rotationBound?: number;
|
|
177
|
+
/** Tick source; tests drive tick() directly and pass a no-op (the timer is
|
|
178
|
+
* never installed when this is provided). */
|
|
179
|
+
readonly setTimer?: ((tick: () => void, intervalMs: number) => void) | undefined;
|
|
180
|
+
}
|
|
181
|
+
export interface RecoveryCoordinator {
|
|
182
|
+
/** One observe→classify→admit pass (coalesced: a tick while ticking joins
|
|
183
|
+
* the in-flight one — the openwork discipline). */
|
|
184
|
+
tick(): Promise<void>;
|
|
185
|
+
/** A manual client submit landed (invariant 7): cancels the pending
|
|
186
|
+
* recovery of the session the user took over (permission replies never
|
|
187
|
+
* reach this seam — the server wires ONLY the submit op here) and
|
|
188
|
+
* re-tracks it for the next crash. Fire-and-forget: journal IO must never
|
|
189
|
+
* break the submit path. */
|
|
190
|
+
noteUserInput(): void;
|
|
191
|
+
/** Structured rows for the web's reconciliation line. */
|
|
192
|
+
status(): readonly RecoveryStatusRow[];
|
|
193
|
+
/** Graceful-shutdown checkpoint (invariant 3): freeze the in-flight
|
|
194
|
+
* session BEFORE the engine dies. Must be awaited before the engine's
|
|
195
|
+
* close/abort. Idempotent; never checkpoints an idle session. */
|
|
196
|
+
stop(): Promise<void>;
|
|
197
|
+
}
|
|
198
|
+
export declare function createRecoveryCoordinator(opts: RecoveryCoordinatorOptions): RecoveryCoordinator;
|
|
199
|
+
//# sourceMappingURL=recovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"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"}
|
package/dist/recovery.js
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
// ω99-242: #2 — 中断任务自动续跑的服务面协调器(重启不丢托付)。北极星=
|
|
2
|
+
// 托付闭环:挂机长任务经得起重启——夜里托付的活,重启后自己验证着续跑,
|
|
3
|
+
// 早上是结果不是一排 todo。参考 openwork/apps/server/src/task-recovery.ts
|
|
4
|
+
// 的判定纪律(观察-分类-认领-限流 + graceful shutdown checkpoint + 丢 ack
|
|
5
|
+
// 永不重试),落在我方的持久资产上(session JSONL 半轮证据 / mission DAG
|
|
6
|
+
// / 跨进程会话租约)。本包不知 CLI:恢复注入走下方 RecoveryPort,由 cli
|
|
7
|
+
// 侧装配(resume + verify-first submit,§三模板在 cli 包)。
|
|
8
|
+
//
|
|
9
|
+
// §二设计不变量的落点(逐条):
|
|
10
|
+
// 1. journal = append-only JSONL(<workspaceRoot>/.wudao/recovery.jsonl,
|
|
11
|
+
// 单文件协调器日志,绝不进各 session JSONL);
|
|
12
|
+
// 2. 认领先落盘再 admission;claim 过但无 settle 的条目重启后归 blocked
|
|
13
|
+
// 交用户,永不二次自动 admission(classifyRecovery 规则 1);
|
|
14
|
+
// 3. 关机 checkpoint:引擎被杀前冻结正在半轮的会话(stop(),shutdown 意
|
|
15
|
+
// 图不被进程被杀产生的 idle/abort 抹掉);
|
|
16
|
+
// 4. 观察分类四态 terminal/blocked/active/unfinished;idle 本身不是中断
|
|
17
|
+
// 的证据(要求同一未完成轮,或我们自己的 checkpoint);
|
|
18
|
+
// 5. verify-first 恢复 prompt 由 port.admit 注入(模板在 cli,逐字钉);
|
|
19
|
+
// 6. 限流:并发恢复 ≤ RECOVERY_CONCURRENCY(2)、启动间隔 ≥
|
|
20
|
+
// RECOVERY_INTERVAL_MS(2s)、大积压轮转公平;
|
|
21
|
+
// 7. 用户手工输入作废该会话的恢复(noteUserInput;权限回复走
|
|
22
|
+
// answerPermission——server 的 submit op 才挂这个 seam,例外成立);
|
|
23
|
+
// 8. 恢复 admission 走正常权限管线(port.admit = resume + submit,cli
|
|
24
|
+
// 侧零旁路——本协调器不做任何权限判断);
|
|
25
|
+
// 9. 恢复顺序沿 mission DAG 依赖拓扑渐进拉起(dagRank 升序,非轮询序);
|
|
26
|
+
// 10. M24 声明:恢复注入发生在 resume 边界=冷转预期(cold turn expected),
|
|
27
|
+
// 代码注释(本文件 + cli/recovery-admission.ts)+ 报告双声明。
|
|
28
|
+
import { appendFile, mkdir, readFile } from "node:fs/promises";
|
|
29
|
+
import { dirname } from "node:path";
|
|
30
|
+
/** ω99-242 — the admission start interval (invariant 6: ≥2s). */
|
|
31
|
+
export const RECOVERY_INTERVAL_MS = 2_000;
|
|
32
|
+
/** ω99-242 — the in-flight recovery ceiling (invariant 6: ≤2). */
|
|
33
|
+
export const RECOVERY_CONCURRENCY = 2;
|
|
34
|
+
/** ω99-242 — backlogs at or below this size are observed in full each tick;
|
|
35
|
+
* larger ones rotate two per tick so observation stays bounded and fair. */
|
|
36
|
+
export const RECOVERY_ROTATION_BOUND = 4;
|
|
37
|
+
const ENTRY_TYPES = new Set(["admit", "checkpoint", "claim", "settle", "cancel"]);
|
|
38
|
+
/** Shape gate for one parsed line — the journal is machine-written; anything
|
|
39
|
+
* else is a skipped line, never a crash (R1's honest-skip law). */
|
|
40
|
+
function validEntry(v) {
|
|
41
|
+
if (typeof v !== "object" || v === null)
|
|
42
|
+
return undefined;
|
|
43
|
+
const e = v;
|
|
44
|
+
if (typeof e.type !== "string" || !ENTRY_TYPES.has(e.type))
|
|
45
|
+
return undefined;
|
|
46
|
+
if (typeof e.sessionId !== "string" || e.sessionId === "")
|
|
47
|
+
return undefined;
|
|
48
|
+
if (typeof e.at !== "number" || !Number.isFinite(e.at))
|
|
49
|
+
return undefined;
|
|
50
|
+
switch (e.type) {
|
|
51
|
+
case "admit":
|
|
52
|
+
if (e.user !== undefined && typeof e.user !== "boolean")
|
|
53
|
+
return undefined;
|
|
54
|
+
break;
|
|
55
|
+
case "settle":
|
|
56
|
+
if (typeof e.verifiedDone !== "number" || !Number.isInteger(e.verifiedDone) || e.verifiedDone < 0)
|
|
57
|
+
return undefined;
|
|
58
|
+
if (typeof e.remaining !== "number" || !Number.isInteger(e.remaining) || e.remaining < 0)
|
|
59
|
+
return undefined;
|
|
60
|
+
break;
|
|
61
|
+
case "cancel":
|
|
62
|
+
if (typeof e.reason !== "string" || e.reason === "")
|
|
63
|
+
return undefined;
|
|
64
|
+
break;
|
|
65
|
+
default:
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
return e;
|
|
69
|
+
}
|
|
70
|
+
/** Fold journal lines (file order) into per-session state. Pure: same lines →
|
|
71
|
+
* same fold. Semantics per entry type:
|
|
72
|
+
* - admit — tracking intent (a run admission happened; `user` marks a manual
|
|
73
|
+
* one, which CONSUMES a standing checkpoint: the user's takeover supersedes
|
|
74
|
+
* the shutdown freeze);
|
|
75
|
+
* - checkpoint — the graceful-shutdown freeze stands;
|
|
76
|
+
* - claim — a recovery claim stands, superseding any checkpoint;
|
|
77
|
+
* - settle — the admission ACK: clears claim/checkpoint, keeps tracking,
|
|
78
|
+
* carries the reconciliation counts;
|
|
79
|
+
* - cancel — intent withdrawn (terminal / user-input / admission-failed):
|
|
80
|
+
* untracks until a LATER admit re-tracks (file order decides). */
|
|
81
|
+
export function foldRecoveryLines(lines) {
|
|
82
|
+
const sessions = new Map();
|
|
83
|
+
let skippedLines = 0;
|
|
84
|
+
for (const raw of lines) {
|
|
85
|
+
if (raw.trim() === "") {
|
|
86
|
+
skippedLines += 1; // honest count: the line yielded no entry
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
let parsed;
|
|
90
|
+
try {
|
|
91
|
+
parsed = JSON.parse(raw);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
skippedLines += 1;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const entry = validEntry(parsed);
|
|
98
|
+
if (entry === undefined) {
|
|
99
|
+
skippedLines += 1;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
const s = sessions.get(entry.sessionId) ?? {
|
|
103
|
+
sessionId: entry.sessionId, tracked: false, checkpointed: false, claimPending: false, lastAt: 0,
|
|
104
|
+
};
|
|
105
|
+
switch (entry.type) {
|
|
106
|
+
case "admit":
|
|
107
|
+
s.tracked = true;
|
|
108
|
+
if (entry.user === true)
|
|
109
|
+
s.checkpointed = false;
|
|
110
|
+
break;
|
|
111
|
+
case "checkpoint":
|
|
112
|
+
s.tracked = true;
|
|
113
|
+
s.checkpointed = true;
|
|
114
|
+
break;
|
|
115
|
+
case "claim":
|
|
116
|
+
s.tracked = true;
|
|
117
|
+
s.claimPending = true;
|
|
118
|
+
s.checkpointed = false;
|
|
119
|
+
break;
|
|
120
|
+
case "settle":
|
|
121
|
+
s.tracked = true;
|
|
122
|
+
s.claimPending = false;
|
|
123
|
+
s.checkpointed = false;
|
|
124
|
+
s.verifiedDone = entry.verifiedDone;
|
|
125
|
+
s.remaining = entry.remaining;
|
|
126
|
+
break;
|
|
127
|
+
case "cancel":
|
|
128
|
+
s.tracked = false;
|
|
129
|
+
s.checkpointed = false;
|
|
130
|
+
s.claimPending = false;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
s.lastAt = entry.at;
|
|
134
|
+
sessions.set(entry.sessionId, s);
|
|
135
|
+
}
|
|
136
|
+
return { sessions, skippedLines };
|
|
137
|
+
}
|
|
138
|
+
/** Open (or create) the coordinator journal at `path`. Appends only — the
|
|
139
|
+
* file's earlier bytes are never rewritten (append-only law). A missing
|
|
140
|
+
* directory is created on first append; a missing/unreadable file folds to
|
|
141
|
+
* the empty state (nothing to recover — an honest zero, not an error). */
|
|
142
|
+
export function createRecoveryJournal(path) {
|
|
143
|
+
let queue = Promise.resolve();
|
|
144
|
+
const append = (entry) => {
|
|
145
|
+
// Serialized: the claim→settle (or cancel) ordering of one recovery must
|
|
146
|
+
// land in the order it happened.
|
|
147
|
+
queue = queue.catch(() => { }).then(async () => {
|
|
148
|
+
await mkdir(dirname(path), { recursive: true });
|
|
149
|
+
await appendFile(path, `${JSON.stringify(entry)}\n`, "utf8");
|
|
150
|
+
});
|
|
151
|
+
return queue;
|
|
152
|
+
};
|
|
153
|
+
return {
|
|
154
|
+
path,
|
|
155
|
+
append,
|
|
156
|
+
fold: async () => {
|
|
157
|
+
let text;
|
|
158
|
+
try {
|
|
159
|
+
text = await readFile(path, "utf8");
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return { sessions: new Map(), skippedLines: 0 };
|
|
163
|
+
}
|
|
164
|
+
return foldRecoveryLines(text.split("\n"));
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/** Classify one observation. Rule order is the judgment discipline:
|
|
169
|
+
* 1. a standing claim without settle (lost ack) → blocked, never re-admitted
|
|
170
|
+
* — even across restarts (invariant 2; trumps every other signal);
|
|
171
|
+
* 2. another live process owns the session → active (observe only);
|
|
172
|
+
* 3. the mission ledger is terminal → terminal (untouched);
|
|
173
|
+
* 4. permission pending / delegated child unresolved → blocked;
|
|
174
|
+
* 5. our own shutdown checkpoint stands, or the same turn is open →
|
|
175
|
+
* unfinished (recoverable) — idle alone is NOT evidence (negative pin);
|
|
176
|
+
* 6. otherwise terminal. */
|
|
177
|
+
export function classifyRecovery(observation, journal) {
|
|
178
|
+
if (journal.claimPending)
|
|
179
|
+
return { state: "blocked", recoverable: false };
|
|
180
|
+
if (observation.leaseHeldByOther)
|
|
181
|
+
return { state: "active", recoverable: false };
|
|
182
|
+
if (observation.missionTerminal)
|
|
183
|
+
return { state: "terminal", recoverable: false };
|
|
184
|
+
if (observation.permissionPending || observation.childPending)
|
|
185
|
+
return { state: "blocked", recoverable: false };
|
|
186
|
+
if (journal.checkpointed || observation.turnOpen)
|
|
187
|
+
return { state: "unfinished", recoverable: true };
|
|
188
|
+
return { state: "terminal", recoverable: false };
|
|
189
|
+
}
|
|
190
|
+
export function createRecoveryCoordinator(opts) {
|
|
191
|
+
const now = opts.now ?? (() => Date.now());
|
|
192
|
+
const intervalMs = opts.intervalMs ?? RECOVERY_INTERVAL_MS;
|
|
193
|
+
const concurrency = opts.concurrency ?? RECOVERY_CONCURRENCY;
|
|
194
|
+
const rotationBound = opts.rotationBound ?? RECOVERY_ROTATION_BOUND;
|
|
195
|
+
const journal = createRecoveryJournal(opts.journalPath);
|
|
196
|
+
const mem = new Map();
|
|
197
|
+
let booted = false;
|
|
198
|
+
let stopped = false;
|
|
199
|
+
let ticking;
|
|
200
|
+
let nextLaunch = 0;
|
|
201
|
+
let cursor = 0;
|
|
202
|
+
const recOf = (sessionId) => {
|
|
203
|
+
const existing = mem.get(sessionId);
|
|
204
|
+
if (existing !== undefined)
|
|
205
|
+
return existing;
|
|
206
|
+
const fresh = {
|
|
207
|
+
sessionId, tracked: false, checkpointed: false, claimPending: false, lastAt: 0, admitted: false, userOwned: false,
|
|
208
|
+
};
|
|
209
|
+
mem.set(sessionId, fresh);
|
|
210
|
+
return fresh;
|
|
211
|
+
};
|
|
212
|
+
const boot = async () => {
|
|
213
|
+
booted = true;
|
|
214
|
+
const fold = await journal.fold();
|
|
215
|
+
for (const s of fold.sessions.values()) {
|
|
216
|
+
const rec = recOf(s.sessionId);
|
|
217
|
+
rec.tracked = s.tracked;
|
|
218
|
+
rec.checkpointed = s.checkpointed;
|
|
219
|
+
rec.claimPending = s.claimPending;
|
|
220
|
+
rec.verifiedDone = s.verifiedDone;
|
|
221
|
+
rec.remaining = s.remaining;
|
|
222
|
+
rec.lastAt = s.lastAt;
|
|
223
|
+
}
|
|
224
|
+
// ω99-242 §四: v1 recovers 「checkpoint 过的半轮 + mission in-flight」—
|
|
225
|
+
// the disk scan names mission-bound sessions the journal never saw (the
|
|
226
|
+
// pre-feature crash path). Deduped against the journal-tracked set.
|
|
227
|
+
const extra = await opts.port.bootCandidates().catch(() => []);
|
|
228
|
+
for (const id of extra)
|
|
229
|
+
if (id !== "" && !mem.has(id))
|
|
230
|
+
recOf(id).tracked = true;
|
|
231
|
+
};
|
|
232
|
+
/** Cancel + untrack. `reason` is the honest cause (journal-visible). */
|
|
233
|
+
const remove = async (sessionId, reason) => {
|
|
234
|
+
await journal.append({ type: "cancel", sessionId, at: now(), reason });
|
|
235
|
+
const rec = mem.get(sessionId);
|
|
236
|
+
if (rec !== undefined) {
|
|
237
|
+
rec.tracked = false;
|
|
238
|
+
rec.checkpointed = false;
|
|
239
|
+
rec.claimPending = false;
|
|
240
|
+
rec.admitted = false;
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
const check = async (sessionId) => {
|
|
244
|
+
const rec = mem.get(sessionId);
|
|
245
|
+
if (rec === undefined || !rec.tracked)
|
|
246
|
+
return;
|
|
247
|
+
const observation = await opts.port.observe(sessionId);
|
|
248
|
+
if (observation === undefined) {
|
|
249
|
+
await remove(sessionId, "gone");
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const cls = classifyRecovery(observation, rec);
|
|
253
|
+
rec.lastState = cls.state;
|
|
254
|
+
rec.lastObs = observation;
|
|
255
|
+
if (cls.state === "active" || cls.state === "blocked")
|
|
256
|
+
return; // hold — observe only
|
|
257
|
+
if (cls.state === "terminal") {
|
|
258
|
+
await remove(sessionId, rec.admitted ? "settled" : "terminal");
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
// unfinished: recoverable — the admission phase below decides when.
|
|
262
|
+
};
|
|
263
|
+
const admitOne = async (rec) => {
|
|
264
|
+
// ω99-242 invariant 2: claim durably BEFORE admission. A lost
|
|
265
|
+
// acknowledgement is never retried — including across another restart
|
|
266
|
+
// (the fold reads this claim back as claimPending → blocked).
|
|
267
|
+
const at = now();
|
|
268
|
+
await journal.append({ type: "claim", sessionId: rec.sessionId, at });
|
|
269
|
+
rec.claimPending = true;
|
|
270
|
+
rec.checkpointed = false;
|
|
271
|
+
rec.admitted = true;
|
|
272
|
+
nextLaunch = at + intervalMs;
|
|
273
|
+
try {
|
|
274
|
+
const settle = await opts.port.admit(rec.sessionId);
|
|
275
|
+
rec.claimPending = false;
|
|
276
|
+
rec.verifiedDone = settle.verifiedDone;
|
|
277
|
+
rec.remaining = settle.remaining;
|
|
278
|
+
await journal.append({
|
|
279
|
+
type: "settle", sessionId: rec.sessionId, at: now(),
|
|
280
|
+
verifiedDone: settle.verifiedDone, remaining: settle.remaining,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
catch (err) {
|
|
284
|
+
// The admission did not land (resume refused / submit refused). The
|
|
285
|
+
// claim discipline still holds — never silently retry: an honest cancel
|
|
286
|
+
// with the cause, one stderr line for the operator, gone this boot.
|
|
287
|
+
await remove(rec.sessionId, "admission-failed");
|
|
288
|
+
process.stderr.write(`wudao-recovery: 恢复 admission 失败(${rec.sessionId},本进程不再重试):${err instanceof Error ? err.message : String(err)}\n`);
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
const tick = () => {
|
|
292
|
+
if (stopped)
|
|
293
|
+
return Promise.resolve();
|
|
294
|
+
if (ticking !== undefined)
|
|
295
|
+
return ticking;
|
|
296
|
+
ticking = (async () => {
|
|
297
|
+
try {
|
|
298
|
+
if (!booted)
|
|
299
|
+
await boot();
|
|
300
|
+
const tracked = [...mem.values()].filter((r) => r.tracked);
|
|
301
|
+
// Fairness (invariant 6): small backlogs observe in full; larger ones
|
|
302
|
+
// rotate two per tick so background reads stay bounded and everyone
|
|
303
|
+
// gets a turn.
|
|
304
|
+
let selected;
|
|
305
|
+
if (tracked.length <= rotationBound) {
|
|
306
|
+
selected = tracked;
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
const a = tracked[cursor % tracked.length];
|
|
310
|
+
const b = tracked[(cursor + 1) % tracked.length];
|
|
311
|
+
cursor += concurrency;
|
|
312
|
+
selected = a !== undefined && b !== undefined ? [...new Set([a, b])] : tracked.slice(0, 2);
|
|
313
|
+
}
|
|
314
|
+
await Promise.all([...new Set(selected)].map((rec) => check(rec.sessionId).catch(() => {
|
|
315
|
+
// An unavailable/ambiguous observation never grants permission to
|
|
316
|
+
// admit — the record keeps its state for the next tick.
|
|
317
|
+
})));
|
|
318
|
+
// Admission phase — SEQUENTIAL, mission-DAG-topological (invariant
|
|
319
|
+
// 9), never while a run is in flight, capped, interval-spaced.
|
|
320
|
+
const recoverable = [...mem.values()]
|
|
321
|
+
.filter((r) => r.tracked && !r.admitted && !r.userOwned && r.lastState === "unfinished" && r.lastObs !== undefined)
|
|
322
|
+
.sort((a, b) => (a.lastObs?.dagRank ?? Number.POSITIVE_INFINITY) - (b.lastObs?.dagRank ?? Number.POSITIVE_INFINITY) || a.lastAt - b.lastAt);
|
|
323
|
+
for (const rec of recoverable) {
|
|
324
|
+
if (opts.port.busy())
|
|
325
|
+
break; // never interrupt a live run
|
|
326
|
+
const inFlight = [...mem.values()].filter((r) => r.tracked && r.admitted).length;
|
|
327
|
+
if (inFlight >= concurrency)
|
|
328
|
+
break; // the in-flight ceiling
|
|
329
|
+
if (now() < nextLaunch)
|
|
330
|
+
break; // the start interval
|
|
331
|
+
await admitOne(rec);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
finally {
|
|
335
|
+
ticking = undefined;
|
|
336
|
+
}
|
|
337
|
+
})();
|
|
338
|
+
return ticking;
|
|
339
|
+
};
|
|
340
|
+
const install = opts.setTimer === undefined
|
|
341
|
+
? (cb, ms) => {
|
|
342
|
+
const t = setInterval(cb, ms);
|
|
343
|
+
t.unref?.();
|
|
344
|
+
}
|
|
345
|
+
: opts.setTimer;
|
|
346
|
+
install(() => void tick(), intervalMs);
|
|
347
|
+
return {
|
|
348
|
+
tick,
|
|
349
|
+
noteUserInput: () => {
|
|
350
|
+
if (stopped)
|
|
351
|
+
return;
|
|
352
|
+
const id = opts.port.currentSession();
|
|
353
|
+
if (id === undefined)
|
|
354
|
+
return;
|
|
355
|
+
void (async () => {
|
|
356
|
+
try {
|
|
357
|
+
const rec = mem.get(id);
|
|
358
|
+
// Invariant 7: the user's manual input voids THIS session's pending
|
|
359
|
+
// recovery (a standing claim must not poison the next boot as a
|
|
360
|
+
// lost ack — the takeover cancels it). The session STAYS tracked:
|
|
361
|
+
// the user is driving entrusted work, and its next crash recovers.
|
|
362
|
+
if (rec !== undefined && (rec.claimPending || rec.admitted || rec.checkpointed)) {
|
|
363
|
+
await journal.append({ type: "cancel", sessionId: id, at: now(), reason: "user-input" });
|
|
364
|
+
rec.claimPending = false;
|
|
365
|
+
rec.admitted = false;
|
|
366
|
+
rec.checkpointed = false;
|
|
367
|
+
rec.tracked = true;
|
|
368
|
+
}
|
|
369
|
+
await journal.append({ type: "admit", sessionId: id, at: now(), user: true });
|
|
370
|
+
const target = rec ?? recOf(id);
|
|
371
|
+
target.tracked = true;
|
|
372
|
+
target.lastAt = now();
|
|
373
|
+
target.userOwned = true;
|
|
374
|
+
}
|
|
375
|
+
catch {
|
|
376
|
+
// Journal IO is best-effort here — never break the submit path.
|
|
377
|
+
}
|
|
378
|
+
})();
|
|
379
|
+
},
|
|
380
|
+
status: () => {
|
|
381
|
+
const rows = [];
|
|
382
|
+
for (const rec of mem.values()) {
|
|
383
|
+
if (!rec.tracked)
|
|
384
|
+
continue; // removed/terminal records report nothing
|
|
385
|
+
const state = rec.claimPending
|
|
386
|
+
? "blocked"
|
|
387
|
+
: rec.admitted
|
|
388
|
+
? "recovering"
|
|
389
|
+
: rec.lastState === "active" || rec.lastState === "blocked" || rec.lastState === "unfinished" || rec.lastState === "terminal"
|
|
390
|
+
? rec.lastState
|
|
391
|
+
: "tracked";
|
|
392
|
+
rows.push({
|
|
393
|
+
sessionId: rec.sessionId,
|
|
394
|
+
state,
|
|
395
|
+
...(rec.verifiedDone !== undefined ? { verifiedDone: rec.verifiedDone } : {}),
|
|
396
|
+
...(rec.remaining !== undefined ? { remaining: rec.remaining } : {}),
|
|
397
|
+
// ω99-248 — thread the record-sourced ask evidence into the row (the
|
|
398
|
+
// morning line's data source); the latest observation owns it and it
|
|
399
|
+
// disappears the moment the record settles (落定不复活).
|
|
400
|
+
...(rec.lastObs?.pendingAsk !== undefined ? { permissionAsk: rec.lastObs.pendingAsk } : {}),
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
return rows;
|
|
404
|
+
},
|
|
405
|
+
stop: async () => {
|
|
406
|
+
if (stopped)
|
|
407
|
+
return;
|
|
408
|
+
stopped = true;
|
|
409
|
+
// Invariant 3: freeze BEFORE engines are killed, so the kill's own
|
|
410
|
+
// idle/abort can never erase the recovery intent. Only a session with
|
|
411
|
+
// a run actually in flight is checkpointed — idle is not intent.
|
|
412
|
+
const id = opts.port.currentSession();
|
|
413
|
+
if (id !== undefined && opts.port.busy()) {
|
|
414
|
+
await journal.append({ type: "checkpoint", sessionId: id, at: now() });
|
|
415
|
+
const rec = mem.get(id);
|
|
416
|
+
if (rec !== undefined) {
|
|
417
|
+
rec.checkpointed = true;
|
|
418
|
+
rec.tracked = true;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
},
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
//# sourceMappingURL=recovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.js","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"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"}
|