@apexops/kernel 0.10.0-next.4 → 0.11.0-next.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.
Files changed (66) hide show
  1. package/README.md +28 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/directory-lock.d.ts +118 -0
  4. package/dist/directory-lock.d.ts.map +1 -0
  5. package/dist/directory-lock.js +587 -0
  6. package/dist/directory-lock.js.map +1 -0
  7. package/dist/files.d.ts +63 -0
  8. package/dist/files.d.ts.map +1 -1
  9. package/dist/files.js +158 -2
  10. package/dist/files.js.map +1 -1
  11. package/dist/index.d.ts +3 -0
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +3 -0
  14. package/dist/index.js.map +1 -1
  15. package/dist/object-store.js +2 -2
  16. package/dist/object-store.js.map +1 -1
  17. package/dist/project-store.d.ts +2 -1
  18. package/dist/project-store.d.ts.map +1 -1
  19. package/dist/project-store.js +1 -0
  20. package/dist/project-store.js.map +1 -1
  21. package/dist/repeat-files.d.ts +66 -0
  22. package/dist/repeat-files.d.ts.map +1 -0
  23. package/dist/repeat-files.js +214 -0
  24. package/dist/repeat-files.js.map +1 -0
  25. package/dist/repeat-guard.d.ts +197 -0
  26. package/dist/repeat-guard.d.ts.map +1 -0
  27. package/dist/repeat-guard.js +462 -0
  28. package/dist/repeat-guard.js.map +1 -0
  29. package/dist/review-capture.d.ts +170 -0
  30. package/dist/review-capture.d.ts.map +1 -0
  31. package/dist/review-capture.js +570 -0
  32. package/dist/review-capture.js.map +1 -0
  33. package/dist/run-repository.d.ts +52 -2
  34. package/dist/run-repository.d.ts.map +1 -1
  35. package/dist/run-repository.js +198 -52
  36. package/dist/run-repository.js.map +1 -1
  37. package/dist/tasks.d.ts.map +1 -1
  38. package/dist/tasks.js +24 -2
  39. package/dist/tasks.js.map +1 -1
  40. package/dist/test/files.test.d.ts +2 -0
  41. package/dist/test/files.test.d.ts.map +1 -0
  42. package/dist/test/files.test.js +140 -0
  43. package/dist/test/files.test.js.map +1 -0
  44. package/dist/test/policies.test.js +1 -1
  45. package/dist/test/policies.test.js.map +1 -1
  46. package/dist/test/repeat-guard.test.d.ts +2 -0
  47. package/dist/test/repeat-guard.test.d.ts.map +1 -0
  48. package/dist/test/repeat-guard.test.js +1106 -0
  49. package/dist/test/repeat-guard.test.js.map +1 -0
  50. package/dist/test/review-capture.test.d.ts +2 -0
  51. package/dist/test/review-capture.test.d.ts.map +1 -0
  52. package/dist/test/review-capture.test.js +291 -0
  53. package/dist/test/review-capture.test.js.map +1 -0
  54. package/dist/test/run-repository.test.js +451 -7
  55. package/dist/test/run-repository.test.js.map +1 -1
  56. package/dist/test/workflow.test.js +32 -1
  57. package/dist/test/workflow.test.js.map +1 -1
  58. package/dist/test/writer-transfer.test.js +6 -1
  59. package/dist/test/writer-transfer.test.js.map +1 -1
  60. package/dist/validator-registry.d.ts.map +1 -1
  61. package/dist/validator-registry.js +10 -1
  62. package/dist/validator-registry.js.map +1 -1
  63. package/dist/workflow-validator-ownership.d.ts.map +1 -1
  64. package/dist/workflow-validator-ownership.js +2 -1
  65. package/dist/workflow-validator-ownership.js.map +1 -1
  66. package/package.json +3 -3
