@sema-agent/core 1.452.0 → 2.0.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/dist/agents/agent-transcript-tool.d.ts +15 -0
- package/dist/agents/agent-transcript-tool.js +122 -0
- package/dist/agents/cascade.js +5 -8
- package/dist/agents/repair-loop.d.ts +1 -1
- package/dist/agents/repair-loop.js +1 -1
- package/dist/agents/retain-ledger.d.ts +60 -0
- package/dist/agents/retain-ledger.js +225 -0
- package/dist/agents/roster-store.d.ts +1 -0
- package/dist/agents/roster-store.js +9 -0
- package/dist/agents/send-message-tool.d.ts +41 -0
- package/dist/agents/send-message-tool.js +469 -0
- package/dist/agents/subagent.d.ts +33 -108
- package/dist/agents/subagent.js +22 -834
- package/dist/agents/teacher.d.ts +0 -1
- package/dist/agents/teacher.js +0 -9
- package/dist/bin/sema-tb.js +3 -3
- package/dist/brain/anthropic.js +15 -40
- package/dist/brain/openai.js +15 -40
- package/dist/brain/reasoning.d.ts +1 -1
- package/dist/brain/stream-engine.js +2 -2
- package/dist/brain/stream-shared.d.ts +17 -0
- package/dist/brain/stream-shared.js +50 -0
- package/dist/brain/timeout.d.ts +1 -1
- package/dist/brain/timeout.js +1 -1
- package/dist/config/catalog.js +1 -3
- package/dist/config/defaults.d.ts +6 -0
- package/dist/config/defaults.js +6 -0
- package/dist/core/ask-question.d.ts +1 -1
- package/dist/core/background-shell.d.ts +1 -1
- package/dist/core/checkpoint-store.d.ts +0 -1
- package/dist/core/exec-gate.d.ts +1 -1
- package/dist/core/exec-output-tail.d.ts +1 -0
- package/dist/core/exec-output-tail.js +6 -0
- package/dist/core/file-snapshot-store.d.ts +1 -1
- package/dist/core/fs-write-gate-policy.d.ts +1 -1
- package/dist/core/git-worktree-env.d.ts +1 -1
- package/dist/core/hooks.d.ts +1 -1
- package/dist/core/image-downsample.d.ts +44 -0
- package/dist/core/image-downsample.js +97 -0
- package/dist/core/lsp.d.ts +1 -1
- package/dist/core/mcp.d.ts +3 -45
- package/dist/core/mcp.js +2 -97
- package/dist/core/media-byte-cap.d.ts +1 -1
- package/dist/core/memory-recall.d.ts +0 -12
- package/dist/core/memory-recall.js +0 -222
- package/dist/core/memory.d.ts +0 -6
- package/dist/core/memory.js +0 -88
- package/dist/core/message-utils.d.ts +1 -1
- package/dist/core/oracle-isolation.d.ts +1 -1
- package/dist/core/remote-env.d.ts +1 -1
- package/dist/core/roles.d.ts +1 -1
- package/dist/core/runner/active-skill-scope.d.ts +1 -1
- package/dist/core/runner/assemble-result.d.ts +0 -2
- package/dist/core/runner/memory-consolidation.d.ts +1 -1
- package/dist/core/runner/prepare-memory.d.ts +16 -0
- package/dist/core/runner/prepare-memory.js +191 -0
- package/dist/core/runner/prepare-task.d.ts +4 -28
- package/dist/core/runner/prepare-task.js +19 -207
- package/dist/core/runner/prompt-suggestions.d.ts +1 -1
- package/dist/core/runner/runtask.d.ts +2 -3
- package/dist/core/runner/runtask.js +337 -529
- package/dist/core/runner/session-rule-policy.d.ts +1 -1
- package/dist/core/runner/synthetic-tools.d.ts +5 -5
- package/dist/core/runner/synthetic-tools.js +4 -4
- package/dist/core/runner/tool-disclosure.d.ts +3 -3
- package/dist/core/runner/tool-disclosure.js +2 -2
- package/dist/core/runner/tool-output-projection.d.ts +6 -0
- package/dist/core/runner/tool-output-projection.js +81 -0
- package/dist/core/runtime.d.ts +1 -1
- package/dist/core/scheduler.d.ts +1 -1
- package/dist/core/sensitive-path-policy.d.ts +1 -1
- package/dist/core/session-reconcile.d.ts +1 -1
- package/dist/core/session-store.d.ts +2 -1
- package/dist/core/session-store.js +2 -1
- package/dist/core/session.d.ts +0 -1
- package/dist/core/task-registry-agent.d.ts +115 -0
- package/dist/core/task-registry-agent.js +1163 -0
- package/dist/core/task-registry-monitor.d.ts +13 -0
- package/dist/core/task-registry-monitor.js +347 -0
- package/dist/core/task-registry-shared.d.ts +294 -0
- package/dist/core/task-registry-shared.js +271 -0
- package/dist/core/task-registry-workflow.d.ts +5 -0
- package/dist/core/task-registry-workflow.js +143 -0
- package/dist/core/task-registry.d.ts +51 -216
- package/dist/core/task-registry.js +127 -1940
- package/dist/core/task-tool-shape.d.ts +32 -0
- package/dist/core/task-tool-shape.js +83 -0
- package/dist/core/tool-policy.d.ts +1 -2
- package/dist/core/tool-result-budget.d.ts +1 -1
- package/dist/core/tool-result-store.d.ts +2 -2
- package/dist/core/tool-result-store.js +1 -1
- package/dist/core/tools.d.ts +2 -3
- package/dist/core/tools.js +0 -8
- package/dist/core/trace.d.ts +2 -2
- package/dist/core/types.d.ts +0 -29
- package/dist/core/workflow-run-store-contract.d.ts +1 -1
- package/dist/core/workflow-run-store-contract.js +19 -19
- package/dist/engine/execution-env/node-execution-env.d.ts +13 -0
- package/dist/engine/execution-env/node-execution-env.js +55 -25
- package/dist/engine/lsp/frame-decoder.d.ts +1 -1
- package/dist/engine/lsp/frame-decoder.js +1 -1
- package/dist/engine/lsp/node-lsp-manager.d.ts +1 -1
- package/dist/engine/lsp/stdio-lsp-transport.js +2 -2
- package/dist/index.d.ts +16 -14
- package/dist/index.js +13 -11
- package/dist/internal/harness-types.d.ts +7 -0
- package/dist/internal/harness-types.js +1 -0
- package/dist/internal/harness.d.ts +1 -7
- package/dist/internal/harness.js +1 -0
- package/dist/orchestration/goal.js +2 -2
- package/dist/orchestration/run-workflow-tool.d.ts +1 -1
- package/dist/orchestration/workflow-types.d.ts +193 -0
- package/dist/orchestration/workflow-types.js +54 -0
- package/dist/orchestration/workflow.d.ts +3 -193
- package/dist/orchestration/workflow.js +7 -59
- package/dist/prompt-assembly/assemble.d.ts +3 -3
- package/dist/prompt-assembly/assemble.js +0 -16
- package/dist/prompt-assembly/explain.d.ts +1 -1
- package/dist/prompt-assembly/tool-catalog.d.ts +1 -1
- package/dist/prompts/default.d.ts +1 -6
- package/dist/prompts/default.js +3 -11
- package/dist/scenarios/env.d.ts +1 -1
- package/dist/scenarios/full-body.d.ts +0 -3
- package/dist/scenarios/full-body.js +0 -2
- package/dist/stores/file/background-agent-store.d.ts +2 -15
- package/dist/stores/file/background-agent-store.js +30 -100
- package/dist/stores/file/checkpoint-store.d.ts +1 -14
- package/dist/stores/file/checkpoint-store.js +47 -119
- package/dist/stores/file/file-snapshot-store.d.ts +1 -1
- package/dist/stores/file/shared-ledger.d.ts +35 -0
- package/dist/stores/file/shared-ledger.js +94 -0
- package/dist/stores/file/workflow-run-store.d.ts +1 -14
- package/dist/stores/file/workflow-run-store.js +24 -93
- package/dist/tools/fs/bash-readonly-classifier.d.ts +8 -0
- package/dist/tools/fs/bash-readonly-classifier.js +166 -0
- package/dist/tools/fs/fs-bash.d.ts +31 -0
- package/dist/tools/fs/fs-bash.js +672 -0
- package/dist/tools/fs/fs-pdf.d.ts +22 -0
- package/dist/tools/fs/fs-pdf.js +236 -0
- package/dist/tools/fs/fs-read.d.ts +8 -0
- package/dist/tools/fs/fs-read.js +286 -0
- package/dist/tools/fs/fs-search-tools.d.ts +5 -0
- package/dist/tools/fs/fs-search-tools.js +173 -0
- package/dist/tools/fs/fs-shared.d.ts +55 -0
- package/dist/tools/fs/fs-shared.js +144 -0
- package/dist/tools/fs/fs-write.d.ts +8 -0
- package/dist/tools/fs/fs-write.js +423 -0
- package/dist/tools/fs/index.d.ts +12 -65
- package/dist/tools/fs/index.js +26 -2090
- package/dist/tools/fs/pdf.d.ts +1 -1
- package/dist/tools/fs/repo-map.d.ts +3 -3
- package/dist/tools/fs/repo-map.js +1 -1
- package/dist/tools/fs/safety.d.ts +1 -1
- package/dist/tools/fs/search.d.ts +1 -1
- package/dist/tools/monitor.d.ts +1 -1
- package/dist/tools/scheduler-tools.d.ts +1 -1
- package/dist/tools/web.d.ts +2 -2
- package/dist/tools/web.js +1 -1
- package/dist/tools/worktree.d.ts +1 -1
- package/package.json +1 -1
- package/dist/core/dynamic-recall.d.ts +0 -32
- package/dist/core/dynamic-recall.js +0 -76
|
@@ -1,105 +1,49 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { BackgroundAgentStoreError, STALE_RUNNING_REAP_ATTRIBUTION, } from "../../core/background-agent-store.js";
|
|
3
|
-
import {
|
|
4
|
-
const
|
|
3
|
+
import { SharedLedgerTable } from "./shared-ledger.js";
|
|
4
|
+
const agentLedgers = new SharedLedgerTable({
|
|
5
|
+
keyOf: (r) => FileBackgroundAgentStore.key(r.handle, r.scope),
|
|
6
|
+
apply: (rows, ev) => {
|
|
7
|
+
if (ev.t === "delete") {
|
|
8
|
+
rows.delete(ev.k);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
rows.set(ev.k, ev.r);
|
|
12
|
+
},
|
|
13
|
+
});
|
|
5
14
|
export class FileBackgroundAgentStore {
|
|
6
|
-
dir;
|
|
7
|
-
tmpDir;
|
|
8
|
-
ledgerPath;
|
|
9
|
-
snapshotPath;
|
|
10
15
|
fsyncEnabled;
|
|
11
16
|
compactEvery;
|
|
12
|
-
|
|
17
|
+
ledger;
|
|
13
18
|
closed = false;
|
|
14
19
|
get rows() {
|
|
15
|
-
return this.
|
|
16
|
-
}
|
|
17
|
-
get locks() {
|
|
18
|
-
return this.shared.locks;
|
|
19
|
-
}
|
|
20
|
-
get ledgerEvents() {
|
|
21
|
-
return this.shared.ledgerEvents;
|
|
22
|
-
}
|
|
23
|
-
set ledgerEvents(v) {
|
|
24
|
-
this.shared.ledgerEvents = v;
|
|
25
|
-
}
|
|
26
|
-
get log() {
|
|
27
|
-
return this.shared.log;
|
|
28
|
-
}
|
|
29
|
-
set log(v) {
|
|
30
|
-
this.shared.log = v;
|
|
20
|
+
return this.ledger.rows;
|
|
31
21
|
}
|
|
32
22
|
constructor(root, opts = {}) {
|
|
33
|
-
this.dir = join(root, "background-agents");
|
|
34
|
-
this.tmpDir = join(root, "tmp");
|
|
35
|
-
this.ledgerPath = join(this.dir, "agents.jsonl");
|
|
36
|
-
this.snapshotPath = join(this.dir, "agents.snapshot.jsonl");
|
|
37
23
|
this.fsyncEnabled = opts.fsync !== false;
|
|
38
24
|
this.compactEvery = opts.compactEvery ?? 1000;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
this.canonicalDir = canonical;
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
this.shared = { rows: new Map(), locks: new Map(), ledgerEvents: 0, log: undefined, refs: 1 };
|
|
50
|
-
this.canonicalDir = canonical;
|
|
51
|
-
sharedAgentDirs.set(canonical, this.shared);
|
|
52
|
-
this.replay();
|
|
53
|
-
this.log = new AppendLog(this.ledgerPath);
|
|
25
|
+
const dir = join(root, "background-agents");
|
|
26
|
+
this.ledger = agentLedgers.acquire({
|
|
27
|
+
dir,
|
|
28
|
+
tmpDir: join(root, "tmp"),
|
|
29
|
+
ledgerPath: join(dir, "agents.jsonl"),
|
|
30
|
+
snapshotPath: join(dir, "agents.snapshot.jsonl"),
|
|
31
|
+
});
|
|
54
32
|
}
|
|
55
|
-
|
|
56
|
-
key(handle, scope) {
|
|
33
|
+
static key(handle, scope) {
|
|
57
34
|
return `${scope}\u0000${handle}`;
|
|
58
35
|
}
|
|
59
|
-
replay() {
|
|
60
|
-
for (const snap of readJsonlRecords(this.snapshotPath)) {
|
|
61
|
-
this.rows.set(this.key(snap.handle, snap.scope), snap);
|
|
62
|
-
}
|
|
63
|
-
const events = readJsonlRecords(this.ledgerPath);
|
|
64
|
-
this.ledgerEvents = events.length;
|
|
65
|
-
for (const ev of events)
|
|
66
|
-
this.applyEvent(ev);
|
|
67
|
-
}
|
|
68
|
-
applyEvent(ev) {
|
|
69
|
-
if (ev.t === "delete") {
|
|
70
|
-
this.rows.delete(ev.k);
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
this.rows.set(ev.k, ev.r);
|
|
74
|
-
}
|
|
75
36
|
withLock(key, fn) {
|
|
76
|
-
|
|
77
|
-
const run = prev.then(() => fn(), () => fn());
|
|
78
|
-
const tail = run.then(() => { }, () => { });
|
|
79
|
-
this.locks.set(key, tail);
|
|
80
|
-
void tail.then(() => {
|
|
81
|
-
if (this.locks.get(key) === tail)
|
|
82
|
-
this.locks.delete(key);
|
|
83
|
-
});
|
|
84
|
-
return run;
|
|
37
|
+
return this.ledger.withLock(key, fn);
|
|
85
38
|
}
|
|
86
39
|
commit(ev) {
|
|
87
|
-
this.
|
|
88
|
-
this.applyEvent(ev);
|
|
89
|
-
this.ledgerEvents++;
|
|
90
|
-
if (this.ledgerEvents >= this.compactEvery) {
|
|
91
|
-
try {
|
|
92
|
-
this.compact();
|
|
93
|
-
}
|
|
94
|
-
catch {
|
|
95
|
-
}
|
|
96
|
-
}
|
|
40
|
+
this.ledger.commit(ev, this.fsyncEnabled, this.compactEvery);
|
|
97
41
|
}
|
|
98
42
|
async put(record) {
|
|
99
43
|
if (record.scope === undefined || record.scope === "") {
|
|
100
44
|
throw new Error("BackgroundAgentStore.put: refusing a row without a scope (world-readable through the fallback)");
|
|
101
45
|
}
|
|
102
|
-
const k =
|
|
46
|
+
const k = FileBackgroundAgentStore.key(record.handle, record.scope);
|
|
103
47
|
return this.withLock(k, () => {
|
|
104
48
|
if (this.rows.has(k)) {
|
|
105
49
|
throw new BackgroundAgentStoreError("agent_record.already_exists", "agent record already exists");
|
|
@@ -110,11 +54,11 @@ export class FileBackgroundAgentStore {
|
|
|
110
54
|
});
|
|
111
55
|
}
|
|
112
56
|
async get(handle, scope) {
|
|
113
|
-
const row = this.rows.get(
|
|
57
|
+
const row = this.rows.get(FileBackgroundAgentStore.key(handle, scope));
|
|
114
58
|
return row ? structuredClone(row) : null;
|
|
115
59
|
}
|
|
116
60
|
async update(handle, scope, record, expect) {
|
|
117
|
-
const k =
|
|
61
|
+
const k = FileBackgroundAgentStore.key(handle, scope);
|
|
118
62
|
return this.withLock(k, () => {
|
|
119
63
|
const live = this.rows.get(k);
|
|
120
64
|
if (!live || live.scope !== scope || live.rev !== expect.rev)
|
|
@@ -128,7 +72,7 @@ export class FileBackgroundAgentStore {
|
|
|
128
72
|
});
|
|
129
73
|
}
|
|
130
74
|
async updateIf(handle, scope, record, expect) {
|
|
131
|
-
const k =
|
|
75
|
+
const k = FileBackgroundAgentStore.key(handle, scope);
|
|
132
76
|
return this.withLock(k, async () => {
|
|
133
77
|
const live = this.rows.get(k);
|
|
134
78
|
if (!live || live.scope !== scope || live.rev !== expect.rev)
|
|
@@ -178,7 +122,7 @@ export class FileBackgroundAgentStore {
|
|
|
178
122
|
return [...new Set([...this.rows.values()].map((r) => r.scope))].sort();
|
|
179
123
|
}
|
|
180
124
|
async delete(handle, scope, expect) {
|
|
181
|
-
const k =
|
|
125
|
+
const k = FileBackgroundAgentStore.key(handle, scope);
|
|
182
126
|
return this.withLock(k, () => {
|
|
183
127
|
const live = this.rows.get(k);
|
|
184
128
|
if (!live)
|
|
@@ -248,15 +192,8 @@ export class FileBackgroundAgentStore {
|
|
|
248
192
|
}
|
|
249
193
|
return changed.size;
|
|
250
194
|
}
|
|
251
|
-
compact() {
|
|
252
|
-
const lines = [...this.rows.values()].map((r) => JSON.stringify(r)).join("\n");
|
|
253
|
-
atomicWriteFile(this.tmpDir, this.snapshotPath, lines.length ? `${lines}\n` : "");
|
|
254
|
-
this.log.closeForSwap();
|
|
255
|
-
atomicWriteFile(this.tmpDir, this.ledgerPath, "");
|
|
256
|
-
this.ledgerEvents = 0;
|
|
257
|
-
}
|
|
258
195
|
compactNow() {
|
|
259
|
-
this.compact();
|
|
196
|
+
this.ledger.compact();
|
|
260
197
|
}
|
|
261
198
|
get size() {
|
|
262
199
|
return this.rows.size;
|
|
@@ -265,14 +202,7 @@ export class FileBackgroundAgentStore {
|
|
|
265
202
|
if (this.closed)
|
|
266
203
|
return;
|
|
267
204
|
this.closed = true;
|
|
268
|
-
|
|
269
|
-
if (st !== undefined && st === this.shared) {
|
|
270
|
-
st.refs -= 1;
|
|
271
|
-
if (st.refs > 0)
|
|
272
|
-
return;
|
|
273
|
-
sharedAgentDirs.delete(this.canonicalDir);
|
|
274
|
-
}
|
|
275
|
-
this.log.close();
|
|
205
|
+
this.ledger.release();
|
|
276
206
|
}
|
|
277
207
|
}
|
|
278
208
|
function toSummary(r) {
|
|
@@ -4,25 +4,13 @@ export interface FileCheckpointStoreOptions {
|
|
|
4
4
|
compactEvery?: number;
|
|
5
5
|
}
|
|
6
6
|
export declare class FileCheckpointStore implements CheckpointStore {
|
|
7
|
-
private readonly dir;
|
|
8
|
-
private readonly tmpDir;
|
|
9
|
-
private readonly ledgerPath;
|
|
10
|
-
private readonly snapshotPath;
|
|
11
7
|
private readonly fsyncEnabled;
|
|
12
8
|
private readonly compactEvery;
|
|
13
|
-
private readonly
|
|
14
|
-
private readonly sharedKey;
|
|
9
|
+
private readonly ledger;
|
|
15
10
|
private closed;
|
|
16
11
|
private get cps();
|
|
17
|
-
private get locks();
|
|
18
|
-
private get ledgerEvents();
|
|
19
|
-
private set ledgerEvents(value);
|
|
20
|
-
private get log();
|
|
21
|
-
private set log(value);
|
|
22
12
|
private fault;
|
|
23
13
|
constructor(root: string, opts?: FileCheckpointStoreOptions);
|
|
24
|
-
private replay;
|
|
25
|
-
private applyEvent;
|
|
26
14
|
private withLock;
|
|
27
15
|
private commit;
|
|
28
16
|
put(token: CheckpointToken, cp: Checkpoint): Promise<void>;
|
|
@@ -37,7 +25,6 @@ export declare class FileCheckpointStore implements CheckpointStore {
|
|
|
37
25
|
reap(scope: string, cutoff: number): Promise<number>;
|
|
38
26
|
listByScope(scope: string): Promise<CheckpointSummary[]>;
|
|
39
27
|
listScopes(): Promise<string[]>;
|
|
40
|
-
private compact;
|
|
41
28
|
compactNow(): void;
|
|
42
29
|
testInjectFault(mode: CheckpointFaultMode | null): void;
|
|
43
30
|
get size(): number;
|
|
@@ -1,124 +1,64 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { CheckpointError, summarizeCheckpoint, validatePendingSteer, winnerFromOutcome, } from "../../core/checkpoint-store.js";
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import { SharedLedgerTable } from "./shared-ledger.js";
|
|
4
|
+
function applyCheckpointEvent(cps, ev) {
|
|
5
|
+
if (ev.t === "put") {
|
|
6
|
+
cps.set(ev.token, ev.cp);
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const cp = cps.get(ev.token);
|
|
10
|
+
if (!cp)
|
|
11
|
+
return;
|
|
12
|
+
switch (ev.t) {
|
|
13
|
+
case "resolve":
|
|
14
|
+
cp.status = "resolved";
|
|
15
|
+
cp.rev = ev.rev;
|
|
16
|
+
if (ev.resolvedOutcome !== undefined)
|
|
17
|
+
cp.resolvedOutcome = ev.resolvedOutcome;
|
|
18
|
+
cp.reopenReason = undefined;
|
|
19
|
+
break;
|
|
20
|
+
case "reopen":
|
|
21
|
+
cp.status = "pending";
|
|
22
|
+
cp.rev = ev.rev;
|
|
23
|
+
cp.reopenReason = ev.reason;
|
|
24
|
+
break;
|
|
25
|
+
case "expire":
|
|
26
|
+
cp.status = "expired";
|
|
27
|
+
break;
|
|
28
|
+
case "steer":
|
|
29
|
+
cp.state.pendingSteer = ev.steer;
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const checkpointLedgers = new SharedLedgerTable({
|
|
34
|
+
keyOf: (cp) => cp.token,
|
|
35
|
+
apply: applyCheckpointEvent,
|
|
36
|
+
});
|
|
5
37
|
export class FileCheckpointStore {
|
|
6
|
-
dir;
|
|
7
|
-
tmpDir;
|
|
8
|
-
ledgerPath;
|
|
9
|
-
snapshotPath;
|
|
10
38
|
fsyncEnabled;
|
|
11
39
|
compactEvery;
|
|
12
|
-
|
|
13
|
-
sharedKey;
|
|
40
|
+
ledger;
|
|
14
41
|
closed = false;
|
|
15
42
|
get cps() {
|
|
16
|
-
return this.
|
|
17
|
-
}
|
|
18
|
-
get locks() {
|
|
19
|
-
return this.shared.locks;
|
|
20
|
-
}
|
|
21
|
-
get ledgerEvents() {
|
|
22
|
-
return this.shared.ledgerEvents;
|
|
23
|
-
}
|
|
24
|
-
set ledgerEvents(v) {
|
|
25
|
-
this.shared.ledgerEvents = v;
|
|
26
|
-
}
|
|
27
|
-
get log() {
|
|
28
|
-
return this.shared.log;
|
|
29
|
-
}
|
|
30
|
-
set log(v) {
|
|
31
|
-
this.shared.log = v;
|
|
43
|
+
return this.ledger.rows;
|
|
32
44
|
}
|
|
33
45
|
fault = null;
|
|
34
46
|
constructor(root, opts = {}) {
|
|
35
|
-
this.dir = join(root, "checkpoints");
|
|
36
|
-
this.tmpDir = join(root, "tmp");
|
|
37
|
-
this.ledgerPath = join(this.dir, "checkpoints.jsonl");
|
|
38
|
-
this.snapshotPath = join(this.dir, "checkpoints.snapshot.jsonl");
|
|
39
47
|
this.fsyncEnabled = opts.fsync !== false;
|
|
40
48
|
this.compactEvery = opts.compactEvery ?? 1000;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
this.shared = live;
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
this.shared = {
|
|
52
|
-
cps: new Map(),
|
|
53
|
-
locks: new Map(),
|
|
54
|
-
ledgerEvents: 0,
|
|
55
|
-
log: undefined,
|
|
56
|
-
refs: 1,
|
|
57
|
-
};
|
|
58
|
-
sharedCheckpointDirs.set(key, this.shared);
|
|
59
|
-
this.replay();
|
|
60
|
-
this.log = new AppendLog(this.ledgerPath);
|
|
61
|
-
}
|
|
62
|
-
replay() {
|
|
63
|
-
for (const snap of readJsonlRecords(this.snapshotPath)) {
|
|
64
|
-
this.cps.set(snap.token, snap);
|
|
65
|
-
}
|
|
66
|
-
const events = readJsonlRecords(this.ledgerPath);
|
|
67
|
-
this.ledgerEvents = events.length;
|
|
68
|
-
for (const ev of events)
|
|
69
|
-
this.applyEvent(ev);
|
|
70
|
-
}
|
|
71
|
-
applyEvent(ev) {
|
|
72
|
-
if (ev.t === "put") {
|
|
73
|
-
this.cps.set(ev.token, ev.cp);
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
const cp = this.cps.get(ev.token);
|
|
77
|
-
if (!cp)
|
|
78
|
-
return;
|
|
79
|
-
switch (ev.t) {
|
|
80
|
-
case "resolve":
|
|
81
|
-
cp.status = "resolved";
|
|
82
|
-
cp.rev = ev.rev;
|
|
83
|
-
if (ev.resolvedOutcome !== undefined)
|
|
84
|
-
cp.resolvedOutcome = ev.resolvedOutcome;
|
|
85
|
-
cp.reopenReason = undefined;
|
|
86
|
-
break;
|
|
87
|
-
case "reopen":
|
|
88
|
-
cp.status = "pending";
|
|
89
|
-
cp.rev = ev.rev;
|
|
90
|
-
cp.reopenReason = ev.reason;
|
|
91
|
-
break;
|
|
92
|
-
case "expire":
|
|
93
|
-
cp.status = "expired";
|
|
94
|
-
break;
|
|
95
|
-
case "steer":
|
|
96
|
-
cp.state.pendingSteer = ev.steer;
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
49
|
+
const dir = join(root, "checkpoints");
|
|
50
|
+
this.ledger = checkpointLedgers.acquire({
|
|
51
|
+
dir,
|
|
52
|
+
tmpDir: join(root, "tmp"),
|
|
53
|
+
ledgerPath: join(dir, "checkpoints.jsonl"),
|
|
54
|
+
snapshotPath: join(dir, "checkpoints.snapshot.jsonl"),
|
|
55
|
+
});
|
|
99
56
|
}
|
|
100
57
|
withLock(token, fn) {
|
|
101
|
-
|
|
102
|
-
const run = prev.then(() => fn(), () => fn());
|
|
103
|
-
const tail = run.then(() => { }, () => { });
|
|
104
|
-
this.locks.set(token, tail);
|
|
105
|
-
void tail.then(() => {
|
|
106
|
-
if (this.locks.get(token) === tail)
|
|
107
|
-
this.locks.delete(token);
|
|
108
|
-
});
|
|
109
|
-
return run;
|
|
58
|
+
return this.ledger.withLock(token, fn);
|
|
110
59
|
}
|
|
111
60
|
commit(ev, fsync) {
|
|
112
|
-
this.
|
|
113
|
-
this.applyEvent(ev);
|
|
114
|
-
this.ledgerEvents++;
|
|
115
|
-
if (this.ledgerEvents >= this.compactEvery) {
|
|
116
|
-
try {
|
|
117
|
-
this.compact();
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
}
|
|
121
|
-
}
|
|
61
|
+
this.ledger.commit(ev, fsync, this.compactEvery);
|
|
122
62
|
}
|
|
123
63
|
async put(token, cp) {
|
|
124
64
|
return this.withLock(token, () => {
|
|
@@ -222,15 +162,8 @@ export class FileCheckpointStore {
|
|
|
222
162
|
out.add(cp.scope);
|
|
223
163
|
return [...out];
|
|
224
164
|
}
|
|
225
|
-
compact() {
|
|
226
|
-
const lines = [...this.cps.values()].map((cp) => JSON.stringify(cp)).join("\n");
|
|
227
|
-
atomicWriteFile(this.tmpDir, this.snapshotPath, lines.length ? `${lines}\n` : "");
|
|
228
|
-
this.log.closeForSwap();
|
|
229
|
-
atomicWriteFile(this.tmpDir, this.ledgerPath, "");
|
|
230
|
-
this.ledgerEvents = 0;
|
|
231
|
-
}
|
|
232
165
|
compactNow() {
|
|
233
|
-
this.compact();
|
|
166
|
+
this.ledger.compact();
|
|
234
167
|
}
|
|
235
168
|
testInjectFault(mode) {
|
|
236
169
|
this.fault = mode;
|
|
@@ -242,11 +175,6 @@ export class FileCheckpointStore {
|
|
|
242
175
|
if (this.closed)
|
|
243
176
|
return;
|
|
244
177
|
this.closed = true;
|
|
245
|
-
this.
|
|
246
|
-
if (this.shared.refs > 0)
|
|
247
|
-
return;
|
|
248
|
-
this.log.close();
|
|
249
|
-
if (sharedCheckpointDirs.get(this.sharedKey) === this.shared)
|
|
250
|
-
sharedCheckpointDirs.delete(this.sharedKey);
|
|
178
|
+
this.ledger.release();
|
|
251
179
|
}
|
|
252
180
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExecutionEnv } from "../../internal/harness.js";
|
|
1
|
+
import type { ExecutionEnv } from "../../internal/harness-types.js";
|
|
2
2
|
import { type FileSnapshotBounds, type FileSnapshotResult, type FileSnapshotStore } from "../../core/file-snapshot-store.js";
|
|
3
3
|
export declare class FileFileSnapshotStore implements FileSnapshotStore {
|
|
4
4
|
private readonly base;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface LedgerPaths {
|
|
2
|
+
readonly dir: string;
|
|
3
|
+
readonly tmpDir: string;
|
|
4
|
+
readonly ledgerPath: string;
|
|
5
|
+
readonly snapshotPath: string;
|
|
6
|
+
}
|
|
7
|
+
export interface LedgerModel<Row, Ev> {
|
|
8
|
+
keyOf(row: Row): string;
|
|
9
|
+
apply(rows: Map<string, Row>, ev: Ev): void;
|
|
10
|
+
}
|
|
11
|
+
export declare class LedgerCore<Row, Ev> {
|
|
12
|
+
private readonly key;
|
|
13
|
+
private readonly paths;
|
|
14
|
+
private readonly model;
|
|
15
|
+
private readonly table;
|
|
16
|
+
readonly rows: Map<string, Row>;
|
|
17
|
+
refs: number;
|
|
18
|
+
private readonly locks;
|
|
19
|
+
private events;
|
|
20
|
+
private handle;
|
|
21
|
+
constructor(key: string, paths: LedgerPaths, model: LedgerModel<Row, Ev>, table: Map<string, LedgerCore<Row, Ev>>);
|
|
22
|
+
bootstrap(): void;
|
|
23
|
+
withLock<T>(key: string, fn: () => Promise<T> | T): Promise<T>;
|
|
24
|
+
commit(ev: Ev, fsync: boolean, compactEvery: number): void;
|
|
25
|
+
compact(): void;
|
|
26
|
+
release(): void;
|
|
27
|
+
get pendingEvents(): number;
|
|
28
|
+
private get log();
|
|
29
|
+
}
|
|
30
|
+
export declare class SharedLedgerTable<Row, Ev> {
|
|
31
|
+
private readonly model;
|
|
32
|
+
private readonly live;
|
|
33
|
+
constructor(model: LedgerModel<Row, Ev>);
|
|
34
|
+
acquire(paths: LedgerPaths): LedgerCore<Row, Ev>;
|
|
35
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { AppendLog, atomicWriteFile, canonicalStoreKey, ensureDir, readJsonlRecords } from "./fs-atomic.js";
|
|
2
|
+
export class LedgerCore {
|
|
3
|
+
key;
|
|
4
|
+
paths;
|
|
5
|
+
model;
|
|
6
|
+
table;
|
|
7
|
+
rows = new Map();
|
|
8
|
+
refs = 1;
|
|
9
|
+
locks = new Map();
|
|
10
|
+
events = 0;
|
|
11
|
+
handle;
|
|
12
|
+
constructor(key, paths, model, table) {
|
|
13
|
+
this.key = key;
|
|
14
|
+
this.paths = paths;
|
|
15
|
+
this.model = model;
|
|
16
|
+
this.table = table;
|
|
17
|
+
}
|
|
18
|
+
bootstrap() {
|
|
19
|
+
for (const row of readJsonlRecords(this.paths.snapshotPath))
|
|
20
|
+
this.rows.set(this.model.keyOf(row), row);
|
|
21
|
+
const events = readJsonlRecords(this.paths.ledgerPath);
|
|
22
|
+
this.events = events.length;
|
|
23
|
+
for (const ev of events)
|
|
24
|
+
this.model.apply(this.rows, ev);
|
|
25
|
+
this.handle = new AppendLog(this.paths.ledgerPath);
|
|
26
|
+
}
|
|
27
|
+
withLock(key, fn) {
|
|
28
|
+
const prev = this.locks.get(key) ?? Promise.resolve();
|
|
29
|
+
const run = prev.then(() => fn(), () => fn());
|
|
30
|
+
const tail = run.then(() => { }, () => { });
|
|
31
|
+
this.locks.set(key, tail);
|
|
32
|
+
void tail.then(() => {
|
|
33
|
+
if (this.locks.get(key) === tail)
|
|
34
|
+
this.locks.delete(key);
|
|
35
|
+
});
|
|
36
|
+
return run;
|
|
37
|
+
}
|
|
38
|
+
commit(ev, fsync, compactEvery) {
|
|
39
|
+
this.log.append(ev, fsync);
|
|
40
|
+
this.model.apply(this.rows, ev);
|
|
41
|
+
this.events++;
|
|
42
|
+
if (this.events >= compactEvery) {
|
|
43
|
+
try {
|
|
44
|
+
this.compact();
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
compact() {
|
|
51
|
+
const lines = [...this.rows.values()].map((r) => JSON.stringify(r)).join("\n");
|
|
52
|
+
atomicWriteFile(this.paths.tmpDir, this.paths.snapshotPath, lines.length ? `${lines}\n` : "");
|
|
53
|
+
this.log.closeForSwap();
|
|
54
|
+
atomicWriteFile(this.paths.tmpDir, this.paths.ledgerPath, "");
|
|
55
|
+
this.events = 0;
|
|
56
|
+
}
|
|
57
|
+
release() {
|
|
58
|
+
this.refs -= 1;
|
|
59
|
+
if (this.refs > 0)
|
|
60
|
+
return;
|
|
61
|
+
if (this.table.get(this.key) === this)
|
|
62
|
+
this.table.delete(this.key);
|
|
63
|
+
this.handle?.close();
|
|
64
|
+
}
|
|
65
|
+
get pendingEvents() {
|
|
66
|
+
return this.events;
|
|
67
|
+
}
|
|
68
|
+
get log() {
|
|
69
|
+
if (this.handle === undefined)
|
|
70
|
+
throw new Error("file ledger: used before bootstrap");
|
|
71
|
+
return this.handle;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export class SharedLedgerTable {
|
|
75
|
+
model;
|
|
76
|
+
live = new Map();
|
|
77
|
+
constructor(model) {
|
|
78
|
+
this.model = model;
|
|
79
|
+
}
|
|
80
|
+
acquire(paths) {
|
|
81
|
+
ensureDir(paths.dir);
|
|
82
|
+
ensureDir(paths.tmpDir);
|
|
83
|
+
const key = canonicalStoreKey(paths.dir);
|
|
84
|
+
const existing = this.live.get(key);
|
|
85
|
+
if (existing !== undefined) {
|
|
86
|
+
existing.refs += 1;
|
|
87
|
+
return existing;
|
|
88
|
+
}
|
|
89
|
+
const core = new LedgerCore(key, paths, this.model, this.live);
|
|
90
|
+
this.live.set(key, core);
|
|
91
|
+
core.bootstrap();
|
|
92
|
+
return core;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -5,24 +5,12 @@ export interface FileWorkflowRunStoreOptions {
|
|
|
5
5
|
compactEvery?: number;
|
|
6
6
|
}
|
|
7
7
|
export declare class FileWorkflowRunStore implements WorkflowRunStore {
|
|
8
|
-
private readonly dir;
|
|
9
|
-
private readonly tmpDir;
|
|
10
|
-
private readonly ledgerPath;
|
|
11
|
-
private readonly snapshotPath;
|
|
12
8
|
private readonly fsyncEnabled;
|
|
13
9
|
private readonly compactEvery;
|
|
14
|
-
private readonly
|
|
10
|
+
private readonly ledger;
|
|
15
11
|
private closed;
|
|
16
|
-
private readonly canonicalDir;
|
|
17
12
|
private get runs();
|
|
18
|
-
private get locks();
|
|
19
|
-
private get ledgerEvents();
|
|
20
|
-
private set ledgerEvents(value);
|
|
21
|
-
private get log();
|
|
22
|
-
private set log(value);
|
|
23
13
|
constructor(root: string, opts?: FileWorkflowRunStoreOptions);
|
|
24
|
-
private replay;
|
|
25
|
-
private applyEvent;
|
|
26
14
|
private withLock;
|
|
27
15
|
private commit;
|
|
28
16
|
put(id: string, run: WorkflowRun): Promise<void>;
|
|
@@ -39,7 +27,6 @@ export declare class FileWorkflowRunStore implements WorkflowRunStore {
|
|
|
39
27
|
maxAgeMs?: number;
|
|
40
28
|
keep?: number;
|
|
41
29
|
}): Promise<number>;
|
|
42
|
-
private compact;
|
|
43
30
|
compactNow(): void;
|
|
44
31
|
get size(): number;
|
|
45
32
|
close(): void;
|