poe-code 8.0.0 → 9.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.
Files changed (45) hide show
  1. package/dist/cli/commands/harness.js +12 -3
  2. package/dist/cli/commands/harness.js.map +1 -1
  3. package/dist/index.js +1515 -712
  4. package/dist/index.js.map +4 -4
  5. package/dist/metafile.json +1 -1
  6. package/package.json +15 -2
  7. package/packages/safejs/dist/chunks/chunk-4YIDX44G.js +40 -0
  8. package/packages/safejs/dist/chunks/chunk-4YIDX44G.js.map +7 -0
  9. package/packages/safejs/dist/chunks/chunk-55ZK3CR7.js +7802 -0
  10. package/packages/safejs/dist/chunks/chunk-55ZK3CR7.js.map +7 -0
  11. package/packages/safejs/dist/chunks/chunk-GQBEEFVQ.js +28798 -0
  12. package/packages/safejs/dist/chunks/chunk-GQBEEFVQ.js.map +7 -0
  13. package/packages/safejs/dist/chunks/chunk-NI4WFFTL.js +5701 -0
  14. package/packages/safejs/dist/chunks/chunk-NI4WFFTL.js.map +7 -0
  15. package/packages/safejs/dist/chunks/inMemory-RLZ4QGDZ.js +48 -0
  16. package/packages/safejs/dist/chunks/inMemory-RLZ4QGDZ.js.map +7 -0
  17. package/packages/safejs/dist/chunks/server-3SNT3RE2.js +12518 -0
  18. package/packages/safejs/dist/chunks/server-3SNT3RE2.js.map +7 -0
  19. package/packages/safejs/dist/chunks/types-BTF37PZ6.js +348 -0
  20. package/packages/safejs/dist/chunks/types-BTF37PZ6.js.map +7 -0
  21. package/packages/safejs/dist/cli.d.ts +2 -0
  22. package/packages/safejs/dist/cli.js +550 -491
  23. package/packages/safejs/dist/cli.js.map +7 -0
  24. package/packages/safejs/dist/core.js +14 -4
  25. package/packages/safejs/dist/core.js.map +7 -0
  26. package/packages/safejs/dist/index.d.ts +3 -0
  27. package/packages/safejs/dist/index.js +791 -30
  28. package/packages/safejs/dist/index.js.map +7 -0
  29. package/packages/safejs/dist/interp/host-bridge.d.ts +2 -0
  30. package/packages/safejs/dist/interp/host-bridge.js +20 -9
  31. package/packages/safejs/dist/interp/host-call.d.ts +5 -0
  32. package/packages/safejs/dist/interp/host-call.js +48 -3
  33. package/packages/safejs/dist/interp/resources.d.ts +7 -0
  34. package/packages/safejs/dist/interp/resources.js +36 -0
  35. package/packages/safejs/dist/modules/mcp-transport.d.ts +26 -0
  36. package/packages/safejs/dist/modules/mcp-transport.js +259 -0
  37. package/packages/safejs/dist/modules/mcp.d.ts +15 -1
  38. package/packages/safejs/dist/modules/mcp.js +107 -0
  39. package/packages/safejs/dist/run.js +6 -3
  40. package/packages/safejs/dist/snapshot/dump-format.d.ts +1 -1
  41. package/packages/safejs/dist/snapshot/dump-format.js +1 -1
  42. package/packages/safejs/dist/snapshot/replay-data.d.ts +1 -0
  43. package/packages/safejs/dist/snapshot/replay-data.js +3 -1
  44. package/packages/safejs/dist/snapshot/replay-inputs.d.ts +2 -2
  45. package/packages/safejs/dist/snapshot/replay-inputs.js +2 -1
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/runner/run-harness.ts", "../src/loader/find-exported.ts", "../src/modules/env.ts", "../src/modules/git.ts", "../src/modules/time.ts"],
4
+ "sourcesContent": ["import { readFile, stat } from \"node:fs/promises\";\nimport { extname } from \"node:path\";\n\nimport { supportsSpawnMode } from \"@poe-code/agent-spawn/configs\";\nimport { SPAWN_MODES, type SpawnMode } from \"@poe-code/agent-spawn/types\";\nimport { hasOwnErrorCode } from \"../error-codes.js\";\nimport { countLineBreaks, extractBlock } from \"../loader/extract-block.js\";\nimport { splitFrontmatter } from \"../loader/frontmatter.js\";\nimport { lint, type Diagnostic } from \"../lint.js\";\nimport { createLintModulesFromRuntimeRegistry } from \"../lint/runtime-modules.js\";\nimport type { ModuleExports, ModuleRegistry } from \"../modules/registry.js\";\nimport type { OtelSink } from \"../observability/otel.js\";\nimport { parseModule } from \"../parse/parser.js\";\nimport { run, type RunResult } from \"../run.js\";\nimport type { SnapshotBackend } from \"../snapshot/backend.js\";\n\ntype HarnessMeta = {\n filepath: string;\n kind: unknown;\n version: unknown;\n};\n\nexport type RunHarnessOptions = {\n modulesFor: (frontmatter: Record<string, unknown>, meta: HarnessMeta) => ModuleRegistry;\n otelSink?: OtelSink;\n signal?: AbortSignal;\n snapshotBackend?: SnapshotBackend;\n snapshotIntervalMs?: number;\n snapshotPath?: string;\n};\n\nexport type RunHarnessExecutionErrorResult = {\n aborted?: true;\n error: unknown;\n ok: false;\n};\n\nexport type RunHarnessResult = RunResult | RunHarnessExecutionErrorResult;\n\nfunction getOwnEntry(record: Record<string, unknown>, key: string): unknown {\n return Object.prototype.hasOwnProperty.call(record, key) ? record[key] : undefined;\n}\n\nfunction createHarnessMeta(filepath: string, frontmatter: Record<string, unknown>): HarnessMeta {\n return {\n filepath,\n kind: getOwnEntry(frontmatter, \"kind\"),\n version: getOwnEntry(frontmatter, \"version\")\n };\n}\n\nfunction assertFrontmatterAgentModes(frontmatter: Record<string, unknown>): void {\n const agents = getOwnEntry(frontmatter, \"agents\");\n if (typeof agents !== \"object\" || agents === null || Array.isArray(agents)) {\n return;\n }\n\n for (const [name, definition] of Object.entries(agents)) {\n if (typeof definition !== \"object\" || definition === null || Array.isArray(definition)) {\n continue;\n }\n\n const record = definition as Record<string, unknown>;\n const agent = getOwnEntry(record, \"agent\");\n const mode = getOwnEntry(record, \"mode\");\n if (typeof agent !== \"string\" || typeof mode !== \"string\") {\n continue;\n }\n\n // Malformed mode strings are reported by the agent module's own validation.\n if (!SPAWN_MODES.includes(mode as SpawnMode)) {\n continue;\n }\n\n if (!supportsSpawnMode(agent, mode as SpawnMode)) {\n throw new Error(`Harness agent \"${name}\": agent \"${agent}\" does not support mode \"${mode}\".`);\n }\n }\n}\n\nexport class LintError extends Error {\n readonly diagnostics: readonly Diagnostic[];\n\n constructor(diagnostics: readonly Diagnostic[]) {\n super(formatLintErrorMessage(diagnostics));\n this.name = \"LintError\";\n this.diagnostics = diagnostics;\n }\n}\n\nexport async function runHarness(\n filepath: string,\n options: RunHarnessOptions\n): Promise<RunHarnessResult> {\n const rawSource = stripByteOrderMark(await readHarnessFile(filepath));\n const { executableSource, frontmatter, isRawScript } = loadExecutableSource(filepath, rawSource);\n assertFrontmatterAgentModes(frontmatter);\n const meta = createHarnessMeta(filepath, frontmatter);\n const modules = excludeHarnessModule(options.modulesFor(frontmatter, meta), isRawScript);\n const diagnostics = lint(executableSource, {\n filename: filepath,\n modules: createLintModulesFromRuntimeRegistry(modules)\n });\n const lintErrors = diagnostics.filter((diagnostic) => diagnostic.severity === \"error\");\n\n if (lintErrors.length > 0) {\n throw new LintError(lintErrors);\n }\n\n return runHarnessSource(executableSource, {\n entryPointArgs: hasDefaultExport(executableSource, filepath) ? [] : undefined,\n filename: filepath,\n modules,\n otelSink: options.otelSink,\n signal: options.signal,\n snapshotBackend: options.snapshotBackend,\n snapshotIntervalMs: options.snapshotIntervalMs,\n snapshotPath: options.snapshotPath\n });\n}\n\nexport async function runHarnessPair(\n filepath: string,\n options: RunHarnessOptions\n): Promise<RunHarnessResult> {\n const pair = resolveHarnessPair(filepath);\n const [rawMarkdown, rawScript] = await Promise.all([\n readHarnessFile(pair.markdownPath),\n readHarnessFile(pair.scriptPath)\n ]);\n const { frontmatter, body } = splitFrontmatter(stripByteOrderMark(rawMarkdown));\n assertFrontmatterAgentModes(frontmatter);\n const executableSource = stripByteOrderMark(rawScript);\n const meta = createHarnessMeta(pair.markdownPath, frontmatter);\n const modules = options.modulesFor(frontmatter, meta);\n const diagnostics = lint(executableSource, {\n allowedExportNames: [\"schema\"],\n defaultExport: {\n parameters: [\"frontmatter\"],\n required: true\n },\n filename: pair.scriptPath,\n frontmatterFields: Object.keys(frontmatter),\n modules: createLintModulesFromRuntimeRegistry(modules)\n });\n const lintErrors = diagnostics.filter((diagnostic) => diagnostic.severity === \"error\");\n\n if (lintErrors.length > 0) {\n throw new LintError(lintErrors);\n }\n\n return runHarnessSource(executableSource, {\n entryPointArgs: [frontmatter],\n filename: pair.scriptPath,\n importMeta: {\n body,\n filepath: pair.markdownPath,\n kind: meta.kind,\n version: meta.version\n },\n modules,\n otelSink: options.otelSink,\n signal: options.signal,\n snapshotBackend: options.snapshotBackend,\n snapshotIntervalMs: options.snapshotIntervalMs,\n snapshotPath: options.snapshotPath\n });\n}\n\nasync function readHarnessFile(filepath: string): Promise<string> {\n try {\n const stats = await stat(filepath);\n if (!stats.isFile()) {\n throw new Error(`Harness path must point to a file: ${filepath}`);\n }\n\n return await readFile(filepath, \"utf8\");\n } catch (error) {\n if (hasOwnErrorCode(error, \"ENOENT\") || hasOwnErrorCode(error, \"ENOTDIR\")) {\n throw new Error(`Harness file not found: ${filepath}`);\n }\n\n throw error;\n }\n}\n\nfunction loadExecutableSource(\n filepath: string,\n source: string\n): {\n executableSource: string;\n frontmatter: Record<string, unknown>;\n isRawScript: boolean;\n} {\n if (isRawScriptPath(filepath)) {\n if (source.length === 0) {\n throw new Error(`No code block found in empty harness file: ${filepath}`);\n }\n\n return {\n executableSource: source,\n frontmatter: {},\n isRawScript: true\n };\n }\n\n const { frontmatter, body } = splitFrontmatter(source);\n if (source.length === 0 || body.trim().length === 0) {\n throw new Error(`No code block found in empty harness file: ${filepath}`);\n }\n\n const bodyStartOffset = source.length - body.length;\n const { source: executableBlock, startOffset } = extractBlock(\n body,\n countLineBreaks(source, 0, bodyStartOffset) + 1\n );\n const absoluteLineOffset = countLineBreaks(source, 0, bodyStartOffset + startOffset);\n\n return {\n executableSource: createLineOffsetSource(stripHashbang(executableBlock), absoluteLineOffset),\n frontmatter,\n isRawScript: false\n };\n}\n\nfunction resolveHarnessPair(filepath: string): { markdownPath: string; scriptPath: string } {\n const extension = extname(filepath);\n const basename = extension.length === 0 ? filepath : filepath.slice(0, -extension.length);\n\n if (isRawScriptExtension(extension)) {\n return {\n markdownPath: `${basename}.md`,\n scriptPath: filepath\n };\n }\n\n return {\n markdownPath: filepath,\n scriptPath: `${basename}.ajs`\n };\n}\n\nfunction isRawScriptPath(filepath: string): boolean {\n return isRawScriptExtension(extname(filepath));\n}\n\nfunction isRawScriptExtension(extension: string): boolean {\n return extension === \".safejs\" || extension === \".ajs\";\n}\n\nasync function runHarnessSource(\n executableSource: string,\n options: Parameters<typeof run>[1]\n): Promise<RunHarnessResult> {\n try {\n return await run(executableSource, options);\n } catch (error) {\n if (isSetupError(error)) {\n throw error;\n }\n\n return {\n ...(isAbortError(error) ? { aborted: true as const } : {}),\n error,\n ok: false\n };\n }\n}\n\nfunction isSetupError(error: unknown): boolean {\n return error instanceof Error && error.name === \"ParseError\";\n}\n\nfunction isAbortError(error: unknown): boolean {\n if (error instanceof Error) {\n return error.name === \"AbortError\";\n }\n\n return (\n typeof error === \"object\" &&\n error !== null &&\n Object.prototype.hasOwnProperty.call(error, \"name\") &&\n (error as { name?: unknown }).name === \"AbortError\"\n );\n}\n\nfunction hasDefaultExport(source: string, filename: string): boolean {\n return parseModule(source, filename).body.some(\n (statement) => statement.type === \"ExportDefaultDeclaration\"\n );\n}\n\nfunction excludeHarnessModule(modules: ModuleRegistry, isRawScript: boolean): ModuleRegistry {\n if (!isRawScript) {\n return modules;\n }\n\n if (modules instanceof Map) {\n const rawModules = new Map(modules);\n rawModules.delete(\"harness\");\n return rawModules;\n }\n\n const rawModules: Record<string, ModuleExports> = {};\n\n for (const [moduleName, moduleExports] of Object.entries(modules)) {\n if (moduleName !== \"harness\") {\n rawModules[moduleName] = moduleExports;\n }\n }\n\n return rawModules;\n}\n\nfunction stripByteOrderMark(source: string): string {\n return source.startsWith(\"\\uFEFF\") ? source.slice(1) : source;\n}\n\nfunction stripHashbang(source: string): string {\n if (!source.startsWith(\"#!\")) {\n return source;\n }\n\n let end = 2;\n while (end < source.length && source[end] !== \"\\n\" && source[end] !== \"\\r\") end += 1;\n return `${\" \".repeat(end)}${source.slice(end)}`;\n}\n\nfunction createLineOffsetSource(source: string, lineOffset: number): string {\n return `${\"\\n\".repeat(Math.max(lineOffset, 0))}${source}`;\n}\n\nfunction formatLintErrorMessage(diagnostics: readonly Diagnostic[]): string {\n if (diagnostics.length === 0) {\n return \"Script lint failed.\";\n }\n\n return diagnostics\n .map(\n (diagnostic) =>\n `${diagnostic.filename}:${diagnostic.line}:${diagnostic.column} ${diagnostic.code} ${diagnostic.message}`\n )\n .join(\"\\n\");\n}\n", "import type { Expression, Module } from \"../parse/parser.js\";\n\nexport function findExportedConstInitializer(module: Module, name: string): Expression | undefined {\n for (const statement of module.body) {\n if (statement.type === \"ExportDefaultDeclaration\") {\n if (name === \"default\") {\n return statement.declaration;\n }\n\n continue;\n }\n\n if (statement.type !== \"ExportNamedDeclaration\") {\n continue;\n }\n\n const declaration = statement.declaration;\n if (declaration.kind !== \"const\") {\n throw new Error(`Exported '${name}' must be declared with const.`);\n }\n\n if (declaration.declarations.length !== 1) {\n throw new Error(\"Exported const declarations must contain exactly one declarator.\");\n }\n\n const declarator = declaration.declarations[0]!;\n if (declarator.id.type !== \"Identifier\") {\n throw new Error(\"Destructured exported const bindings are not supported.\");\n }\n\n if (declarator.id.name !== name) {\n continue;\n }\n\n if (declarator.init === undefined) {\n throw new Error(`Exported const '${name}' must have an initializer.`);\n }\n\n return declarator.init;\n }\n\n return undefined;\n}\n", "export function makeEnvModule(allowList: readonly string[]): {\n get(name: string): string | undefined;\n} {\n const allowedNames = normalizeAllowList(allowList);\n\n return {\n get(name) {\n const normalizedName = readNonEmptyString(name, \"Environment variable name\");\n\n if (!allowedNames.has(normalizedName)) {\n return undefined;\n }\n\n return process.env[normalizedName];\n }\n };\n}\n\nfunction normalizeAllowList(allowList: readonly string[]): ReadonlySet<string> {\n if (!Array.isArray(allowList)) {\n throw new Error(\"Environment allow list must be an array of non-empty strings.\");\n }\n\n return new Set(\n allowList.map((entry, index) => readNonEmptyString(entry, `Environment allow list[${index}]`))\n );\n}\n\nfunction readNonEmptyString(value: unknown, label: string): string {\n if (typeof value !== \"string\") {\n throw new Error(`${label} must be a non-empty string.`);\n }\n\n const normalizedValue = value.trim();\n\n if (normalizedValue.length === 0) {\n throw new Error(`${label} must be a non-empty string.`);\n }\n\n return normalizedValue;\n}\n", "import { execFile } from \"node:child_process\";\nimport { mkdir, readlink, realpath, rm, stat } from \"node:fs/promises\";\nimport { dirname, isAbsolute, resolve } from \"node:path\";\nimport { containsPath, isSamePath, resolveCanonicalPath } from \"./canonical-path.js\";\n\nexport type GitSavepoint = {\n head: string;\n stashRef?: string;\n};\n\nexport type GitCommitOptions = {\n message: string;\n files?: string[];\n};\n\nexport type GitWorktree = {\n path: string;\n branch: string;\n};\n\nexport type GitWorktreeCreateOptions = {\n base?: string;\n path?: string;\n};\n\ntype ExecFileResult = {\n stdout: string;\n stderr: string;\n};\n\nconst GIT_EXEC_MAX_BUFFER = 10 * 1024 * 1024;\nconst SAVEPOINT_REF_PREFIX = \"refs/poe-code/checkpoints/\";\n\nexport function makeGitModule(cwd: string): {\n head(): Promise<string>;\n checkpoint(): Promise<GitSavepoint>;\n commit(options: GitCommitOptions): Promise<string>;\n revert(savepoint: GitSavepoint): Promise<void>;\n diff(): Promise<string>;\n worktreeCreate(branch: string, options?: GitWorktreeCreateOptions): Promise<GitWorktree>;\n worktreeRemove(path: string): Promise<void>;\n worktreeList(): Promise<GitWorktree[]>;\n} {\n const normalizedCwd = readNonEmptyString(cwd, \"Git module cwd\");\n\n return {\n async head() {\n return runGitAndTrim(normalizedCwd, [\"rev-parse\", \"HEAD\"]);\n },\n\n async checkpoint() {\n const currentHead = await runGitAndTrim(normalizedCwd, [\"rev-parse\", \"HEAD\"]);\n const status = await runGitAndTrim(normalizedCwd, [\"status\", \"--porcelain\"]);\n\n if (status.length === 0) {\n return {\n head: currentHead\n };\n }\n\n const stashRef = createSavepointRef();\n const stashMessage = `poe-code checkpoint ${stashRef}`;\n\n await runGit(normalizedCwd, [\n \"stash\",\n \"push\",\n \"--include-untracked\",\n \"--message\",\n stashMessage\n ]);\n\n try {\n const stashOid = await runGitAndTrim(normalizedCwd, [\"rev-parse\", \"stash@{0}\"]);\n await runGit(normalizedCwd, [\"update-ref\", stashRef, stashOid]);\n await runGit(normalizedCwd, [\"stash\", \"apply\", \"--index\", stashRef]);\n await runGit(normalizedCwd, [\"stash\", \"drop\", \"stash@{0}\"]);\n\n return {\n head: currentHead,\n stashRef\n };\n } catch (error) {\n await cleanupFailedCheckpoint(normalizedCwd, stashRef);\n throw error;\n }\n },\n\n async commit(options) {\n const normalizedOptions = normalizeCommitOptions(options);\n\n if (normalizedOptions.files === undefined) {\n await runGit(normalizedCwd, [\"add\", \"--all\"]);\n await runGit(normalizedCwd, [\"commit\", \"--message\", normalizedOptions.message]);\n } else {\n await runGit(normalizedCwd, [\"add\", \"--\", ...normalizedOptions.files]);\n await runGit(normalizedCwd, [\n \"commit\",\n \"--message\",\n normalizedOptions.message,\n \"--\",\n ...normalizedOptions.files\n ]);\n }\n return runGitAndTrim(normalizedCwd, [\"rev-parse\", \"HEAD\"]);\n },\n\n async revert(savepoint) {\n const normalizedSavepoint = normalizeSavepoint(savepoint);\n\n try {\n await runGit(normalizedCwd, [\"reset\", \"--hard\", normalizedSavepoint.head]);\n await runGit(normalizedCwd, [\"clean\", \"--force\", \"-d\"]);\n\n if (normalizedSavepoint.stashRef !== undefined) {\n await runGit(normalizedCwd, [\"stash\", \"apply\", \"--index\", normalizedSavepoint.stashRef]);\n }\n } finally {\n if (normalizedSavepoint.stashRef !== undefined) {\n await deleteSavepointRef(normalizedCwd, normalizedSavepoint.stashRef);\n }\n }\n },\n\n async diff() {\n const { stdout } = await runGit(normalizedCwd, [\"diff\", \"HEAD\", \"--\"]);\n return stdout;\n },\n\n async worktreeCreate(branch, options) {\n const normalizedBranch = readNonEmptyString(branch, \"Git worktree branch\");\n const normalizedOptions = normalizeWorktreeCreateOptions(options);\n const repoRoot = await getRepoRoot(normalizedCwd);\n const worktreePath = await resolveWorktreePath(\n repoRoot,\n normalizedOptions.path ?? createDefaultWorktreePath(repoRoot, normalizedBranch),\n \"Git worktree path\"\n );\n\n if (await gitRefExists(normalizedCwd, `refs/heads/${normalizedBranch}`)) {\n throw new Error(`Git worktree branch '${normalizedBranch}' already exists.`);\n }\n\n await mkdir(dirname(worktreePath), { recursive: true });\n\n try {\n await runGit(normalizedCwd, [\n \"worktree\",\n \"add\",\n \"-b\",\n normalizedBranch,\n worktreePath,\n normalizedOptions.base\n ]);\n } catch (error) {\n if (isBranchAlreadyExistsError(error, normalizedBranch)) {\n throw new Error(`Git worktree branch '${normalizedBranch}' already exists.`);\n }\n\n throw error;\n }\n\n return {\n path: worktreePath,\n branch: normalizedBranch\n };\n },\n\n async worktreeRemove(path) {\n const repoRoot = await getRepoRoot(normalizedCwd);\n const worktreePath = await resolveWorktreePath(repoRoot, path, \"Git worktree path\");\n const worktrees = await listWorktrees(normalizedCwd);\n\n if (!worktrees.some((worktree) => resolve(worktree.path) === worktreePath)) {\n return;\n }\n\n await runGit(normalizedCwd, [\"worktree\", \"remove\", \"--force\", worktreePath]);\n await rm(worktreePath, { recursive: true, force: true });\n },\n\n async worktreeList() {\n return listWorktrees(normalizedCwd);\n }\n };\n}\n\nfunction normalizeCommitOptions(options: GitCommitOptions | unknown): GitCommitOptions {\n if (!isRecord(options)) {\n throw new Error(\"Git commit options must be an object.\");\n }\n\n const files = getOwnProperty(options, \"files\");\n\n return {\n message: readNonEmptyString(getOwnProperty(options, \"message\"), \"Git commit options message\"),\n files:\n files === undefined ? undefined : readNonEmptyStringArray(files, \"Git commit options files\")\n };\n}\n\nfunction normalizeSavepoint(savepoint: GitSavepoint | unknown): GitSavepoint {\n if (!isRecord(savepoint)) {\n throw new Error(\"Git savepoint must be an object.\");\n }\n\n const stashRefValue = getOwnProperty(savepoint, \"stashRef\");\n const stashRef =\n stashRefValue === undefined\n ? undefined\n : readNonEmptyString(stashRefValue, \"Git savepoint stashRef\");\n if (stashRef !== undefined && !stashRef.startsWith(SAVEPOINT_REF_PREFIX)) {\n throw new Error(\"Git savepoint stashRef must be a Poe checkpoint ref.\");\n }\n\n return {\n head: readNonEmptyString(getOwnProperty(savepoint, \"head\"), \"Git savepoint head\"),\n stashRef\n };\n}\n\nfunction normalizeWorktreeCreateOptions(\n options: GitWorktreeCreateOptions | unknown\n): GitWorktreeCreateOptions & {\n base: string;\n} {\n if (options === undefined) {\n return {\n base: \"HEAD\"\n };\n }\n\n if (!isRecord(options)) {\n throw new Error(\"Git worktree create options must be an object.\");\n }\n\n const base = getOwnProperty(options, \"base\");\n const path = getOwnProperty(options, \"path\");\n\n return {\n base:\n base === undefined ? \"HEAD\" : readNonEmptyString(base, \"Git worktree create options base\"),\n path:\n path === undefined ? undefined : readNonEmptyString(path, \"Git worktree create options path\")\n };\n}\n\nfunction createSavepointRef(): string {\n return `${SAVEPOINT_REF_PREFIX}${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction createDefaultWorktreePath(repoRoot: string, branch: string): string {\n return resolve(repoRoot, \".poe-code\", \"worktrees\", createSafeBranchDirectoryName(branch));\n}\n\nfunction createSafeBranchDirectoryName(branch: string): string {\n return encodeURIComponent(branch);\n}\n\nasync function getRepoRoot(cwd: string): Promise<string> {\n return runGitAndTrim(cwd, [\"rev-parse\", \"--show-toplevel\"]);\n}\n\nasync function gitRefExists(cwd: string, ref: string): Promise<boolean> {\n try {\n await runGit(cwd, [\"show-ref\", \"--verify\", \"--quiet\", ref]);\n return true;\n } catch (error) {\n void error;\n return false;\n }\n}\n\nasync function listWorktrees(cwd: string): Promise<GitWorktree[]> {\n const { stdout } = await runGit(cwd, [\"worktree\", \"list\", \"--porcelain\"]);\n return parseWorktreeList(stdout);\n}\n\nfunction parseWorktreeList(output: string): GitWorktree[] {\n const worktrees: GitWorktree[] = [];\n let current: Partial<GitWorktree> = {};\n\n const finishCurrent = () => {\n if (current.path !== undefined && current.branch !== undefined) {\n worktrees.push({\n path: current.path,\n branch: current.branch\n });\n }\n\n current = {};\n };\n\n for (const line of output.split(\"\\n\")) {\n if (line.length === 0) {\n finishCurrent();\n continue;\n }\n\n if (line.startsWith(\"worktree \")) {\n current.path = line.slice(\"worktree \".length);\n continue;\n }\n\n if (line.startsWith(\"branch \")) {\n current.branch = parseBranchRef(line.slice(\"branch \".length));\n }\n }\n\n finishCurrent();\n return worktrees;\n}\n\nfunction parseBranchRef(ref: string): string {\n const branchPrefix = \"refs/heads/\";\n return ref.startsWith(branchPrefix) ? ref.slice(branchPrefix.length) : ref;\n}\n\nasync function resolveWorktreePath(repoRoot: string, path: string, label: string): Promise<string> {\n const resolvedRoot = resolve(repoRoot);\n const resolvedPath = isAbsolute(path) ? resolve(path) : resolve(resolvedRoot, path);\n const canonicalRoot = await realpath(resolvedRoot);\n const canonicalPath = await resolveCanonicalPath({ realpath, readlink }, resolvedPath);\n\n // A worktree lives somewhere under the repository, never at the repository root\n // itself, so root-itself is rejected alongside anything outside it. Both answers\n // come from the filesystem rather than the spelling: on a case-insensitive\n // filesystem a differently cased path still names the repository root.\n const [inside, isRootItself] = await Promise.all([\n containsPath(stat, canonicalRoot, canonicalPath),\n isSamePath(stat, canonicalPath, canonicalRoot)\n ]);\n\n if (!inside || isRootItself) {\n throw new Error(`${label} must be inside the git repository.`);\n }\n\n return canonicalPath;\n}\n\nfunction isBranchAlreadyExistsError(error: unknown, branch: string): boolean {\n return (\n error instanceof Error &&\n error.message.includes(branch) &&\n error.message.includes(\"already exists\")\n );\n}\n\nasync function runGitAndTrim(cwd: string, args: string[]): Promise<string> {\n const { stdout } = await runGit(cwd, args);\n return stdout.trim();\n}\n\nasync function runGit(cwd: string, args: string[]): Promise<ExecFileResult> {\n return new Promise((resolve, reject) => {\n execFile(\n \"git\",\n args,\n {\n cwd,\n encoding: \"utf8\",\n maxBuffer: GIT_EXEC_MAX_BUFFER\n },\n (error, stdout, stderr) => {\n if (error) {\n reject(\n new Error(\n `${formatGitCommand(args)} failed: ${pickGitFailureMessage(stdout, stderr, error)}`\n )\n );\n return;\n }\n\n resolve({\n stdout,\n stderr\n });\n }\n );\n });\n}\n\nasync function cleanupFailedCheckpoint(cwd: string, stashRef: string): Promise<void> {\n await deleteSavepointRef(cwd, stashRef);\n const restored = await tryRunGit(cwd, [\"stash\", \"apply\", \"--index\", \"stash@{0}\"]);\n if (restored) {\n await tryRunGit(cwd, [\"stash\", \"drop\", \"stash@{0}\"]);\n }\n}\n\nasync function deleteSavepointRef(cwd: string, stashRef: string): Promise<void> {\n await tryRunGit(cwd, [\"update-ref\", \"--delete\", stashRef]);\n}\n\nasync function tryRunGit(cwd: string, args: string[]): Promise<boolean> {\n try {\n await runGit(cwd, args);\n return true;\n } catch (error) {\n void error;\n return false;\n }\n}\n\nfunction formatGitCommand(args: string[]): string {\n return [\"git\", ...args].join(\" \");\n}\n\nfunction pickGitFailureMessage(stdout: string, stderr: string, error: Error): string {\n const stderrText = stderr.trim();\n\n if (stderrText.length > 0) {\n return stderrText;\n }\n\n const stdoutText = stdout.trim();\n\n if (stdoutText.length > 0) {\n return stdoutText;\n }\n\n return error.message;\n}\n\nfunction readNonEmptyString(value: unknown, label: string): string {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new Error(`${label} must be a non-empty string.`);\n }\n\n return value;\n}\n\nfunction readNonEmptyStringArray(value: unknown, label: string): string[] {\n if (!Array.isArray(value) || value.length === 0) {\n throw new Error(`${label} must be a non-empty array of strings.`);\n }\n\n const entries = value.map((entry, index) => readNonEmptyString(entry, `${label}[${index}]`));\n return [...entries];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction getOwnProperty<Name extends PropertyKey>(value: object, name: Name): unknown {\n return hasOwnProperty(value, name) ? value[name] : undefined;\n}\n\nfunction hasOwnProperty<Name extends PropertyKey>(\n value: object,\n name: Name\n): value is Record<Name, unknown> {\n return Object.prototype.hasOwnProperty.call(value, name);\n}\n", "import { createSeededRandom } from \"../interp/globals/math.js\";\n\nexport type TimeModuleOptions = {\n now?: () => number;\n random?: () => number;\n seed?: number;\n signal?: AbortSignal;\n};\n\nexport function makeTimeModule(options: TimeModuleOptions = {}): {\n random: () => number;\n now: () => number;\n sleep: (ms: number) => Promise<void>;\n uuid: () => string;\n} {\n const hasDeterministicRandom = options.random !== undefined || options.seed !== undefined;\n const random =\n options.random ??\n (options.seed === undefined ? () => Math.random() : createSeededRandom(options.seed).next);\n const now = options.now ?? (() => Date.now());\n const signal = options.signal;\n\n async function sleep(ms: number): Promise<void> {\n assertSleepDelay(ms);\n\n return await new Promise<void>((resolve, reject) => {\n if (signal?.aborted === true) {\n reject(createSleepAbortError());\n return;\n }\n\n const start = performance.now();\n let timeout = setTimeout(resolveSleep, ms);\n\n function cleanup(): void {\n clearTimeout(timeout);\n signal?.removeEventListener(\"abort\", rejectSleep);\n }\n\n function resolveSleep(): void {\n const remainingMs = ms - (performance.now() - start);\n if (remainingMs > 0) {\n timeout = setTimeout(resolveSleep, remainingMs);\n return;\n }\n\n cleanup();\n resolve();\n }\n\n function rejectSleep(): void {\n cleanup();\n reject(createSleepAbortError());\n }\n\n signal?.addEventListener(\"abort\", rejectSleep, { once: true });\n });\n }\n\n return {\n random,\n now,\n sleep,\n uuid: hasDeterministicRandom ? () => createRandomUuid(random) : () => crypto.randomUUID()\n };\n}\n\nfunction createRandomUuid(random: () => number): string {\n const bytes = Array.from({ length: 16 }, () => Math.floor(readRandomUnit(random()) * 256));\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = bytes.map((byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n\n return [\n hex.slice(0, 8),\n hex.slice(8, 12),\n hex.slice(12, 16),\n hex.slice(16, 20),\n hex.slice(20)\n ].join(\"-\");\n}\n\nfunction readRandomUnit(value: number): number {\n if (Number.isFinite(value) && value >= 0 && value < 1) {\n return value;\n }\n\n throw new RangeError(\"time.uuid() random source must return a number in [0, 1).\");\n}\n\nfunction assertSleepDelay(ms: number): void {\n if (Number.isFinite(ms) && ms >= 0) {\n return;\n }\n\n throw new RangeError(\"time.sleep(ms) requires a non-negative finite millisecond delay.\");\n}\n\nfunction createSleepAbortError(): Error {\n return new Error(\"time.sleep aborted.\");\n}\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,UAAU,YAAY;AAC/B,SAAS,eAAe;AAsCxB,SAAS,YAAY,QAAiC,KAAsB;AAC1E,SAAO,OAAO,UAAU,eAAe,KAAK,QAAQ,GAAG,IAAI,OAAO,GAAG,IAAI;AAC3E;AAEA,SAAS,kBAAkB,UAAkB,aAAmD;AAC9F,SAAO;AAAA,IACL;AAAA,IACA,MAAM,YAAY,aAAa,MAAM;AAAA,IACrC,SAAS,YAAY,aAAa,SAAS;AAAA,EAC7C;AACF;AAEA,SAAS,4BAA4B,aAA4C;AAC/E,QAAM,SAAS,YAAY,aAAa,QAAQ;AAChD,MAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,MAAM,QAAQ,MAAM,GAAG;AAC1E;AAAA,EACF;AAEA,aAAW,CAAC,MAAM,UAAU,KAAK,OAAO,QAAQ,MAAM,GAAG;AACvD,QAAI,OAAO,eAAe,YAAY,eAAe,QAAQ,MAAM,QAAQ,UAAU,GAAG;AACtF;AAAA,IACF;AAEA,UAAM,SAAS;AACf,UAAM,QAAQ,YAAY,QAAQ,OAAO;AACzC,UAAM,OAAO,YAAY,QAAQ,MAAM;AACvC,QAAI,OAAO,UAAU,YAAY,OAAO,SAAS,UAAU;AACzD;AAAA,IACF;AAGA,QAAI,CAAC,YAAY,SAAS,IAAiB,GAAG;AAC5C;AAAA,IACF;AAEA,QAAI,CAAC,kBAAkB,OAAO,IAAiB,GAAG;AAChD,YAAM,IAAI,MAAM,kBAAkB,IAAI,aAAa,KAAK,4BAA4B,IAAI,IAAI;AAAA,IAC9F;AAAA,EACF;AACF;AAEO,IAAM,YAAN,cAAwB,MAAM;AAAA,EAC1B;AAAA,EAET,YAAY,aAAoC;AAC9C,UAAM,uBAAuB,WAAW,CAAC;AACzC,SAAK,OAAO;AACZ,SAAK,cAAc;AAAA,EACrB;AACF;AAEA,eAAsB,WACpB,UACA,SAC2B;AAC3B,QAAM,YAAY,mBAAmB,MAAM,gBAAgB,QAAQ,CAAC;AACpE,QAAM,EAAE,kBAAkB,aAAa,YAAY,IAAI,qBAAqB,UAAU,SAAS;AAC/F,8BAA4B,WAAW;AACvC,QAAM,OAAO,kBAAkB,UAAU,WAAW;AACpD,QAAM,UAAU,qBAAqB,QAAQ,WAAW,aAAa,IAAI,GAAG,WAAW;AACvF,QAAM,cAAc,KAAK,kBAAkB;AAAA,IACzC,UAAU;AAAA,IACV,SAAS,qCAAqC,OAAO;AAAA,EACvD,CAAC;AACD,QAAM,aAAa,YAAY,OAAO,CAAC,eAAe,WAAW,aAAa,OAAO;AAErF,MAAI,WAAW,SAAS,GAAG;AACzB,UAAM,IAAI,UAAU,UAAU;AAAA,EAChC;AAEA,SAAO,iBAAiB,kBAAkB;AAAA,IACxC,gBAAgB,iBAAiB,kBAAkB,QAAQ,IAAI,CAAC,IAAI;AAAA,IACpE,UAAU;AAAA,IACV;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB,QAAQ,QAAQ;AAAA,IAChB,iBAAiB,QAAQ;AAAA,IACzB,oBAAoB,QAAQ;AAAA,IAC5B,cAAc,QAAQ;AAAA,EACxB,CAAC;AACH;AAEA,eAAsB,eACpB,UACA,SAC2B;AAC3B,QAAM,OAAO,mBAAmB,QAAQ;AACxC,QAAM,CAAC,aAAa,SAAS,IAAI,MAAM,QAAQ,IAAI;AAAA,IACjD,gBAAgB,KAAK,YAAY;AAAA,IACjC,gBAAgB,KAAK,UAAU;AAAA,EACjC,CAAC;AACD,QAAM,EAAE,aAAa,KAAK,IAAI,iBAAiB,mBAAmB,WAAW,CAAC;AAC9E,8BAA4B,WAAW;AACvC,QAAM,mBAAmB,mBAAmB,SAAS;AACrD,QAAM,OAAO,kBAAkB,KAAK,cAAc,WAAW;AAC7D,QAAM,UAAU,QAAQ,WAAW,aAAa,IAAI;AACpD,QAAM,cAAc,KAAK,kBAAkB;AAAA,IACzC,oBAAoB,CAAC,QAAQ;AAAA,IAC7B,eAAe;AAAA,MACb,YAAY,CAAC,aAAa;AAAA,MAC1B,UAAU;AAAA,IACZ;AAAA,IACA,UAAU,KAAK;AAAA,IACf,mBAAmB,OAAO,KAAK,WAAW;AAAA,IAC1C,SAAS,qCAAqC,OAAO;AAAA,EACvD,CAAC;AACD,QAAM,aAAa,YAAY,OAAO,CAAC,eAAe,WAAW,aAAa,OAAO;AAErF,MAAI,WAAW,SAAS,GAAG;AACzB,UAAM,IAAI,UAAU,UAAU;AAAA,EAChC;AAEA,SAAO,iBAAiB,kBAAkB;AAAA,IACxC,gBAAgB,CAAC,WAAW;AAAA,IAC5B,UAAU,KAAK;AAAA,IACf,YAAY;AAAA,MACV;AAAA,MACA,UAAU,KAAK;AAAA,MACf,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,IAChB;AAAA,IACA;AAAA,IACA,UAAU,QAAQ;AAAA,IAClB,QAAQ,QAAQ;AAAA,IAChB,iBAAiB,QAAQ;AAAA,IACzB,oBAAoB,QAAQ;AAAA,IAC5B,cAAc,QAAQ;AAAA,EACxB,CAAC;AACH;AAEA,eAAe,gBAAgB,UAAmC;AAChE,MAAI;AACF,UAAM,QAAQ,MAAM,KAAK,QAAQ;AACjC,QAAI,CAAC,MAAM,OAAO,GAAG;AACnB,YAAM,IAAI,MAAM,sCAAsC,QAAQ,EAAE;AAAA,IAClE;AAEA,WAAO,MAAM,SAAS,UAAU,MAAM;AAAA,EACxC,SAAS,OAAO;AACd,QAAI,gBAAgB,OAAO,QAAQ,KAAK,gBAAgB,OAAO,SAAS,GAAG;AACzE,YAAM,IAAI,MAAM,2BAA2B,QAAQ,EAAE;AAAA,IACvD;AAEA,UAAM;AAAA,EACR;AACF;AAEA,SAAS,qBACP,UACA,QAKA;AACA,MAAI,gBAAgB,QAAQ,GAAG;AAC7B,QAAI,OAAO,WAAW,GAAG;AACvB,YAAM,IAAI,MAAM,8CAA8C,QAAQ,EAAE;AAAA,IAC1E;AAEA,WAAO;AAAA,MACL,kBAAkB;AAAA,MAClB,aAAa,CAAC;AAAA,MACd,aAAa;AAAA,IACf;AAAA,EACF;AAEA,QAAM,EAAE,aAAa,KAAK,IAAI,iBAAiB,MAAM;AACrD,MAAI,OAAO,WAAW,KAAK,KAAK,KAAK,EAAE,WAAW,GAAG;AACnD,UAAM,IAAI,MAAM,8CAA8C,QAAQ,EAAE;AAAA,EAC1E;AAEA,QAAM,kBAAkB,OAAO,SAAS,KAAK;AAC7C,QAAM,EAAE,QAAQ,iBAAiB,YAAY,IAAI;AAAA,IAC/C;AAAA,IACA,gBAAgB,QAAQ,GAAG,eAAe,IAAI;AAAA,EAChD;AACA,QAAM,qBAAqB,gBAAgB,QAAQ,GAAG,kBAAkB,WAAW;AAEnF,SAAO;AAAA,IACL,kBAAkB,uBAAuB,cAAc,eAAe,GAAG,kBAAkB;AAAA,IAC3F;AAAA,IACA,aAAa;AAAA,EACf;AACF;AAEA,SAAS,mBAAmB,UAAgE;AAC1F,QAAM,YAAY,QAAQ,QAAQ;AAClC,QAAM,WAAW,UAAU,WAAW,IAAI,WAAW,SAAS,MAAM,GAAG,CAAC,UAAU,MAAM;AAExF,MAAI,qBAAqB,SAAS,GAAG;AACnC,WAAO;AAAA,MACL,cAAc,GAAG,QAAQ;AAAA,MACzB,YAAY;AAAA,IACd;AAAA,EACF;AAEA,SAAO;AAAA,IACL,cAAc;AAAA,IACd,YAAY,GAAG,QAAQ;AAAA,EACzB;AACF;AAEA,SAAS,gBAAgB,UAA2B;AAClD,SAAO,qBAAqB,QAAQ,QAAQ,CAAC;AAC/C;AAEA,SAAS,qBAAqB,WAA4B;AACxD,SAAO,cAAc,aAAa,cAAc;AAClD;AAEA,eAAe,iBACb,kBACA,SAC2B;AAC3B,MAAI;AACF,WAAO,MAAM,IAAI,kBAAkB,OAAO;AAAA,EAC5C,SAAS,OAAO;AACd,QAAI,aAAa,KAAK,GAAG;AACvB,YAAM;AAAA,IACR;AAEA,WAAO;AAAA,MACL,GAAI,aAAa,KAAK,IAAI,EAAE,SAAS,KAAc,IAAI,CAAC;AAAA,MACxD;AAAA,MACA,IAAI;AAAA,IACN;AAAA,EACF;AACF;AAEA,SAAS,aAAa,OAAyB;AAC7C,SAAO,iBAAiB,SAAS,MAAM,SAAS;AAClD;AAEA,SAAS,aAAa,OAAyB;AAC7C,MAAI,iBAAiB,OAAO;AAC1B,WAAO,MAAM,SAAS;AAAA,EACxB;AAEA,SACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAO,UAAU,eAAe,KAAK,OAAO,MAAM,KACjD,MAA6B,SAAS;AAE3C;AAEA,SAAS,iBAAiB,QAAgB,UAA2B;AACnE,SAAO,YAAY,QAAQ,QAAQ,EAAE,KAAK;AAAA,IACxC,CAAC,cAAc,UAAU,SAAS;AAAA,EACpC;AACF;AAEA,SAAS,qBAAqB,SAAyB,aAAsC;AAC3F,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,EACT;AAEA,MAAI,mBAAmB,KAAK;AAC1B,UAAMA,cAAa,IAAI,IAAI,OAAO;AAClC,IAAAA,YAAW,OAAO,SAAS;AAC3B,WAAOA;AAAA,EACT;AAEA,QAAM,aAA4C,CAAC;AAEnD,aAAW,CAAC,YAAY,aAAa,KAAK,OAAO,QAAQ,OAAO,GAAG;AACjE,QAAI,eAAe,WAAW;AAC5B,iBAAW,UAAU,IAAI;AAAA,IAC3B;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,mBAAmB,QAAwB;AAClD,SAAO,OAAO,WAAW,QAAQ,IAAI,OAAO,MAAM,CAAC,IAAI;AACzD;AAEA,SAAS,cAAc,QAAwB;AAC7C,MAAI,CAAC,OAAO,WAAW,IAAI,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,MAAI,MAAM;AACV,SAAO,MAAM,OAAO,UAAU,OAAO,GAAG,MAAM,QAAQ,OAAO,GAAG,MAAM,KAAM,QAAO;AACnF,SAAO,GAAG,IAAI,OAAO,GAAG,CAAC,GAAG,OAAO,MAAM,GAAG,CAAC;AAC/C;AAEA,SAAS,uBAAuB,QAAgB,YAA4B;AAC1E,SAAO,GAAG,KAAK,OAAO,KAAK,IAAI,YAAY,CAAC,CAAC,CAAC,GAAG,MAAM;AACzD;AAEA,SAAS,uBAAuB,aAA4C;AAC1E,MAAI,YAAY,WAAW,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,SAAO,YACJ;AAAA,IACC,CAAC,eACC,GAAG,WAAW,QAAQ,IAAI,WAAW,IAAI,IAAI,WAAW,MAAM,IAAI,WAAW,IAAI,IAAI,WAAW,OAAO;AAAA,EAC3G,EACC,KAAK,IAAI;AACd;;;ACrVO,SAAS,6BAA6B,QAAgB,MAAsC;AACjG,aAAW,aAAa,OAAO,MAAM;AACnC,QAAI,UAAU,SAAS,4BAA4B;AACjD,UAAI,SAAS,WAAW;AACtB,eAAO,UAAU;AAAA,MACnB;AAEA;AAAA,IACF;AAEA,QAAI,UAAU,SAAS,0BAA0B;AAC/C;AAAA,IACF;AAEA,UAAM,cAAc,UAAU;AAC9B,QAAI,YAAY,SAAS,SAAS;AAChC,YAAM,IAAI,MAAM,aAAa,IAAI,gCAAgC;AAAA,IACnE;AAEA,QAAI,YAAY,aAAa,WAAW,GAAG;AACzC,YAAM,IAAI,MAAM,kEAAkE;AAAA,IACpF;AAEA,UAAM,aAAa,YAAY,aAAa,CAAC;AAC7C,QAAI,WAAW,GAAG,SAAS,cAAc;AACvC,YAAM,IAAI,MAAM,yDAAyD;AAAA,IAC3E;AAEA,QAAI,WAAW,GAAG,SAAS,MAAM;AAC/B;AAAA,IACF;AAEA,QAAI,WAAW,SAAS,QAAW;AACjC,YAAM,IAAI,MAAM,mBAAmB,IAAI,6BAA6B;AAAA,IACtE;AAEA,WAAO,WAAW;AAAA,EACpB;AAEA,SAAO;AACT;;;AC1CO,SAAS,cAAc,WAE5B;AACA,QAAM,eAAe,mBAAmB,SAAS;AAEjD,SAAO;AAAA,IACL,IAAI,MAAM;AACR,YAAM,iBAAiB,mBAAmB,MAAM,2BAA2B;AAE3E,UAAI,CAAC,aAAa,IAAI,cAAc,GAAG;AACrC,eAAO;AAAA,MACT;AAEA,aAAO,QAAQ,IAAI,cAAc;AAAA,IACnC;AAAA,EACF;AACF;AAEA,SAAS,mBAAmB,WAAmD;AAC7E,MAAI,CAAC,MAAM,QAAQ,SAAS,GAAG;AAC7B,UAAM,IAAI,MAAM,+DAA+D;AAAA,EACjF;AAEA,SAAO,IAAI;AAAA,IACT,UAAU,IAAI,CAAC,OAAO,UAAU,mBAAmB,OAAO,0BAA0B,KAAK,GAAG,CAAC;AAAA,EAC/F;AACF;AAEA,SAAS,mBAAmB,OAAgB,OAAuB;AACjE,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,IAAI,MAAM,GAAG,KAAK,8BAA8B;AAAA,EACxD;AAEA,QAAM,kBAAkB,MAAM,KAAK;AAEnC,MAAI,gBAAgB,WAAW,GAAG;AAChC,UAAM,IAAI,MAAM,GAAG,KAAK,8BAA8B;AAAA,EACxD;AAEA,SAAO;AACT;;;ACxCA,SAAS,gBAAgB;AACzB,SAAS,OAAO,UAAU,UAAU,IAAI,QAAAC,aAAY;AACpD,SAAS,SAAS,YAAY,eAAe;AA4B7C,IAAM,sBAAsB,KAAK,OAAO;AACxC,IAAM,uBAAuB;AAEtB,SAAS,cAAc,KAS5B;AACA,QAAM,gBAAgBC,oBAAmB,KAAK,gBAAgB;AAE9D,SAAO;AAAA,IACL,MAAM,OAAO;AACX,aAAO,cAAc,eAAe,CAAC,aAAa,MAAM,CAAC;AAAA,IAC3D;AAAA,IAEA,MAAM,aAAa;AACjB,YAAM,cAAc,MAAM,cAAc,eAAe,CAAC,aAAa,MAAM,CAAC;AAC5E,YAAM,SAAS,MAAM,cAAc,eAAe,CAAC,UAAU,aAAa,CAAC;AAE3E,UAAI,OAAO,WAAW,GAAG;AACvB,eAAO;AAAA,UACL,MAAM;AAAA,QACR;AAAA,MACF;AAEA,YAAM,WAAW,mBAAmB;AACpC,YAAM,eAAe,uBAAuB,QAAQ;AAEpD,YAAM,OAAO,eAAe;AAAA,QAC1B;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAED,UAAI;AACF,cAAM,WAAW,MAAM,cAAc,eAAe,CAAC,aAAa,WAAW,CAAC;AAC9E,cAAM,OAAO,eAAe,CAAC,cAAc,UAAU,QAAQ,CAAC;AAC9D,cAAM,OAAO,eAAe,CAAC,SAAS,SAAS,WAAW,QAAQ,CAAC;AACnE,cAAM,OAAO,eAAe,CAAC,SAAS,QAAQ,WAAW,CAAC;AAE1D,eAAO;AAAA,UACL,MAAM;AAAA,UACN;AAAA,QACF;AAAA,MACF,SAAS,OAAO;AACd,cAAM,wBAAwB,eAAe,QAAQ;AACrD,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IAEA,MAAM,OAAO,SAAS;AACpB,YAAM,oBAAoB,uBAAuB,OAAO;AAExD,UAAI,kBAAkB,UAAU,QAAW;AACzC,cAAM,OAAO,eAAe,CAAC,OAAO,OAAO,CAAC;AAC5C,cAAM,OAAO,eAAe,CAAC,UAAU,aAAa,kBAAkB,OAAO,CAAC;AAAA,MAChF,OAAO;AACL,cAAM,OAAO,eAAe,CAAC,OAAO,MAAM,GAAG,kBAAkB,KAAK,CAAC;AACrE,cAAM,OAAO,eAAe;AAAA,UAC1B;AAAA,UACA;AAAA,UACA,kBAAkB;AAAA,UAClB;AAAA,UACA,GAAG,kBAAkB;AAAA,QACvB,CAAC;AAAA,MACH;AACA,aAAO,cAAc,eAAe,CAAC,aAAa,MAAM,CAAC;AAAA,IAC3D;AAAA,IAEA,MAAM,OAAO,WAAW;AACtB,YAAM,sBAAsB,mBAAmB,SAAS;AAExD,UAAI;AACF,cAAM,OAAO,eAAe,CAAC,SAAS,UAAU,oBAAoB,IAAI,CAAC;AACzE,cAAM,OAAO,eAAe,CAAC,SAAS,WAAW,IAAI,CAAC;AAEtD,YAAI,oBAAoB,aAAa,QAAW;AAC9C,gBAAM,OAAO,eAAe,CAAC,SAAS,SAAS,WAAW,oBAAoB,QAAQ,CAAC;AAAA,QACzF;AAAA,MACF,UAAE;AACA,YAAI,oBAAoB,aAAa,QAAW;AAC9C,gBAAM,mBAAmB,eAAe,oBAAoB,QAAQ;AAAA,QACtE;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,OAAO;AACX,YAAM,EAAE,OAAO,IAAI,MAAM,OAAO,eAAe,CAAC,QAAQ,QAAQ,IAAI,CAAC;AACrE,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,eAAe,QAAQ,SAAS;AACpC,YAAM,mBAAmBA,oBAAmB,QAAQ,qBAAqB;AACzE,YAAM,oBAAoB,+BAA+B,OAAO;AAChE,YAAM,WAAW,MAAM,YAAY,aAAa;AAChD,YAAM,eAAe,MAAM;AAAA,QACzB;AAAA,QACA,kBAAkB,QAAQ,0BAA0B,UAAU,gBAAgB;AAAA,QAC9E;AAAA,MACF;AAEA,UAAI,MAAM,aAAa,eAAe,cAAc,gBAAgB,EAAE,GAAG;AACvE,cAAM,IAAI,MAAM,wBAAwB,gBAAgB,mBAAmB;AAAA,MAC7E;AAEA,YAAM,MAAM,QAAQ,YAAY,GAAG,EAAE,WAAW,KAAK,CAAC;AAEtD,UAAI;AACF,cAAM,OAAO,eAAe;AAAA,UAC1B;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,kBAAkB;AAAA,QACpB,CAAC;AAAA,MACH,SAAS,OAAO;AACd,YAAI,2BAA2B,OAAO,gBAAgB,GAAG;AACvD,gBAAM,IAAI,MAAM,wBAAwB,gBAAgB,mBAAmB;AAAA,QAC7E;AAEA,cAAM;AAAA,MACR;AAEA,aAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,IAEA,MAAM,eAAe,MAAM;AACzB,YAAM,WAAW,MAAM,YAAY,aAAa;AAChD,YAAM,eAAe,MAAM,oBAAoB,UAAU,MAAM,mBAAmB;AAClF,YAAM,YAAY,MAAM,cAAc,aAAa;AAEnD,UAAI,CAAC,UAAU,KAAK,CAAC,aAAa,QAAQ,SAAS,IAAI,MAAM,YAAY,GAAG;AAC1E;AAAA,MACF;AAEA,YAAM,OAAO,eAAe,CAAC,YAAY,UAAU,WAAW,YAAY,CAAC;AAC3E,YAAM,GAAG,cAAc,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,IACzD;AAAA,IAEA,MAAM,eAAe;AACnB,aAAO,cAAc,aAAa;AAAA,IACpC;AAAA,EACF;AACF;AAEA,SAAS,uBAAuB,SAAuD;AACrF,MAAI,CAAC,SAAS,OAAO,GAAG;AACtB,UAAM,IAAI,MAAM,uCAAuC;AAAA,EACzD;AAEA,QAAM,QAAQ,eAAe,SAAS,OAAO;AAE7C,SAAO;AAAA,IACL,SAASA,oBAAmB,eAAe,SAAS,SAAS,GAAG,4BAA4B;AAAA,IAC5F,OACE,UAAU,SAAY,SAAY,wBAAwB,OAAO,0BAA0B;AAAA,EAC/F;AACF;AAEA,SAAS,mBAAmB,WAAiD;AAC3E,MAAI,CAAC,SAAS,SAAS,GAAG;AACxB,UAAM,IAAI,MAAM,kCAAkC;AAAA,EACpD;AAEA,QAAM,gBAAgB,eAAe,WAAW,UAAU;AAC1D,QAAM,WACJ,kBAAkB,SACd,SACAA,oBAAmB,eAAe,wBAAwB;AAChE,MAAI,aAAa,UAAa,CAAC,SAAS,WAAW,oBAAoB,GAAG;AACxE,UAAM,IAAI,MAAM,sDAAsD;AAAA,EACxE;AAEA,SAAO;AAAA,IACL,MAAMA,oBAAmB,eAAe,WAAW,MAAM,GAAG,oBAAoB;AAAA,IAChF;AAAA,EACF;AACF;AAEA,SAAS,+BACP,SAGA;AACA,MAAI,YAAY,QAAW;AACzB,WAAO;AAAA,MACL,MAAM;AAAA,IACR;AAAA,EACF;AAEA,MAAI,CAAC,SAAS,OAAO,GAAG;AACtB,UAAM,IAAI,MAAM,gDAAgD;AAAA,EAClE;AAEA,QAAM,OAAO,eAAe,SAAS,MAAM;AAC3C,QAAM,OAAO,eAAe,SAAS,MAAM;AAE3C,SAAO;AAAA,IACL,MACE,SAAS,SAAY,SAASA,oBAAmB,MAAM,kCAAkC;AAAA,IAC3F,MACE,SAAS,SAAY,SAAYA,oBAAmB,MAAM,kCAAkC;AAAA,EAChG;AACF;AAEA,SAAS,qBAA6B;AACpC,SAAO,GAAG,oBAAoB,GAAG,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACrG;AAEA,SAAS,0BAA0B,UAAkB,QAAwB;AAC3E,SAAO,QAAQ,UAAU,aAAa,aAAa,8BAA8B,MAAM,CAAC;AAC1F;AAEA,SAAS,8BAA8B,QAAwB;AAC7D,SAAO,mBAAmB,MAAM;AAClC;AAEA,eAAe,YAAY,KAA8B;AACvD,SAAO,cAAc,KAAK,CAAC,aAAa,iBAAiB,CAAC;AAC5D;AAEA,eAAe,aAAa,KAAa,KAA+B;AACtE,MAAI;AACF,UAAM,OAAO,KAAK,CAAC,YAAY,YAAY,WAAW,GAAG,CAAC;AAC1D,WAAO;AAAA,EACT,SAAS,OAAO;AACd,SAAK;AACL,WAAO;AAAA,EACT;AACF;AAEA,eAAe,cAAc,KAAqC;AAChE,QAAM,EAAE,OAAO,IAAI,MAAM,OAAO,KAAK,CAAC,YAAY,QAAQ,aAAa,CAAC;AACxE,SAAO,kBAAkB,MAAM;AACjC;AAEA,SAAS,kBAAkB,QAA+B;AACxD,QAAM,YAA2B,CAAC;AAClC,MAAI,UAAgC,CAAC;AAErC,QAAM,gBAAgB,MAAM;AAC1B,QAAI,QAAQ,SAAS,UAAa,QAAQ,WAAW,QAAW;AAC9D,gBAAU,KAAK;AAAA,QACb,MAAM,QAAQ;AAAA,QACd,QAAQ,QAAQ;AAAA,MAClB,CAAC;AAAA,IACH;AAEA,cAAU,CAAC;AAAA,EACb;AAEA,aAAW,QAAQ,OAAO,MAAM,IAAI,GAAG;AACrC,QAAI,KAAK,WAAW,GAAG;AACrB,oBAAc;AACd;AAAA,IACF;AAEA,QAAI,KAAK,WAAW,WAAW,GAAG;AAChC,cAAQ,OAAO,KAAK,MAAM,YAAY,MAAM;AAC5C;AAAA,IACF;AAEA,QAAI,KAAK,WAAW,SAAS,GAAG;AAC9B,cAAQ,SAAS,eAAe,KAAK,MAAM,UAAU,MAAM,CAAC;AAAA,IAC9D;AAAA,EACF;AAEA,gBAAc;AACd,SAAO;AACT;AAEA,SAAS,eAAe,KAAqB;AAC3C,QAAM,eAAe;AACrB,SAAO,IAAI,WAAW,YAAY,IAAI,IAAI,MAAM,aAAa,MAAM,IAAI;AACzE;AAEA,eAAe,oBAAoB,UAAkB,MAAc,OAAgC;AACjG,QAAM,eAAe,QAAQ,QAAQ;AACrC,QAAM,eAAe,WAAW,IAAI,IAAI,QAAQ,IAAI,IAAI,QAAQ,cAAc,IAAI;AAClF,QAAM,gBAAgB,MAAM,SAAS,YAAY;AACjD,QAAM,gBAAgB,MAAM,qBAAqB,EAAE,UAAU,SAAS,GAAG,YAAY;AAMrF,QAAM,CAAC,QAAQ,YAAY,IAAI,MAAM,QAAQ,IAAI;AAAA,IAC/C,aAAaC,OAAM,eAAe,aAAa;AAAA,IAC/C,WAAWA,OAAM,eAAe,aAAa;AAAA,EAC/C,CAAC;AAED,MAAI,CAAC,UAAU,cAAc;AAC3B,UAAM,IAAI,MAAM,GAAG,KAAK,qCAAqC;AAAA,EAC/D;AAEA,SAAO;AACT;AAEA,SAAS,2BAA2B,OAAgB,QAAyB;AAC3E,SACE,iBAAiB,SACjB,MAAM,QAAQ,SAAS,MAAM,KAC7B,MAAM,QAAQ,SAAS,gBAAgB;AAE3C;AAEA,eAAe,cAAc,KAAa,MAAiC;AACzE,QAAM,EAAE,OAAO,IAAI,MAAM,OAAO,KAAK,IAAI;AACzC,SAAO,OAAO,KAAK;AACrB;AAEA,eAAe,OAAO,KAAa,MAAyC;AAC1E,SAAO,IAAI,QAAQ,CAACC,UAAS,WAAW;AACtC;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,QACE;AAAA,QACA,UAAU;AAAA,QACV,WAAW;AAAA,MACb;AAAA,MACA,CAAC,OAAO,QAAQ,WAAW;AACzB,YAAI,OAAO;AACT;AAAA,YACE,IAAI;AAAA,cACF,GAAG,iBAAiB,IAAI,CAAC,YAAY,sBAAsB,QAAQ,QAAQ,KAAK,CAAC;AAAA,YACnF;AAAA,UACF;AACA;AAAA,QACF;AAEA,QAAAA,SAAQ;AAAA,UACN;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,eAAe,wBAAwB,KAAa,UAAiC;AACnF,QAAM,mBAAmB,KAAK,QAAQ;AACtC,QAAM,WAAW,MAAM,UAAU,KAAK,CAAC,SAAS,SAAS,WAAW,WAAW,CAAC;AAChF,MAAI,UAAU;AACZ,UAAM,UAAU,KAAK,CAAC,SAAS,QAAQ,WAAW,CAAC;AAAA,EACrD;AACF;AAEA,eAAe,mBAAmB,KAAa,UAAiC;AAC9E,QAAM,UAAU,KAAK,CAAC,cAAc,YAAY,QAAQ,CAAC;AAC3D;AAEA,eAAe,UAAU,KAAa,MAAkC;AACtE,MAAI;AACF,UAAM,OAAO,KAAK,IAAI;AACtB,WAAO;AAAA,EACT,SAAS,OAAO;AACd,SAAK;AACL,WAAO;AAAA,EACT;AACF;AAEA,SAAS,iBAAiB,MAAwB;AAChD,SAAO,CAAC,OAAO,GAAG,IAAI,EAAE,KAAK,GAAG;AAClC;AAEA,SAAS,sBAAsB,QAAgB,QAAgB,OAAsB;AACnF,QAAM,aAAa,OAAO,KAAK;AAE/B,MAAI,WAAW,SAAS,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,OAAO,KAAK;AAE/B,MAAI,WAAW,SAAS,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,SAAO,MAAM;AACf;AAEA,SAASF,oBAAmB,OAAgB,OAAuB;AACjE,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,WAAW,GAAG;AAC1D,UAAM,IAAI,MAAM,GAAG,KAAK,8BAA8B;AAAA,EACxD;AAEA,SAAO;AACT;AAEA,SAAS,wBAAwB,OAAgB,OAAyB;AACxE,MAAI,CAAC,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAAG;AAC/C,UAAM,IAAI,MAAM,GAAG,KAAK,wCAAwC;AAAA,EAClE;AAEA,QAAM,UAAU,MAAM,IAAI,CAAC,OAAO,UAAUA,oBAAmB,OAAO,GAAG,KAAK,IAAI,KAAK,GAAG,CAAC;AAC3F,SAAO,CAAC,GAAG,OAAO;AACpB;AAEA,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEA,SAAS,eAAyC,OAAe,MAAqB;AACpF,SAAO,eAAe,OAAO,IAAI,IAAI,MAAM,IAAI,IAAI;AACrD;AAEA,SAAS,eACP,OACA,MACgC;AAChC,SAAO,OAAO,UAAU,eAAe,KAAK,OAAO,IAAI;AACzD;;;AC5bO,SAAS,eAAe,UAA6B,CAAC,GAK3D;AACA,QAAM,yBAAyB,QAAQ,WAAW,UAAa,QAAQ,SAAS;AAChF,QAAM,SACJ,QAAQ,WACP,QAAQ,SAAS,SAAY,MAAM,KAAK,OAAO,IAAI,mBAAmB,QAAQ,IAAI,EAAE;AACvF,QAAM,MAAM,QAAQ,QAAQ,MAAM,KAAK,IAAI;AAC3C,QAAM,SAAS,QAAQ;AAEvB,iBAAe,MAAM,IAA2B;AAC9C,qBAAiB,EAAE;AAEnB,WAAO,MAAM,IAAI,QAAc,CAACG,UAAS,WAAW;AAClD,UAAI,QAAQ,YAAY,MAAM;AAC5B,eAAO,sBAAsB,CAAC;AAC9B;AAAA,MACF;AAEA,YAAM,QAAQ,YAAY,IAAI;AAC9B,UAAI,UAAU,WAAW,cAAc,EAAE;AAEzC,eAAS,UAAgB;AACvB,qBAAa,OAAO;AACpB,gBAAQ,oBAAoB,SAAS,WAAW;AAAA,MAClD;AAEA,eAAS,eAAqB;AAC5B,cAAM,cAAc,MAAM,YAAY,IAAI,IAAI;AAC9C,YAAI,cAAc,GAAG;AACnB,oBAAU,WAAW,cAAc,WAAW;AAC9C;AAAA,QACF;AAEA,gBAAQ;AACR,QAAAA,SAAQ;AAAA,MACV;AAEA,eAAS,cAAoB;AAC3B,gBAAQ;AACR,eAAO,sBAAsB,CAAC;AAAA,MAChC;AAEA,cAAQ,iBAAiB,SAAS,aAAa,EAAE,MAAM,KAAK,CAAC;AAAA,IAC/D,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,yBAAyB,MAAM,iBAAiB,MAAM,IAAI,MAAM,OAAO,WAAW;AAAA,EAC1F;AACF;AAEA,SAAS,iBAAiB,QAA8B;AACtD,QAAM,QAAQ,MAAM,KAAK,EAAE,QAAQ,GAAG,GAAG,MAAM,KAAK,MAAM,eAAe,OAAO,CAAC,IAAI,GAAG,CAAC;AACzF,QAAM,CAAC,IAAK,MAAM,CAAC,IAAI,KAAQ;AAC/B,QAAM,CAAC,IAAK,MAAM,CAAC,IAAI,KAAQ;AAC/B,QAAM,MAAM,MAAM,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAE3E,SAAO;AAAA,IACL,IAAI,MAAM,GAAG,CAAC;AAAA,IACd,IAAI,MAAM,GAAG,EAAE;AAAA,IACf,IAAI,MAAM,IAAI,EAAE;AAAA,IAChB,IAAI,MAAM,IAAI,EAAE;AAAA,IAChB,IAAI,MAAM,EAAE;AAAA,EACd,EAAE,KAAK,GAAG;AACZ;AAEA,SAAS,eAAe,OAAuB;AAC7C,MAAI,OAAO,SAAS,KAAK,KAAK,SAAS,KAAK,QAAQ,GAAG;AACrD,WAAO;AAAA,EACT;AAEA,QAAM,IAAI,WAAW,2DAA2D;AAClF;AAEA,SAAS,iBAAiB,IAAkB;AAC1C,MAAI,OAAO,SAAS,EAAE,KAAK,MAAM,GAAG;AAClC;AAAA,EACF;AAEA,QAAM,IAAI,WAAW,kEAAkE;AACzF;AAEA,SAAS,wBAA+B;AACtC,SAAO,IAAI,MAAM,qBAAqB;AACxC;",
6
+ "names": ["rawModules", "stat", "readNonEmptyString", "stat", "resolve", "resolve"]
7
+ }
@@ -7,6 +7,8 @@ type CallerInjectedFunction = {
7
7
  bivarianceHack(...args: readonly unknown[]): unknown;
8
8
  }["bivarianceHack"];
