stitchkit 0.103.8 → 0.103.9
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/CHANGELOG.md +13 -0
- package/dist/agent-runtime/sandbox-process.d.ts +2 -2
- package/dist/agent-runtime/sandbox-process.d.ts.map +1 -1
- package/dist/durability/contract.d.ts +3 -2
- package/dist/durability/contract.d.ts.map +1 -1
- package/dist/durability/effect.d.ts.map +1 -1
- package/dist/durability/engine.d.ts.map +1 -1
- package/dist/durability/json.d.ts.map +1 -1
- package/dist/entrypoints/agent-runtime/harness.js +2 -2
- package/dist/entrypoints/agent-runtime/sandbox.js +29 -81
- package/dist/entrypoints/agent-runtime.js +2 -2
- package/dist/entrypoints/cli.js +1 -1
- package/dist/entrypoints/files.d.ts +2 -0
- package/dist/entrypoints/files.d.ts.map +1 -1
- package/dist/entrypoints/files.js +6 -2
- package/dist/entrypoints/primitives.js +77 -57
- package/dist/entrypoints/process.d.ts +4 -0
- package/dist/entrypoints/process.d.ts.map +1 -0
- package/dist/entrypoints/process.js +13 -0
- package/dist/entrypoints/tools.js +12 -3
- package/dist/files/boundary.d.ts +19 -1
- package/dist/files/boundary.d.ts.map +1 -1
- package/dist/files/file-io.d.ts +5 -1
- package/dist/files/file-io.d.ts.map +1 -1
- package/dist/files/read.d.ts +13 -0
- package/dist/files/read.d.ts.map +1 -0
- package/dist/index-12m2v2a9.js +98 -0
- package/dist/index-8a9ksm91.js +95 -0
- package/dist/{index-90ppdx3q.js → index-9jb61j5a.js} +1 -1
- package/dist/{index-5vyd0fkb.js → index-ary5s7k0.js} +40 -61
- package/dist/index-c1jmtxa0.js +74 -0
- package/dist/index-eeqmg7qm.js +202 -0
- package/dist/index-mbnnw3f2.js +25 -0
- package/dist/{index-v4a2fjtk.js → index-sybedc8b.js} +124 -55
- package/dist/internal/abort-race.d.ts +3 -0
- package/dist/internal/abort-race.d.ts.map +1 -0
- package/dist/internal/atomic-file.d.ts +20 -2
- package/dist/internal/atomic-file.d.ts.map +1 -1
- package/dist/internal/atomic-publication.d.ts +19 -0
- package/dist/internal/atomic-publication.d.ts.map +1 -0
- package/dist/internal/json-data.d.ts +8 -0
- package/dist/internal/json-data.d.ts.map +1 -0
- package/dist/primitives/canonical-json.d.ts +11 -0
- package/dist/primitives/canonical-json.d.ts.map +1 -0
- package/dist/primitives/index.d.ts +1 -0
- package/dist/primitives/index.d.ts.map +1 -1
- package/dist/process/command-owner.d.ts +13 -0
- package/dist/process/command-owner.d.ts.map +1 -0
- package/dist/process/command.d.ts +3 -0
- package/dist/process/command.d.ts.map +1 -0
- package/dist/process/contract.d.ts +35 -0
- package/dist/process/contract.d.ts.map +1 -0
- package/dist/process/group.d.ts +6 -0
- package/dist/process/group.d.ts.map +1 -0
- package/dist/tools/transfer/managed-file-error.d.ts.map +1 -1
- package/llms/files.txt +8 -0
- package/llms/primitives.txt +31 -1
- package/llms/process.txt +167 -0
- package/llms/stitchkit.txt +1 -0
- package/llms/tools.txt +18 -0
- package/llms-full.txt +216 -1
- package/llms.txt +6 -4
- package/package.json +5 -1
- package/dist/index-aan6hqb9.js +0 -65
package/dist/files/boundary.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ManagedFileRef } from '../contract/file-ref.js';
|
|
2
|
-
export type ManagedFileErrorCode = 'FILE_INVALID_PATH' | 'FILE_NOT_FOUND' | 'FILE_NOT_REGULAR' | 'FILE_OUTSIDE_ROOT' | 'FILE_TOO_LARGE' | 'FILE_EXISTS' | 'FILE_INSPECTION_REJECTED' | 'FILE_IO_ERROR';
|
|
2
|
+
export type ManagedFileErrorCode = 'FILE_INVALID_PATH' | 'FILE_NOT_FOUND' | 'FILE_NOT_REGULAR' | 'FILE_OUTSIDE_ROOT' | 'FILE_TOO_LARGE' | 'FILE_EXISTS' | 'FILE_INSPECTION_REJECTED' | 'FILE_UNSAFE_LINK' | 'FILE_CHANGED' | 'FILE_UNSUPPORTED' | 'FILE_IO_ERROR';
|
|
3
3
|
export declare class ManagedFileError extends Error {
|
|
4
4
|
readonly code: ManagedFileErrorCode;
|
|
5
5
|
constructor(code: ManagedFileErrorCode, message: string, options?: ErrorOptions);
|
|
@@ -27,6 +27,14 @@ export interface ManagedFileBoundaryConfig {
|
|
|
27
27
|
onCleanupError?: (error: unknown) => void;
|
|
28
28
|
}
|
|
29
29
|
export interface ManagedFileReadOptions {
|
|
30
|
+
/** Include descriptor metadata in the result; default false keeps transport sources unchanged. */
|
|
31
|
+
observe?: boolean;
|
|
32
|
+
/** Refuse a symlink at the requested leaf. Ancestors remain trusted. */
|
|
33
|
+
rejectSymlinks?: boolean;
|
|
34
|
+
/** Require descriptor nlink===1; transient publication links are also refused. */
|
|
35
|
+
singleLink?: boolean;
|
|
36
|
+
/** Compare descriptor metadata around byte read; not a hostile-writer snapshot. */
|
|
37
|
+
stable?: boolean;
|
|
30
38
|
maxBytes?: number;
|
|
31
39
|
signal?: AbortSignal;
|
|
32
40
|
}
|
|
@@ -38,9 +46,19 @@ export interface ManagedFileWriteOptions {
|
|
|
38
46
|
name?: string;
|
|
39
47
|
signal?: AbortSignal;
|
|
40
48
|
}
|
|
49
|
+
export interface FileObservation {
|
|
50
|
+
dev: number;
|
|
51
|
+
ino: number;
|
|
52
|
+
size: number;
|
|
53
|
+
nlink: number;
|
|
54
|
+
mtimeMs: number;
|
|
55
|
+
ctimeMs: number;
|
|
56
|
+
}
|
|
41
57
|
export interface ManagedFileSource {
|
|
42
58
|
ref: ManagedFileRef;
|
|
43
59
|
bytes: Uint8Array;
|
|
60
|
+
/** Included by createManagedFileBoundary when observe is true; custom sources may omit it. */
|
|
61
|
+
observation?: FileObservation;
|
|
44
62
|
}
|
|
45
63
|
export interface ManagedFileBoundary {
|
|
46
64
|
read(path: string, options?: ManagedFileReadOptions): Promise<ManagedFileSource>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../../src/files/boundary.ts"],"names":[],"mappings":"AAIA,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../../src/files/boundary.ts"],"names":[],"mappings":"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"}
|
package/dist/files/file-io.d.ts
CHANGED
|
@@ -6,7 +6,11 @@ export declare function inspectedRef(path: string, size: number, inspection: Man
|
|
|
6
6
|
mediaType?: string;
|
|
7
7
|
name?: string;
|
|
8
8
|
}): ManagedFileRef;
|
|
9
|
-
export declare function readHandle(handle:
|
|
9
|
+
export declare function readHandle(handle: {
|
|
10
|
+
read(buffer: Uint8Array, offset: number, length: number): Promise<{
|
|
11
|
+
bytesRead: number;
|
|
12
|
+
}>;
|
|
13
|
+
}, maxBytes: number, signal?: AbortSignal): Promise<Uint8Array>;
|
|
10
14
|
export declare function writeSource(handle: Awaited<ReturnType<typeof open>>, source: Uint8Array | ReadableStream<Uint8Array>, maxBytes: number, inspectionBytes: number, signal?: AbortSignal): Promise<{
|
|
11
15
|
size: number;
|
|
12
16
|
prefix: Uint8Array;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-io.d.ts","sourceRoot":"","sources":["../../src/files/file-io.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,KAAK,cAAc,EAAwB,MAAM,sBAAsB,CAAC;AACjF,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,0BAA0B,EAC/B,KAAK,oBAAoB,EAC1B,MAAM,YAAY,CAAC;AA+BpB,wBAAsB,WAAW,CAC/B,SAAS,EAAE,oBAAoB,GAAG,SAAS,EAC3C,KAAK,EAAE,IAAI,CAAC,0BAA0B,EAAE,QAAQ,CAAC,EACjD,SAAS,EAAE,MAAM,EACjB,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,qBAAqB,CAAC,CAmBhC;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,qBAAqB,EACjC,QAAQ,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,GACnD,cAAc,CAiBhB;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"file-io.d.ts","sourceRoot":"","sources":["../../src/files/file-io.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,KAAK,cAAc,EAAwB,MAAM,sBAAsB,CAAC;AACjF,OAAO,EAEL,KAAK,qBAAqB,EAC1B,KAAK,0BAA0B,EAC/B,KAAK,oBAAoB,EAC1B,MAAM,YAAY,CAAC;AA+BpB,wBAAsB,WAAW,CAC/B,SAAS,EAAE,oBAAoB,GAAG,SAAS,EAC3C,KAAK,EAAE,IAAI,CAAC,0BAA0B,EAAE,QAAQ,CAAC,EACjD,SAAS,EAAE,MAAM,EACjB,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,qBAAqB,CAAC,CAmBhC;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,qBAAqB,EACjC,QAAQ,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,GACnD,cAAc,CAiBhB;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE;IACN,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC1F,EACD,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC,CAkCrB;AAcD,wBAAsB,WAAW,CAC/B,MAAM,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,EACxC,MAAM,EAAE,UAAU,GAAG,cAAc,CAAC,UAAU,CAAC,EAC/C,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,EACvB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CAAC,CA2C/C"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type Stats } from 'node:fs';
|
|
2
|
+
import { type FileHandle } from 'node:fs/promises';
|
|
3
|
+
import type { ManagedFileInspector } from './boundary.js';
|
|
4
|
+
import { type ManagedFileReadOptions, type ManagedFileSource } from './boundary.js';
|
|
5
|
+
/** The descriptor, rather than a preceding path stat, decides kind/link/stability. */
|
|
6
|
+
export declare function readManagedDescriptor(target: string, path: string, maxBytes: number, options: ManagedFileReadOptions, inspection: {
|
|
7
|
+
inspect?: ManagedFileInspector;
|
|
8
|
+
bytes: number;
|
|
9
|
+
timeoutMs: number;
|
|
10
|
+
}, openDescriptor?: (path: string, flags: number) => Promise<Pick<FileHandle, 'read' | 'close'> & {
|
|
11
|
+
stat(): Promise<Stats>;
|
|
12
|
+
}>): Promise<ManagedFileSource>;
|
|
13
|
+
//# sourceMappingURL=read.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/files/read.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,KAAK,UAAU,EAAQ,MAAM,kBAAkB,CAAC;AAEzD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAEL,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACvB,MAAM,YAAY,CAAC;AAGpB,sFAAsF;AACtF,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,sBAAsB,EAC/B,UAAU,EAAE;IAAE,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EAChF,cAAc,GAAE,CACd,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,KACV,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG;IAAE,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,CAAA;CAAE,CAAQ,GACnF,OAAO,CAAC,iBAAiB,CAAC,CAyE5B"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {
|
|
2
|
+
publishAtomicFile,
|
|
3
|
+
publishAtomicFileSync
|
|
4
|
+
} from "./index-8a9ksm91.js";
|
|
5
|
+
|
|
6
|
+
// src/internal/atomic-file.ts
|
|
7
|
+
import { randomBytes } from "node:crypto";
|
|
8
|
+
import { closeSync, fchmodSync, fsyncSync, openSync, unlinkSync, writeSync } from "node:fs";
|
|
9
|
+
import { open, unlink } from "node:fs/promises";
|
|
10
|
+
import { basename, dirname, join } from "node:path";
|
|
11
|
+
var DEFAULT_MODE = 384;
|
|
12
|
+
function stagingPath(target) {
|
|
13
|
+
return join(dirname(target), `.${basename(target)}.${randomBytes(12).toString("hex")}.tmp`);
|
|
14
|
+
}
|
|
15
|
+
function bytesOf(data) {
|
|
16
|
+
return typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
17
|
+
}
|
|
18
|
+
async function writeFileAtomic2(target, data, options = {}) {
|
|
19
|
+
return writeAtomicFileData(target, data, options);
|
|
20
|
+
}
|
|
21
|
+
async function writeAtomicFileData(target, data, options, io = { open, unlink, publish: publishAtomicFile }) {
|
|
22
|
+
const mode = options.mode ?? DEFAULT_MODE;
|
|
23
|
+
const staged = stagingPath(target);
|
|
24
|
+
const handle = await io.open(staged, "wx", mode);
|
|
25
|
+
try {
|
|
26
|
+
let failed = false;
|
|
27
|
+
let failure;
|
|
28
|
+
try {
|
|
29
|
+
await handle.writeFile(bytesOf(data));
|
|
30
|
+
await handle.chmod(mode);
|
|
31
|
+
if (options.durability !== "none")
|
|
32
|
+
await handle.sync();
|
|
33
|
+
} catch (error) {
|
|
34
|
+
failed = true;
|
|
35
|
+
failure = error;
|
|
36
|
+
} finally {
|
|
37
|
+
try {
|
|
38
|
+
await handle.close();
|
|
39
|
+
} catch (error) {
|
|
40
|
+
if (!failed) {
|
|
41
|
+
failed = true;
|
|
42
|
+
failure = error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (failed)
|
|
47
|
+
throw failure;
|
|
48
|
+
await io.publish(staged, target, options.replace ?? true, options.durability === "directory");
|
|
49
|
+
} catch (error) {
|
|
50
|
+
await io.unlink(staged).catch(() => {
|
|
51
|
+
return;
|
|
52
|
+
});
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function writeFileAtomicSync2(target, data, options = {}) {
|
|
57
|
+
const mode = options.mode ?? DEFAULT_MODE;
|
|
58
|
+
const bytes = bytesOf(data);
|
|
59
|
+
const staged = stagingPath(target);
|
|
60
|
+
const handle = openSync(staged, "wx", mode);
|
|
61
|
+
try {
|
|
62
|
+
let failed = false;
|
|
63
|
+
let failure;
|
|
64
|
+
try {
|
|
65
|
+
for (let offset = 0;offset < bytes.byteLength; ) {
|
|
66
|
+
const written = writeSync(handle, bytes, offset, bytes.byteLength - offset);
|
|
67
|
+
if (written === 0)
|
|
68
|
+
throw new Error("zero-byte atomic-file write");
|
|
69
|
+
offset += written;
|
|
70
|
+
}
|
|
71
|
+
fchmodSync(handle, mode);
|
|
72
|
+
if (options.durability !== "none")
|
|
73
|
+
fsyncSync(handle);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
failed = true;
|
|
76
|
+
failure = error;
|
|
77
|
+
} finally {
|
|
78
|
+
try {
|
|
79
|
+
closeSync(handle);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
if (!failed) {
|
|
82
|
+
failed = true;
|
|
83
|
+
failure = error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (failed)
|
|
88
|
+
throw failure;
|
|
89
|
+
publishAtomicFileSync(staged, target, options.replace ?? true, options.durability === "directory");
|
|
90
|
+
} catch (error) {
|
|
91
|
+
try {
|
|
92
|
+
unlinkSync(staged);
|
|
93
|
+
} catch {}
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export { writeFileAtomic2, writeFileAtomicSync2 };
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// src/internal/atomic-publication.ts
|
|
2
|
+
import { closeSync, fsyncSync, linkSync, openSync, renameSync, unlinkSync } from "node:fs";
|
|
3
|
+
import { link, open, rename, unlink } from "node:fs/promises";
|
|
4
|
+
import { dirname } from "node:path";
|
|
5
|
+
|
|
6
|
+
class AtomicFilePublicationError2 extends Error {
|
|
7
|
+
phase;
|
|
8
|
+
published = true;
|
|
9
|
+
constructor(phase, cause) {
|
|
10
|
+
super(`File published but ${phase} failed`, { cause });
|
|
11
|
+
this.phase = phase;
|
|
12
|
+
this.name = "AtomicFilePublicationError";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
var native = { rename, link, unlink, open };
|
|
16
|
+
async function publishAtomicFile(staged, target, replace, directorySync, io = native) {
|
|
17
|
+
if (directorySync && process.platform === "win32")
|
|
18
|
+
throw new Error("Directory durability is unsupported on Windows");
|
|
19
|
+
const directory = directorySync ? await io.open(dirname(target), "r") : undefined;
|
|
20
|
+
let failed = false;
|
|
21
|
+
let failure;
|
|
22
|
+
try {
|
|
23
|
+
if (replace)
|
|
24
|
+
await io.rename(staged, target);
|
|
25
|
+
else {
|
|
26
|
+
await io.link(staged, target);
|
|
27
|
+
try {
|
|
28
|
+
await io.unlink(staged);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
throw new AtomicFilePublicationError2("cleanup", error);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
await directory?.sync();
|
|
35
|
+
} catch (error) {
|
|
36
|
+
throw new AtomicFilePublicationError2("directory-sync", error);
|
|
37
|
+
}
|
|
38
|
+
} catch (error) {
|
|
39
|
+
failed = true;
|
|
40
|
+
failure = error;
|
|
41
|
+
} finally {
|
|
42
|
+
try {
|
|
43
|
+
await directory?.close();
|
|
44
|
+
} catch (error) {
|
|
45
|
+
if (!failed) {
|
|
46
|
+
failed = true;
|
|
47
|
+
failure = new AtomicFilePublicationError2("directory-close", error);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (failed)
|
|
52
|
+
throw failure;
|
|
53
|
+
}
|
|
54
|
+
function publishAtomicFileSync(staged, target, replace, directorySync) {
|
|
55
|
+
if (directorySync && process.platform === "win32")
|
|
56
|
+
throw new Error("Directory durability is unsupported on Windows");
|
|
57
|
+
const directory = directorySync ? openSync(dirname(target), "r") : undefined;
|
|
58
|
+
let failed = false;
|
|
59
|
+
let failure;
|
|
60
|
+
try {
|
|
61
|
+
if (replace)
|
|
62
|
+
renameSync(staged, target);
|
|
63
|
+
else {
|
|
64
|
+
linkSync(staged, target);
|
|
65
|
+
try {
|
|
66
|
+
unlinkSync(staged);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
throw new AtomicFilePublicationError2("cleanup", error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
if (directory !== undefined)
|
|
73
|
+
fsyncSync(directory);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
throw new AtomicFilePublicationError2("directory-sync", error);
|
|
76
|
+
}
|
|
77
|
+
} catch (error) {
|
|
78
|
+
failed = true;
|
|
79
|
+
failure = error;
|
|
80
|
+
} finally {
|
|
81
|
+
try {
|
|
82
|
+
if (directory !== undefined)
|
|
83
|
+
closeSync(directory);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
if (!failed) {
|
|
86
|
+
failed = true;
|
|
87
|
+
failure = new AtomicFilePublicationError2("directory-close", error);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (failed)
|
|
92
|
+
throw failure;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export { AtomicFilePublicationError2, publishAtomicFile, publishAtomicFileSync };
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isJsonData
|
|
3
|
+
} from "./index-c1jmtxa0.js";
|
|
4
|
+
import {
|
|
5
|
+
raceAbort
|
|
6
|
+
} from "./index-mbnnw3f2.js";
|
|
7
|
+
|
|
1
8
|
// src/durability/contract.ts
|
|
2
9
|
import { z } from "zod";
|
|
3
10
|
var DURABILITY_STEP_EVENT_KIND2 = "durability/step";
|
|
@@ -93,44 +100,12 @@ class ParkAbortedError2 extends Error {
|
|
|
93
100
|
|
|
94
101
|
// src/durability/json.ts
|
|
95
102
|
import { z as z2 } from "zod";
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
return Number.isFinite(value) && !Object.is(value, -0);
|
|
103
|
-
if (typeof value !== "object" || ancestors.has(value))
|
|
104
|
-
return false;
|
|
105
|
-
const array = Array.isArray(value);
|
|
106
|
-
const prototype = Object.getPrototypeOf(value);
|
|
107
|
-
if (array ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null)
|
|
108
|
-
return false;
|
|
109
|
-
ancestors.add(value);
|
|
110
|
-
try {
|
|
111
|
-
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
112
|
-
const keys = Reflect.ownKeys(descriptors);
|
|
113
|
-
if (array && keys.length !== value.length + 1)
|
|
114
|
-
return false;
|
|
115
|
-
for (const key of keys) {
|
|
116
|
-
if (array && key === "length")
|
|
117
|
-
continue;
|
|
118
|
-
if (typeof key !== "string")
|
|
119
|
-
return false;
|
|
120
|
-
if (array && (!/^(0|[1-9][0-9]*)$/.test(key) || Number(key) >= value.length))
|
|
121
|
-
return false;
|
|
122
|
-
const descriptor = descriptors[key];
|
|
123
|
-
if (!descriptor?.enumerable || !("value" in descriptor))
|
|
124
|
-
return false;
|
|
125
|
-
if (!losslessJson(descriptor.value, ancestors, depth + 1))
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
return true;
|
|
129
|
-
} finally {
|
|
130
|
-
ancestors.delete(value);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
var DurableJsonSchema = z2.custom((value) => losslessJson(value), "Expected lossless JSON data (finite numbers, plain objects and dense arrays; no accessors)");
|
|
103
|
+
var DurableJsonSchema = z2.custom((value) => isJsonData(value, {
|
|
104
|
+
maxDepth: 100,
|
|
105
|
+
maxNodes: Number.MAX_SAFE_INTEGER,
|
|
106
|
+
maxBytes: Number.MAX_SAFE_INTEGER,
|
|
107
|
+
omitObjectUndefined: false
|
|
108
|
+
}), "Expected lossless JSON data (finite numbers, plain objects and dense arrays; no accessors)");
|
|
134
109
|
|
|
135
110
|
// src/durability/ledger.ts
|
|
136
111
|
function stepKey(conversationId, runId, stepName) {
|
|
@@ -292,30 +267,25 @@ async function append(context, payload, name) {
|
|
|
292
267
|
payload: { runId: context.runId, effectName: name, ...payload }
|
|
293
268
|
});
|
|
294
269
|
}
|
|
295
|
-
async function reconcile(context, name, handlers, timeoutMs) {
|
|
270
|
+
async function reconcile(context, name, handlers, timeoutMs, outerSignal) {
|
|
271
|
+
const caller = [context.signal, outerSignal].filter((signal) => signal !== undefined);
|
|
296
272
|
const controller = new AbortController;
|
|
297
|
-
|
|
298
|
-
const
|
|
299
|
-
timer = setTimeout(() => {
|
|
300
|
-
controller.abort(new Error(`reconcile did not finish within ${timeoutMs} ms`));
|
|
301
|
-
resolve("timeout");
|
|
302
|
-
}, timeoutMs);
|
|
303
|
-
});
|
|
273
|
+
const timeout = setTimeout(() => controller.abort(new Error(`reconcile did not finish within ${timeoutMs} ms`)), timeoutMs);
|
|
274
|
+
const signal = AbortSignal.any([...caller, controller.signal]);
|
|
304
275
|
let found;
|
|
305
276
|
try {
|
|
306
|
-
found = await Promise.
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
277
|
+
found = await raceAbort(Promise.resolve().then(() => {
|
|
278
|
+
signal.throwIfAborted();
|
|
279
|
+
return handlers.reconcile(signal);
|
|
280
|
+
}), signal);
|
|
281
|
+
signal.throwIfAborted();
|
|
310
282
|
} catch (cause) {
|
|
311
|
-
|
|
283
|
+
if (caller.some((signal) => signal.aborted))
|
|
284
|
+
throw new StepAbortedError2(name);
|
|
285
|
+
throw new EffectUnresolvedError2(name, controller.signal.aborted ? "reconcile-timeout" : "reconcile-failed", { cause });
|
|
312
286
|
} finally {
|
|
313
|
-
clearTimeout(
|
|
314
|
-
|
|
315
|
-
if (found === "timeout") {
|
|
316
|
-
throw new EffectUnresolvedError2(name, "reconcile-timeout", {
|
|
317
|
-
cause: controller.signal.reason
|
|
318
|
-
});
|
|
287
|
+
clearTimeout(timeout);
|
|
288
|
+
controller.abort(new Error("Reconciliation completed"));
|
|
319
289
|
}
|
|
320
290
|
if (found === null) {
|
|
321
291
|
await append(context, { phase: "uncertain" }, name);
|
|
@@ -327,8 +297,8 @@ async function reconcile(context, name, handlers, timeoutMs) {
|
|
|
327
297
|
}
|
|
328
298
|
async function executeEffect(context, name, handlers, options) {
|
|
329
299
|
const timeoutMs = options?.reconcileTimeoutMs ?? DEFAULT_RECONCILE_TIMEOUT_MS;
|
|
330
|
-
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
331
|
-
throw new TypeError(`reconcileTimeoutMs must be a positive finite number, got ${timeoutMs}`);
|
|
300
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2147483647) {
|
|
301
|
+
throw new TypeError(`reconcileTimeoutMs must be a positive finite number at most 2147483647, got ${timeoutMs}`);
|
|
332
302
|
}
|
|
333
303
|
const key = effectKey(context.conversationId, context.runId, name);
|
|
334
304
|
const recorded = (await context.readLedger()).effects.get(key);
|
|
@@ -340,7 +310,7 @@ async function executeEffect(context, name, handlers, options) {
|
|
|
340
310
|
if (options?.signal?.aborted || context.signal?.aborted)
|
|
341
311
|
throw new StepAbortedError2(name);
|
|
342
312
|
if (recorded)
|
|
343
|
-
return reconcile(context, name, handlers, timeoutMs);
|
|
313
|
+
return reconcile(context, name, handlers, timeoutMs, options?.signal);
|
|
344
314
|
await append(context, { phase: "intent" }, name);
|
|
345
315
|
let proof;
|
|
346
316
|
try {
|
|
@@ -581,7 +551,16 @@ function createLocalStepDurability2(options) {
|
|
|
581
551
|
const key = effectKey(conversationId, runId, name);
|
|
582
552
|
const existing = inFlightEffects.get(key);
|
|
583
553
|
if (existing) {
|
|
584
|
-
|
|
554
|
+
const signals = [options.signal, runOptions?.signal].filter((signal) => signal !== undefined);
|
|
555
|
+
const result = detachedResult(existing);
|
|
556
|
+
if (signals.length === 0)
|
|
557
|
+
return result;
|
|
558
|
+
const signal = AbortSignal.any(signals);
|
|
559
|
+
return raceAbort(result, signal).catch((error) => {
|
|
560
|
+
if (signal.aborted)
|
|
561
|
+
throw new StepAbortedError2(name);
|
|
562
|
+
throw error;
|
|
563
|
+
});
|
|
585
564
|
}
|
|
586
565
|
const pending = executeEffect({ store, conversationId, runId, signal: options.signal, readLedger }, name, handlers, runOptions);
|
|
587
566
|
inFlightEffects.set(key, pending);
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// src/internal/json-data.ts
|
|
2
|
+
function isJsonData(value, limits) {
|
|
3
|
+
const ancestors = new Set;
|
|
4
|
+
let nodes = 0;
|
|
5
|
+
let bytes = 0;
|
|
6
|
+
const charge = (amount) => {
|
|
7
|
+
bytes += amount;
|
|
8
|
+
return bytes <= limits.maxBytes;
|
|
9
|
+
};
|
|
10
|
+
const textSize = (text) => {
|
|
11
|
+
if (text.length > limits.maxBytes)
|
|
12
|
+
return limits.maxBytes + 1;
|
|
13
|
+
return new TextEncoder().encode(JSON.stringify(text)).byteLength;
|
|
14
|
+
};
|
|
15
|
+
const visit = (item, depth) => {
|
|
16
|
+
if (++nodes > limits.maxNodes || depth > limits.maxDepth)
|
|
17
|
+
return false;
|
|
18
|
+
if (item === null)
|
|
19
|
+
return charge(4);
|
|
20
|
+
if (typeof item === "string")
|
|
21
|
+
return charge(textSize(item));
|
|
22
|
+
if (typeof item === "boolean")
|
|
23
|
+
return charge(item ? 4 : 5);
|
|
24
|
+
if (typeof item === "number")
|
|
25
|
+
return Number.isFinite(item) && !Object.is(item, -0) && charge(String(item).length);
|
|
26
|
+
if (typeof item !== "object" || ancestors.has(item))
|
|
27
|
+
return false;
|
|
28
|
+
const array = Array.isArray(item);
|
|
29
|
+
const prototype = Object.getPrototypeOf(item);
|
|
30
|
+
if (array ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null)
|
|
31
|
+
return false;
|
|
32
|
+
if (array && item.length > limits.maxNodes - nodes)
|
|
33
|
+
return false;
|
|
34
|
+
const keys = Reflect.ownKeys(item);
|
|
35
|
+
if (keys.length > limits.maxNodes - nodes + (array ? 1 : 0))
|
|
36
|
+
return false;
|
|
37
|
+
if (array && keys.length !== item.length + 1)
|
|
38
|
+
return false;
|
|
39
|
+
ancestors.add(item);
|
|
40
|
+
try {
|
|
41
|
+
if (!charge(2))
|
|
42
|
+
return false;
|
|
43
|
+
let members = 0;
|
|
44
|
+
for (const key of keys) {
|
|
45
|
+
if (array && key === "length")
|
|
46
|
+
continue;
|
|
47
|
+
if (typeof key !== "string")
|
|
48
|
+
return false;
|
|
49
|
+
if (array && (!/^(0|[1-9][0-9]*)$/.test(key) || Number(key) >= item.length))
|
|
50
|
+
return false;
|
|
51
|
+
const descriptor = Object.getOwnPropertyDescriptor(item, key);
|
|
52
|
+
if (!descriptor?.enumerable || !("value" in descriptor))
|
|
53
|
+
return false;
|
|
54
|
+
if (!array && limits.omitObjectUndefined && descriptor.value === undefined) {
|
|
55
|
+
if (++nodes > limits.maxNodes)
|
|
56
|
+
return false;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (members++ > 0 && !charge(1))
|
|
60
|
+
return false;
|
|
61
|
+
if (!array && !charge(textSize(key) + 1))
|
|
62
|
+
return false;
|
|
63
|
+
if (!visit(descriptor.value, depth + 1))
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
} finally {
|
|
68
|
+
ancestors.delete(item);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
return visit(value, 0);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export { isJsonData };
|