@@ -0,0 +1,214 @@
1
+ import { createHash } from "node:crypto";
2
+ import { constants } from "node:fs";
3
+ import { lstat, open, readdir } from "node:fs/promises";
4
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
5
+ import { sha256Json } from "./canonical.js";
6
+ import { retrySharingViolations } from "./files.js";
7
+ /** Most entries a repeat file snapshot covers; a larger tree fails the snapshot, which disables replay and storage. */
8
+ export const MAX_REPEAT_FILE_ENTRIES = 20_000;
9
+ /** Most file bytes a repeat file snapshot hashes; a larger tree fails the snapshot, which disables replay and storage. */
10
+ export const MAX_REPEAT_FILE_BYTES = 64 * 1024 * 1024;
11
+ const HASH_CHUNK_BYTES = 64 * 1024;
12
+ const HASH_ATTEMPTS = 3;
13
+ /** A bound tree is too large or kept changing while it was read, so it cannot be bound by content. */
14
+ export class RepeatFileSnapshotError extends Error {
15
+ constructor(message) {
16
+ super(message);
17
+ this.name = "RepeatFileSnapshotError";
18
+ }
19
+ }
20
+ function within(root, path) {
21
+ const offset = relative(root, path);
22
+ return offset === "" || (offset !== ".." && !offset.startsWith(`..${sep}`) && !isAbsolute(offset));
23
+ }
24
+ function statKey(metadata) {
25
+ return [metadata.dev, metadata.ino, metadata.mode, metadata.size, metadata.mtimeNs, metadata.ctimeNs].join(":");
26
+ }
27
+ function missing(error) {
28
+ const code = error.code;
29
+ return code === "ENOENT" || code === "ENOTDIR";
30
+ }
31
+ /**
32
+ * Reads the trees under `roots` and hashes every regular file with streaming SHA-256. Roots nested in another root are
33
+ * read once. Symbolic links below a root are bound by their own stat and never followed; a root that is a symbolic
34
+ * link fails the snapshot. A file that keeps changing while it is hashed, or a tree over the entry or byte budget,
35
+ * fails the snapshot with {@link RepeatFileSnapshotError}. Windows sharing violations are retried.
36
+ */
37
+ export async function snapshotRepeatFiles(roots, options = {}) {
38
+ const maxEntries = options.maxEntries ?? MAX_REPEAT_FILE_ENTRIES;
39
+ const maxBytes = options.maxBytes ?? MAX_REPEAT_FILE_BYTES;
40
+ const retry = {
41
+ ...(options.platform === undefined ? {} : { platform: options.platform }),
42
+ ...(options.attempts === undefined ? {} : { attempts: options.attempts }),
43
+ ...(options.sleep === undefined ? {} : { sleep: options.sleep }),
44
+ };
45
+ const unique = [...new Set(roots.map((root) => resolve(root)))].sort();
46
+ const entries = {};
47
+ let count = 0;
48
+ let bytes = 0;
49
+ const add = (path, entry) => {
50
+ if (++count > maxEntries)
51
+ throw new RepeatFileSnapshotError("Repeat-bound files exceed their entry budget");
52
+ entries[path] = entry;
53
+ };
54
+ const hash = async (path) => {
55
+ for (let attempt = 1;; attempt += 1) {
56
+ let handle;
57
+ try {
58
+ handle = await retrySharingViolations(() => open(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0)), retry);
59
+ }
60
+ catch (error) {
61
+ if (missing(error))
62
+ return undefined;
63
+ throw error;
64
+ }
65
+ try {
66
+ const before = await handle.stat({ bigint: true });
67
+ if (!before.isFile())
68
+ throw new RepeatFileSnapshotError("A repeat-bound file changed type while it was read");
69
+ const digest = createHash("sha256");
70
+ const buffer = Buffer.allocUnsafe(HASH_CHUNK_BYTES);
71
+ let read = 0;
72
+ for (;;) {
73
+ const { bytesRead } = await retrySharingViolations(() => handle.read(buffer, 0, buffer.byteLength, null), retry);
74
+ if (bytesRead === 0)
75
+ break;
76
+ read += bytesRead;
77
+ if (bytes + read > maxBytes)
78
+ throw new RepeatFileSnapshotError("Repeat-bound files exceed their byte budget");
79
+ digest.update(buffer.subarray(0, bytesRead));
80
+ }
81
+ const after = await handle.stat({ bigint: true });
82
+ if (statKey(before) === statKey(after) && BigInt(read) === after.size) {
83
+ bytes += read;
84
+ return { type: "file", sha256: digest.digest("hex"), stat: statKey(after) };
85
+ }
86
+ }
87
+ finally {
88
+ await handle.close();
89
+ }
90
+ if (attempt >= HASH_ATTEMPTS)
91
+ throw new RepeatFileSnapshotError("A repeat-bound file kept changing while it was read");
92
+ }
93
+ };
94
+ const visit = async (path, root) => {
95
+ let metadata;
96
+ try {
97
+ metadata = await retrySharingViolations(() => lstat(path, { bigint: true }), retry);
98
+ }
99
+ catch (error) {
100
+ if (missing(error))
101
+ return;
102
+ throw error;
103
+ }
104
+ if (metadata.isDirectory()) {
105
+ add(path, { type: "directory" });
106
+ let names;
107
+ try {
108
+ names = await retrySharingViolations(() => readdir(path), retry);
109
+ }
110
+ catch (error) {
111
+ if (missing(error))
112
+ return;
113
+ throw error;
114
+ }
115
+ for (const name of names.sort())
116
+ await visit(join(path, name), false);
117
+ return;
118
+ }
119
+ if (root && metadata.isSymbolicLink())
120
+ throw new RepeatFileSnapshotError("A repeat-bound root is a symbolic link");
121
+ if (!metadata.isFile()) {
122
+ add(path, { type: "other", stat: statKey(metadata) });
123
+ return;
124
+ }
125
+ const entry = await hash(path);
126
+ if (entry !== undefined)
127
+ add(path, entry);
128
+ };
129
+ for (const root of unique) {
130
+ if (!unique.some((other) => other !== root && within(other, root)))
131
+ continue;
132
+ // A nested root is read through its enclosing root, so check it here for the symbolic-link rule.
133
+ try {
134
+ const metadata = await retrySharingViolations(() => lstat(root, { bigint: true }), retry);
135
+ if (metadata.isSymbolicLink())
136
+ throw new RepeatFileSnapshotError("A repeat-bound root is a symbolic link");
137
+ }
138
+ catch (error) {
139
+ if (!missing(error))
140
+ throw error;
141
+ }
142
+ }
143
+ for (const root of unique) {
144
+ if (unique.some((other) => other !== root && within(other, root)))
145
+ continue;
146
+ await visit(root, true);
147
+ }
148
+ return { roots: unique, entries };
149
+ }
150
+ /** Digest of a snapshot for the repeat state token; equal only for equal roots, paths, contents and stat identities. */
151
+ export function repeatFilesDigest(snapshot) {
152
+ return sha256Json({ version: 1, roots: snapshot.roots, entries: snapshot.entries });
153
+ }
154
+ /**
155
+ * Writes a repeat-guarded operation made itself to bound files, in order. The guard compares the bound trees after the
156
+ * operation with their content at its start changed only by these writes.
157
+ */
158
+ export class RepeatFileWrites {
159
+ #writes = [];
160
+ /** The operation wrote `path` with content of SHA-256 `sha256`, creating missing parent directories. */
161
+ file(path, sha256) {
162
+ if (!/^[0-9a-f]{64}$/u.test(sha256))
163
+ throw new TypeError("Repeat file write needs a SHA-256 content hash");
164
+ this.#writes.push({ path: resolve(path), sha256 });
165
+ }
166
+ /** The operation removed `path` and everything below it. */
167
+ removed(path) {
168
+ this.#writes.push({ path: resolve(path), sha256: null });
169
+ }
170
+ get writes() {
171
+ return this.#writes;
172
+ }
173
+ }
174
+ /**
175
+ * True when every tree bound at `start` holds, in `end`, exactly its start content changed only by the operation's own
176
+ * `writes`: a file the operation wrote holds what it wrote, and every other path is unchanged in content and stat
177
+ * identity. A bound root missing from `end` fails the check, so `end` must be read over at least the start roots.
178
+ */
179
+ export function repeatFilesHeld(start, end, writes) {
180
+ if (!start.roots.every((root) => end.roots.includes(root)))
181
+ return false;
182
+ const rootOf = (path) => start.roots.filter((root) => within(root, path)).sort((left, right) => right.length - left.length)[0];
183
+ const expected = new Map(Object.entries(start.entries).filter(([path]) => rootOf(path) !== undefined));
184
+ for (const { path, sha256 } of writes?.writes ?? []) {
185
+ if (sha256 === null) {
186
+ for (const key of [...expected.keys()])
187
+ if (within(path, key))
188
+ expected.delete(key);
189
+ continue;
190
+ }
191
+ const root = rootOf(path);
192
+ if (root === undefined)
193
+ continue;
194
+ expected.set(path, { type: "written", sha256 });
195
+ for (let parent = dirname(path); within(root, parent); parent = dirname(parent)) {
196
+ if (!expected.has(parent))
197
+ expected.set(parent, { type: "directory" });
198
+ if (parent === root)
199
+ break;
200
+ }
201
+ }
202
+ const actual = Object.entries(end.entries).filter(([path]) => rootOf(path) !== undefined);
203
+ if (actual.length !== expected.size)
204
+ return false;
205
+ return actual.every(([path, entry]) => {
206
+ const wanted = expected.get(path);
207
+ if (wanted === undefined)
208
+ return false;
209
+ if (wanted.type === "written")
210
+ return entry.type === "file" && entry.sha256 === wanted.sha256;
211
+ return sha256Json(wanted) === sha256Json(entry);
212
+ });
213
+ }
214
+ //# sourceMappingURL=repeat-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repeat-files.js","sourceRoot":"","sources":["../src/repeat-files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,197 @@
1
+ import { type EventV1, type ProjectId, type RepeatGuardRecordV1, type RunId } from "@apexops/contracts";
2
+ import { type RepeatFileSnapshot, type RepeatFileWrites } from "./repeat-files.js";
3
+ /** Run-scoped file holding the bounded set of calls that may still be answered from their original result. */
4
+ export declare const REPEAT_GUARD_FILE = ".repeat-guard.json";
5
+ /** Run-scoped, hash-chained audit journal of answered repeats. Separate from the run journal, whose head it keeps. */
6
+ export declare const REPEAT_JOURNAL_DIRECTORY = "repeats";
7
+ export declare const REPEAT_EVENT_TYPE = "call.repeated";
8
+ /**
9
+ * Workspace-scoped directory lock serializing guarded calls across processes from record lookup through record
10
+ * publication. It does not depend on the selected run, so calls that change the selection are serialized too.
11
+ */
12
+ export declare const REPEAT_LOCK_FILE = ".repeat-guard.lock";
13
+ export declare const DEFAULT_REPEAT_LOCK_WAIT_MS = 30000;
14
+ export declare const DEFAULT_REPEAT_WINDOW_MS: number;
15
+ export declare const MAX_REPEAT_RECORDS = 16;
16
+ export declare const MAX_REPEAT_RESULT_BYTES: number;
17
+ export interface RepeatCall {
18
+ /** Stable operation name, for example the MCP tool name. */
19
+ operation: string;
20
+ /** Worktree that issued the call; compared in canonical form. */
21
+ workspace: string;
22
+ /** JSON arguments after input validation; undefined members are dropped and keys are sorted. */
23
+ arguments: unknown;
24
+ }
25
+ export interface RepeatStateInput {
26
+ selection: {
27
+ projectId: string;
28
+ runId: string;
29
+ };
30
+ projects: readonly string[];
31
+ runHash: string;
32
+ journalHead: string | null;
33
+ writer: string | null;
34
+ /**
35
+ * Content digest of the mutable files the run's operations read, such as staged work and generated source trees, from
36
+ * {@link repeatFilesDigest}.
37
+ */
38
+ files?: string | null;
39
+ }
40
+ /** The selected run a call applies to, with the state token that decides the repeat window. */
41
+ export interface RepeatScope {
42
+ runDirectory: string;
43
+ projectId: ProjectId;
44
+ runId: RunId;
45
+ ownerEpoch: number;
46
+ state: string;
47
+ /**
48
+ * Content of the mutable files the state token covers, read with it. The snapshot taken before a call executes binds
49
+ * its record: the result is stored only if those trees still hold that content afterwards, changed only by the
50
+ * call's own {@link RepeatSafeHooks.writes}.
51
+ */
52
+ files?: RepeatFileSnapshot;
53
+ }
54
+ export interface RepeatSafeHooks<T> {
55
+ /**
56
+ * Machine-local workspace directory that holds the repeat lock, created when missing. While its parent does not exist
57
+ * there is no workspace state yet, and calls run unserialized.
58
+ */
59
+ lockDirectory: string;
60
+ /** Current selected run and state token, or undefined when no run is selected. */
61
+ scope(): Promise<RepeatScope | undefined>;
62
+ /** Ownership check for an answered repeat, such as the run writer lease. Must not write state. */
63
+ assertReplayAllowed(scope: RepeatScope): Promise<void>;
64
+ execute(): Promise<T>;
65
+ /** Optional instant after which the original result must not be returned, such as a task expiry. */
66
+ validUntil?(value: T): string | undefined;
67
+ /**
68
+ * Optional check that the call identity still holds, for example that a file bound by content was not modified since
69
+ * the identity was taken. Before a replay, false makes the call execute instead; after execution, false returns the
70
+ * result without storing it.
71
+ */
72
+ identityStable?(): Promise<boolean>;
73
+ /**
74
+ * Writes the operation makes to files the scope binds, filled while `execute` runs. Any other change to a bound file
75
+ * between the scope read before execution and the one after it returns the result without storing it.
76
+ */
77
+ writes?: RepeatFileWrites;
78
+ }
79
+ export interface RepeatSafeOptions {
80
+ clock?: () => Date;
81
+ idSource?: () => string;
82
+ windowMs?: number;
83
+ maxRecords?: number;
84
+ /** Longest wait for another guarded call in the workspace before failing with {@link RepeatGuardBusyError}. */
85
+ lockWaitMs?: number;
86
+ /** Cancels the call while it waits for the lock, and before lookup or execution once the lock is held. */
87
+ signal?: AbortSignal;
88
+ }
89
+ /** Another guarded call in the workspace is still running; the caller should refresh state and retry later. */
90
+ /** The caller cancelled the call before it started; nothing was looked up, executed or recorded. */
91
+ export declare class RepeatGuardCancelledError extends Error {
92
+ constructor();
93
+ }
94
+ export declare class RepeatGuardBusyError extends Error {
95
+ constructor();
96
+ }
97
+ /**
98
+ * The workspace changed while its lock holder waited without the lock, so a decision taken during the wait would apply
99
+ * to state it was not taken against. Nothing after the wait ran; the caller should refresh state and run again.
100
+ */
101
+ export declare class RepeatGuardStaleError extends Error {
102
+ constructor();
103
+ }
104
+ /**
105
+ * A snapshot read reached a step that needs the workspace lock, such as finishing a pending recovery. Nothing was
106
+ * written; {@link withRepeatGuardReadLock} discards the attempt and waits for the lock instead.
107
+ */
108
+ export declare class RepeatGuardSnapshotUnavailableError extends Error {
109
+ constructor();
110
+ }
111
+ /**
112
+ * A holder's share window could not be closed, so snapshot readers might still trust it. Every later atomic write and
113
+ * share in the holder's operation fails with this error, and so does the operation itself, even if it handled the
114
+ * error; releasing the lock then closes the window. The caller should run the operation again.
115
+ */
116
+ export declare class RepeatGuardShareCloseError extends Error {
117
+ constructor(cause: unknown);
118
+ }
119
+ /** How {@link withRepeatGuardReadLock} runs a read: holding the workspace lock, or as a validated snapshot without it. */
120
+ export type RepeatGuardReadMode = "locked" | "snapshot";
121
+ /** The workspace lock as held by one {@link withRepeatGuardLock} operation. */
122
+ export interface WorkspaceLockHold {
123
+ /**
124
+ * Gives the workspace lock up while `wait` runs, for example while a command waits for interactive input, then takes
125
+ * it again and throws {@link RepeatGuardStaleError} unless `state` returns the token it returned before the wait.
126
+ * When `state` fails before the wait the lock is kept, because an unchanged state could not be proven afterwards. A
127
+ * workspace created during the wait is locked from then on. Not reentrant: one suspension at a time.
128
+ */
129
+ suspend<R>(wait: () => Promise<R>, state: () => Promise<string>): Promise<R>;
130
+ /**
131
+ * Keeps the workspace lock while `operation` runs, for example a long provider, Git or Azure CLI process, but lets
132
+ * {@link withRepeatGuardReadLock} readers in other processes read the workspace meanwhile. The holder must not write
133
+ * workspace state until `operation` settles. Writers still wait. While this hold has no lock to share, because the
134
+ * workspace has no state yet or the hold is suspended, it runs `operation` without a share window.
135
+ * When the window cannot be closed afterwards it throws {@link RepeatGuardShareCloseError}, which also fences every
136
+ * later write of the holding operation.
137
+ */
138
+ share<R>(operation: () => Promise<R>): Promise<R>;
139
+ }
140
+ export interface RepeatSafeOutcome<T> {
141
+ value: T;
142
+ repeated: boolean;
143
+ fingerprint: string;
144
+ }
145
+ /** A stored result; the persisted file is the versioned `repeat-guard-records-v1` contract. */
146
+ export type RepeatRecord = RepeatGuardRecordV1;
147
+ /** Identifies a call independent of state: operation, canonical worktree and canonical arguments. */
148
+ export declare function repeatCallHash(call: RepeatCall): string;
149
+ /** Fingerprint of a call against the run state it applies to. */
150
+ export declare function repeatFingerprint(callHash: string, state: string | null): string;
151
+ /**
152
+ * State token for the repeat window. It changes whenever the selection, the project set, the selected run document,
153
+ * its journal head, its writer lease holder or the content of its mutable files changes; lease expiry renewals do not
154
+ * change it.
155
+ */
156
+ export declare function repeatStateToken(input: RepeatStateInput): string;
157
+ /**
158
+ * Reads the run's repeat records. An absent, unsafe or malformed file yields no records: losing a record only makes a
159
+ * repeat a new request that normal kernel checks decide, so corruption cannot replay a forged result.
160
+ */
161
+ export declare function readRepeatRecords(runDirectory: string): Promise<RepeatRecord[]>;
162
+ export declare function readRepeatEvents(runDirectory: string): Promise<EventV1[]>;
163
+ /**
164
+ * Executes a state-changing call at most once per run state. An identical call (same operation, worktree and
165
+ * arguments) made while the selected run is unchanged since the original succeeded, and within the window, returns the
166
+ * original serialized result and appends a `call.repeated` audit event instead of executing again. Any intervening
167
+ * state change, an expired window, a failed original call or a missing record makes the call a new request. A result
168
+ * is stored only if the files the scope binds kept their content from the start of the call, changed only by the
169
+ * call's own writes, so a later repeat is never answered from a result computed on other content. Guarded
170
+ * calls in one workspace are serialized across processes from record lookup through record publication, including
171
+ * calls that create or change the selected run.
172
+ */
173
+ export declare function executeRepeatSafe<T extends object>(call: RepeatCall, hooks: RepeatSafeHooks<T>, options?: RepeatSafeOptions): Promise<RepeatSafeOutcome<T>>;
174
+ /**
175
+ * Runs `operation` while holding the workspace repeat lock in `lockDirectory`. Guarded calls take it, and so must every
176
+ * other writer of the same workspace (such as state-changing CLI commands), so that no write lands between a guarded
177
+ * call's state reads, execution and record publication. While the parent of `lockDirectory` does not exist there is no
178
+ * workspace state yet, and `operation` runs unserialized. The lock is not reentrant. `operation` may give the lock up
179
+ * while it waits, for example for interactive input, through {@link WorkspaceLockHold.suspend}; guarded calls never do.
180
+ */
181
+ export declare function withRepeatGuardLock<T>(lockDirectory: string, operation: (hold: WorkspaceLockHold) => Promise<T>, options?: {
182
+ lockWaitMs?: number;
183
+ signal?: AbortSignal;
184
+ }): Promise<T>;
185
+ /**
186
+ * Runs a read that may need the workspace lock, for example to finish a pending recovery. It takes the lock like
187
+ * {@link withRepeatGuardLock} when the lock is free. While another process holds the lock but shares it through
188
+ * {@link WorkspaceLockHold.share}, `operation` runs in `snapshot` mode without the lock instead, and its outcome counts
189
+ * only if the holder kept the same share window open throughout, so the holder wrote nothing meanwhile. In `snapshot`
190
+ * mode `operation` must not write workspace state; it throws {@link RepeatGuardSnapshotUnavailableError} instead, and
191
+ * the read waits for the lock. Fails with {@link RepeatGuardBusyError} after `lockWaitMs` without either.
192
+ */
193
+ export declare function withRepeatGuardReadLock<T>(lockDirectory: string, operation: (mode: RepeatGuardReadMode) => Promise<T>, options?: {
194
+ lockWaitMs?: number;
195
+ signal?: AbortSignal;
196
+ }): Promise<T>;
197
+ //# sourceMappingURL=repeat-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repeat-guard.d.ts","sourceRoot":"","sources":["../src/repeat-guard.ts"],"names":[],"mappings":"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"}