9
9
  type HostBridgeOptions = {
10
+ registerCapabilities?: boolean;
11
+ capabilityPath?: readonly string[];
10
12
  budget: Budget;
11
13
  hostCalls?: HostCallJournal;
12
14
  moduleId?: string;
@@ -29,18 +29,19 @@ export function readHostOperationPolicy(operation) {
29
29
  return hostOperationPolicies.get(operation);
30
30
  }
31
31
  export function wrapCallerInjectedBindings(bindings, options) {
32
+ options = { ...options, registerCapabilities: true };
32
33
  const state = { seen: new WeakMap() };
33
34
  const copied = Object.fromEntries(Object.entries(bindings).map(([name, value]) => [
34
35
  name,
35
36
  typeof value === "function"
36
- ? wrapCallerInjectedFunction(name, value, options, state)
37
- : copyHostValueToSandbox(value, [], { ...options, operation: name }, state, "<root>")
37
+ ? wrapCallerInjectedFunction(name, value, { ...options, capabilityPath: [name] }, state)
38
+ : copyHostValueToSandbox(value, [], { ...options, operation: name, capabilityPath: [name] }, state, "<root>")
38
39
  ]));
39
40
  options.budget.provisionDataUsage(measureSandboxData(Object.values(copied)));
40
41
  return copied;
41
42
  }
42
43
  function wrapCallerInjectedFunction(name, value, options, state) {
43
- const existing = state.seen.get(value);
44
+ const existing = state.seen.get(value) ?? options.hostCalls?.nativeClosures.get(value);
44
45
  if (existing !== undefined)
45
46
  return existing;
46
47
  const bindingName = name === "default" && value.name.length > 0 ? value.name : name;
@@ -132,6 +133,12 @@ function wrapCallerInjectedFunction(name, value, options, state) {
132
133
  name: bindingName,
133
134
  properties: (closure) => {
134
135
  state.seen.set(value, closure);
136
+ if (options.registerCapabilities) {
137
+ options.hostCalls?.registerHostCapability(JSON.stringify([
138
+ options.moduleId ?? "<bindings>",
139
+ ...(options.capabilityPath ?? [bindingName])
140
+ ]), closure, value);
141
+ }
135
142
  return (copyFunctionProperties(callable, [], options, state, options.operation ?? bindingName) ?? {});
136
143
  }
137
144
  });
@@ -316,7 +323,7 @@ function isAbortReason(reason) {
316
323
  ("code" in reason && reason.code === "aborted")));
