poe-code 8.0.0 → 8.0.1

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.
@@ -1,30 +1,1072 @@
1
- export { parse } from "./parse.js";
2
- export { parseModule } from "./parse/parser.js";
3
- export { lint } from "./lint.js";
4
- export { run } from "./run.js";
5
- export { createReplayableRandom } from "./random.js";
6
- export { noopOtelSink } from "./observability/otel.js";
7
- export { FileSnapshotBackend } from "./snapshot/backend.js";
8
- export { declareHostOperation } from "./interp/host-bridge.js";
9
- export { HostCallResumabilityError } from "./interp/host-call.js";
10
- export { HostOperationResumePolicyError, registerPendingHostCallPolicy } from "./snapshot/policy.js";
11
- export { dump } from "./dump.js";
12
- export { restore } from "./restore.js";
13
- export { SnapshotValidationError } from "./snapshot/validation.js";
14
- export { Budget, SandboxError } from "./interp/budget.js";
15
- export { formatInterpreterError } from "./error/format.js";
16
- export { deepCopyFromSandbox, deepCopyToSandbox } from "./interp/values.js";
17
- export { runHarness, runHarnessPair } from "./runner/run-harness.js";
18
- export { extractBlock } from "./loader/extract-block.js";
19
- export { findExportedConstInitializer } from "./loader/find-exported.js";
20
- export { splitFrontmatter } from "./loader/frontmatter.js";
21
- export { AgentSpawnError, createSpawnUsageAccumulator, makeAgentModule, runWithSpawnUsageAccumulator } from "./modules/agent.js";
22
- export { makeEnvModule } from "./modules/env.js";
23
- export { makeFailModule } from "./modules/fail.js";
24
- export { makeFsModule } from "./modules/fs.js";
25
- export { makeGitModule } from "./modules/git.js";
26
- export { makeHarnessModule } from "./modules/harness.js";
27
- export { makeLogModule } from "./modules/log.js";
28
- export { makeMetricModule } from "./modules/metric.js";
29
- export { makeMcpModule } from "./modules/mcp.js";
30
- export { makeTimeModule } from "./modules/time.js";
1
+ import {
2
+ AgentSpawnError,
3
+ SPAWN_MODES,
4
+ containsPath,
5
+ countLineBreaks,
6
+ createLintModulesFromRuntimeRegistry,
7
+ createSpawnUsageAccumulator,
8
+ extractBlock,
9
+ formatInterpreterError,
10
+ isSamePath,
11
+ makeAgentModule,
12
+ makeFailModule,
13
+ makeFsModule,
14
+ makeHarnessModule,
15
+ makeLogModule,
16
+ makeMetricModule,
17
+ resolveCanonicalPath,
18
+ runWithSpawnUsageAccumulator,
19
+ splitFrontmatter,
20
+ supportsSpawnMode
21
+ } from "./chunks/chunk-AVDRLE3Y.js";
22
+ import {
23
+ Budget,
24
+ FileSnapshotBackend,
25
+ HostCallResumabilityError,
26
+ HostOperationResumePolicyError,
27
+ SandboxError,
28
+ SnapshotValidationError,
29
+ createReplayableRandom,
30
+ createSeededRandom,
31
+ declareHostOperation,
32
+ deepCopyFromSandbox,
33
+ deepCopyToSandbox,
34
+ dump,
35
+ hasOwnErrorCode,
36
+ lint,
37
+ noopOtelSink,
38
+ parse,
39
+ parseModule,
40
+ registerPendingHostCallPolicy,
41
+ restore,
42
+ run
43
+ } from "./chunks/chunk-ME5BTAZT.js";
44
+
45
+ // packages/safejs/src/runner/run-harness.ts
46
+ import { readFile, stat } from "node:fs/promises";
47
+ import { extname } from "node:path";
48
+ function getOwnEntry(record, key) {
49
+ return Object.prototype.hasOwnProperty.call(record, key) ? record[key] : void 0;
50
+ }
51
+ function createHarnessMeta(filepath, frontmatter) {
52
+ return {
53
+ filepath,
54
+ kind: getOwnEntry(frontmatter, "kind"),
55
+ version: getOwnEntry(frontmatter, "version")
56
+ };
57
+ }
58
+ function assertFrontmatterAgentModes(frontmatter) {
59
+ const agents = getOwnEntry(frontmatter, "agents");
60
+ if (typeof agents !== "object" || agents === null || Array.isArray(agents)) {
61
+ return;
62
+ }
63
+ for (const [name, definition] of Object.entries(agents)) {
64
+ if (typeof definition !== "object" || definition === null || Array.isArray(definition)) {
65
+ continue;
66
+ }
67
+ const record = definition;
68
+ const agent = getOwnEntry(record, "agent");
69
+ const mode = getOwnEntry(record, "mode");
70
+ if (typeof agent !== "string" || typeof mode !== "string") {
71
+ continue;
72
+ }
73
+ if (!SPAWN_MODES.includes(mode)) {
74
+ continue;
75
+ }
76
+ if (!supportsSpawnMode(agent, mode)) {
77
+ throw new Error(`Harness agent "${name}": agent "${agent}" does not support mode "${mode}".`);
78
+ }
79
+ }
80
+ }
81
+ var LintError = class extends Error {
82
+ diagnostics;
83
+ constructor(diagnostics) {
84
+ super(formatLintErrorMessage(diagnostics));
85
+ this.name = "LintError";
86
+ this.diagnostics = diagnostics;
87
+ }
88
+ };
89
+ async function runHarness(filepath, options) {
90
+ const rawSource = stripByteOrderMark(await readHarnessFile(filepath));
91
+ const { executableSource, frontmatter, isRawScript } = loadExecutableSource(filepath, rawSource);
92
+ assertFrontmatterAgentModes(frontmatter);
93
+ const meta = createHarnessMeta(filepath, frontmatter);
94
+ const modules = excludeHarnessModule(options.modulesFor(frontmatter, meta), isRawScript);
95
+ const diagnostics = lint(executableSource, {
96
+ filename: filepath,
97
+ modules: createLintModulesFromRuntimeRegistry(modules)
98
+ });
99
+ const lintErrors = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
100
+ if (lintErrors.length > 0) {
101
+ throw new LintError(lintErrors);
102
+ }
103
+ return runHarnessSource(executableSource, {
104
+ entryPointArgs: hasDefaultExport(executableSource, filepath) ? [] : void 0,
105
+ filename: filepath,
106
+ modules,
107
+ otelSink: options.otelSink,
108
+ signal: options.signal,
109
+ snapshotBackend: options.snapshotBackend,
110
+ snapshotIntervalMs: options.snapshotIntervalMs,
111
+ snapshotPath: options.snapshotPath
112
+ });
113
+ }
114
+ async function runHarnessPair(filepath, options) {
115
+ const pair = resolveHarnessPair(filepath);
116
+ const [rawMarkdown, rawScript] = await Promise.all([
117
+ readHarnessFile(pair.markdownPath),
118
+ readHarnessFile(pair.scriptPath)
119
+ ]);
120
+ const { frontmatter, body } = splitFrontmatter(stripByteOrderMark(rawMarkdown));
121
+ assertFrontmatterAgentModes(frontmatter);
122
+ const executableSource = stripByteOrderMark(rawScript);
123
+ const meta = createHarnessMeta(pair.markdownPath, frontmatter);
124
+ const modules = options.modulesFor(frontmatter, meta);
125
+ const diagnostics = lint(executableSource, {
126
+ allowedExportNames: ["schema"],
127
+ defaultExport: {
128
+ parameters: ["frontmatter"],
129
+ required: true
130
+ },
131
+ filename: pair.scriptPath,
132
+ frontmatterFields: Object.keys(frontmatter),
133
+ modules: createLintModulesFromRuntimeRegistry(modules)
134
+ });
135
+ const lintErrors = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
136
+ if (lintErrors.length > 0) {
137
+ throw new LintError(lintErrors);
138
+ }
139
+ return runHarnessSource(executableSource, {
140
+ entryPointArgs: [frontmatter],
141
+ filename: pair.scriptPath,
142
+ importMeta: {
143
+ body,
144
+ filepath: pair.markdownPath,
145
+ kind: meta.kind,
146
+ version: meta.version
147
+ },
148
+ modules,
149
+ otelSink: options.otelSink,
150
+ signal: options.signal,
151
+ snapshotBackend: options.snapshotBackend,
152
+ snapshotIntervalMs: options.snapshotIntervalMs,
153
+ snapshotPath: options.snapshotPath
154
+ });
155
+ }
156
+ async function readHarnessFile(filepath) {
157
+ try {
158
+ const stats = await stat(filepath);
159
+ if (!stats.isFile()) {
160
+ throw new Error(`Harness path must point to a file: ${filepath}`);
161
+ }
162
+ return await readFile(filepath, "utf8");
163
+ } catch (error) {
164
+ if (hasOwnErrorCode(error, "ENOENT") || hasOwnErrorCode(error, "ENOTDIR")) {
165
+ throw new Error(`Harness file not found: ${filepath}`);
166
+ }
167
+ throw error;
168
+ }
169
+ }
170
+ function loadExecutableSource(filepath, source) {
171
+ if (isRawScriptPath(filepath)) {
172
+ if (source.length === 0) {
173
+ throw new Error(`No code block found in empty harness file: ${filepath}`);
174
+ }
175
+ return {
176
+ executableSource: source,
177
+ frontmatter: {},
178
+ isRawScript: true
179
+ };
180
+ }
181
+ const { frontmatter, body } = splitFrontmatter(source);
182
+ if (source.length === 0 || body.trim().length === 0) {
183
+ throw new Error(`No code block found in empty harness file: ${filepath}`);
184
+ }
185
+ const bodyStartOffset = source.length - body.length;
186
+ const { source: executableBlock, startOffset } = extractBlock(
187
+ body,
188
+ countLineBreaks(source, 0, bodyStartOffset) + 1
189
+ );
190
+ const absoluteLineOffset = countLineBreaks(source, 0, bodyStartOffset + startOffset);
191
+ return {
192
+ executableSource: createLineOffsetSource(stripHashbang(executableBlock), absoluteLineOffset),
193
+ frontmatter,
194
+ isRawScript: false
195
+ };
196
+ }
197
+ function resolveHarnessPair(filepath) {
198
+ const extension = extname(filepath);
199
+ const basename = extension.length === 0 ? filepath : filepath.slice(0, -extension.length);
200
+ if (isRawScriptExtension(extension)) {
201
+ return {
202
+ markdownPath: `${basename}.md`,
203
+ scriptPath: filepath
204
+ };
205
+ }
206
+ return {
207
+ markdownPath: filepath,
208
+ scriptPath: `${basename}.ajs`
209
+ };
210
+ }
211
+ function isRawScriptPath(filepath) {
212
+ return isRawScriptExtension(extname(filepath));
213
+ }
214
+ function isRawScriptExtension(extension) {
215
+ return extension === ".safejs" || extension === ".ajs";
216
+ }
217
+ async function runHarnessSource(executableSource, options) {
218
+ try {
219
+ return await run(executableSource, options);
220
+ } catch (error) {
221
+ if (isSetupError(error)) {
222
+ throw error;
223
+ }
224
+ return {
225
+ ...isAbortError(error) ? { aborted: true } : {},
226
+ error,
227
+ ok: false
228
+ };
229
+ }
230
+ }
231
+ function isSetupError(error) {
232
+ return error instanceof Error && error.name === "ParseError";
233
+ }
234
+ function isAbortError(error) {
235
+ if (error instanceof Error) {
236
+ return error.name === "AbortError";
237
+ }
238
+ return typeof error === "object" && error !== null && Object.prototype.hasOwnProperty.call(error, "name") && error.name === "AbortError";
239
+ }
240
+ function hasDefaultExport(source, filename) {
241
+ return parseModule(source, filename).body.some(
242
+ (statement) => statement.type === "ExportDefaultDeclaration"
243
+ );
244
+ }
245
+ function excludeHarnessModule(modules, isRawScript) {
246
+ if (!isRawScript) {
247
+ return modules;
248
+ }
249
+ if (modules instanceof Map) {
250
+ const rawModules2 = new Map(modules);
251
+ rawModules2.delete("harness");
252
+ return rawModules2;
253
+ }
254
+ const rawModules = {};
255
+ for (const [moduleName, moduleExports] of Object.entries(modules)) {
256
+ if (moduleName !== "harness") {
257
+ rawModules[moduleName] = moduleExports;
258
+ }
259
+ }
260
+ return rawModules;
261
+ }
262
+ function stripByteOrderMark(source) {
263
+ return source.startsWith("\uFEFF") ? source.slice(1) : source;
264
+ }
265
+ function stripHashbang(source) {
266
+ if (!source.startsWith("#!")) {
267
+ return source;
268
+ }
269
+ let end = 2;
270
+ while (end < source.length && source[end] !== "\n" && source[end] !== "\r") end += 1;
271
+ return `${" ".repeat(end)}${source.slice(end)}`;
272
+ }
273
+ function createLineOffsetSource(source, lineOffset) {
274
+ return `${"\n".repeat(Math.max(lineOffset, 0))}${source}`;
275
+ }
276
+ function formatLintErrorMessage(diagnostics) {
277
+ if (diagnostics.length === 0) {
278
+ return "Script lint failed.";
279
+ }
280
+ return diagnostics.map(
281
+ (diagnostic) => `${diagnostic.filename}:${diagnostic.line}:${diagnostic.column} ${diagnostic.code} ${diagnostic.message}`
282
+ ).join("\n");
283
+ }
284
+
285
+ // packages/safejs/src/loader/find-exported.ts
286
+ function findExportedConstInitializer(module, name) {
287
+ for (const statement of module.body) {
288
+ if (statement.type === "ExportDefaultDeclaration") {
289
+ if (name === "default") {
290
+ return statement.declaration;
291
+ }
292
+ continue;
293
+ }
294
+ if (statement.type !== "ExportNamedDeclaration") {
295
+ continue;
296
+ }
297
+ const declaration = statement.declaration;
298
+ if (declaration.kind !== "const") {
299
+ throw new Error(`Exported '${name}' must be declared with const.`);
300
+ }
301
+ if (declaration.declarations.length !== 1) {
302
+ throw new Error("Exported const declarations must contain exactly one declarator.");
303
+ }
304
+ const declarator = declaration.declarations[0];
305
+ if (declarator.id.type !== "Identifier") {
306
+ throw new Error("Destructured exported const bindings are not supported.");
307
+ }
308
+ if (declarator.id.name !== name) {
309
+ continue;
310
+ }
311
+ if (declarator.init === void 0) {
312
+ throw new Error(`Exported const '${name}' must have an initializer.`);
313
+ }
314
+ return declarator.init;
315
+ }
316
+ return void 0;
317
+ }
318
+
319
+ // packages/safejs/src/modules/env.ts
320
+ function makeEnvModule(allowList) {
321
+ const allowedNames = normalizeAllowList(allowList);
322
+ return {
323
+ get(name) {
324
+ const normalizedName = readNonEmptyString(name, "Environment variable name");
325
+ if (!allowedNames.has(normalizedName)) {
326
+ return void 0;
327
+ }
328
+ return process.env[normalizedName];
329
+ }
330
+ };
331
+ }
332
+ function normalizeAllowList(allowList) {
333
+ if (!Array.isArray(allowList)) {
334
+ throw new Error("Environment allow list must be an array of non-empty strings.");
335
+ }
336
+ return new Set(
337
+ allowList.map((entry, index) => readNonEmptyString(entry, `Environment allow list[${index}]`))
338
+ );
339
+ }
340
+ function readNonEmptyString(value, label) {
341
+ if (typeof value !== "string") {
342
+ throw new Error(`${label} must be a non-empty string.`);
343
+ }
344
+ const normalizedValue = value.trim();
345
+ if (normalizedValue.length === 0) {
346
+ throw new Error(`${label} must be a non-empty string.`);
347
+ }
348
+ return normalizedValue;
349
+ }
350
+
351
+ // packages/safejs/src/modules/git.ts
352
+ import { execFile } from "node:child_process";
353
+ import { mkdir, readlink, realpath, rm, stat as stat2 } from "node:fs/promises";
354
+ import { dirname, isAbsolute, resolve } from "node:path";
355
+ var GIT_EXEC_MAX_BUFFER = 10 * 1024 * 1024;
356
+ var SAVEPOINT_REF_PREFIX = "refs/poe-code/checkpoints/";
357
+ function makeGitModule(cwd) {
358
+ const normalizedCwd = readNonEmptyString2(cwd, "Git module cwd");
359
+ return {
360
+ async head() {
361
+ return runGitAndTrim(normalizedCwd, ["rev-parse", "HEAD"]);
362
+ },
363
+ async checkpoint() {
364
+ const currentHead = await runGitAndTrim(normalizedCwd, ["rev-parse", "HEAD"]);
365
+ const status = await runGitAndTrim(normalizedCwd, ["status", "--porcelain"]);
366
+ if (status.length === 0) {
367
+ return {
368
+ head: currentHead
369
+ };
370
+ }
371
+ const stashRef = createSavepointRef();
372
+ const stashMessage = `poe-code checkpoint ${stashRef}`;
373
+ await runGit(normalizedCwd, [
374
+ "stash",
375
+ "push",
376
+ "--include-untracked",
377
+ "--message",
378
+ stashMessage
379
+ ]);
380
+ try {
381
+ const stashOid = await runGitAndTrim(normalizedCwd, ["rev-parse", "stash@{0}"]);
382
+ await runGit(normalizedCwd, ["update-ref", stashRef, stashOid]);
383
+ await runGit(normalizedCwd, ["stash", "apply", "--index", stashRef]);
384
+ await runGit(normalizedCwd, ["stash", "drop", "stash@{0}"]);
385
+ return {
386
+ head: currentHead,
387
+ stashRef
388
+ };
389
+ } catch (error) {
390
+ await cleanupFailedCheckpoint(normalizedCwd, stashRef);
391
+ throw error;
392
+ }
393
+ },
394
+ async commit(options) {
395
+ const normalizedOptions = normalizeCommitOptions(options);
396
+ if (normalizedOptions.files === void 0) {
397
+ await runGit(normalizedCwd, ["add", "--all"]);
398
+ await runGit(normalizedCwd, ["commit", "--message", normalizedOptions.message]);
399
+ } else {
400
+ await runGit(normalizedCwd, ["add", "--", ...normalizedOptions.files]);
401
+ await runGit(normalizedCwd, [
402
+ "commit",
403
+ "--message",
404
+ normalizedOptions.message,
405
+ "--",
406
+ ...normalizedOptions.files
407
+ ]);
408
+ }
409
+ return runGitAndTrim(normalizedCwd, ["rev-parse", "HEAD"]);
410
+ },
411
+ async revert(savepoint) {
412
+ const normalizedSavepoint = normalizeSavepoint(savepoint);
413
+ try {
414
+ await runGit(normalizedCwd, ["reset", "--hard", normalizedSavepoint.head]);
415
+ await runGit(normalizedCwd, ["clean", "--force", "-d"]);
416
+ if (normalizedSavepoint.stashRef !== void 0) {
417
+ await runGit(normalizedCwd, ["stash", "apply", "--index", normalizedSavepoint.stashRef]);
418
+ }
419
+ } finally {
420
+ if (normalizedSavepoint.stashRef !== void 0) {
421
+ await deleteSavepointRef(normalizedCwd, normalizedSavepoint.stashRef);
422
+ }
423
+ }
424
+ },
425
+ async diff() {
426
+ const { stdout } = await runGit(normalizedCwd, ["diff", "HEAD", "--"]);
427
+ return stdout;
428
+ },
429
+ async worktreeCreate(branch, options) {
430
+ const normalizedBranch = readNonEmptyString2(branch, "Git worktree branch");
431
+ const normalizedOptions = normalizeWorktreeCreateOptions(options);
432
+ const repoRoot = await getRepoRoot(normalizedCwd);
433
+ const worktreePath = await resolveWorktreePath(
434
+ repoRoot,
435
+ normalizedOptions.path ?? createDefaultWorktreePath(repoRoot, normalizedBranch),
436
+ "Git worktree path"
437
+ );
438
+ if (await gitRefExists(normalizedCwd, `refs/heads/${normalizedBranch}`)) {
439
+ throw new Error(`Git worktree branch '${normalizedBranch}' already exists.`);
440
+ }
441
+ await mkdir(dirname(worktreePath), { recursive: true });
442
+ try {
443
+ await runGit(normalizedCwd, [
444
+ "worktree",
445
+ "add",
446
+ "-b",
447
+ normalizedBranch,
448
+ worktreePath,
449
+ normalizedOptions.base
450
+ ]);
451
+ } catch (error) {
452
+ if (isBranchAlreadyExistsError(error, normalizedBranch)) {
453
+ throw new Error(`Git worktree branch '${normalizedBranch}' already exists.`);
454
+ }
455
+ throw error;
456
+ }
457
+ return {
458
+ path: worktreePath,
459
+ branch: normalizedBranch
460
+ };
461
+ },
462
+ async worktreeRemove(path) {
463
+ const repoRoot = await getRepoRoot(normalizedCwd);
464
+ const worktreePath = await resolveWorktreePath(repoRoot, path, "Git worktree path");
465
+ const worktrees = await listWorktrees(normalizedCwd);
466
+ if (!worktrees.some((worktree) => resolve(worktree.path) === worktreePath)) {
467
+ return;
468
+ }
469
+ await runGit(normalizedCwd, ["worktree", "remove", "--force", worktreePath]);
470
+ await rm(worktreePath, { recursive: true, force: true });
471
+ },
472
+ async worktreeList() {
473
+ return listWorktrees(normalizedCwd);
474
+ }
475
+ };
476
+ }
477
+ function normalizeCommitOptions(options) {
478
+ if (!isRecord(options)) {
479
+ throw new Error("Git commit options must be an object.");
480
+ }
481
+ const files = getOwnProperty(options, "files");
482
+ return {
483
+ message: readNonEmptyString2(getOwnProperty(options, "message"), "Git commit options message"),
484
+ files: files === void 0 ? void 0 : readNonEmptyStringArray(files, "Git commit options files")
485
+ };
486
+ }
487
+ function normalizeSavepoint(savepoint) {
488
+ if (!isRecord(savepoint)) {
489
+ throw new Error("Git savepoint must be an object.");
490
+ }
491
+ const stashRefValue = getOwnProperty(savepoint, "stashRef");
492
+ const stashRef = stashRefValue === void 0 ? void 0 : readNonEmptyString2(stashRefValue, "Git savepoint stashRef");
493
+ if (stashRef !== void 0 && !stashRef.startsWith(SAVEPOINT_REF_PREFIX)) {
494
+ throw new Error("Git savepoint stashRef must be a Poe checkpoint ref.");
495
+ }
496
+ return {
497
+ head: readNonEmptyString2(getOwnProperty(savepoint, "head"), "Git savepoint head"),
498
+ stashRef
499
+ };
500
+ }
501
+ function normalizeWorktreeCreateOptions(options) {
502
+ if (options === void 0) {
503
+ return {
504
+ base: "HEAD"
505
+ };
506
+ }
507
+ if (!isRecord(options)) {
508
+ throw new Error("Git worktree create options must be an object.");
509
+ }
510
+ const base = getOwnProperty(options, "base");
511
+ const path = getOwnProperty(options, "path");
512
+ return {
513
+ base: base === void 0 ? "HEAD" : readNonEmptyString2(base, "Git worktree create options base"),
514
+ path: path === void 0 ? void 0 : readNonEmptyString2(path, "Git worktree create options path")
515
+ };
516
+ }
517
+ function createSavepointRef() {
518
+ return `${SAVEPOINT_REF_PREFIX}${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
519
+ }
520
+ function createDefaultWorktreePath(repoRoot, branch) {
521
+ return resolve(repoRoot, ".poe-code", "worktrees", createSafeBranchDirectoryName(branch));
522
+ }
523
+ function createSafeBranchDirectoryName(branch) {
524
+ return encodeURIComponent(branch);
525
+ }
526
+ async function getRepoRoot(cwd) {
527
+ return runGitAndTrim(cwd, ["rev-parse", "--show-toplevel"]);
528
+ }
529
+ async function gitRefExists(cwd, ref) {
530
+ try {
531
+ await runGit(cwd, ["show-ref", "--verify", "--quiet", ref]);
532
+ return true;
533
+ } catch (error) {
534
+ void error;
535
+ return false;
536
+ }
537
+ }
538
+ async function listWorktrees(cwd) {
539
+ const { stdout } = await runGit(cwd, ["worktree", "list", "--porcelain"]);
540
+ return parseWorktreeList(stdout);
541
+ }
542
+ function parseWorktreeList(output) {
543
+ const worktrees = [];
544
+ let current = {};
545
+ const finishCurrent = () => {
546
+ if (current.path !== void 0 && current.branch !== void 0) {
547
+ worktrees.push({
548
+ path: current.path,
549
+ branch: current.branch
550
+ });
551
+ }
552
+ current = {};
553
+ };
554
+ for (const line of output.split("\n")) {
555
+ if (line.length === 0) {
556
+ finishCurrent();
557
+ continue;
558
+ }
559
+ if (line.startsWith("worktree ")) {
560
+ current.path = line.slice("worktree ".length);
561
+ continue;
562
+ }
563
+ if (line.startsWith("branch ")) {
564
+ current.branch = parseBranchRef(line.slice("branch ".length));
565
+ }
566
+ }
567
+ finishCurrent();
568
+ return worktrees;
569
+ }
570
+ function parseBranchRef(ref) {
571
+ const branchPrefix = "refs/heads/";
572
+ return ref.startsWith(branchPrefix) ? ref.slice(branchPrefix.length) : ref;
573
+ }
574
+ async function resolveWorktreePath(repoRoot, path, label) {
575
+ const resolvedRoot = resolve(repoRoot);
576
+ const resolvedPath = isAbsolute(path) ? resolve(path) : resolve(resolvedRoot, path);
577
+ const canonicalRoot = await realpath(resolvedRoot);
578
+ const canonicalPath = await resolveCanonicalPath({ realpath, readlink }, resolvedPath);
579
+ const [inside, isRootItself] = await Promise.all([
580
+ containsPath(stat2, canonicalRoot, canonicalPath),
581
+ isSamePath(stat2, canonicalPath, canonicalRoot)
582
+ ]);
583
+ if (!inside || isRootItself) {
584
+ throw new Error(`${label} must be inside the git repository.`);
585
+ }
586
+ return canonicalPath;
587
+ }
588
+ function isBranchAlreadyExistsError(error, branch) {
589
+ return error instanceof Error && error.message.includes(branch) && error.message.includes("already exists");
590
+ }
591
+ async function runGitAndTrim(cwd, args) {
592
+ const { stdout } = await runGit(cwd, args);
593
+ return stdout.trim();
594
+ }
595
+ async function runGit(cwd, args) {
596
+ return new Promise((resolve2, reject) => {
597
+ execFile(
598
+ "git",
599
+ args,
600
+ {
601
+ cwd,
602
+ encoding: "utf8",
603
+ maxBuffer: GIT_EXEC_MAX_BUFFER
604
+ },
605
+ (error, stdout, stderr) => {
606
+ if (error) {
607
+ reject(
608
+ new Error(
609
+ `${formatGitCommand(args)} failed: ${pickGitFailureMessage(stdout, stderr, error)}`
610
+ )
611
+ );
612
+ return;
613
+ }
614
+ resolve2({
615
+ stdout,
616
+ stderr
617
+ });
618
+ }
619
+ );
620
+ });
621
+ }
622
+ async function cleanupFailedCheckpoint(cwd, stashRef) {
623
+ await deleteSavepointRef(cwd, stashRef);
624
+ const restored = await tryRunGit(cwd, ["stash", "apply", "--index", "stash@{0}"]);
625
+ if (restored) {
626
+ await tryRunGit(cwd, ["stash", "drop", "stash@{0}"]);
627
+ }
628
+ }
629
+ async function deleteSavepointRef(cwd, stashRef) {
630
+ await tryRunGit(cwd, ["update-ref", "--delete", stashRef]);
631
+ }
632
+ async function tryRunGit(cwd, args) {
633
+ try {
634
+ await runGit(cwd, args);
635
+ return true;
636
+ } catch (error) {
637
+ void error;
638
+ return false;
639
+ }
640
+ }
641
+ function formatGitCommand(args) {
642
+ return ["git", ...args].join(" ");
643
+ }
644
+ function pickGitFailureMessage(stdout, stderr, error) {
645
+ const stderrText = stderr.trim();
646
+ if (stderrText.length > 0) {
647
+ return stderrText;
648
+ }
649
+ const stdoutText = stdout.trim();
650
+ if (stdoutText.length > 0) {
651
+ return stdoutText;
652
+ }
653
+ return error.message;
654
+ }
655
+ function readNonEmptyString2(value, label) {
656
+ if (typeof value !== "string" || value.trim().length === 0) {
657
+ throw new Error(`${label} must be a non-empty string.`);
658
+ }
659
+ return value;
660
+ }
661
+ function readNonEmptyStringArray(value, label) {
662
+ if (!Array.isArray(value) || value.length === 0) {
663
+ throw new Error(`${label} must be a non-empty array of strings.`);
664
+ }
665
+ const entries = value.map((entry, index) => readNonEmptyString2(entry, `${label}[${index}]`));
666
+ return [...entries];
667
+ }
668
+ function isRecord(value) {
669
+ return typeof value === "object" && value !== null;
670
+ }
671
+ function getOwnProperty(value, name) {
672
+ return hasOwnProperty(value, name) ? value[name] : void 0;
673
+ }
674
+ function hasOwnProperty(value, name) {
675
+ return Object.prototype.hasOwnProperty.call(value, name);
676
+ }
677
+
678
+ // packages/safejs/src/modules/mcp.ts
679
+ var DEFAULT_TOOL_BATCH_CONCURRENCY = 4;
680
+ function makeMcpModule(connectMcp) {
681
+ return {
682
+ server(server) {
683
+ return normalizeServerHandle(server);
684
+ },
685
+ async client(handle) {
686
+ const connection = validateConnection(await connectMcp(normalizeServerHandle(handle)));
687
+ return {
688
+ async tools() {
689
+ return normalizeToolsResult(await connection.listTools());
690
+ },
691
+ async tool(name, args) {
692
+ return connection.callTool(
693
+ createNullRecord({
694
+ name: readNonEmptyTrimmedString(name, "MCP tool name"),
695
+ ...args === void 0 ? {} : { arguments: readToolArguments(args) }
696
+ })
697
+ );
698
+ },
699
+ async toolBatch(calls) {
700
+ return executeToolBatch(connection, readToolBatchCalls(calls));
701
+ }
702
+ };
703
+ }
704
+ };
705
+ }
706
+ async function executeToolBatch(connection, calls) {
707
+ if (calls.length === 0) {
708
+ return [];
709
+ }
710
+ if (connection.callToolBatch !== void 0 && calls.every((call) => call.ok)) {
711
+ try {
712
+ return normalizeProtocolBatchResults(
713
+ await connection.callToolBatch(calls.map((call) => call.request)),
714
+ calls.length
715
+ );
716
+ } catch (error) {
717
+ const batchError = normalizeToolBatchError(error);
718
+ return calls.map(
719
+ () => createNullRecord({
720
+ ok: false,
721
+ error: batchError
722
+ })
723
+ );
724
+ }
725
+ }
726
+ return executeToolBatchWithConcurrency(connection, calls, DEFAULT_TOOL_BATCH_CONCURRENCY);
727
+ }
728
+ async function executeToolBatchWithConcurrency(connection, calls, concurrency) {
729
+ const results = new Array(calls.length);
730
+ let nextIndex = 0;
731
+ let disconnectError;
732
+ const workerCount = Math.min(concurrency, calls.length);
733
+ const workers = Array.from({ length: workerCount }, async () => {
734
+ while (nextIndex < calls.length) {
735
+ const index = nextIndex;
736
+ nextIndex += 1;
737
+ const call = calls[index];
738
+ if (call === void 0) {
739
+ continue;
740
+ }
741
+ if (!call.ok) {
742
+ results[index] = createNullRecord({
743
+ ok: false,
744
+ error: call.error
745
+ });
746
+ continue;
747
+ }
748
+ if (disconnectError !== void 0) {
749
+ results[index] = createNullRecord({
750
+ ok: false,
751
+ error: disconnectError
752
+ });
753
+ continue;
754
+ }
755
+ try {
756
+ results[index] = createNullRecord({
757
+ ok: true,
758
+ value: await connection.callTool(call.request)
759
+ });
760
+ } catch (error) {
761
+ const toolError = normalizeToolBatchError(error);
762
+ results[index] = createNullRecord({
763
+ ok: false,
764
+ error: toolError
765
+ });
766
+ if (isMcpDisconnectError(toolError)) {
767
+ disconnectError = toolError;
768
+ }
769
+ }
770
+ }
771
+ });
772
+ await Promise.all(workers);
773
+ return results;
774
+ }
775
+ function normalizeProtocolBatchResults(value, expectedLength) {
776
+ if (!Array.isArray(value) || value.length !== expectedLength) {
777
+ throw new Error(
778
+ "MCP callToolBatch() must resolve to a result array matching the input length."
779
+ );
780
+ }
781
+ return value.map((entry, index) => normalizeProtocolBatchResult(entry, index));
782
+ }
783
+ function normalizeProtocolBatchResult(value, index) {
784
+ if (!isRecord2(value)) {
785
+ throw new Error(`MCP callToolBatch()[${index}] must be a result envelope.`);
786
+ }
787
+ const ok = getOwnProperty2(value, "ok");
788
+ if (typeof ok !== "boolean") {
789
+ throw new Error(`MCP callToolBatch()[${index}] must be a result envelope.`);
790
+ }
791
+ if (ok === true) {
792
+ return createNullRecord({
793
+ ok: true,
794
+ value: getOwnProperty2(value, "value")
795
+ });
796
+ }
797
+ return createNullRecord({
798
+ ok: false,
799
+ error: normalizeToolBatchError(getOwnProperty2(value, "error"))
800
+ });
801
+ }
802
+ function normalizeServerHandle(value) {
803
+ if (!isRecord2(value)) {
804
+ throw new Error("MCP server must be an object.");
805
+ }
806
+ const command = getOwnProperty2(value, "command");
807
+ const args = getOwnProperty2(value, "args");
808
+ const env = getOwnProperty2(value, "env");
809
+ return createNullRecord({
810
+ command: readNonEmptyTrimmedString(command, "MCP server command"),
811
+ ...args === void 0 ? {} : { args: readStringArray(args, "MCP server args") },
812
+ ...env === void 0 ? {} : { env: readStringRecord(env, "MCP server env") }
813
+ });
814
+ }
815
+ function validateConnection(value) {
816
+ if (!isRecord2(value) || typeof value.listTools !== "function" || typeof value.callTool !== "function") {
817
+ throw new Error("connectMcp must resolve to an object with listTools() and callTool().");
818
+ }
819
+ const callToolBatch = getOwnProperty2(value, "callToolBatch");
820
+ return createNullRecord({
821
+ listTools: () => value.listTools(),
822
+ callTool: (params) => value.callTool(params),
823
+ ...typeof callToolBatch === "function" ? {
824
+ callToolBatch: (params) => callToolBatch(params)
825
+ } : {}
826
+ });
827
+ }
828
+ function normalizeToolsResult(value) {
829
+ const tools = isRecord2(value) ? getOwnProperty2(value, "tools") : void 0;
830
+ if (!Array.isArray(tools)) {
831
+ throw new Error("MCP listTools() must resolve to an object with a tools array.");
832
+ }
833
+ return tools.map((tool, index) => normalizeTool(tool, index));
834
+ }
835
+ function normalizeTool(value, index) {
836
+ if (!isRecord2(value)) {
837
+ throw new Error(`MCP tool[${index}] must be an object.`);
838
+ }
839
+ const description = getOwnProperty2(value, "description");
840
+ const inputSchema = getOwnProperty2(value, "inputSchema");
841
+ return createNullRecord({
842
+ name: readNonEmptyString3(getOwnProperty2(value, "name"), `MCP tool[${index}] name`),
843
+ ...description === void 0 ? {} : { description: readOptionalString(description, `MCP tool[${index}] description`) },
844
+ ...inputSchema === void 0 ? {} : { schema: inputSchema }
845
+ });
846
+ }
847
+ function readStringArray(value, label) {
848
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
849
+ throw new Error(`${label} must be an array of strings.`);
850
+ }
851
+ return [...value];
852
+ }
853
+ function readStringRecord(value, label) {
854
+ if (!isRecord2(value)) {
855
+ throw new Error(`${label} must be an object.`);
856
+ }
857
+ const entries = Object.entries(value);
858
+ if (entries.some(([, entry]) => typeof entry !== "string")) {
859
+ throw new Error(`${label} must be a string record.`);
860
+ }
861
+ return createNullRecord(Object.fromEntries(entries));
862
+ }
863
+ function readOptionalString(value, label) {
864
+ if (value === void 0) {
865
+ return void 0;
866
+ }
867
+ if (typeof value !== "string") {
868
+ throw new Error(`${label} must be a string.`);
869
+ }
870
+ return value;
871
+ }
872
+ function readNonEmptyString3(value, label) {
873
+ const text = readOptionalString(value, label);
874
+ if (text === void 0 || text.trim().length === 0) {
875
+ throw new Error(`${label} must be a non-empty string.`);
876
+ }
877
+ return text;
878
+ }
879
+ function readNonEmptyTrimmedString(value, label) {
880
+ return readNonEmptyString3(value, label).trim();
881
+ }
882
+ function readToolArguments(value) {
883
+ if (!isPlainObjectRecord(value)) {
884
+ throw new Error("MCP tool arguments must be an object.");
885
+ }
886
+ return createNullRecord(Object.fromEntries(Object.entries(value)));
887
+ }
888
+ function readToolBatchCalls(value) {
889
+ if (!Array.isArray(value)) {
890
+ throw new Error("MCP toolBatch calls must be an array.");
891
+ }
892
+ return value.map((entry, index) => normalizeToolBatchCall(entry, index));
893
+ }
894
+ function normalizeToolBatchCall(value, index) {
895
+ try {
896
+ if (!isRecord2(value)) {
897
+ throw new Error(`MCP toolBatch call[${index}] must be an object.`);
898
+ }
899
+ const args = getOwnProperty2(value, "args");
900
+ return createNullRecord({
901
+ ok: true,
902
+ request: createNullRecord({
903
+ name: readNonEmptyTrimmedString(
904
+ getOwnProperty2(value, "name"),
905
+ `MCP toolBatch call[${index}] name`
906
+ ),
907
+ ...args === void 0 ? {} : { arguments: readToolArguments(args) }
908
+ })
909
+ });
910
+ } catch (error) {
911
+ return createNullRecord({
912
+ ok: false,
913
+ error: normalizeToolBatchError(error)
914
+ });
915
+ }
916
+ }
917
+ function normalizeToolBatchError(error) {
918
+ if (error instanceof Error) {
919
+ return createNullRecord({
920
+ name: error.name.length === 0 ? "Error" : error.name,
921
+ message: error.message
922
+ });
923
+ }
924
+ if (isRecord2(error)) {
925
+ const nameValue = getOwnProperty2(error, "name");
926
+ const messageValue = getOwnProperty2(error, "message");
927
+ const name = typeof nameValue === "string" && nameValue.trim().length > 0 ? nameValue : "Error";
928
+ const message = typeof messageValue === "string" ? messageValue : String(error);
929
+ return createNullRecord({
930
+ name,
931
+ message
932
+ });
933
+ }
934
+ return createNullRecord({
935
+ name: "Error",
936
+ message: error === void 0 ? "" : String(error)
937
+ });
938
+ }
939
+ function isMcpDisconnectError(error) {
940
+ const message = error.message.toLowerCase();
941
+ return message.includes("disconnected") || message.includes("connection closed") || message.includes("transport closed") || message.includes("disposed") || message.includes("eof");
942
+ }
943
+ function isRecord2(value) {
944
+ return typeof value === "object" && value !== null;
945
+ }
946
+ function getOwnProperty2(value, name) {
947
+ return hasOwnProperty2(value, name) ? value[name] : void 0;
948
+ }
949
+ function hasOwnProperty2(value, name) {
950
+ return Object.prototype.hasOwnProperty.call(value, name);
951
+ }
952
+ function createNullRecord(value) {
953
+ return Object.assign(/* @__PURE__ */ Object.create(null), value);
954
+ }
955
+ function isPlainObjectRecord(value) {
956
+ if (!isRecord2(value) || Array.isArray(value)) {
957
+ return false;
958
+ }
959
+ const prototype = Object.getPrototypeOf(value);
960
+ return prototype === Object.prototype || prototype === null;
961
+ }
962
+
963
+ // packages/safejs/src/modules/time.ts
964
+ function makeTimeModule(options = {}) {
965
+ const hasDeterministicRandom = options.random !== void 0 || options.seed !== void 0;
966
+ const random = options.random ?? (options.seed === void 0 ? () => Math.random() : createSeededRandom(options.seed).next);
967
+ const now = options.now ?? (() => Date.now());
968
+ const signal = options.signal;
969
+ async function sleep(ms) {
970
+ assertSleepDelay(ms);
971
+ return await new Promise((resolve2, reject) => {
972
+ if (signal?.aborted === true) {
973
+ reject(createSleepAbortError());
974
+ return;
975
+ }
976
+ const start = performance.now();
977
+ let timeout = setTimeout(resolveSleep, ms);
978
+ function cleanup() {
979
+ clearTimeout(timeout);
980
+ signal?.removeEventListener("abort", rejectSleep);
981
+ }
982
+ function resolveSleep() {
983
+ const remainingMs = ms - (performance.now() - start);
984
+ if (remainingMs > 0) {
985
+ timeout = setTimeout(resolveSleep, remainingMs);
986
+ return;
987
+ }
988
+ cleanup();
989
+ resolve2();
990
+ }
991
+ function rejectSleep() {
992
+ cleanup();
993
+ reject(createSleepAbortError());
994
+ }
995
+ signal?.addEventListener("abort", rejectSleep, { once: true });
996
+ });
997
+ }
998
+ return {
999
+ random,
1000
+ now,
1001
+ sleep,
1002
+ uuid: hasDeterministicRandom ? () => createRandomUuid(random) : () => crypto.randomUUID()
1003
+ };
1004
+ }
1005
+ function createRandomUuid(random) {
1006
+ const bytes = Array.from({ length: 16 }, () => Math.floor(readRandomUnit(random()) * 256));
1007
+ bytes[6] = bytes[6] & 15 | 64;
1008
+ bytes[8] = bytes[8] & 63 | 128;
1009
+ const hex = bytes.map((byte) => byte.toString(16).padStart(2, "0")).join("");
1010
+ return [
1011
+ hex.slice(0, 8),
1012
+ hex.slice(8, 12),
1013
+ hex.slice(12, 16),
1014
+ hex.slice(16, 20),
1015
+ hex.slice(20)
1016
+ ].join("-");
1017
+ }
1018
+ function readRandomUnit(value) {
1019
+ if (Number.isFinite(value) && value >= 0 && value < 1) {
1020
+ return value;
1021
+ }
1022
+ throw new RangeError("time.uuid() random source must return a number in [0, 1).");
1023
+ }
1024
+ function assertSleepDelay(ms) {
1025
+ if (Number.isFinite(ms) && ms >= 0) {
1026
+ return;
1027
+ }
1028
+ throw new RangeError("time.sleep(ms) requires a non-negative finite millisecond delay.");
1029
+ }
1030
+ function createSleepAbortError() {
1031
+ return new Error("time.sleep aborted.");
1032
+ }
1033
+ export {
1034
+ AgentSpawnError,
1035
+ Budget,
1036
+ FileSnapshotBackend,
1037
+ HostCallResumabilityError,
1038
+ HostOperationResumePolicyError,
1039
+ SandboxError,
1040
+ SnapshotValidationError,
1041
+ createReplayableRandom,
1042
+ createSpawnUsageAccumulator,
1043
+ declareHostOperation,
1044
+ deepCopyFromSandbox,
1045
+ deepCopyToSandbox,
1046
+ dump,
1047
+ extractBlock,
1048
+ findExportedConstInitializer,
1049
+ formatInterpreterError,
1050
+ lint,
1051
+ makeAgentModule,
1052
+ makeEnvModule,
1053
+ makeFailModule,
1054
+ makeFsModule,
1055
+ makeGitModule,
1056
+ makeHarnessModule,
1057
+ makeLogModule,
1058
+ makeMcpModule,
1059
+ makeMetricModule,
1060
+ makeTimeModule,
1061
+ noopOtelSink,
1062
+ parse,
1063
+ parseModule,
1064
+ registerPendingHostCallPolicy,
1065
+ restore,
1066
+ run,
1067
+ runHarness,
1068
+ runHarnessPair,
1069
+ runWithSpawnUsageAccumulator,
1070
+ splitFrontmatter
1071
+ };
1072
+ //# sourceMappingURL=index.js.map