@smthrs/std 1.0.0-rc.6 → 1.0.0-rc.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +39 -37
- package/dist/cjs/Bash.d.ts +10 -0
- package/dist/cjs/Bash.d.ts.map +1 -1
- package/dist/cjs/Bash.js +13 -12
- package/dist/cjs/Bash.js.map +3 -3
- package/dist/cjs/Checkpoints.d.ts +37 -0
- package/dist/cjs/Checkpoints.d.ts.map +1 -1
- package/dist/cjs/Checkpoints.js +75 -0
- package/dist/cjs/Checkpoints.js.map +2 -2
- package/dist/cjs/internal/EnvelopePrecheck.d.ts +2 -0
- package/dist/cjs/internal/EnvelopePrecheck.d.ts.map +1 -1
- package/dist/cjs/internal/EnvelopePrecheck.js +217 -34
- package/dist/cjs/internal/EnvelopePrecheck.js.map +3 -3
- package/dist/cjs/internal/Exec.d.ts +26 -14
- package/dist/cjs/internal/Exec.d.ts.map +1 -1
- package/dist/cjs/internal/Exec.js +73 -31
- package/dist/cjs/internal/Exec.js.map +2 -2
- package/dist/cjs/internal/Text.d.ts +11 -0
- package/dist/cjs/internal/Text.d.ts.map +1 -1
- package/dist/cjs/internal/Text.js +3 -0
- package/dist/cjs/internal/Text.js.map +2 -2
- package/dist/esm/Bash.d.ts +10 -0
- package/dist/esm/Bash.d.ts.map +1 -1
- package/dist/esm/Bash.js +22 -13
- package/dist/esm/Bash.js.map +1 -1
- package/dist/esm/Checkpoints.d.ts +37 -0
- package/dist/esm/Checkpoints.d.ts.map +1 -1
- package/dist/esm/Checkpoints.js +85 -0
- package/dist/esm/Checkpoints.js.map +1 -1
- package/dist/esm/internal/EnvelopePrecheck.d.ts +2 -0
- package/dist/esm/internal/EnvelopePrecheck.d.ts.map +1 -1
- package/dist/esm/internal/EnvelopePrecheck.js +295 -60
- package/dist/esm/internal/EnvelopePrecheck.js.map +1 -1
- package/dist/esm/internal/Exec.d.ts +26 -14
- package/dist/esm/internal/Exec.d.ts.map +1 -1
- package/dist/esm/internal/Exec.js +116 -30
- package/dist/esm/internal/Exec.js.map +1 -1
- package/dist/esm/internal/Text.d.ts +11 -0
- package/dist/esm/internal/Text.d.ts.map +1 -1
- package/dist/esm/internal/Text.js +11 -0
- package/dist/esm/internal/Text.js.map +1 -1
- package/docs/README.md +3 -1
- package/docs/api.md +21 -18
- package/docs/concepts/limits-and-disclosure.md +8 -5
- package/docs/guides/pin-a-checkpoint.md +20 -0
- package/docs/guides/run-a-shell-command.md +16 -6
- package/docs/installation.md +13 -9
- package/docs/quickstart.md +1 -1
- package/docs/reference/flows.md +2 -2
- package/docs/troubleshooting.md +15 -3
- package/package.json +6 -6
- package/src/Bash.ts +25 -13
- package/src/Checkpoints.ts +137 -0
- package/src/internal/EnvelopePrecheck.ts +295 -63
- package/src/internal/Exec.ts +157 -51
- package/src/internal/Text.ts +15 -0
package/dist/esm/Bash.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Bash.js","sourceRoot":"","sources":["../../src/Bash.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bash flow declaration and portable handler.\n *\n * Two ways in, and the difference is where the payload is parsed. `command` is\n * a line for the platform shell, exactly as before. `script` is program text\n * delivered to an interpreter on standard input, as data: nothing quotes it,\n * escapes it, or terminates it with a heredoc marker, so the class of failure\n * that produced `docker exec c bash -lc '…python - <<EOF…'` cannot occur. Its\n * `args` reach the script as arguments rather than as interpolated text.\n *\n * `container` names a container the host knows how to reach, and the argv is\n * built by the injected {@link Container} transport rather than typed by the\n * caller. A host that binds no transport refuses such a call by saying so; the\n * requirement is optional here precisely so a host without containers is not\n * forced to declare one.\n *\n * @since 1.0.0\n */\n\nimport type * as ChildProcessSpawner from \"@smthrs/kernel/ChildProcessSpawner\"\nimport * as Path from \"@smthrs/kernel/Path\"\nimport * as Effect from \"effect/Effect\"\nimport * as Option from \"effect/Option\"\nimport * as Schema from \"effect/Schema\"\nimport * as Container from \"./Container.ts\"\nimport { capability, envelope } from \"./internal/Declaration.ts\"\nimport * as Diagnostics from \"./internal/Diagnostics.ts\"\nimport { outsideEnvelope } from \"./internal/EnvelopePrecheck.ts\"\nimport * as Exec from \"./internal/Exec.ts\"\nimport { MAX_SHELL_OUTPUT_BYTES, truncateBytes } from \"./internal/Text.ts\"\nimport * as Probe from \"./Probe.ts\"\nimport * as StdError from \"./StdError.ts\"\nimport * as TreeFingerprint from \"./TreeFingerprint.ts\"\n\n/**\n * Registry name for the bash flow.\n *\n * @category identifiers\n * @since 1.0.0\n */\nexport const name = \"bash\"\n\n/**\n * Model-facing description of the bash flow.\n *\n * Capped at 200 characters and read every frame, which is why the\n * invalid-probe contract is not restated here: the cell contract teaches it\n * once, and a result that has one explains itself.\n *\n * @category descriptions\n * @since 1.0.0\n */\nexport const description =\n \"Run a shell command, or an interpreter over a script passed as data instead of quoted into a line. container routes it through the host's container transport. mode:hermetic pre-checks path tokens.\"\n\n/**\n * Default command timeout in milliseconds.\n *\n * Ten minutes lets full builds and test suites finish while ensuring a stalled\n * process eventually releases the flow.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const DEFAULT_TIMEOUT_MS = 600_000\n\nconst Command = Schema.optional(Schema.String.annotate({\n description: \"Shell command line to execute; give this or script, never both\"\n}))\nconst Script = Schema.optional(Schema.String.annotate({\n description: \"Program text delivered to the interpreter on stdin as data, so it needs no quoting or heredoc\"\n}))\nconst Interpreter = Schema.optional(Schema.String.annotate({\n description:\n \"Program that reads script on stdin; defaults to bash. python, python3, sh, zsh, node, ruby and perl are known\"\n}))\nconst Args = Schema.optional(\n Schema.Array(Schema.String).annotate({\n description: \"Arguments passed to the script as data, never interpolated into it\"\n })\n)\nconst Stdin = Schema.optional(Schema.String.annotate({\n description: \"Text written to the command's standard input; use script instead when the text is the program\"\n}))\nconst ContainerName = Schema.optional(Schema.String.annotate({\n description: \"Run inside this container through the host's container transport; requires mode:unhermetic\"\n}))\nconst Cwd = Schema.optional(Schema.String).annotate({\n description: \"Working directory for the command\",\n message: \"cwd must be a path string. For a checkpoint, pass { at: ctx.base } as the third ctx.call argument.\"\n})\nconst Env = Schema.optional(\n Schema.Record(Schema.String, Schema.String).annotate({ description: \"Environment variables for the command\" })\n)\nconst TimeoutMs = Schema.optional(Schema.Number).annotate({\n description: \"Wall-clock timeout in milliseconds; defaults to 600000 ms\",\n message: \"timeoutMs must be a number of milliseconds.\"\n})\n\n/**\n * Input schema for the bash flow.\n *\n * The mode named `hermetic` requires explicit read and write envelopes for a\n * lexical pre-check; it does not by itself confine the process. Unhermetic mode\n * deliberately omits those declarations so the distinction stays visible in\n * the input shape.\n *\n * @category schemas\n * @since 1.0.0\n */\nexport const Input = Schema.Union([\n Schema.Struct({\n mode: Schema.Literal(\"hermetic\").annotate({\n description: \"Lexically pre-check explicit path tokens against declared reads and writes; does not sandbox\"\n }),\n command: Command,\n script: Script,\n interpreter: Interpreter,\n args: Args,\n stdin: Stdin,\n container: ContainerName,\n reads: Schema.Array(Schema.NonEmptyString).annotate({ description: \"Paths or path globs the command may read\" }),\n writes: Schema.Array(Schema.NonEmptyString).annotate({ description: \"Paths or path globs the command may write\" }),\n cwd: Cwd,\n env: Env,\n timeoutMs: TimeoutMs\n }),\n Schema.Struct({\n // Omitted means unhermetic: it is the envelope `effects` already declares\n // for every call, so the default grants nothing a named mode would not.\n mode: Schema.Literal(\"unhermetic\").annotate({\n description: \"Run without a declared filesystem envelope as an irreversible effect; the default\"\n }).pipe(Schema.optionalKey, Schema.withDecodingDefaultKey(Effect.succeed(\"unhermetic\" as const))),\n command: Command,\n script: Script,\n interpreter: Interpreter,\n args: Args,\n stdin: Stdin,\n container: ContainerName,\n cwd: Cwd,\n env: Env,\n timeoutMs: TimeoutMs\n })\n])\n\n/**\n * Decoded input accepted by the bash handler.\n *\n * @category models\n * @since 1.0.0\n */\nexport type Input = typeof Input.Type\n\n/**\n * Output schema for the bash flow.\n *\n * `invalidProbe` is present when the shell itself refused to start the\n * command, which its exit code says outright: 127 for a program it could not\n * find, 126 for one it could not execute. Only that. Reading a runner's own\n * wording is a judgment rather than a fact, and `Probe.classify` makes it with\n * Jev for the `test` flow, whose caller is asking about a suite; a shell call\n * is any command at all and stays free of the gateway. See `Probe`.\n *\n * The `<stream>Truncated` flags are a wire convention, not a display detail.\n * A truncated capture is a fragment of what the process printed, and the\n * harness reads these flags to refuse a later write of those exact bytes —\n * `@smthrs/harness/TruncatedOutput` is the consuming half. Renaming a flag, or\n * dropping it when the capture is cut, disarms that guard silently.\n *\n * @category schemas\n * @since 1.0.0\n */\nexport const Output = Schema.Struct({\n exitCode: Schema.Number.annotate({ description: \"Command exit code, including non-zero codes\" }),\n stdout: Schema.String.annotate({\n description: \"Captured stdout; only the tail, and not the whole output, when stdoutTruncated is true\"\n }),\n stderr: Schema.String.annotate({\n description: \"Captured stderr; only the tail, and not the whole output, when stderrTruncated is true\"\n }),\n stdoutTruncated: Schema.Boolean.annotate({\n description:\n \"Whether stdout exceeded the capture limit, leaving stdout a fragment that must not be written to a file\"\n }),\n stderrTruncated: Schema.Boolean.annotate({\n description:\n \"Whether stderr exceeded the capture limit, leaving stderr a fragment that must not be written to a file\"\n }),\n stdoutDroppedBytes: Schema.Number.annotate({ description: \"UTF-8 bytes omitted from the start of stdout\" }),\n stderrDroppedBytes: Schema.Number.annotate({ description: \"UTF-8 bytes omitted from the start of stderr\" }),\n invalidProbe: Schema.optional(\n Probe.InvalidProbe.annotate({\n description: \"Reserved for a confirmed launch failure; an exit code alone does not establish one\"\n })\n ),\n mutated: Schema.optional(\n Schema.Boolean.annotate({\n description:\n \"Present for a containerised command whose working directory was measured before and after it: true when the tree changed, false when it held still. Absent when the tree was not measured, which is never read as unchanged.\"\n })\n )\n})\n\n/**\n * Decoded output returned by the bash handler.\n *\n * @category models\n * @since 1.0.0\n */\nexport type Output = typeof Output.Type\n\n/**\n * Static conservative effect envelope for the bash flow.\n *\n * The declaration is the registry-time worst case because the mode is not\n * known until an invocation is decoded.\n *\n * @category effects\n * @since 1.0.0\n */\nexport const effects = envelope({\n tier: \"irreversible\",\n mode: \"expected\",\n reads: [],\n writes: []\n})\n\n/**\n * Narrows the effect envelope for a decoded invocation.\n *\n * @category effects\n * @since 1.0.0\n */\nexport const effectsFor = (input: Input) =>\n input.mode === \"hermetic\"\n ? envelope({\n tier: \"irreversible\",\n mode: \"hermetic\",\n reads: input.reads,\n writes: input.writes\n })\n : envelope({\n tier: \"irreversible\",\n mode: \"expected\",\n reads: [],\n writes: []\n })\n\n/**\n * Capabilities required by the bash flow.\n *\n * @category capabilities\n * @since 1.0.0\n */\nexport const capabilities = [capability(\"proc:spawn\", \"*\")]\n\n/**\n * Declaration-only bash flow.\n *\n * @category flows\n * @since 1.0.0\n */\nexport const flow = { name, description, input: Input, output: Output, capabilities, effects } as const\n\n/*\n * `bash` declares no activity on purpose. What a shell call does is in the\n * command, not in the declaration: the same flow runs a test suite, a type\n * check, and `cat`. A reader that recognises a direct runner says so from the\n * recorded command; everything else stays unknown, which is what an absent\n * activity means.\n */\n\n/**\n * How one recorded call to this flow reads: the verb for each settlement, the\n * input field that is its subject, and the measured output fields a one-line\n * summary may count.\n *\n * Display metadata only, governed by `@smthrs/registry`\n * `Descriptor.CallPresentation` and checked where `StandardFlows` binds it.\n *\n * @category presentation\n * @since 1.0.0-rc.0\n */\nexport const presentation = {\n verb: { pending: \"running\", success: \"ran\", failure: \"failed to run\" },\n subject: \"command\",\n result: \"command\"\n} as const\n\n/**\n * Select standard input explicitly. Shells need `--` before script arguments\n * so even an argument such as `-c` cannot replace the stdin program.\n * Other supported interpreters treat arguments after `-` as program data.\n */\nconst stdinArguments = (interpreter: string): ReadonlyArray<string> | undefined => {\n switch (interpreter) {\n case \"bash\":\n case \"zsh\":\n case \"sh\":\n case \"dash\":\n return [\"-s\", \"--\"]\n case \"python\":\n case \"python3\":\n case \"node\":\n case \"ruby\":\n case \"perl\":\n return [\"-\"]\n default:\n return undefined\n }\n}\n\n/** Whether a script is shell text the lexical pre-check can actually read. */\nconst shells = new Set([\"bash\", \"zsh\", \"sh\", \"dash\"])\n\n/** What one invocation runs, before any container transport rewrites it. */\ninterface Plan {\n readonly file: string\n readonly args: ReadonlyArray<string> | undefined\n readonly stdin: string | undefined\n readonly env: Record<string, string> | undefined\n readonly quoted: string\n}\n\nconst invalid = (message: string): StdError.StdError => new StdError.StdError({ code: \"invalid_input\", message })\n\n/** The invocation an input names, or the failure explaining why it names none. */\nconst plan = (input: Input): Plan | StdError.StdError => {\n const interpreter = input.interpreter ?? \"bash\"\n if (input.command !== undefined && input.script !== undefined) {\n return invalid(\"Give command or script, never both: a script is program text, a command is a shell line\")\n }\n if (input.command === undefined && input.script === undefined) {\n return invalid(\"Give either command (a shell line) or script (program text run by interpreter)\")\n }\n if (input.script !== undefined && input.stdin !== undefined) {\n return invalid(\"A script already arrives on standard input, so it cannot also carry stdin\")\n }\n if (input.command !== undefined && input.args !== undefined) {\n return invalid(\"args belong to a script; a command carries its own arguments in the line\")\n }\n if (input.command !== undefined && input.interpreter !== undefined) {\n return invalid(\"interpreter runs a script; a command runs in the platform shell\")\n }\n if (input.mode === \"hermetic\" && input.container !== undefined) {\n return invalid(\n \"A container has its own filesystem, so declared reads and writes cannot describe it. Use mode:unhermetic for a containerised command\"\n )\n }\n if (input.mode === \"hermetic\" && input.script !== undefined && !shells.has(interpreter)) {\n return invalid(\n `The hermetic pre-check reads shell text, and ${interpreter} is not a shell. Run this script with mode:unhermetic, or express it as shell`\n )\n }\n if (input.command !== undefined) {\n return { file: input.command, args: undefined, stdin: input.stdin, env: input.env, quoted: input.command }\n }\n const selector = stdinArguments(interpreter)\n if (selector === undefined) {\n return invalid(`Unsupported script interpreter: ${interpreter}`)\n }\n const args = [...selector, ...(input.args ?? [])]\n return {\n file: interpreter,\n args,\n stdin: input.script,\n env: input.env,\n quoted: [interpreter, ...args].join(\" \")\n }\n}\n\n/**\n * The plan, refused when its input names no one invocation or when a hermetic\n * call's paths fall outside its declared envelope. Spawned and terminal\n * execution share this one check.\n */\nconst precheck = (input: Input, path: Path.Path): Plan | StdError.StdError => {\n const intent = plan(input)\n if (intent instanceof StdError.StdError || input.mode !== \"hermetic\") return intent\n // A planned call carries exactly one of command or script.\n return outsideEnvelope(input, input.command ?? input.script!, path) ?? intent\n}\n\n/** The plan as the host will spawn it, once the container transport has had it. */\nconst routed = (\n plan: Plan,\n input: Input,\n transport: Option.Option<Container.Container>\n): Effect.Effect<Plan, StdError.StdError> => {\n if (input.container === undefined) return Effect.succeed(plan)\n if (Option.isNone(transport)) return Effect.fail(Container.unavailable(input.container))\n return Effect.map(\n transport.value.exec(request(plan, input, input.container)),\n (routing): Plan => ({\n file: routing.file,\n args: routing.args,\n stdin: plan.stdin,\n env: routing.env,\n // Diagnostics describe the logical invocation, not transport arguments.\n quoted: plan.quoted\n })\n )\n}\n\n/**\n * One containerised request, always through a login shell.\n *\n * A container's environment is what its profile makes it. The images this\n * harness runs against activate the project's interpreter — a conda env, a\n * virtualenv, a modified `PATH` — from `/etc/profile.d`, so a program spawned\n * directly by `docker exec` gets a different Python from the one that owns the\n * repository's dependencies. That is measured, not theoretical: r90 typed\n * `docker exec … bash -lc '…'` by hand and got the activation for free, r91\n * routed `interpreter: \"python3\"` straight at the container and filled its\n * traces with `ModuleNotFoundError: No module named 'numpy'` and\n * `exec: \"/usr/local/bin/python\": no such file or directory`. Thirty of 45\n * instances then spent 138 cells scanning `/opt` for the real interpreter.\n *\n * So the login shell is the transport's, not the caller's. A command line is\n * one argument to `bash -lc`, as before. A program run under an interpreter\n * becomes `bash -lc 'exec \"$@\"' bash <interpreter> <args…>`: the shell reads\n * the profile, then `exec` replaces it with the interpreter, so the script\n * still arrives on the inherited standard input and no argument is ever\n * re-parsed as shell text.\n */\nconst request = (plan: Plan, input: Input, container: string): Container.Request => ({\n container,\n file: \"bash\",\n args: plan.args === undefined\n ? [\"-lc\", plan.file]\n : [\"-lc\", `exec \"$@\"`, \"bash\", plan.file, ...plan.args],\n ...(input.cwd === undefined ? {} : { cwd: input.cwd }),\n ...(input.env === undefined ? {} : { env: input.env }),\n stdin: plan.stdin !== undefined\n})\n\n/** How long one fingerprint of the container's working directory may take. */\nconst FINGERPRINT_TIMEOUT_MS = 60_000\n\n/**\n * Measures the tree a containerised command is about to run in, or just ran in.\n *\n * The host's workspace walk cannot see a container's filesystem, so the\n * measurement is taken where the tree is: `TreeFingerprint.script` runs\n * through the same transport as the command, in the same working directory,\n * and answers with one checksum. Every way this can fail — no transport, a\n * container without `find` or `stat`, a timeout, a bounded listing — is read\n * as \"unmeasured\" and reported as an absent `mutated`, never as a tree that\n * held still. The time it costs is bounded by the command's own timeout so a\n * stalled container cannot spend more waiting to be measured than it may\n * spend running.\n */\nconst fingerprint = (\n input: Input,\n transport: Option.Option<Container.Container>\n): Effect.Effect<TreeFingerprint.Measurement | undefined, never, ChildProcessSpawner.ChildProcessSpawner> =>\n Effect.gen(function*() {\n if (input.container === undefined || Option.isNone(transport)) return undefined\n const routing = yield* transport.value.exec({\n container: input.container,\n file: \"sh\",\n args: [\"-c\", TreeFingerprint.script()],\n ...(input.cwd === undefined ? {} : { cwd: input.cwd }),\n stdin: false\n })\n const result = yield* Exec.exec(routing.file, {\n ...(routing.env === undefined ? {} : { env: routing.env }),\n timeoutMs: Math.min(input.timeoutMs ?? DEFAULT_TIMEOUT_MS, FINGERPRINT_TIMEOUT_MS),\n maxCaptureBytes: MAX_SHELL_OUTPUT_BYTES,\n args: routing.args\n })\n return result.exitCode === 0 ? TreeFingerprint.parse(result.stdout) : undefined\n }).pipe(Effect.orElseSucceed(() => undefined))\n\n/**\n * Executes a shell command through the permission-aware kernel service.\n *\n * Non-zero exit codes remain successful values. Only timeout, host, permission,\n * and declared-envelope pre-check failures use the typed error channel.\n *\n * @category handlers\n * @since 1.0.0\n */\nexport const run = Effect.fn(\"Bash.run\")(function*(\n input: Input\n): Effect.fn.Return<Output, StdError.StdError, ChildProcessSpawner.ChildProcessSpawner | Path.Path> {\n const intent = precheck(input, yield* Path.Path)\n if (intent instanceof StdError.StdError) return yield* Effect.fail(intent)\n // The working directory belongs to the container. The transport supplies\n // any environment overrides its host process needs to forward into it.\n const contained = input.container !== undefined\n const transport = yield* Effect.serviceOption(Container.Container)\n const spawned = yield* routed(intent, input, transport)\n\n // The tree the command runs in, before it runs. Measured in the container\n // because nothing on this host can see it; see `TreeFingerprint`.\n const before = yield* fingerprint(input, transport)\n const result = yield* Exec.exec(spawned.file, {\n ...(input.cwd === undefined || contained ? {} : { cwd: input.cwd }),\n ...(spawned.env === undefined ? {} : { env: spawned.env }),\n timeoutMs: input.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n maxCaptureBytes: MAX_SHELL_OUTPUT_BYTES,\n ...(spawned.args === undefined ? {} : { args: spawned.args }),\n ...(spawned.stdin === undefined ? {} : { stdin: spawned.stdin })\n }).pipe(Effect.mapError((error) => Exec.toStdError(spawned.quoted, error)))\n const mutated = TreeFingerprint.moved(before, yield* fingerprint(input, transport))\n // A command on this host may have changed files a language server holds\n // open; a container's files are not the ones it was sent.\n if (!contained) yield* Diagnostics.refresh\n const stdout = truncateBytes(result.stdout, MAX_SHELL_OUTPUT_BYTES, { keep: \"tail\" })\n const stderr = truncateBytes(result.stderr, MAX_SHELL_OUTPUT_BYTES, { keep: \"tail\" })\n // Exit codes alone cannot establish whether the shell refused to launch a\n // command or a launched runner chose that status after executing tests.\n return {\n exitCode: result.exitCode,\n stdout: stdout.text,\n stderr: stderr.text,\n stdoutTruncated: result.stdoutDroppedBytes > 0 || stdout.truncated,\n stderrTruncated: result.stderrDroppedBytes > 0 || stderr.truncated,\n stdoutDroppedBytes: result.stdoutDroppedBytes + stdout.droppedBytes,\n stderrDroppedBytes: result.stderrDroppedBytes + stderr.droppedBytes,\n ...(mutated === undefined ? {} : { mutated })\n }\n})\n\n/**\n * What one call runs, after planning and before any transport: the program,\n * its arguments (absent for a shell command line), standard input, the\n * caller's environment, and the logical invocation as a person reads it.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface Invocation {\n readonly file: string\n readonly args: ReadonlyArray<string> | undefined\n readonly stdin: string | undefined\n readonly env: Record<string, string> | undefined\n readonly quoted: string\n}\n\n/**\n * The invocation `input` names, or the typed refusal explaining why it names\n * none. The same plan {@link run} spawns; a terminal provider runs it instead.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const invocation = (input: Input): Invocation | StdError.StdError => plan(input)\n\n/**\n * {@link run}'s contract through a registered terminal (T-TRM-05): the same\n * plan, hermetic pre-check, bounded output and diagnostics refresh, with\n * execution supplied by `execute` instead of a spawn on this host. A terminal\n * has no container transport, so a containerised call is refused before it\n * reaches `execute`.\n *\n * @category handlers\n * @since 1.0.0\n */\nexport const inTerminal = <R>(\n execute: (input: Input) => Effect.Effect<Output, StdError.StdError, R>\n) =>\n Effect.fn(\"Bash.inTerminal\")(function*(\n input: Input\n ): Effect.fn.Return<Output, StdError.StdError, R | Path.Path> {\n const intent = precheck(input, yield* Path.Path)\n if (intent instanceof StdError.StdError) return yield* Effect.fail(intent)\n if (input.container !== undefined) return yield* Effect.fail(Container.unavailable(input.container))\n const output = yield* execute(input)\n // The terminal ran on this workspace: files a language server holds may\n // have changed, exactly as after a spawn here.\n yield* Diagnostics.refresh\n return output\n })\n\n/**\n * The refusal a sealed host answers a call outside its container with.\n *\n * @category errors\n * @since 1.0.0-rc.1\n */\nexport const outsideContainer = (sealedTo: string, requested: string | undefined): StdError.StdError =>\n new StdError.StdError({\n code: \"outside_container\",\n message: requested === undefined\n ? `This host runs commands only inside container \"${sealedTo}\". Name it: { mode: \"unhermetic\", container: \"${sealedTo}\", command: \"...\" }.`\n : `This host runs commands only inside container \"${sealedTo}\", not \"${requested}\".`\n })\n\n/**\n * {@link run}, sealed to one container.\n *\n * A host that must not be reachable from the cell (a benchmark host holding\n * other tasks' tests and reference solutions, say) binds this instead of\n * {@link run}: a call naming `sealedTo` runs as usual, and every other call,\n * including one with no container that would run on the host, fails with\n * `outside_container` before anything is spawned.\n *\n * @category handlers\n * @since 1.0.0-rc.1\n */\nexport const sealed = (sealedTo: string) =>\n(\n input: Input\n): Effect.Effect<Output, StdError.StdError, ChildProcessSpawner.ChildProcessSpawner | Path.Path> =>\n input.container === sealedTo ? run(input) : Effect.fail(outsideContainer(sealedTo, input.container))\n"]}
|
|
1
|
+
{"version":3,"file":"Bash.js","sourceRoot":"","sources":["../../src/Bash.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bash flow declaration and portable handler.\n *\n * Two ways in, and the difference is where the payload is parsed. `command` is\n * a line for the platform shell, exactly as before. `script` is program text\n * delivered to an interpreter on standard input, as data: nothing quotes it,\n * escapes it, or terminates it with a heredoc marker, so the class of failure\n * that produced `docker exec c bash -lc '…python - <<EOF…'` cannot occur. Its\n * `args` reach the script as arguments rather than as interpolated text.\n *\n * `container` names a container the host knows how to reach, and the argv is\n * built by the injected {@link Container} transport rather than typed by the\n * caller. A host that binds no transport refuses such a call by saying so; the\n * requirement is optional here precisely so a host without containers is not\n * forced to declare one.\n *\n * @since 1.0.0\n */\n\nimport type * as ChildProcessSpawner from \"@smthrs/kernel/ChildProcessSpawner\"\nimport * as Path from \"@smthrs/kernel/Path\"\nimport * as Effect from \"effect/Effect\"\nimport * as Option from \"effect/Option\"\nimport * as Schema from \"effect/Schema\"\nimport * as Container from \"./Container.ts\"\nimport { capability, envelope } from \"./internal/Declaration.ts\"\nimport * as Diagnostics from \"./internal/Diagnostics.ts\"\nimport { outsideEnvelope } from \"./internal/EnvelopePrecheck.ts\"\nimport * as Exec from \"./internal/Exec.ts\"\nimport { elideMiddle as elide, MAX_SHELL_OUTPUT_BYTES } from \"./internal/Text.ts\"\nimport * as Probe from \"./Probe.ts\"\nimport * as StdError from \"./StdError.ts\"\nimport * as TreeFingerprint from \"./TreeFingerprint.ts\"\n\n/**\n * Registry name for the bash flow.\n *\n * @category identifiers\n * @since 1.0.0\n */\nexport const name = \"bash\"\n\n/**\n * Model-facing description of the bash flow.\n *\n * Capped at 200 characters and read every frame, which is why the\n * invalid-probe contract is not restated here: the cell contract teaches it\n * once, and a result that has one explains itself.\n *\n * @category descriptions\n * @since 1.0.0\n */\nexport const description =\n \"Run a shell command, or an interpreter over a script passed as data instead of quoted into a line. container routes it through the host's container transport. mode:hermetic pre-checks path tokens.\"\n\n/**\n * Default command timeout in milliseconds.\n *\n * Ten minutes lets full builds and test suites finish while ensuring a stalled\n * process eventually releases the flow.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const DEFAULT_TIMEOUT_MS = 600_000\n\nconst Command = Schema.optional(Schema.String.annotate({\n description: \"Shell command line to execute; give this or script, never both\"\n}))\nconst Script = Schema.optional(Schema.String.annotate({\n description: \"Program text delivered to the interpreter on stdin as data, so it needs no quoting or heredoc\"\n}))\nconst Interpreter = Schema.optional(Schema.String.annotate({\n description:\n \"Program that reads script on stdin; defaults to bash. python, python3, sh, zsh, node, ruby and perl are known\"\n}))\nconst Args = Schema.optional(\n Schema.Array(Schema.String).annotate({\n description: \"Arguments passed to the script as data, never interpolated into it\"\n })\n)\nconst Stdin = Schema.optional(Schema.String.annotate({\n description: \"Text written to the command's standard input; use script instead when the text is the program\"\n}))\nconst ContainerName = Schema.optional(Schema.String.annotate({\n description: \"Run inside this container through the host's container transport; requires mode:unhermetic\"\n}))\nconst Cwd = Schema.optional(Schema.String).annotate({\n description: \"Working directory for the command\",\n message: \"cwd must be a path string. For a checkpoint, pass { at: ctx.base } as the third ctx.call argument.\"\n})\nconst Env = Schema.optional(\n Schema.Record(Schema.String, Schema.String).annotate({ description: \"Environment variables for the command\" })\n)\nconst TimeoutMs = Schema.optional(Schema.Number).annotate({\n description: \"Wall-clock timeout in milliseconds; defaults to 600000 ms\",\n message: \"timeoutMs must be a number of milliseconds.\"\n})\n\n/**\n * Input schema for the bash flow.\n *\n * The mode named `hermetic` requires explicit read and write envelopes for a\n * lexical pre-check; it does not by itself confine the process. Unhermetic mode\n * deliberately omits those declarations so the distinction stays visible in\n * the input shape.\n *\n * @category schemas\n * @since 1.0.0\n */\nexport const Input = Schema.Union([\n Schema.Struct({\n mode: Schema.Literal(\"hermetic\").annotate({\n description: \"Lexically pre-check explicit path tokens against declared reads and writes; does not sandbox\"\n }),\n command: Command,\n script: Script,\n interpreter: Interpreter,\n args: Args,\n stdin: Stdin,\n container: ContainerName,\n reads: Schema.Array(Schema.NonEmptyString).annotate({ description: \"Paths or path globs the command may read\" }),\n writes: Schema.Array(Schema.NonEmptyString).annotate({ description: \"Paths or path globs the command may write\" }),\n cwd: Cwd,\n env: Env,\n timeoutMs: TimeoutMs\n }),\n Schema.Struct({\n // Omitted means unhermetic: it is the envelope `effects` already declares\n // for every call, so the default grants nothing a named mode would not.\n mode: Schema.Literal(\"unhermetic\").annotate({\n description: \"Run without a declared filesystem envelope as an irreversible effect; the default\"\n }).pipe(Schema.optionalKey, Schema.withDecodingDefaultKey(Effect.succeed(\"unhermetic\" as const))),\n command: Command,\n script: Script,\n interpreter: Interpreter,\n args: Args,\n stdin: Stdin,\n container: ContainerName,\n cwd: Cwd,\n env: Env,\n timeoutMs: TimeoutMs\n })\n])\n\n/**\n * Decoded input accepted by the bash handler.\n *\n * @category models\n * @since 1.0.0\n */\nexport type Input = typeof Input.Type\n\n/**\n * Output schema for the bash flow.\n *\n * `invalidProbe` is present when the shell itself refused to start the\n * command, which its exit code says outright: 127 for a program it could not\n * find, 126 for one it could not execute. Only that. Reading a runner's own\n * wording is a judgment rather than a fact, and `Probe.classify` makes it with\n * Jev for the `test` flow, whose caller is asking about a suite; a shell call\n * is any command at all and stays free of the gateway. See `Probe`.\n *\n * The `<stream>Truncated` flags are a wire convention, not a display detail.\n * A truncated capture is a fragment of what the process printed, and the\n * harness reads these flags to refuse a later write of those exact bytes —\n * `@smthrs/harness/TruncatedOutput` is the consuming half. Renaming a flag, or\n * dropping it when the capture is cut, disarms that guard silently.\n *\n * @category schemas\n * @since 1.0.0\n */\nexport const Output = Schema.Struct({\n exitCode: Schema.Number.annotate({ description: \"Command exit code, including non-zero codes\" }),\n stdout: Schema.String.annotate({\n description:\n \"Captured stdout; when stdoutTruncated is true, only its start and end, with one line naming the bytes omitted between them\"\n }),\n stderr: Schema.String.annotate({\n description:\n \"Captured stderr; when stderrTruncated is true, only its start and end, with one line naming the bytes omitted between them\"\n }),\n stdoutTruncated: Schema.Boolean.annotate({\n description:\n \"Whether stdout exceeded the capture limit, leaving stdout a fragment that must not be written to a file\"\n }),\n stderrTruncated: Schema.Boolean.annotate({\n description:\n \"Whether stderr exceeded the capture limit, leaving stderr a fragment that must not be written to a file\"\n }),\n stdoutDroppedBytes: Schema.Number.annotate({ description: \"UTF-8 bytes omitted from the middle of stdout\" }),\n stderrDroppedBytes: Schema.Number.annotate({ description: \"UTF-8 bytes omitted from the middle of stderr\" }),\n invalidProbe: Schema.optional(\n Probe.InvalidProbe.annotate({\n description: \"Reserved for a confirmed launch failure; an exit code alone does not establish one\"\n })\n ),\n mutated: Schema.optional(\n Schema.Boolean.annotate({\n description:\n \"Present for a containerised command whose working directory was measured before and after it: true when the tree changed, false when it held still. Absent when the tree was not measured, which is never read as unchanged.\"\n })\n )\n})\n\n/**\n * Decoded output returned by the bash handler.\n *\n * @category models\n * @since 1.0.0\n */\nexport type Output = typeof Output.Type\n\n/**\n * The text of a stream cut to its start and end: the start, one line naming\n * the bytes omitted between them, and the end. A host that captures output\n * itself, such as a terminal behind {@link inTerminal}, cuts with this so every\n * {@link Output} reads the same.\n *\n * @category rendering\n * @since 1.0.0-rc.5\n */\nexport const elideMiddle: (start: string, droppedBytes: number, end: string) => string = elide\n\n/**\n * Static conservative effect envelope for the bash flow.\n *\n * The declaration is the registry-time worst case because the mode is not\n * known until an invocation is decoded.\n *\n * @category effects\n * @since 1.0.0\n */\nexport const effects = envelope({\n tier: \"irreversible\",\n mode: \"expected\",\n reads: [],\n writes: []\n})\n\n/**\n * Narrows the effect envelope for a decoded invocation.\n *\n * @category effects\n * @since 1.0.0\n */\nexport const effectsFor = (input: Input) =>\n input.mode === \"hermetic\"\n ? envelope({\n tier: \"irreversible\",\n mode: \"hermetic\",\n reads: input.reads,\n writes: input.writes\n })\n : envelope({\n tier: \"irreversible\",\n mode: \"expected\",\n reads: [],\n writes: []\n })\n\n/**\n * Capabilities required by the bash flow.\n *\n * @category capabilities\n * @since 1.0.0\n */\nexport const capabilities = [capability(\"proc:spawn\", \"*\")]\n\n/**\n * Declaration-only bash flow.\n *\n * @category flows\n * @since 1.0.0\n */\nexport const flow = { name, description, input: Input, output: Output, capabilities, effects } as const\n\n/*\n * `bash` declares no activity on purpose. What a shell call does is in the\n * command, not in the declaration: the same flow runs a test suite, a type\n * check, and `cat`. A reader that recognises a direct runner says so from the\n * recorded command; everything else stays unknown, which is what an absent\n * activity means.\n */\n\n/**\n * How one recorded call to this flow reads: the verb for each settlement, the\n * input field that is its subject, and the measured output fields a one-line\n * summary may count.\n *\n * Display metadata only, governed by `@smthrs/registry`\n * `Descriptor.CallPresentation` and checked where `StandardFlows` binds it.\n *\n * @category presentation\n * @since 1.0.0-rc.0\n */\nexport const presentation = {\n verb: { pending: \"running\", success: \"ran\", failure: \"failed to run\" },\n subject: \"command\",\n result: \"command\"\n} as const\n\n/**\n * Select standard input explicitly. Shells need `--` before script arguments\n * so even an argument such as `-c` cannot replace the stdin program.\n * Other supported interpreters treat arguments after `-` as program data.\n */\nconst stdinArguments = (interpreter: string): ReadonlyArray<string> | undefined => {\n switch (interpreter) {\n case \"bash\":\n case \"zsh\":\n case \"sh\":\n case \"dash\":\n return [\"-s\", \"--\"]\n case \"python\":\n case \"python3\":\n case \"node\":\n case \"ruby\":\n case \"perl\":\n return [\"-\"]\n default:\n return undefined\n }\n}\n\n/** Whether a script is shell text the lexical pre-check can actually read. */\nconst shells = new Set([\"bash\", \"zsh\", \"sh\", \"dash\"])\n\n/** What one invocation runs, before any container transport rewrites it. */\ninterface Plan {\n readonly file: string\n readonly args: ReadonlyArray<string> | undefined\n readonly stdin: string | undefined\n readonly env: Record<string, string> | undefined\n readonly quoted: string\n}\n\nconst invalid = (message: string): StdError.StdError => new StdError.StdError({ code: \"invalid_input\", message })\n\n/** The invocation an input names, or the failure explaining why it names none. */\nconst plan = (input: Input): Plan | StdError.StdError => {\n const interpreter = input.interpreter ?? \"bash\"\n if (input.command !== undefined && input.script !== undefined) {\n return invalid(\"Give command or script, never both: a script is program text, a command is a shell line\")\n }\n if (input.command === undefined && input.script === undefined) {\n return invalid(\"Give either command (a shell line) or script (program text run by interpreter)\")\n }\n if (input.script !== undefined && input.stdin !== undefined) {\n return invalid(\"A script already arrives on standard input, so it cannot also carry stdin\")\n }\n if (input.command !== undefined && input.args !== undefined) {\n return invalid(\"args belong to a script; a command carries its own arguments in the line\")\n }\n if (input.command !== undefined && input.interpreter !== undefined) {\n return invalid(\"interpreter runs a script; a command runs in the platform shell\")\n }\n if (input.mode === \"hermetic\" && input.container !== undefined) {\n return invalid(\n \"A container has its own filesystem, so declared reads and writes cannot describe it. Use mode:unhermetic for a containerised command\"\n )\n }\n if (input.mode === \"hermetic\" && input.script !== undefined && !shells.has(interpreter)) {\n return invalid(\n `The hermetic pre-check reads shell text, and ${interpreter} is not a shell. Run this script with mode:unhermetic, or express it as shell`\n )\n }\n if (input.command !== undefined) {\n return { file: input.command, args: undefined, stdin: input.stdin, env: input.env, quoted: input.command }\n }\n const selector = stdinArguments(interpreter)\n if (selector === undefined) {\n return invalid(`Unsupported script interpreter: ${interpreter}`)\n }\n const args = [...selector, ...(input.args ?? [])]\n return {\n file: interpreter,\n args,\n stdin: input.script,\n env: input.env,\n quoted: [interpreter, ...args].join(\" \")\n }\n}\n\n/**\n * The plan, refused when its input names no one invocation or when a hermetic\n * call's paths fall outside its declared envelope. Spawned and terminal\n * execution share this one check.\n */\nconst precheck = (input: Input, path: Path.Path): Plan | StdError.StdError => {\n const intent = plan(input)\n if (intent instanceof StdError.StdError || input.mode !== \"hermetic\") return intent\n // A planned call carries exactly one of command or script.\n return outsideEnvelope(input, input.command ?? input.script!, path) ?? intent\n}\n\n/** The plan as the host will spawn it, once the container transport has had it. */\nconst routed = (\n plan: Plan,\n input: Input,\n transport: Option.Option<Container.Container>\n): Effect.Effect<Plan, StdError.StdError> => {\n if (input.container === undefined) return Effect.succeed(plan)\n if (Option.isNone(transport)) return Effect.fail(Container.unavailable(input.container))\n return Effect.map(\n transport.value.exec(request(plan, input, input.container)),\n (routing): Plan => ({\n file: routing.file,\n args: routing.args,\n stdin: plan.stdin,\n env: routing.env,\n // Diagnostics describe the logical invocation, not transport arguments.\n quoted: plan.quoted\n })\n )\n}\n\n/**\n * One containerised request, always through a login shell.\n *\n * A container's environment is what its profile makes it. The images this\n * harness runs against activate the project's interpreter — a conda env, a\n * virtualenv, a modified `PATH` — from `/etc/profile.d`, so a program spawned\n * directly by `docker exec` gets a different Python from the one that owns the\n * repository's dependencies. That is measured, not theoretical: r90 typed\n * `docker exec … bash -lc '…'` by hand and got the activation for free, r91\n * routed `interpreter: \"python3\"` straight at the container and filled its\n * traces with `ModuleNotFoundError: No module named 'numpy'` and\n * `exec: \"/usr/local/bin/python\": no such file or directory`. Thirty of 45\n * instances then spent 138 cells scanning `/opt` for the real interpreter.\n *\n * So the login shell is the transport's, not the caller's. A command line is\n * one argument to `bash -lc`, as before. A program run under an interpreter\n * becomes `bash -lc 'exec \"$@\"' bash <interpreter> <args…>`: the shell reads\n * the profile, then `exec` replaces it with the interpreter, so the script\n * still arrives on the inherited standard input and no argument is ever\n * re-parsed as shell text.\n */\nconst request = (plan: Plan, input: Input, container: string): Container.Request => ({\n container,\n file: \"bash\",\n args: plan.args === undefined\n ? [\"-lc\", plan.file]\n : [\"-lc\", `exec \"$@\"`, \"bash\", plan.file, ...plan.args],\n ...(input.cwd === undefined ? {} : { cwd: input.cwd }),\n ...(input.env === undefined ? {} : { env: input.env }),\n stdin: plan.stdin !== undefined\n})\n\n/** How long one fingerprint of the container's working directory may take. */\nconst FINGERPRINT_TIMEOUT_MS = 60_000\n\n/**\n * Measures the tree a containerised command is about to run in, or just ran in.\n *\n * The host's workspace walk cannot see a container's filesystem, so the\n * measurement is taken where the tree is: `TreeFingerprint.script` runs\n * through the same transport as the command, in the same working directory,\n * and answers with one checksum. Every way this can fail — no transport, a\n * container without `find` or `stat`, a timeout, a bounded listing — is read\n * as \"unmeasured\" and reported as an absent `mutated`, never as a tree that\n * held still. The time it costs is bounded by the command's own timeout so a\n * stalled container cannot spend more waiting to be measured than it may\n * spend running.\n */\nconst fingerprint = (\n input: Input,\n transport: Option.Option<Container.Container>\n): Effect.Effect<TreeFingerprint.Measurement | undefined, never, ChildProcessSpawner.ChildProcessSpawner> =>\n Effect.gen(function*() {\n if (input.container === undefined || Option.isNone(transport)) return undefined\n const routing = yield* transport.value.exec({\n container: input.container,\n file: \"sh\",\n args: [\"-c\", TreeFingerprint.script()],\n ...(input.cwd === undefined ? {} : { cwd: input.cwd }),\n stdin: false\n })\n const result = yield* Exec.exec(routing.file, {\n ...(routing.env === undefined ? {} : { env: routing.env }),\n timeoutMs: Math.min(input.timeoutMs ?? DEFAULT_TIMEOUT_MS, FINGERPRINT_TIMEOUT_MS),\n maxCaptureBytes: MAX_SHELL_OUTPUT_BYTES,\n args: routing.args\n })\n return result.exitCode === 0 ? TreeFingerprint.parse(result.stdout) : undefined\n }).pipe(Effect.orElseSucceed(() => undefined))\n\n/**\n * Executes a shell command through the permission-aware kernel service.\n *\n * Non-zero exit codes remain successful values. Only timeout, host, permission,\n * and declared-envelope pre-check failures use the typed error channel.\n *\n * @category handlers\n * @since 1.0.0\n */\nexport const run = Effect.fn(\"Bash.run\")(function*(\n input: Input\n): Effect.fn.Return<Output, StdError.StdError, ChildProcessSpawner.ChildProcessSpawner | Path.Path> {\n const intent = precheck(input, yield* Path.Path)\n if (intent instanceof StdError.StdError) return yield* Effect.fail(intent)\n // The working directory belongs to the container. The transport supplies\n // any environment overrides its host process needs to forward into it.\n const contained = input.container !== undefined\n const transport = yield* Effect.serviceOption(Container.Container)\n const spawned = yield* routed(intent, input, transport)\n\n // The tree the command runs in, before it runs. Measured in the container\n // because nothing on this host can see it; see `TreeFingerprint`.\n const before = yield* fingerprint(input, transport)\n const result = yield* Exec.exec(spawned.file, {\n ...(input.cwd === undefined || contained ? {} : { cwd: input.cwd }),\n ...(spawned.env === undefined ? {} : { env: spawned.env }),\n timeoutMs: input.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n maxCaptureBytes: MAX_SHELL_OUTPUT_BYTES,\n overflow: \"ends\",\n ...(spawned.args === undefined ? {} : { args: spawned.args }),\n ...(spawned.stdin === undefined ? {} : { stdin: spawned.stdin })\n }).pipe(Effect.mapError((error) => Exec.toStdError(spawned.quoted, error)))\n const mutated = TreeFingerprint.moved(before, yield* fingerprint(input, transport))\n // A command on this host may have changed files a language server holds\n // open; a container's files are not the ones it was sent.\n if (!contained) yield* Diagnostics.refresh\n // Exit codes alone cannot establish whether the shell refused to launch a\n // command or a launched runner chose that status after executing tests.\n return {\n exitCode: result.exitCode,\n stdout: result.stdout,\n stderr: result.stderr,\n stdoutTruncated: result.stdoutDroppedBytes > 0,\n stderrTruncated: result.stderrDroppedBytes > 0,\n stdoutDroppedBytes: result.stdoutDroppedBytes,\n stderrDroppedBytes: result.stderrDroppedBytes,\n ...(mutated === undefined ? {} : { mutated })\n }\n})\n\n/**\n * What one call runs, after planning and before any transport: the program,\n * its arguments (absent for a shell command line), standard input, the\n * caller's environment, and the logical invocation as a person reads it.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface Invocation {\n readonly file: string\n readonly args: ReadonlyArray<string> | undefined\n readonly stdin: string | undefined\n readonly env: Record<string, string> | undefined\n readonly quoted: string\n}\n\n/**\n * The invocation `input` names, or the typed refusal explaining why it names\n * none. The same plan {@link run} spawns; a terminal provider runs it instead.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const invocation = (input: Input): Invocation | StdError.StdError => plan(input)\n\n/**\n * {@link run}'s contract through a registered terminal (T-TRM-05): the same\n * plan, hermetic pre-check, bounded output and diagnostics refresh, with\n * execution supplied by `execute` instead of a spawn on this host. A terminal\n * has no container transport, so a containerised call is refused before it\n * reaches `execute`.\n *\n * @category handlers\n * @since 1.0.0\n */\nexport const inTerminal = <R>(\n execute: (input: Input) => Effect.Effect<Output, StdError.StdError, R>\n) =>\n Effect.fn(\"Bash.inTerminal\")(function*(\n input: Input\n ): Effect.fn.Return<Output, StdError.StdError, R | Path.Path> {\n const intent = precheck(input, yield* Path.Path)\n if (intent instanceof StdError.StdError) return yield* Effect.fail(intent)\n if (input.container !== undefined) return yield* Effect.fail(Container.unavailable(input.container))\n const output = yield* execute(input)\n // The terminal ran on this workspace: files a language server holds may\n // have changed, exactly as after a spawn here.\n yield* Diagnostics.refresh\n return output\n })\n\n/**\n * The refusal a sealed host answers a call outside its container with.\n *\n * @category errors\n * @since 1.0.0-rc.1\n */\nexport const outsideContainer = (sealedTo: string, requested: string | undefined): StdError.StdError =>\n new StdError.StdError({\n code: \"outside_container\",\n message: requested === undefined\n ? `This host runs commands only inside container \"${sealedTo}\". Name it: { mode: \"unhermetic\", container: \"${sealedTo}\", command: \"...\" }.`\n : `This host runs commands only inside container \"${sealedTo}\", not \"${requested}\".`\n })\n\n/**\n * {@link run}, sealed to one container.\n *\n * A host that must not be reachable from the cell (a benchmark host holding\n * other tasks' tests and reference solutions, say) binds this instead of\n * {@link run}: a call naming `sealedTo` runs as usual, and every other call,\n * including one with no container that would run on the host, fails with\n * `outside_container` before anything is spawned.\n *\n * @category handlers\n * @since 1.0.0-rc.1\n */\nexport const sealed = (sealedTo: string) =>\n(\n input: Input\n): Effect.Effect<Output, StdError.StdError, ChildProcessSpawner.ChildProcessSpawner | Path.Path> =>\n input.container === sealedTo ? run(input) : Effect.fail(outsideContainer(sealedTo, input.container))\n"]}
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
*/
|
|
72
72
|
import { ChildProcessSpawner } from "@smthrs/kernel/ChildProcessSpawner";
|
|
73
73
|
import { Context, Effect, Layer, Schema } from "effect";
|
|
74
|
+
import * as Container from "./Container.ts";
|
|
74
75
|
import * as StdError from "./StdError.ts";
|
|
75
76
|
/**
|
|
76
77
|
* The id naming the tree a run opened on.
|
|
@@ -265,6 +266,42 @@ export declare const makeGit: (options: GitOptions) => Effect.Effect<Checkpoints
|
|
|
265
266
|
* @since 1.0.0
|
|
266
267
|
*/
|
|
267
268
|
export declare const layerGit: (options: GitOptions) => Layer.Layer<Checkpoints, never, ChildProcessSpawner>;
|
|
269
|
+
/**
|
|
270
|
+
* What {@link makeContainer} needs to know about the container it pins in.
|
|
271
|
+
*
|
|
272
|
+
* @category models
|
|
273
|
+
* @since 1.0.0-rc.8
|
|
274
|
+
*/
|
|
275
|
+
export interface ContainerOptions {
|
|
276
|
+
/** The container `bash` runs in. */
|
|
277
|
+
readonly container: string;
|
|
278
|
+
/** A directory of the repository inside it; absent is the container's own working directory. */
|
|
279
|
+
readonly cwd?: string | undefined;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Builds the store for a host whose `bash` runs in one container.
|
|
283
|
+
*
|
|
284
|
+
* The repository is the container's, so every git command runs there, through
|
|
285
|
+
* the same {@link Container.Container} transport `bash` takes. The host store
|
|
286
|
+
* looked in the CLI's own directory instead and refused every `at` in 112 of
|
|
287
|
+
* 123 trials of the 2026-10-14 SWE-bench Pro run. This keeps that store's
|
|
288
|
+
* shape: a checkpoint is a `stash create` commit named in config, and a tree
|
|
289
|
+
* is checked out under the repository for one call, so a relocated `bash`
|
|
290
|
+
* call keeps its subdirectory and still finds what the workspace installs
|
|
291
|
+
* beside it. That git may run what the repository configures, but only inside
|
|
292
|
+
* the container the agent already commands.
|
|
293
|
+
*
|
|
294
|
+
* @category constructors
|
|
295
|
+
* @since 1.0.0-rc.8
|
|
296
|
+
*/
|
|
297
|
+
export declare const makeContainer: (options: ContainerOptions) => Effect.Effect<Checkpoints, never, ChildProcessSpawner | Container.Container>;
|
|
298
|
+
/**
|
|
299
|
+
* Provides {@link makeContainer}.
|
|
300
|
+
*
|
|
301
|
+
* @category layers
|
|
302
|
+
* @since 1.0.0-rc.8
|
|
303
|
+
*/
|
|
304
|
+
export declare const layerContainer: (options: ContainerOptions) => Layer.Layer<Checkpoints, never, ChildProcessSpawner | Container.Container>;
|
|
268
305
|
/**
|
|
269
306
|
* Rewrites one call's input so the call runs against a materialized checkpoint.
|
|
270
307
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkpoints.d.ts","sourceRoot":"","sources":["../../src/Checkpoints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsEG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAA;AACxE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;
|
|
1
|
+
{"version":3,"file":"Checkpoints.d.ts","sourceRoot":"","sources":["../../src/Checkpoints.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/Checkpoints.js
CHANGED
|
@@ -71,6 +71,8 @@
|
|
|
71
71
|
*/
|
|
72
72
|
import { ChildProcessSpawner } from "@smthrs/kernel/ChildProcessSpawner";
|
|
73
73
|
import { Context, Effect, Layer, Schema } from "effect";
|
|
74
|
+
import * as Container from "./Container.js";
|
|
75
|
+
import * as Exec from "./internal/Exec.js";
|
|
74
76
|
import { GitWorktree } from "./internal/GitWorktree.js";
|
|
75
77
|
import { withoutTrailingSlash } from "./internal/Paths.js";
|
|
76
78
|
import * as Prune from "./internal/Prune.js";
|
|
@@ -284,6 +286,89 @@ const gitStore = (options, spawner) => {
|
|
|
284
286
|
* @since 1.0.0
|
|
285
287
|
*/
|
|
286
288
|
export const layerGit = (options) => Layer.effect(Checkpoints)(makeGit(options));
|
|
289
|
+
/**
|
|
290
|
+
* `$1` is the id. Records the working tree with `stash create` over a copy of
|
|
291
|
+
* the index, names the commit in config as {@link makeGit} does, and prints it.
|
|
292
|
+
*/
|
|
293
|
+
const pinScript = [
|
|
294
|
+
"set -e",
|
|
295
|
+
"cd \"$(git rev-parse --show-toplevel)\"",
|
|
296
|
+
"copy=$(mktemp -d)",
|
|
297
|
+
"trap 'rm -rf -- \"$copy\"' EXIT",
|
|
298
|
+
"index=$(git rev-parse --git-path index)",
|
|
299
|
+
"if [ -e \"$index\" ]; then cp -p -- \"$index\" \"$copy/index\"; fi",
|
|
300
|
+
"commit=$(GIT_INDEX_FILE=\"$copy/index\" git -c user.name=flows -c user.email=flows@localhost stash create \"flows checkpoint $1\")",
|
|
301
|
+
"[ -n \"$commit\" ] || commit=$(git rev-parse --verify 'HEAD^{commit}')",
|
|
302
|
+
`git config "${configSection}.$1" "$commit"`,
|
|
303
|
+
"printf '%s\\n' \"$commit\""
|
|
304
|
+
].join("\n");
|
|
305
|
+
/**
|
|
306
|
+
* `$1` is the id, `$2` the lease. Checks the tree out at
|
|
307
|
+
* `<top>/.flows-checkpoints/<id>-<lease>` in a repository of its own whose
|
|
308
|
+
* objects are the workspace's, and prints the top and the checkout.
|
|
309
|
+
*/
|
|
310
|
+
const checkoutScript = [
|
|
311
|
+
"set -e",
|
|
312
|
+
"top=$(git rev-parse --show-toplevel)",
|
|
313
|
+
"cd \"$top\"",
|
|
314
|
+
"common=$(cd \"$(git rev-parse --git-common-dir)\" && pwd -P)",
|
|
315
|
+
`if [ "$1" = ${baseId} ]; then`,
|
|
316
|
+
` commit=$(git rev-parse --verify --quiet '${TestRunner.captureBase}^{commit}' || git rev-parse --verify --quiet 'HEAD^{commit}') || { echo "No commit resolves from ${TestRunner.captureBase} or HEAD in $top." >&2; exit 1; }`,
|
|
317
|
+
"else",
|
|
318
|
+
` commit=$(git config --get "${configSection}.$1") || { echo "No checkpoint is stored under $1 in $top. Take the reading on the live tree instead." >&2; exit 1; }`,
|
|
319
|
+
"fi",
|
|
320
|
+
`dir="$top/${scratchDirectory}/$1-$2"`,
|
|
321
|
+
"trap 'rm -rf -- \"$dir\"' EXIT",
|
|
322
|
+
`mkdir -p -- "$top/${scratchDirectory}"`,
|
|
323
|
+
"git init --quiet \"$dir\"",
|
|
324
|
+
"printf '%s/objects\\n' \"$common\" > \"$dir/.git/objects/info/alternates\"",
|
|
325
|
+
"git -C \"$dir\" checkout --quiet --detach \"$commit\"",
|
|
326
|
+
"trap - EXIT",
|
|
327
|
+
"printf '%s\\n%s\\n' \"$top\" \"$dir\""
|
|
328
|
+
].join("\n");
|
|
329
|
+
/**
|
|
330
|
+
* Builds the store for a host whose `bash` runs in one container.
|
|
331
|
+
*
|
|
332
|
+
* The repository is the container's, so every git command runs there, through
|
|
333
|
+
* the same {@link Container.Container} transport `bash` takes. The host store
|
|
334
|
+
* looked in the CLI's own directory instead and refused every `at` in 112 of
|
|
335
|
+
* 123 trials of the 2026-10-14 SWE-bench Pro run. This keeps that store's
|
|
336
|
+
* shape: a checkpoint is a `stash create` commit named in config, and a tree
|
|
337
|
+
* is checked out under the repository for one call, so a relocated `bash`
|
|
338
|
+
* call keeps its subdirectory and still finds what the workspace installs
|
|
339
|
+
* beside it. That git may run what the repository configures, but only inside
|
|
340
|
+
* the container the agent already commands.
|
|
341
|
+
*
|
|
342
|
+
* @category constructors
|
|
343
|
+
* @since 1.0.0-rc.8
|
|
344
|
+
*/
|
|
345
|
+
export const makeContainer = (options) => Effect.gen(function* () {
|
|
346
|
+
const spawner = yield* ChildProcessSpawner;
|
|
347
|
+
const transport = yield* Container.Container;
|
|
348
|
+
const sh = (script, args) => transport.exec({
|
|
349
|
+
container: options.container,
|
|
350
|
+
file: "sh",
|
|
351
|
+
args: ["-c", script, "sh", ...args],
|
|
352
|
+
cwd: options.cwd,
|
|
353
|
+
stdin: false
|
|
354
|
+
}).pipe(Effect.flatMap((plan) => Exec.exec(plan.file, { args: plan.args, env: plan.env }).pipe(Effect.mapError((error) => Exec.toStdError(plan.file, error)))), Effect.flatMap((result) => result.exitCode === 0
|
|
355
|
+
? Effect.succeed(result.stdout.trim().split("\n"))
|
|
356
|
+
: Effect.fail(failed(`git in container ${options.container} failed: ${result.stderr.trim()}`))), Effect.provideService(ChildProcessSpawner, spawner));
|
|
357
|
+
const named = (id) => namable.test(id)
|
|
358
|
+
? Effect.void
|
|
359
|
+
: Effect.fail(failed(`A checkpoint id must match ${namable.source}; ${id} does not.`, "invalid_input"));
|
|
360
|
+
return make({
|
|
361
|
+
capture: (id) => named(id).pipe(Effect.andThen(sh(pinScript, [id])), Effect.map(([commit]) => new Snapshot({ id, ref: commit }))),
|
|
362
|
+
materialize: (id, use) => named(id).pipe(Effect.andThen(Effect.acquireUseRelease(sh(checkoutScript, [id, globalThis.crypto.randomUUID()]), ([root, checkout]) => use({ id, host: checkout, guest: checkout, root: root, guestRoot: root }), ([, checkout]) => sh("rm -rf -- \"$1\"", [checkout]).pipe(Effect.catch((error) => Effect.logWarning("could not remove a scratch checkout").pipe(Effect.annotateLogs({ path: checkout, reason: error.message })))))))
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
/**
|
|
366
|
+
* Provides {@link makeContainer}.
|
|
367
|
+
*
|
|
368
|
+
* @category layers
|
|
369
|
+
* @since 1.0.0-rc.8
|
|
370
|
+
*/
|
|
371
|
+
export const layerContainer = (options) => Layer.effect(Checkpoints)(makeContainer(options));
|
|
287
372
|
/**
|
|
288
373
|
* Rewrites one call's input so the call runs against a materialized checkpoint.
|
|
289
374
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkpoints.js","sourceRoot":"","sources":["../../src/Checkpoints.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pinned trees, and the scratch checkouts a call runs against.\n *\n * A checkpoint is a **value**: minting one changes nothing about the workspace,\n * and running a call against one leaves the live tree exactly as it stands.\n * That is the whole of what it buys. Before it existed, the only way a run\n * could answer \"did this command fail before my change\" was to undo the change\n * — and the journals price that exactly. On `sympy__sympy-13878` the r95repl\n * lane applied one byte-identical 4,789-character patch **five times**, four of\n * those applications preceded by `git checkout -- sympy/stats/crv_types.py`,\n * because a clean fails-before proof required reverting the very work it was\n * meant to prove.\n *\n * This module is the host half of `@smthrs/harness`'s `ctx.checkpoint()` and\n * `ctx.call(flow, input, { at })`. The harness owns the surface, the identity,\n * the bound and the refusals; nothing in it knows what a tree is. This owns the\n * two host operations that need one:\n *\n * - {@link Checkpoints.capture} records the working tree under an id, and\n * - {@link Checkpoints.materialize} gives that tree back as a directory for the\n * length of one call.\n *\n * ## The git binding, and the container constraint it is shaped by\n *\n * {@link layerGit} records a checkpoint with `git stash create`, then names the\n * commit it prints in this repository's own git config rather than with a ref —\n * see {@link configSection} for why a ref would hand the agent its own edit back\n * as history. `stash create` is the one git command\n * that records the working tree and changes nothing else: it does not move the\n * worktree and does not touch the stash ref. It runs against a shadow\n * `GIT_DIR` over a copy of the workspace index (see `internal/GitWorktree.ts`),\n * so it never writes the repository's index and never runs a program the\n * workspace's `.git` names. That matters more than it sounds. The agent runs `git` in this same\n * workspace and its own `git diff` is the run's evidence, so a capture that\n * staged into the real index would be the harness editing the evidence while\n * recording it. A tree with nothing to record prints nothing, and the\n * checkpoint is then `HEAD` — which is exactly what the tree is.\n *\n * Untracked files are not in the recorded tree, which matches how the rig\n * captures a patch: `capture-patch.sh` drops paths that did not exist when the\n * agent started, so a checkpoint holds what a patch would hold.\n *\n * The materialization is a detached checkout at `<root>/.flows-checkpoints/<id>-<lease>`\n * — **inside** the workspace, which looks wrong and is the only thing that\n * works. A benchmark check runs through `docker exec` inside `/testbed`, and\n * `/testbed` is a bind mount of the live workspace. A scratch checkout anywhere\n * else on the host is simply not visible to the container, so the call would run\n * against a path that does not exist. Placed under the workspace it is visible\n * at the same subpath under the mount, needs no second mount, and needs no\n * change to how the rig starts its container. `TestRun`'s `against: \"base\"`\n * reached the same conclusion first and this reuses its shape;\n * `evals/swebench/run-instance.sh` excludes the directory from patch capture for\n * the same reason it excludes `.flows-test-base`.\n *\n * The checkout is added and removed around **one call**, which is stricter than\n * the frame lifetime the design asks for and much simpler: there is no cache to\n * invalidate, no id to leak, and a checkpoint used by three calls pays three\n * checkouts rather than risking one stale directory. A run that reuses a\n * checkpoint across frames simply re-materializes it, because the ref is what\n * persists.\n *\n * ## What can be pointed at a checkpoint, and what cannot\n *\n * {@link relocate} is the closed answer, and `Relocate.ts` owns it. That half\n * knows the input shapes of `bash`, `read`, `ls`, `grep` and `glob`; this half\n * knows git. They change for different reasons, so they are different modules,\n * and {@link relocate} is re-exported here because the harness reaches the whole\n * feature through this one.\n *\n * @since 1.0.0\n */\n\nimport { ChildProcessSpawner } from \"@smthrs/kernel/ChildProcessSpawner\"\nimport { Context, Effect, Layer, Schema } from \"effect\"\nimport { GitWorktree } from \"./internal/GitWorktree.ts\"\nimport { withoutTrailingSlash } from \"./internal/Paths.ts\"\nimport * as Prune from \"./internal/Prune.ts\"\nimport * as StdError from \"./StdError.ts\"\nimport * as TestRunner from \"./TestRunner.ts\"\n\n/**\n * The id naming the tree a run opened on.\n *\n * It is not minted: the host either recorded one before the agent started or it\n * did not. {@link layerGit} resolves it to {@link TestRunner.captureBase} and\n * then to `HEAD`, which is the same precedence `TestRun` uses for the same\n * question — so a workspace that has one answer for a baseline has one answer\n * for both.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const baseId = \"base\"\n\n/**\n * The directory checkpoints are materialized into, relative to the repository\n * root and therefore also relative to a container's view of it.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const scratchDirectory = Prune.checkpointScratch\n\n/**\n * The git-config section {@link layerGit} records minted checkpoints under.\n *\n * Config and **not a ref**, and that is the whole of the decision. A ref is\n * history: `git log --all` lists it, `git show` prints it, and `git log --all\n * -S` searches it — so a checkpoint named by a ref would hand an agent a commit\n * containing its own edit and let it read that back as if it were upstream\n * work. That exact class cost the measured program real money before the rig\n * moved jj's store out of the workspace (`evals/swebench/run-instance.sh`:\n * django-13346 applied two of its own snapshots as a fake fix), and a ref here\n * would reintroduce it one wave later.\n *\n * The commit object itself is unreferenced. It stays alive for the life of the\n * run — nothing in a run prunes — and a checkout by id works perfectly well,\n * which is measured in `CheckpointsFixture.test.ts`. So the tree is reachable\n * by id and no command that walks refs can reach it:\n * `git log`, `git log --all`, `git log -S` and `git show <ref>` all answer\n * exactly as they did before the capture.\n *\n * One command still sees it, measured rather than argued in\n * `CheckpointsFixture.test.ts`: `git fsck` reports it as a dangling commit,\n * because that is what an unreferenced commit is. A checkout is a standalone\n * repository, not a worktree of this one, so `git log --all` does not list it\n * even while a call runs in it. It is not reachable by a name that looks like project history, and the cell\n * contract's environment section says outright that a dangling commit is this\n * harness holding the agent's own tree — which is the half of the django-13346\n * lesson that a store cannot enforce.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const configSection = \"flows-checkpoint\"\n\n/**\n * One tree this run has pinned.\n *\n * `ref` is the store's own name for it. Nothing above this module interprets\n * it; it travels so the journal says which tree a checkpointed call read.\n *\n * @category models\n * @since 1.0.0\n */\nexport class Snapshot extends Schema.Class<Snapshot>(\"@smthrs/std/Checkpoints/Snapshot\")({\n id: Schema.String,\n ref: Schema.String\n}) {}\n\n/**\n * A tree handed back as a directory, for the length of one call.\n *\n * `host` is where the process running on this machine finds it. `guest` is\n * where a container finds the same directory, which is the same path when no\n * container is in play. They differ exactly as `TestRunner.Runner`'s `cwd` and\n * `root` differ, and for the same reason: one directory, two names.\n *\n * `root` and `guestRoot` are the workspace itself under those same two names.\n * They travel because a call that named a directory *inside* the workspace has\n * to keep it: a check that runs in `tests/` and is pointed at a checkpoint must\n * run in the checkpoint's `tests/`, not at its top. Silently dropping that\n * subpath is how a baseline comes back failing for a reason nobody chose, which\n * is the one failure a checkpoint exists to make impossible.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface Materialized {\n readonly id: string\n readonly host: string\n readonly guest: string\n /** The workspace this checkpoint is a tree of, as this machine names it. */\n readonly root: string\n /** The workspace as a container names it; the same as {@link Materialized.root} when none does. */\n readonly guestRoot: string\n}\n\n/**\n * The two host operations a checkpoint needs.\n *\n * @category services\n * @since 1.0.0\n */\nexport interface Checkpoints {\n /** Records the working tree as it stands, under `id`. */\n readonly capture: (id: string) => Effect.Effect<Snapshot, StdError.StdError>\n /**\n * Hands `id` back as a directory for the length of the effect it wraps.\n *\n * Scoped rather than returned, because the directory has to be removed\n * however the call ends — a benchmark run killed at its wall-clock budget\n * would otherwise leave a second checkout of the whole repository in a tree\n * whose diff is the run's answer.\n */\n readonly materialize: <A, E, R>(\n id: string,\n use: (materialized: Materialized) => Effect.Effect<A, E, R>\n ) => Effect.Effect<A, E | StdError.StdError, R>\n}\n\n/**\n * The {@link Checkpoints} service tag.\n *\n * @category services\n * @since 1.0.0\n */\nexport const Checkpoints: Context.Service<Checkpoints, Checkpoints> = Context.Service(\"@smthrs/std/Checkpoints\")\n\n/**\n * Builds a store from its two operations.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const make = (service: Checkpoints): Checkpoints => Checkpoints.of(service)\n\n/**\n * The refusal a host with nowhere to pin a tree answers with.\n *\n * @category errors\n * @since 1.0.0\n */\nexport const unavailable = (id: string): StdError.StdError =>\n new StdError.StdError({\n code: \"provider_unavailable\",\n message: `This host pins no trees, so it holds no checkpoint \"${id}\". Take the reading on the live tree instead.`\n })\n\n/**\n * Builds a store for a host that pins nothing.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const makeNoop = (): Checkpoints =>\n make({\n capture: (id) => Effect.fail(unavailable(id)),\n materialize: (id) => Effect.fail(unavailable(id))\n })\n\n/**\n * Provides {@link makeNoop}.\n *\n * @category layers\n * @since 1.0.0\n */\nexport const layerNoop: Layer.Layer<Checkpoints> = Layer.succeed(Checkpoints, makeNoop())\n\n/**\n * What {@link layerGit} needs to know about the repository it pins.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface GitOptions {\n /** The host path of the git repository whose trees are pinned. */\n readonly root: string\n /**\n * Where a container sees {@link GitOptions.root}, when one does.\n *\n * Absent means there is no container, and the guest path of a materialized\n * checkpoint is then its host path.\n */\n readonly cwd?: string | undefined\n /**\n * The ref naming the tree the run opened on, for {@link baseId}.\n *\n * Absent takes `TestRunner.captureBase` and then `HEAD`, which is the\n * precedence `TestRun` already uses.\n */\n readonly baseRef?: string | undefined\n}\n\nconst failed = (message: string, code: StdError.Code = \"command_failed\"): StdError.StdError =>\n new StdError.StdError({ code, message })\n\n/**\n * A checkpoint id, restricted to what can safely become a git-config key and a\n * directory name. No dots: a dot would split the config key into another\n * subsection.\n */\nconst namable = /^[A-Za-z0-9][A-Za-z0-9-]*$/\n\n/**\n * Builds the git-backed store.\n *\n * Every git command that records or checks out a tree runs against a shadow\n * `GIT_DIR` the harness owns, never the workspace's `.git`, which the agent\n * can write; see `internal/GitWorktree.ts`. The capture reads a copy of the\n * workspace index, so the agent's own index is untouched: its `git diff` is the\n * run's evidence and must not move because the harness recorded something.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const makeGit = (\n options: GitOptions\n): Effect.Effect<Checkpoints, never, ChildProcessSpawner> =>\n Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner\n return gitStore(options, spawner)\n })\n\n/**\n * The store itself, with the spawner it runs git through already resolved.\n *\n * Resolving it here rather than threading it through {@link Checkpoints} keeps\n * the service's own requirement empty, which is what lets a browser host bind\n * {@link layerNoop} against the identical interface — the same shape\n * `TestRunner` and `Container` take.\n */\nconst gitStore = (options: GitOptions, spawner: ChildProcessSpawner[\"Service\"]): Checkpoints => {\n const spawn = <A, E>(effect: Effect.Effect<A, E, ChildProcessSpawner>): Effect.Effect<A, E> =>\n Effect.provideService(effect, ChildProcessSpawner, spawner)\n const root = withoutTrailingSlash(options.root)\n const guestRoot = options.cwd === undefined ? root : withoutTrailingSlash(options.cwd)\n const keyOf = (id: string) => `${configSection}.${id}`\n const baseRefs = options.baseRef === undefined ? [TestRunner.captureBase, \"HEAD\"] : [options.baseRef]\n\n /**\n * The commit one id names.\n *\n * `base` is the one id nobody mints, so it is resolved from refs — the same\n * precedence `TestRun` uses. Every other id was recorded by {@link capture}\n * into this repository's config file, which is where a checkpoint is named\n * *without* becoming history. See {@link configSection}.\n */\n const commitOf = (id: string) =>\n Effect.gen(function*() {\n if (id === baseId) return (yield* GitWorktree.resolveCommit(root, baseRefs)).commit\n const repository = yield* GitWorktree.open(root)\n const found = yield* GitWorktree.config(repository, [\"--get\", keyOf(id)])\n const commit = found.stdout.trim()\n if (found.exitCode !== 0 || commit === \"\") {\n return yield* Effect.fail(\n failed(\n `No checkpoint is stored under ${id} in ${root}. Take the reading on the live tree instead.`,\n \"not_found\"\n )\n )\n }\n return commit\n })\n\n const capture = (id: string) =>\n spawn(Effect.gen(function*() {\n if (!namable.test(id)) {\n return yield* Effect.fail(\n failed(`A checkpoint id must match ${namable.source}; ${id} does not.`, \"invalid_input\")\n )\n }\n const repository = yield* GitWorktree.open(root)\n const commit = yield* GitWorktree.withShadow(repository, (shadow) =>\n Effect.gen(function*() {\n const head = (yield* GitWorktree.resolveCommit(root, [\"HEAD\"])).commit\n yield* shadow.adoptIndex\n const pinned = yield* shadow.git([\"update-ref\", \"--no-deref\", \"HEAD\", head])\n if (pinned.exitCode !== 0) {\n return yield* Effect.fail(failed(`Could not record the working tree: ${pinned.stderr.trim()}`))\n }\n const recorded = yield* shadow.git([\"stash\", \"create\", `flows checkpoint ${id}`], root)\n if (recorded.exitCode !== 0) {\n return yield* Effect.fail(failed(`Could not record the working tree: ${recorded.stderr.trim()}`))\n }\n // Nothing to record means the working tree IS the commit it is on, so\n // that commit is the checkpoint. `stash create` says so by printing\n // nothing, which is not an error and must not be read as one.\n return recorded.stdout.trim() === \"\" ? head : recorded.stdout.trim()\n }))\n const named = yield* GitWorktree.config(repository, [keyOf(id), commit])\n if (named.exitCode !== 0) {\n return yield* Effect.fail(failed(`Could not name the checkpoint: ${named.stderr.trim()}`))\n }\n return new Snapshot({ id, ref: commit })\n }))\n\n const materialize = <A, E, R>(\n id: string,\n use: (materialized: Materialized) => Effect.Effect<A, E, R>\n ): Effect.Effect<A, E | StdError.StdError, R> =>\n Effect.gen(function*() {\n if (!namable.test(id)) {\n return yield* Effect.fail(\n failed(`A checkpoint id must match ${namable.source}; ${id} does not.`, \"invalid_input\")\n )\n }\n const commit = yield* spawn(commitOf(id))\n const context = yield* Effect.context<R>()\n return yield* GitWorktree.withDetachedWorktree(\n root,\n `${scratchDirectory}/${id}`,\n commit,\n (host) =>\n use({ id, host, guest: `${guestRoot}${host.slice(root.length)}`, root, guestRoot }).pipe(\n Effect.provideContext(context)\n )\n ).pipe(Effect.provideService(ChildProcessSpawner, spawner))\n })\n\n return make({ capture, materialize })\n}\n\n/**\n * Provides {@link makeGit}.\n *\n * @category layers\n * @since 1.0.0\n */\nexport const layerGit = (options: GitOptions): Layer.Layer<Checkpoints, never, ChildProcessSpawner> =>\n Layer.effect(Checkpoints)(makeGit(options))\n/**\n * Rewrites one call's input so the call runs against a materialized checkpoint.\n *\n * Re-exported from {@link Relocate}, which owns the table of flows and fields\n * this rewrite is driven by. It is exported here because the harness reaches the\n * whole checkpoint feature through this module.\n *\n * @category conversions\n * @since 1.0.0\n */\nexport { relocate, type Relocation } from \"./Relocate.ts\"\n"]}
|
|
1
|
+
{"version":3,"file":"Checkpoints.js","sourceRoot":"","sources":["../../src/Checkpoints.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Pinned trees, and the scratch checkouts a call runs against.\n *\n * A checkpoint is a **value**: minting one changes nothing about the workspace,\n * and running a call against one leaves the live tree exactly as it stands.\n * That is the whole of what it buys. Before it existed, the only way a run\n * could answer \"did this command fail before my change\" was to undo the change\n * — and the journals price that exactly. On `sympy__sympy-13878` the r95repl\n * lane applied one byte-identical 4,789-character patch **five times**, four of\n * those applications preceded by `git checkout -- sympy/stats/crv_types.py`,\n * because a clean fails-before proof required reverting the very work it was\n * meant to prove.\n *\n * This module is the host half of `@smthrs/harness`'s `ctx.checkpoint()` and\n * `ctx.call(flow, input, { at })`. The harness owns the surface, the identity,\n * the bound and the refusals; nothing in it knows what a tree is. This owns the\n * two host operations that need one:\n *\n * - {@link Checkpoints.capture} records the working tree under an id, and\n * - {@link Checkpoints.materialize} gives that tree back as a directory for the\n * length of one call.\n *\n * ## The git binding, and the container constraint it is shaped by\n *\n * {@link layerGit} records a checkpoint with `git stash create`, then names the\n * commit it prints in this repository's own git config rather than with a ref —\n * see {@link configSection} for why a ref would hand the agent its own edit back\n * as history. `stash create` is the one git command\n * that records the working tree and changes nothing else: it does not move the\n * worktree and does not touch the stash ref. It runs against a shadow\n * `GIT_DIR` over a copy of the workspace index (see `internal/GitWorktree.ts`),\n * so it never writes the repository's index and never runs a program the\n * workspace's `.git` names. That matters more than it sounds. The agent runs `git` in this same\n * workspace and its own `git diff` is the run's evidence, so a capture that\n * staged into the real index would be the harness editing the evidence while\n * recording it. A tree with nothing to record prints nothing, and the\n * checkpoint is then `HEAD` — which is exactly what the tree is.\n *\n * Untracked files are not in the recorded tree, which matches how the rig\n * captures a patch: `capture-patch.sh` drops paths that did not exist when the\n * agent started, so a checkpoint holds what a patch would hold.\n *\n * The materialization is a detached checkout at `<root>/.flows-checkpoints/<id>-<lease>`\n * — **inside** the workspace, which looks wrong and is the only thing that\n * works. A benchmark check runs through `docker exec` inside `/testbed`, and\n * `/testbed` is a bind mount of the live workspace. A scratch checkout anywhere\n * else on the host is simply not visible to the container, so the call would run\n * against a path that does not exist. Placed under the workspace it is visible\n * at the same subpath under the mount, needs no second mount, and needs no\n * change to how the rig starts its container. `TestRun`'s `against: \"base\"`\n * reached the same conclusion first and this reuses its shape;\n * `evals/swebench/run-instance.sh` excludes the directory from patch capture for\n * the same reason it excludes `.flows-test-base`.\n *\n * The checkout is added and removed around **one call**, which is stricter than\n * the frame lifetime the design asks for and much simpler: there is no cache to\n * invalidate, no id to leak, and a checkpoint used by three calls pays three\n * checkouts rather than risking one stale directory. A run that reuses a\n * checkpoint across frames simply re-materializes it, because the ref is what\n * persists.\n *\n * ## What can be pointed at a checkpoint, and what cannot\n *\n * {@link relocate} is the closed answer, and `Relocate.ts` owns it. That half\n * knows the input shapes of `bash`, `read`, `ls`, `grep` and `glob`; this half\n * knows git. They change for different reasons, so they are different modules,\n * and {@link relocate} is re-exported here because the harness reaches the whole\n * feature through this one.\n *\n * @since 1.0.0\n */\n\nimport { ChildProcessSpawner } from \"@smthrs/kernel/ChildProcessSpawner\"\nimport { Context, Effect, Layer, Schema } from \"effect\"\nimport * as Container from \"./Container.ts\"\nimport * as Exec from \"./internal/Exec.ts\"\nimport { GitWorktree } from \"./internal/GitWorktree.ts\"\nimport { withoutTrailingSlash } from \"./internal/Paths.ts\"\nimport * as Prune from \"./internal/Prune.ts\"\nimport * as StdError from \"./StdError.ts\"\nimport * as TestRunner from \"./TestRunner.ts\"\n\n/**\n * The id naming the tree a run opened on.\n *\n * It is not minted: the host either recorded one before the agent started or it\n * did not. {@link layerGit} resolves it to {@link TestRunner.captureBase} and\n * then to `HEAD`, which is the same precedence `TestRun` uses for the same\n * question — so a workspace that has one answer for a baseline has one answer\n * for both.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const baseId = \"base\"\n\n/**\n * The directory checkpoints are materialized into, relative to the repository\n * root and therefore also relative to a container's view of it.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const scratchDirectory = Prune.checkpointScratch\n\n/**\n * The git-config section {@link layerGit} records minted checkpoints under.\n *\n * Config and **not a ref**, and that is the whole of the decision. A ref is\n * history: `git log --all` lists it, `git show` prints it, and `git log --all\n * -S` searches it — so a checkpoint named by a ref would hand an agent a commit\n * containing its own edit and let it read that back as if it were upstream\n * work. That exact class cost the measured program real money before the rig\n * moved jj's store out of the workspace (`evals/swebench/run-instance.sh`:\n * django-13346 applied two of its own snapshots as a fake fix), and a ref here\n * would reintroduce it one wave later.\n *\n * The commit object itself is unreferenced. It stays alive for the life of the\n * run — nothing in a run prunes — and a checkout by id works perfectly well,\n * which is measured in `CheckpointsFixture.test.ts`. So the tree is reachable\n * by id and no command that walks refs can reach it:\n * `git log`, `git log --all`, `git log -S` and `git show <ref>` all answer\n * exactly as they did before the capture.\n *\n * One command still sees it, measured rather than argued in\n * `CheckpointsFixture.test.ts`: `git fsck` reports it as a dangling commit,\n * because that is what an unreferenced commit is. A checkout is a standalone\n * repository, not a worktree of this one, so `git log --all` does not list it\n * even while a call runs in it. It is not reachable by a name that looks like project history, and the cell\n * contract's environment section says outright that a dangling commit is this\n * harness holding the agent's own tree — which is the half of the django-13346\n * lesson that a store cannot enforce.\n *\n * @category constants\n * @since 1.0.0\n */\nexport const configSection = \"flows-checkpoint\"\n\n/**\n * One tree this run has pinned.\n *\n * `ref` is the store's own name for it. Nothing above this module interprets\n * it; it travels so the journal says which tree a checkpointed call read.\n *\n * @category models\n * @since 1.0.0\n */\nexport class Snapshot extends Schema.Class<Snapshot>(\"@smthrs/std/Checkpoints/Snapshot\")({\n id: Schema.String,\n ref: Schema.String\n}) {}\n\n/**\n * A tree handed back as a directory, for the length of one call.\n *\n * `host` is where the process running on this machine finds it. `guest` is\n * where a container finds the same directory, which is the same path when no\n * container is in play. They differ exactly as `TestRunner.Runner`'s `cwd` and\n * `root` differ, and for the same reason: one directory, two names.\n *\n * `root` and `guestRoot` are the workspace itself under those same two names.\n * They travel because a call that named a directory *inside* the workspace has\n * to keep it: a check that runs in `tests/` and is pointed at a checkpoint must\n * run in the checkpoint's `tests/`, not at its top. Silently dropping that\n * subpath is how a baseline comes back failing for a reason nobody chose, which\n * is the one failure a checkpoint exists to make impossible.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface Materialized {\n readonly id: string\n readonly host: string\n readonly guest: string\n /** The workspace this checkpoint is a tree of, as this machine names it. */\n readonly root: string\n /** The workspace as a container names it; the same as {@link Materialized.root} when none does. */\n readonly guestRoot: string\n}\n\n/**\n * The two host operations a checkpoint needs.\n *\n * @category services\n * @since 1.0.0\n */\nexport interface Checkpoints {\n /** Records the working tree as it stands, under `id`. */\n readonly capture: (id: string) => Effect.Effect<Snapshot, StdError.StdError>\n /**\n * Hands `id` back as a directory for the length of the effect it wraps.\n *\n * Scoped rather than returned, because the directory has to be removed\n * however the call ends — a benchmark run killed at its wall-clock budget\n * would otherwise leave a second checkout of the whole repository in a tree\n * whose diff is the run's answer.\n */\n readonly materialize: <A, E, R>(\n id: string,\n use: (materialized: Materialized) => Effect.Effect<A, E, R>\n ) => Effect.Effect<A, E | StdError.StdError, R>\n}\n\n/**\n * The {@link Checkpoints} service tag.\n *\n * @category services\n * @since 1.0.0\n */\nexport const Checkpoints: Context.Service<Checkpoints, Checkpoints> = Context.Service(\"@smthrs/std/Checkpoints\")\n\n/**\n * Builds a store from its two operations.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const make = (service: Checkpoints): Checkpoints => Checkpoints.of(service)\n\n/**\n * The refusal a host with nowhere to pin a tree answers with.\n *\n * @category errors\n * @since 1.0.0\n */\nexport const unavailable = (id: string): StdError.StdError =>\n new StdError.StdError({\n code: \"provider_unavailable\",\n message: `This host pins no trees, so it holds no checkpoint \"${id}\". Take the reading on the live tree instead.`\n })\n\n/**\n * Builds a store for a host that pins nothing.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const makeNoop = (): Checkpoints =>\n make({\n capture: (id) => Effect.fail(unavailable(id)),\n materialize: (id) => Effect.fail(unavailable(id))\n })\n\n/**\n * Provides {@link makeNoop}.\n *\n * @category layers\n * @since 1.0.0\n */\nexport const layerNoop: Layer.Layer<Checkpoints> = Layer.succeed(Checkpoints, makeNoop())\n\n/**\n * What {@link layerGit} needs to know about the repository it pins.\n *\n * @category models\n * @since 1.0.0\n */\nexport interface GitOptions {\n /** The host path of the git repository whose trees are pinned. */\n readonly root: string\n /**\n * Where a container sees {@link GitOptions.root}, when one does.\n *\n * Absent means there is no container, and the guest path of a materialized\n * checkpoint is then its host path.\n */\n readonly cwd?: string | undefined\n /**\n * The ref naming the tree the run opened on, for {@link baseId}.\n *\n * Absent takes `TestRunner.captureBase` and then `HEAD`, which is the\n * precedence `TestRun` already uses.\n */\n readonly baseRef?: string | undefined\n}\n\nconst failed = (message: string, code: StdError.Code = \"command_failed\"): StdError.StdError =>\n new StdError.StdError({ code, message })\n\n/**\n * A checkpoint id, restricted to what can safely become a git-config key and a\n * directory name. No dots: a dot would split the config key into another\n * subsection.\n */\nconst namable = /^[A-Za-z0-9][A-Za-z0-9-]*$/\n\n/**\n * Builds the git-backed store.\n *\n * Every git command that records or checks out a tree runs against a shadow\n * `GIT_DIR` the harness owns, never the workspace's `.git`, which the agent\n * can write; see `internal/GitWorktree.ts`. The capture reads a copy of the\n * workspace index, so the agent's own index is untouched: its `git diff` is the\n * run's evidence and must not move because the harness recorded something.\n *\n * @category constructors\n * @since 1.0.0\n */\nexport const makeGit = (\n options: GitOptions\n): Effect.Effect<Checkpoints, never, ChildProcessSpawner> =>\n Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner\n return gitStore(options, spawner)\n })\n\n/**\n * The store itself, with the spawner it runs git through already resolved.\n *\n * Resolving it here rather than threading it through {@link Checkpoints} keeps\n * the service's own requirement empty, which is what lets a browser host bind\n * {@link layerNoop} against the identical interface — the same shape\n * `TestRunner` and `Container` take.\n */\nconst gitStore = (options: GitOptions, spawner: ChildProcessSpawner[\"Service\"]): Checkpoints => {\n const spawn = <A, E>(effect: Effect.Effect<A, E, ChildProcessSpawner>): Effect.Effect<A, E> =>\n Effect.provideService(effect, ChildProcessSpawner, spawner)\n const root = withoutTrailingSlash(options.root)\n const guestRoot = options.cwd === undefined ? root : withoutTrailingSlash(options.cwd)\n const keyOf = (id: string) => `${configSection}.${id}`\n const baseRefs = options.baseRef === undefined ? [TestRunner.captureBase, \"HEAD\"] : [options.baseRef]\n\n /**\n * The commit one id names.\n *\n * `base` is the one id nobody mints, so it is resolved from refs — the same\n * precedence `TestRun` uses. Every other id was recorded by {@link capture}\n * into this repository's config file, which is where a checkpoint is named\n * *without* becoming history. See {@link configSection}.\n */\n const commitOf = (id: string) =>\n Effect.gen(function*() {\n if (id === baseId) return (yield* GitWorktree.resolveCommit(root, baseRefs)).commit\n const repository = yield* GitWorktree.open(root)\n const found = yield* GitWorktree.config(repository, [\"--get\", keyOf(id)])\n const commit = found.stdout.trim()\n if (found.exitCode !== 0 || commit === \"\") {\n return yield* Effect.fail(\n failed(\n `No checkpoint is stored under ${id} in ${root}. Take the reading on the live tree instead.`,\n \"not_found\"\n )\n )\n }\n return commit\n })\n\n const capture = (id: string) =>\n spawn(Effect.gen(function*() {\n if (!namable.test(id)) {\n return yield* Effect.fail(\n failed(`A checkpoint id must match ${namable.source}; ${id} does not.`, \"invalid_input\")\n )\n }\n const repository = yield* GitWorktree.open(root)\n const commit = yield* GitWorktree.withShadow(repository, (shadow) =>\n Effect.gen(function*() {\n const head = (yield* GitWorktree.resolveCommit(root, [\"HEAD\"])).commit\n yield* shadow.adoptIndex\n const pinned = yield* shadow.git([\"update-ref\", \"--no-deref\", \"HEAD\", head])\n if (pinned.exitCode !== 0) {\n return yield* Effect.fail(failed(`Could not record the working tree: ${pinned.stderr.trim()}`))\n }\n const recorded = yield* shadow.git([\"stash\", \"create\", `flows checkpoint ${id}`], root)\n if (recorded.exitCode !== 0) {\n return yield* Effect.fail(failed(`Could not record the working tree: ${recorded.stderr.trim()}`))\n }\n // Nothing to record means the working tree IS the commit it is on, so\n // that commit is the checkpoint. `stash create` says so by printing\n // nothing, which is not an error and must not be read as one.\n return recorded.stdout.trim() === \"\" ? head : recorded.stdout.trim()\n }))\n const named = yield* GitWorktree.config(repository, [keyOf(id), commit])\n if (named.exitCode !== 0) {\n return yield* Effect.fail(failed(`Could not name the checkpoint: ${named.stderr.trim()}`))\n }\n return new Snapshot({ id, ref: commit })\n }))\n\n const materialize = <A, E, R>(\n id: string,\n use: (materialized: Materialized) => Effect.Effect<A, E, R>\n ): Effect.Effect<A, E | StdError.StdError, R> =>\n Effect.gen(function*() {\n if (!namable.test(id)) {\n return yield* Effect.fail(\n failed(`A checkpoint id must match ${namable.source}; ${id} does not.`, \"invalid_input\")\n )\n }\n const commit = yield* spawn(commitOf(id))\n const context = yield* Effect.context<R>()\n return yield* GitWorktree.withDetachedWorktree(\n root,\n `${scratchDirectory}/${id}`,\n commit,\n (host) =>\n use({ id, host, guest: `${guestRoot}${host.slice(root.length)}`, root, guestRoot }).pipe(\n Effect.provideContext(context)\n )\n ).pipe(Effect.provideService(ChildProcessSpawner, spawner))\n })\n\n return make({ capture, materialize })\n}\n\n/**\n * Provides {@link makeGit}.\n *\n * @category layers\n * @since 1.0.0\n */\nexport const layerGit = (options: GitOptions): Layer.Layer<Checkpoints, never, ChildProcessSpawner> =>\n Layer.effect(Checkpoints)(makeGit(options))\n\n/**\n * What {@link makeContainer} needs to know about the container it pins in.\n *\n * @category models\n * @since 1.0.0-rc.8\n */\nexport interface ContainerOptions {\n /** The container `bash` runs in. */\n readonly container: string\n /** A directory of the repository inside it; absent is the container's own working directory. */\n readonly cwd?: string | undefined\n}\n\n/**\n * `$1` is the id. Records the working tree with `stash create` over a copy of\n * the index, names the commit in config as {@link makeGit} does, and prints it.\n */\nconst pinScript = [\n \"set -e\",\n \"cd \\\"$(git rev-parse --show-toplevel)\\\"\",\n \"copy=$(mktemp -d)\",\n \"trap 'rm -rf -- \\\"$copy\\\"' EXIT\",\n \"index=$(git rev-parse --git-path index)\",\n \"if [ -e \\\"$index\\\" ]; then cp -p -- \\\"$index\\\" \\\"$copy/index\\\"; fi\",\n \"commit=$(GIT_INDEX_FILE=\\\"$copy/index\\\" git -c user.name=flows -c user.email=flows@localhost stash create \\\"flows checkpoint $1\\\")\",\n \"[ -n \\\"$commit\\\" ] || commit=$(git rev-parse --verify 'HEAD^{commit}')\",\n `git config \"${configSection}.$1\" \"$commit\"`,\n \"printf '%s\\\\n' \\\"$commit\\\"\"\n].join(\"\\n\")\n\n/**\n * `$1` is the id, `$2` the lease. Checks the tree out at\n * `<top>/.flows-checkpoints/<id>-<lease>` in a repository of its own whose\n * objects are the workspace's, and prints the top and the checkout.\n */\nconst checkoutScript = [\n \"set -e\",\n \"top=$(git rev-parse --show-toplevel)\",\n \"cd \\\"$top\\\"\",\n \"common=$(cd \\\"$(git rev-parse --git-common-dir)\\\" && pwd -P)\",\n `if [ \"$1\" = ${baseId} ]; then`,\n ` commit=$(git rev-parse --verify --quiet '${TestRunner.captureBase}^{commit}' || git rev-parse --verify --quiet 'HEAD^{commit}') || { echo \"No commit resolves from ${TestRunner.captureBase} or HEAD in $top.\" >&2; exit 1; }`,\n \"else\",\n ` commit=$(git config --get \"${configSection}.$1\") || { echo \"No checkpoint is stored under $1 in $top. Take the reading on the live tree instead.\" >&2; exit 1; }`,\n \"fi\",\n `dir=\"$top/${scratchDirectory}/$1-$2\"`,\n \"trap 'rm -rf -- \\\"$dir\\\"' EXIT\",\n `mkdir -p -- \"$top/${scratchDirectory}\"`,\n \"git init --quiet \\\"$dir\\\"\",\n \"printf '%s/objects\\\\n' \\\"$common\\\" > \\\"$dir/.git/objects/info/alternates\\\"\",\n \"git -C \\\"$dir\\\" checkout --quiet --detach \\\"$commit\\\"\",\n \"trap - EXIT\",\n \"printf '%s\\\\n%s\\\\n' \\\"$top\\\" \\\"$dir\\\"\"\n].join(\"\\n\")\n\n/**\n * Builds the store for a host whose `bash` runs in one container.\n *\n * The repository is the container's, so every git command runs there, through\n * the same {@link Container.Container} transport `bash` takes. The host store\n * looked in the CLI's own directory instead and refused every `at` in 112 of\n * 123 trials of the 2026-10-14 SWE-bench Pro run. This keeps that store's\n * shape: a checkpoint is a `stash create` commit named in config, and a tree\n * is checked out under the repository for one call, so a relocated `bash`\n * call keeps its subdirectory and still finds what the workspace installs\n * beside it. That git may run what the repository configures, but only inside\n * the container the agent already commands.\n *\n * @category constructors\n * @since 1.0.0-rc.8\n */\nexport const makeContainer = (\n options: ContainerOptions\n): Effect.Effect<Checkpoints, never, ChildProcessSpawner | Container.Container> =>\n Effect.gen(function*() {\n const spawner = yield* ChildProcessSpawner\n const transport = yield* Container.Container\n const sh = (script: string, args: ReadonlyArray<string>) =>\n transport.exec({\n container: options.container,\n file: \"sh\",\n args: [\"-c\", script, \"sh\", ...args],\n cwd: options.cwd,\n stdin: false\n }).pipe(\n Effect.flatMap((plan) =>\n Exec.exec(plan.file, { args: plan.args, env: plan.env }).pipe(\n Effect.mapError((error) => Exec.toStdError(plan.file, error))\n )\n ),\n Effect.flatMap((result) =>\n result.exitCode === 0\n ? Effect.succeed(result.stdout.trim().split(\"\\n\"))\n : Effect.fail(failed(`git in container ${options.container} failed: ${result.stderr.trim()}`))\n ),\n Effect.provideService(ChildProcessSpawner, spawner)\n )\n const named = (id: string) =>\n namable.test(id)\n ? Effect.void\n : Effect.fail(failed(`A checkpoint id must match ${namable.source}; ${id} does not.`, \"invalid_input\"))\n return make({\n capture: (id) =>\n named(id).pipe(\n Effect.andThen(sh(pinScript, [id])),\n Effect.map(([commit]) => new Snapshot({ id, ref: commit! }))\n ),\n materialize: (id, use) =>\n named(id).pipe(Effect.andThen(Effect.acquireUseRelease(\n sh(checkoutScript, [id, globalThis.crypto.randomUUID()]),\n ([root, checkout]) => use({ id, host: checkout!, guest: checkout!, root: root!, guestRoot: root! }),\n ([, checkout]) =>\n sh(\"rm -rf -- \\\"$1\\\"\", [checkout!]).pipe(\n Effect.catch((error) =>\n Effect.logWarning(\"could not remove a scratch checkout\").pipe(\n Effect.annotateLogs({ path: checkout, reason: error.message })\n )\n )\n )\n )))\n })\n })\n\n/**\n * Provides {@link makeContainer}.\n *\n * @category layers\n * @since 1.0.0-rc.8\n */\nexport const layerContainer = (\n options: ContainerOptions\n): Layer.Layer<Checkpoints, never, ChildProcessSpawner | Container.Container> =>\n Layer.effect(Checkpoints)(makeContainer(options))\n\n/**\n * Rewrites one call's input so the call runs against a materialized checkpoint.\n *\n * Re-exported from {@link Relocate}, which owns the table of flows and fields\n * this rewrite is driven by. It is exported here because the harness reaches the\n * whole checkpoint feature through this module.\n *\n * @category conversions\n * @since 1.0.0\n */\nexport { relocate, type Relocation } from \"./Relocate.ts\"\n"]}
|
|
@@ -52,6 +52,8 @@ export interface PathReference {
|
|
|
52
52
|
/**
|
|
53
53
|
* Every path token the shell text names, one segment at a time.
|
|
54
54
|
*
|
|
55
|
+
* @throws {StdError.StdError} `invalid_input` when substitutions or quotes nest
|
|
56
|
+
* deeper than the scan reads.
|
|
55
57
|
* @category pre-check
|
|
56
58
|
* @since 1.0.0
|
|
57
59
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EnvelopePrecheck.d.ts","sourceRoot":"","sources":["../../../src/internal/EnvelopePrecheck.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,KAAK,IAAI,MAAM,qBAAqB,CAAA;AAChD,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAA;AAG1C;;;;;;;;;GASG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IACrC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACvC;AAED;;;;;GAKG;AACH,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAErC;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;
|
|
1
|
+
{"version":3,"file":"EnvelopePrecheck.d.ts","sourceRoot":"","sources":["../../../src/internal/EnvelopePrecheck.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,KAAK,IAAI,MAAM,qBAAqB,CAAA;AAChD,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAA;AAG1C;;;;;;;;;GASG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IACrC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;CACvC;AAED;;;;;GAKG;AACH,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAErC;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAkUD;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,KAAG,aAAa,CAAC,aAAa,CAClE,CAAA;AAmB9B;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAC1B,OAAO,QAAQ,EACf,MAAM,MAAM,EACZ,MAAM,IAAI,CAAC,IAAI,KACd,QAAQ,CAAC,QAAQ,GAAG,SAgDtB,CAAA"}
|