317
324
  }
318
325
  function copyHostResultToSandbox(result, stackFrames, options) {
319
- const value = copyHostValueToSandbox(result, stackFrames, options, {
326
+ const value = copyHostValueToSandbox(result, stackFrames, { ...options, registerCapabilities: false }, {
320
327
  seen: new WeakMap()
321
328
  }, "<root>");
322
329
  options.budget.provisionDataUsage(measureSandboxData([value]));
@@ -603,7 +610,7 @@ function copyHostValueToSandbox(value, stackFrames, options, state, path) {
603
610
  if (!("value" in descriptor)) {
604
611
  throw new TypeError(`Unsupported sandbox value at ${path}[${index}]: accessor property`);
605
612
  }
606
- copy[index] = copyHostValueToSandbox(descriptor.value, stackFrames, options, state, `${path}[${index}]`);
613
+ copy[index] = copyHostValueToSandbox(descriptor.value, stackFrames, { ...options, capabilityPath: [...(options.capabilityPath ?? []), String(index)] }, state, `${path}[${index}]`);
607
614
  }
608
615
  return copy;
609
616
  }
@@ -616,7 +623,8 @@ function copyHostValueToSandbox(value, stackFrames, options, state, path) {
616
623
  state.seen.set(value, copy);
617
624
  budget.allocateCollectionEntries(value.size);
618
625
  for (const [key, entry] of value) {
619
- copy.entries.set(copyHostValueToSandbox(key, stackFrames, options, state, `${path}.<key>`), copyHostValueToSandbox(entry, stackFrames, options, state, `${path}.<value>`));
626
+ const ordinal = copy.entries.size;
627
+ copy.entries.set(copyHostValueToSandbox(key, stackFrames, { ...options, capabilityPath: [...(options.capabilityPath ?? []), `key:${ordinal}`] }, state, `${path}.<key>`), copyHostValueToSandbox(entry, stackFrames, { ...options, capabilityPath: [...(options.capabilityPath ?? []), `value:${ordinal}`] }, state, `${path}.<value>`));
620
628
  }
621
629
  return copy;
622
630
  }
@@ -629,7 +637,10 @@ function copyHostValueToSandbox(value, stackFrames, options, state, path) {
629
637
  state.seen.set(value, copy);
630
638
  budget.allocateCollectionEntries(value.size);
631
639
  for (const entry of value) {
632
- copy.values.add(copyHostValueToSandbox(entry, stackFrames, options, state, `${path}.<value>`));
640
+ copy.values.add(copyHostValueToSandbox(entry, stackFrames, {
641
+ ...options,
642
+ capabilityPath: [...(options.capabilityPath ?? []), String(copy.values.size)]
643
+ }, state, `${path}.<value>`));
633
644
  }
634
645
  return copy;
635
646
  }
@@ -653,7 +664,7 @@ function copyHostValueToSandbox(value, stackFrames, options, state, path) {
653
664
  enumerable: true,
654
665
  configurable: true,
655
666
  writable: true,
656
- value: copyHostValueToSandbox(descriptor.value, stackFrames, options, state, joinPath(path, key))
667
+ value: copyHostValueToSandbox(descriptor.value, stackFrames, { ...options, capabilityPath: [...(options.capabilityPath ?? []), key] }, state, joinPath(path, key))
657
668
  });
658
669
  }
