@sema-agent/core 1.451.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/observer.js +3 -0
- 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 +40 -813
- 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/ask-question.js +5 -5
- package/dist/core/background-agent-store.d.ts +2 -0
- package/dist/core/background-agent-store.js +5 -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 +19 -1
- package/dist/core/exec-output-tail.js +44 -5
- 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-session.d.ts +1 -0
- package/dist/core/lsp-session.js +24 -6
- package/dist/core/lsp.d.ts +11 -1
- package/dist/core/lsp.js +63 -6
- package/dist/core/mailbox-store.d.ts +3 -2
- package/dist/core/mailbox-store.js +19 -4
- 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 +29 -208
- 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-reconcile.js +5 -2
- 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-notification.d.ts +1 -0
- 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 +54 -213
- package/dist/core/task-registry.js +190 -1766
- 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 +3 -3
- package/dist/core/tools.d.ts +7 -3
- package/dist/core/tools.js +3 -8
- package/dist/core/trace.d.ts +2 -2
- package/dist/core/types.d.ts +1 -29
- package/dist/core/workflow-journal-store.d.ts +2 -0
- package/dist/core/workflow-journal-store.js +14 -0
- 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 +14 -0
- package/dist/engine/execution-env/node-execution-env.js +184 -44
- 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 +4 -2
- package/dist/engine/lsp/node-lsp-manager.js +22 -5
- package/dist/engine/lsp/stdio-lsp-transport.d.ts +1 -1
- package/dist/engine/lsp/stdio-lsp-transport.js +19 -8
- package/dist/index.d.ts +18 -16
- package/dist/index.js +15 -13
- 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 +2 -1
- package/dist/orchestration/run-workflow-tool.js +29 -6
- package/dist/orchestration/workflow-types.d.ts +193 -0
- package/dist/orchestration/workflow-types.js +54 -0
- package/dist/orchestration/workflow.d.ts +3 -184
- package/dist/orchestration/workflow.js +87 -64
- 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/cc/mailbox-store.js +6 -1
- package/dist/stores/file/background-agent-store.d.ts +2 -15
- package/dist/stores/file/background-agent-store.js +33 -102
- 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/mailbox-store.d.ts +1 -1
- package/dist/stores/file/mailbox-store.js +9 -7
- 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/encoding.d.ts +5 -0
- package/dist/tools/fs/encoding.js +6 -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 -2026
- package/dist/tools/fs/notebook.d.ts +43 -0
- package/dist/tools/fs/notebook.js +141 -0
- 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 +3 -3
- package/dist/tools/fs/safety.d.ts +1 -1
- package/dist/tools/fs/search.d.ts +1 -1
- package/dist/tools/fs/search.js +141 -12
- package/dist/tools/gitea-issue.js +4 -2
- package/dist/tools/monitor.d.ts +1 -1
- package/dist/tools/monitor.js +12 -8
- package/dist/tools/scheduler-tools.d.ts +1 -1
- package/dist/tools/scheduler-tools.js +16 -16
- package/dist/tools/task-list.js +34 -12
- package/dist/tools/web.d.ts +4 -2
- package/dist/tools/web.js +106 -20
- package/dist/tools/worktree.d.ts +1 -1
- package/dist/tools/worktree.js +14 -14
- package/package.json +1 -1
- package/dist/core/dynamic-recall.d.ts +0 -32
- package/dist/core/dynamic-recall.js +0 -76
package/dist/prompts/default.js
CHANGED
|
@@ -351,7 +351,6 @@ export const TOOL_PARAM_JSON = `
|
|
|
351
351
|
## Tool-call arguments
|
|
352
352
|
An object or array parameter value must be a single JSON value — never write parameter-tag markup (XML-ish <param> tags) inside a JSON value. Pass the structured value directly as JSON.`;
|
|
353
353
|
export const CODE_SYSTEM_PROMPT = CODE_AGENT_PROMPT + AUTONOMY_SELF_AUDIT + ANTI_VERBOSITY + TOOL_PARAM_JSON;
|
|
354
|
-
export const FULL_BODY_SYSTEM_PROMPT = CODE_SYSTEM_PROMPT;
|
|
355
354
|
function joinParts(parts) {
|
|
356
355
|
return parts.filter((p) => Boolean(p && p.trim())).join("\n\n");
|
|
357
356
|
}
|
|
@@ -389,9 +388,6 @@ function defaultStableSystem(ctx) {
|
|
|
389
388
|
}
|
|
390
389
|
export const defaultPromptProvider = {
|
|
391
390
|
stableSystem: defaultStableSystem,
|
|
392
|
-
system(ctx) {
|
|
393
|
-
return composeSystemPrompt(defaultStableSystem(ctx), ctx.memoryBlock);
|
|
394
|
-
},
|
|
395
391
|
};
|
|
396
392
|
const PROBE_MEMORY_A = "<user_memory>\nAAAA durable fact alpha (2026-01-01 00:00 UTC)\n</user_memory>";
|
|
397
393
|
const PROBE_MEMORY_B = "<user_memory>\nBBBB durable fact bravo (2099-12-31 23:59 UTC)\n</user_memory>";
|
|
@@ -399,13 +395,9 @@ export function analyzePromptCacheFriendliness(provider, opts = {}) {
|
|
|
399
395
|
const tools = opts.tools ?? [];
|
|
400
396
|
const minRatio = opts.minStablePrefixRatio ?? 0.5;
|
|
401
397
|
const build = (memoryBlock) => {
|
|
402
|
-
if (provider.stableSystem)
|
|
403
|
-
|
|
404
|
-
}
|
|
405
|
-
if (provider.system) {
|
|
406
|
-
return provider.system({ userSystemPrompt: opts.userSystemPrompt, memoryBlock, tools, memoryEnabled: true });
|
|
407
|
-
}
|
|
408
|
-
throw new Error("PromptProvider implements neither stableSystem nor system");
|
|
398
|
+
if (!provider.stableSystem)
|
|
399
|
+
throw new Error("PromptProvider does not implement stableSystem");
|
|
400
|
+
return composeSystemPrompt(provider.stableSystem({ userSystemPrompt: opts.userSystemPrompt, tools, memoryEnabled: true }), memoryBlock);
|
|
409
401
|
};
|
|
410
402
|
const a = build(PROBE_MEMORY_A);
|
|
411
403
|
const b = build(PROBE_MEMORY_B);
|
package/dist/scenarios/env.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ThinkingLevel } from "../internal/harness.js";
|
|
1
|
+
import type { ThinkingLevel } from "../internal/harness-types.js";
|
|
2
2
|
import { type ScenarioId } from "./scenario-registry.js";
|
|
3
3
|
export type OrchestrationMode = "solo" | "teacher";
|
|
4
4
|
export declare const DEFAULT_REASONING_INTENSITY: ThinkingLevel;
|
|
@@ -10,8 +10,5 @@ export interface CodeToolsConfig {
|
|
|
10
10
|
taskList?: boolean;
|
|
11
11
|
taskListStore?: TaskListStore;
|
|
12
12
|
}
|
|
13
|
-
export type FullBodyToolsConfig = CodeToolsConfig;
|
|
14
13
|
export declare function assembleCodeTools(cfg?: CodeToolsConfig): ToolSpec[];
|
|
15
|
-
export declare const assembleFullBodyTools: typeof assembleCodeTools;
|
|
16
14
|
export declare const CODE_ROLE: RoleSpec;
|
|
17
|
-
export declare const FULL_BODY_ROLE: RoleSpec;
|
|
@@ -17,6 +17,4 @@ export function assembleCodeTools(cfg = {}) {
|
|
|
17
17
|
tools.push(...createTaskListTools(cfg.taskListStore));
|
|
18
18
|
return tools;
|
|
19
19
|
}
|
|
20
|
-
export const assembleFullBodyTools = assembleCodeTools;
|
|
21
20
|
export const CODE_ROLE = { systemPrompt: CODE_SYSTEM_PROMPT, thinking: "high" };
|
|
22
|
-
export const FULL_BODY_ROLE = CODE_ROLE;
|
|
@@ -89,9 +89,12 @@ export function createCcFileMailboxStore(opts) {
|
|
|
89
89
|
leases.set(key, { owner, expiresAt: t + ttlMs, maxSeq });
|
|
90
90
|
return Promise.resolve({ maxSeq, messages: pending });
|
|
91
91
|
},
|
|
92
|
-
ack: (scope, handle, upToSeq) => {
|
|
92
|
+
ack: (scope, handle, owner, upToSeq) => {
|
|
93
93
|
requireDefaultScope(scope);
|
|
94
94
|
const path = inboxPath(handle);
|
|
95
|
+
const held = leases.get(path);
|
|
96
|
+
if (held === undefined || held.owner !== owner)
|
|
97
|
+
return Promise.resolve();
|
|
95
98
|
if (!existsSync(path))
|
|
96
99
|
return Promise.resolve();
|
|
97
100
|
return withCcLock(path, () => {
|
|
@@ -105,6 +108,8 @@ export function createCcFileMailboxStore(opts) {
|
|
|
105
108
|
}
|
|
106
109
|
if (changed)
|
|
107
110
|
saveBox(path, box);
|
|
111
|
+
if (held.maxSeq <= upToSeq)
|
|
112
|
+
leases.delete(path);
|
|
108
113
|
});
|
|
109
114
|
},
|
|
110
115
|
releaseLease: (scope, handle, owner) => {
|
|
@@ -4,25 +4,13 @@ export interface FileBackgroundAgentStoreOptions {
|
|
|
4
4
|
compactEvery?: number;
|
|
5
5
|
}
|
|
6
6
|
export declare class FileBackgroundAgentStore implements BackgroundAgentStore {
|
|
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
|
|
9
|
+
private readonly ledger;
|
|
14
10
|
private closed;
|
|
15
11
|
private get rows();
|
|
16
|
-
private get locks();
|
|
17
|
-
private get ledgerEvents();
|
|
18
|
-
private set ledgerEvents(value);
|
|
19
|
-
private get log();
|
|
20
|
-
private set log(value);
|
|
21
12
|
constructor(root: string, opts?: FileBackgroundAgentStoreOptions);
|
|
22
|
-
|
|
23
|
-
private key;
|
|
24
|
-
private replay;
|
|
25
|
-
private applyEvent;
|
|
13
|
+
static key(handle: string, scope: string): string;
|
|
26
14
|
private withLock;
|
|
27
15
|
private commit;
|
|
28
16
|
put(record: BackgroundAgentRecord): Promise<void>;
|
|
@@ -53,7 +41,6 @@ export declare class FileBackgroundAgentStore implements BackgroundAgentStore {
|
|
|
53
41
|
keep?: number;
|
|
54
42
|
staleRunningMaxAgeMs?: number;
|
|
55
43
|
}): Promise<number>;
|
|
56
|
-
private compact;
|
|
57
44
|
compactNow(): void;
|
|
58
45
|
get size(): number;
|
|
59
46
|
close(): void;
|
|
@@ -1,105 +1,49 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
-
import { BackgroundAgentStoreError, } from "../../core/background-agent-store.js";
|
|
3
|
-
import {
|
|
4
|
-
const
|
|
2
|
+
import { BackgroundAgentStoreError, STALE_RUNNING_REAP_ATTRIBUTION, } from "../../core/background-agent-store.js";
|
|
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)
|
|
@@ -205,7 +149,8 @@ export class FileBackgroundAgentStore {
|
|
|
205
149
|
const next = structuredClone(live);
|
|
206
150
|
next.status = "failed";
|
|
207
151
|
next.stoppedBy = "system";
|
|
208
|
-
next.summary = next.summary ??
|
|
152
|
+
next.summary = next.summary ?? STALE_RUNNING_REAP_ATTRIBUTION;
|
|
153
|
+
next.error = next.error ?? STALE_RUNNING_REAP_ATTRIBUTION;
|
|
209
154
|
next.settledAt = now;
|
|
210
155
|
next.updatedAt = now;
|
|
211
156
|
next.rev = live.rev + 1;
|
|
@@ -247,15 +192,8 @@ export class FileBackgroundAgentStore {
|
|
|
247
192
|
}
|
|
248
193
|
return changed.size;
|
|
249
194
|
}
|
|
250
|
-
compact() {
|
|
251
|
-
const lines = [...this.rows.values()].map((r) => JSON.stringify(r)).join("\n");
|
|
252
|
-
atomicWriteFile(this.tmpDir, this.snapshotPath, lines.length ? `${lines}\n` : "");
|
|
253
|
-
this.log.closeForSwap();
|
|
254
|
-
atomicWriteFile(this.tmpDir, this.ledgerPath, "");
|
|
255
|
-
this.ledgerEvents = 0;
|
|
256
|
-
}
|
|
257
195
|
compactNow() {
|
|
258
|
-
this.compact();
|
|
196
|
+
this.ledger.compact();
|
|
259
197
|
}
|
|
260
198
|
get size() {
|
|
261
199
|
return this.rows.size;
|
|
@@ -264,14 +202,7 @@ export class FileBackgroundAgentStore {
|
|
|
264
202
|
if (this.closed)
|
|
265
203
|
return;
|
|
266
204
|
this.closed = true;
|
|
267
|
-
|
|
268
|
-
if (st !== undefined && st === this.shared) {
|
|
269
|
-
st.refs -= 1;
|
|
270
|
-
if (st.refs > 0)
|
|
271
|
-
return;
|
|
272
|
-
sharedAgentDirs.delete(this.canonicalDir);
|
|
273
|
-
}
|
|
274
|
-
this.log.close();
|
|
205
|
+
this.ledger.release();
|
|
275
206
|
}
|
|
276
207
|
}
|
|
277
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;
|
|
@@ -21,7 +21,7 @@ export declare class FileMailboxStore implements MailboxStore {
|
|
|
21
21
|
sentAt: number;
|
|
22
22
|
}): Promise<number>;
|
|
23
23
|
claimLease(scope: string, handle: string, owner: string, ttlMs: number, now?: number): Promise<MailboxLease | null>;
|
|
24
|
-
ack(scope: string, handle: string, upToSeq: number): Promise<void>;
|
|
24
|
+
ack(scope: string, handle: string, owner: string, upToSeq: number): Promise<void>;
|
|
25
25
|
releaseLease(scope: string, handle: string, owner: string): Promise<void>;
|
|
26
26
|
peekCount(scope: string, handle: string): Promise<number>;
|
|
27
27
|
drop(scope: string, handle: string): Promise<void>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { join, resolve, sep } from "node:path";
|
|
2
2
|
import { existsSync, realpathSync } from "node:fs";
|
|
3
|
-
import {} from "../../core/mailbox-store.js";
|
|
3
|
+
import { newestSentAt, } from "../../core/mailbox-store.js";
|
|
4
4
|
import { AppendLog, atomicWriteFile, canonicalStoreKey, ensureDir, readJsonlRecords, sanitizeScope, sanitizePathComponent } from "./fs-atomic.js";
|
|
5
5
|
function realpathSyncSafe(p) {
|
|
6
6
|
try {
|
|
@@ -86,7 +86,7 @@ export class FileMailboxStore {
|
|
|
86
86
|
lines.push(JSON.stringify({ t: "lease", ...b.lease }));
|
|
87
87
|
b.log.closeForSwap();
|
|
88
88
|
atomicWriteFile(this.tmpDir, path, lines.length ? `${lines.join("\n")}\n` : "");
|
|
89
|
-
b.events =
|
|
89
|
+
b.events = 0;
|
|
90
90
|
}
|
|
91
91
|
async append(scope, handle, msg) {
|
|
92
92
|
if (scope === undefined || scope === "")
|
|
@@ -111,9 +111,11 @@ export class FileMailboxStore {
|
|
|
111
111
|
return { messages: b.messages.map((m) => ({ ...m })), maxSeq };
|
|
112
112
|
});
|
|
113
113
|
}
|
|
114
|
-
async ack(scope, handle, upToSeq) {
|
|
114
|
+
async ack(scope, handle, owner, upToSeq) {
|
|
115
115
|
return withPathLock(this.lockKey(scope, handle), () => {
|
|
116
116
|
const b = this.load(scope, handle);
|
|
117
|
+
if (b.lease === undefined || b.lease.owner !== owner)
|
|
118
|
+
return;
|
|
117
119
|
this.commit(b, this.boxPath(scope, handle), { t: "ack", upToSeq });
|
|
118
120
|
});
|
|
119
121
|
}
|
|
@@ -160,15 +162,15 @@ export class FileMailboxStore {
|
|
|
160
162
|
for (const [key, b] of [...sharedBoxes]) {
|
|
161
163
|
if (!b.path.startsWith(prefix))
|
|
162
164
|
continue;
|
|
163
|
-
const seen = b.messages
|
|
164
|
-
if (seen === undefined || seen
|
|
165
|
+
const seen = newestSentAt(b.messages);
|
|
166
|
+
if (seen === undefined || seen >= now - maxAgeMs)
|
|
165
167
|
continue;
|
|
166
168
|
const swept = await withPathLock(key, () => {
|
|
167
169
|
const live = sharedBoxes.get(key);
|
|
168
170
|
if (live === undefined || live !== b)
|
|
169
171
|
return false;
|
|
170
|
-
const newest = b.messages
|
|
171
|
-
if (newest === undefined || newest
|
|
172
|
+
const newest = newestSentAt(b.messages);
|
|
173
|
+
if (newest === undefined || newest >= now - maxAgeMs)
|
|
172
174
|
return false;
|
|
173
175
|
b.messages = [];
|
|
174
176
|
delete b.lease;
|
|
@@ -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
|
+
}
|