659
670
  return copy;
@@ -725,7 +736,7 @@ function copyFunctionProperties(callable, stackFrames, options, state, path) {
725
736
  if (typeof descriptor.value !== "function") {
726
737
  continue;
727
738
  }
728
- properties[key] = copyHostValueToSandbox(descriptor.value, stackFrames, options, state, joinPath(path, key));
739
+ properties[key] = copyHostValueToSandbox(descriptor.value, stackFrames, { ...options, capabilityPath: [...(options.capabilityPath ?? []), key] }, state, joinPath(path, key));
729
740
  }
730
741
  return Object.keys(properties).length > 0 ? properties : undefined;
731
742
  }
@@ -83,8 +83,10 @@ export declare class HostCallJournal {
83
83
  private retainedSize;
84
84
  private readonly outcomeSizes;
85
85
  private readonly capabilities;
86
+ private readonly requiredHostCapabilities;
86
87
  private readonly capabilityIds;
87
88
  readonly nativeClosures: WeakMap<object, SandboxClosure>;
89
+ private readonly hostSources;
88
90
  private readonly encodedOutcomes;
89
91
  private readonly callbackSizes;
90
92
  private readonly completedCallbackOwners;
@@ -110,6 +112,9 @@ export declare class HostCallJournal {
110
112
  snapshot(): HostCallRecord[];
111
113
  dispose(): void;
112
114
  registerCallbackFunction(record: HostCallRecord, id: number, closure: SandboxClosure, native: object): void;
115
+ registerHostCapability(path: string, closure: SandboxClosure, native: object): void;
116
+ rebindHostCapability(original: SandboxClosure, restored: SandboxClosure): void;
117
+ validateHostCapabilities(): void;
113
118
  waitForCapability(id: string): Promise<void>;
114
119
  trackCallbackCompletion(record: HostCallRecord, callbacks: readonly Promise<unknown>[]): void;
115
120
  recordCallback(record: HostCallRecord, id: number, args: SandboxValue[], step: number): string;
@@ -36,8 +36,10 @@ export class HostCallJournal {
36
36
  retainedSize = 0;
37
37
  outcomeSizes = new Map();
38
38
  capabilities = new Map();
39
+ requiredHostCapabilities = new Set();
39
40
  capabilityIds = new WeakMap();
40
41
  nativeClosures = new WeakMap();
42
+ hostSources = new WeakMap();
41
43
  encodedOutcomes = new Map();
42
44
  callbackSizes = new Map();
43
45
  completedCallbackOwners = new Set();
@@ -55,7 +57,7 @@ export class HostCallJournal {
55
57
  this.budget = budget;
56
58
  this.recordedReplay = replay !== undefined;
57
59
  if (replay !== undefined) {
58
- const replayRecords = restoreReplayCalls(replay, this.encodedOutcomes, this.callbackSizes);
60
+ const replayRecords = restoreReplayCalls(replay, this.encodedOutcomes, this.callbackSizes, this.requiredHostCapabilities);
59
61
  const restoredRunId = replayRecords[0]?.runId ?? records[0]?.runId ?? randomUUID();
60
62
  validateRestoredRecords(records, restoredRunId, sourceHash);
61
63
  for (const record of records) {
@@ -221,6 +223,34 @@ export class HostCallJournal {
221
223
  this.capabilityIds.set(closure, identity);
222
224
  this.nativeClosures.set(native, closure);
223
225
  }
226
+ registerHostCapability(path, closure, native) {
227
+ const identity = `host:${path}`;
228
+ const existing = this.capabilities.get(identity);
229
+ if (existing !== undefined && existing !== closure)
230
+ throw new TypeError(`Conflicting replay capability '${identity}'.`);
231
+ this.capabilities.set(identity, closure);
232
+ if (!this.capabilityIds.has(closure))
233
+ this.capabilityIds.set(closure, identity);
234
+ if (!this.nativeClosures.has(native))
235
+ this.nativeClosures.set(native, closure);
236
+ this.hostSources.set(closure, native);
237
+ }
238
+ rebindHostCapability(original, restored) {
239
+ const identity = this.capabilityIds.get(original);
240
+ const source = this.hostSources.get(original);
241
+ if (identity === undefined || source === undefined)
242
+ return;
243
+ this.capabilities.set(identity, restored);
244
+ this.capabilityIds.set(restored, identity);
245
+ this.nativeClosures.set(source, restored);
246
+ this.hostSources.set(restored, source);
247
+ }
248
+ validateHostCapabilities() {
249
+ for (const identity of this.requiredHostCapabilities) {
250
+ if (!this.capabilities.has(identity))
251
+ throw new UnresolvedReplayCapabilityError(identity);
252
+ }
253
+ }
224
254
  waitForCapability(id) {
225
255
  if (this.capabilities.has(id))
226
256
  return Promise.resolve();
@@ -338,7 +368,7 @@ function copyOutcome(outcome) {
338
368
  ? { status: "fulfilled", value: deepCopyToSandbox(outcome.value) }
339
369
  : { status: "rejected", reason: deepCopyToSandbox(outcome.reason) };
340
370
  }
341
- function restoreReplayCalls(input, encodedOutcomes, callbackSizes) {
371
+ function restoreReplayCalls(input, encodedOutcomes, callbackSizes, requiredHostCapabilities = new Set()) {
342
372
  validateSnapshotData(input);
343
373
  if (input === null ||
344
374
  typeof input !== "object" ||
@@ -364,7 +394,22 @@ function restoreReplayCalls(input, encodedOutcomes, callbackSizes) {
364
394
  }));
365
395
  }
366
396
  }
367
- const resolveCapability = (id) => capabilities.get(id);
397
+ const resolveCapability = (id) => {
398
+ if (!capabilities.has(id) && id.startsWith("host:")) {
399
+ const path = JSON.parse(id.slice(5));
400
+ if (!Array.isArray(path) ||
401
+ path.length < 2 ||
402
+ path.some((value) => typeof value !== "string"))
403
+ throw new TypeError("Invalid host capability identity.");
404
+ requiredHostCapabilities.add(id);
405
+ capabilities.set(id, createSandboxClosure({
406
+ call: () => {
407
+ throw new TypeError("Host capability has not been reconstructed.");
408
+ }
409
+ }));
410
+ }
411
+ return capabilities.get(id);
412
+ };
368
413
  return input.calls.map((entry, index) => {
369
414
  if (entry === null || typeof entry !== "object" || Array.isArray(entry))
370
415
  throw new TypeError("Invalid replay call.");
@@ -0,0 +1,7 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ export type RunResources = {
3
+ signal: AbortSignal;
4
+ add(close: () => Promise<void>): void;
5
+ };
6
+ export declare const runResources: AsyncLocalStorage<RunResources>;
7
+ export declare function withRunResources<Result>(signal: AbortSignal | undefined, execute: () => Promise<Result>): Promise<Result>;
@@ -0,0 +1,36 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ export const runResources = new AsyncLocalStorage();
3
+ export async function withRunResources(signal, execute) {
4
+ const controller = new AbortController();
5
+ const cancel = () => controller.abort(signal?.reason);
6
+ const cleanups = new Set();
7
+ const resources = {
8
+ signal: controller.signal,
9
+ add(close) {
10
+ cleanups.add(close);
11
+ }
12
+ };
13
+ signal?.addEventListener("abort", cancel, { once: true });
14
+ if (signal?.aborted)
15
+ cancel();
16
+ let result;
17
+ let failure;
18
+ let errors = [];
19
+ try {
20
+ result = await runResources.run(resources, execute);
21
+ }
22
+ catch (error) {
23
+ failure = { reason: error };
24
+ }
25
+ finally {
26
+ signal?.removeEventListener("abort", cancel);
27
+ controller.abort(new Error("SafeJS run finished."));
28
+ const outcomes = await Promise.allSettled([...cleanups].map((close) => Promise.resolve().then(close)));
29
+ errors = outcomes.flatMap((outcome) => (outcome.status === "rejected" ? [outcome.reason] : []));
30
+ }
31
+ if (failure !== undefined)
32
+ throw failure.reason;
33
+ if (errors.length > 0)
34
+ throw new AggregateError(errors, "SafeJS resource cleanup failed.");
35
+ return result;
36
+ }
@@ -0,0 +1,26 @@
1
+ import { McpClient, type HttpTransportFetch, type StdioSpawn } from "../../../tiny-mcp-client/dist/index.js";
2
+ export type McpServerConfig = {
3
+ command: string;
4
+ args?: string[];
5
+ cwd?: string;
6
+ env?: Record<string, string>;
7
+ } | {
8
+ url: string;
9
+ headers?: Record<string, string>;
10
+ };
11
+ export type McpModuleOptions = {
12
+ servers: Record<string, McpServerConfig>;
13
+ requestTimeoutMs?: number;
14
+ closeTimeoutMs?: number;
15
+ maxToolPages?: number;
16
+ signal?: AbortSignal;
17
+ fetch?: HttpTransportFetch;
18
+ spawn?: StdioSpawn;
19
+ };
20
+ export type ManagedMcpConnection = {
21
+ ready: Promise<McpClient>;
22
+ close(): Promise<void>;
23
+ };
24
+ export declare function parseMcpConfig(source: string, directory: string): McpModuleOptions;
25
+ export declare function normalizeMcpOptions(value: unknown, keys?: string[]): McpModuleOptions;
26
+ export declare function connectMcpTransport(server: McpServerConfig, options: McpModuleOptions, signal?: AbortSignal): ManagedMcpConnection;