diff --git a/README.md b/README.md index 9b0075c8..07a2fabd 100644 --- a/README.md +++ b/README.md @@ -806,8 +806,8 @@ untrusted. A job resumes with another maintainer's answer only once you approve it in the inbox. opencode runs as `opencode serve` on localhost behind a password made for each turn. -The flake's `runner` package is a `quest` whose Claude Code and opencode (and on -Linux, bubblewrap and socat) are pinned by this repository's lock +The flake's `runner` package pins Claude Code 2.1.305 from its official release +manifest; opencode (and on Linux, bubblewrap and socat) follow this repository's lock (`nix run github:kixelated/quest#runner -- runner`); otherwise `claude` and `opencode` come from `QUEST_RUNNER_CLAUDE` and `QUEST_RUNNER_OPENCODE`, or `PATH`. State lives in `$QUEST_RUNNER_HOME`, by default diff --git a/flake.nix b/flake.nix index f54f89f4..df7714e7 100644 --- a/flake.nix +++ b/flake.nix @@ -29,7 +29,12 @@ config.allowUnfreePredicate = pkg: nixpkgs.lib.getName pkg == "claude-code"; }; quest = pkgs.callPackage ./nix/package.nix { }; - # `quest runner` with the agent CLIs it drives pinned by this flake's lock, and on + # https://downloads.claude.ai/claude-code-releases/2.1.305/manifest.zst.json + # Keep the tested runner version independent of nixpkgs. + claudeCode = pkgs.claude-code.override { + manifest = pkgs.lib.importJSON ./nix/claude-code.json; + }; + # `quest runner` with pinned agent CLIs, and on # Linux the bubblewrap and socat Claude Code's sandbox runs its jobs' commands in. runner = pkgs.symlinkJoin { name = "quest-runner-${quest.version}"; @@ -37,7 +42,7 @@ nativeBuildInputs = [ pkgs.makeWrapper ]; postBuild = '' wrapProgram $out/bin/quest \ - --set-default QUEST_RUNNER_CLAUDE ${pkgs.lib.getExe pkgs.claude-code} \ + --set-default QUEST_RUNNER_CLAUDE ${pkgs.lib.getExe claudeCode} \ --set-default QUEST_RUNNER_OPENCODE ${pkgs.lib.getExe pkgs.opencode} ${pkgs.lib.optionalString pkgs.stdenv.isLinux "--prefix PATH : ${ pkgs.lib.makeBinPath [ pkgs.bubblewrap diff --git a/nix/claude-code.json b/nix/claude-code.json new file mode 100644 index 00000000..db01c18c --- /dev/null +++ b/nix/claude-code.json @@ -0,0 +1,17 @@ +{ + "version": "2.1.305", + "platforms": { + "darwin-arm64": { + "binary": "claude.zst", + "checksum": "1dddc458ce8aacc49677b37c97efbba37a1b8f12bb3cbaf26388a47fbda4a6a9" + }, + "linux-arm64": { + "binary": "claude.zst", + "checksum": "2823ad5678a72913e8b2f747e4a82fc6b157cb4896e13b8abafc07fbe278446b" + }, + "linux-x64": { + "binary": "claude.zst", + "checksum": "45cfa7d4f6244024a3b1f0a1e6806afa254013f8065e8ea9ba937d7d696a26a3" + } + } +} diff --git a/quest/m0/kix/sandbox/README.md b/quest/m0/kix/sandbox/README.md index e90c6d78..b1ee789d 100644 --- a/quest/m0/kix/sandbox/README.md +++ b/quest/m0/kix/sandbox/README.md @@ -72,7 +72,6 @@ Shipped in [kixelated/quest#212](https://github.com/kixelated/quest/pull/212) ## Required -- [Claude Code pin](/quest/m0/kix/sandbox/claude-pin.md) - the runner's pinned Claude Code moves to the version the sandbox docs describe - [Head approval](/quest/m0/kix/sandbox/head-approval.md) - a maintainer approves an exact head, such as one kix pushed, for host runners - [opencode sandboxing](/quest/m0/kix/sandbox/opencode.md) - opencode jobs' commands run in the same sandbox as Claude Code's - [macOS sandbox](/quest/m0/kix/sandbox/macos.md) - the boundary verified under Seatbelt, then the answer-approval gate removed diff --git a/quest/m0/kix/sandbox/claude-pin.md b/quest/m0/kix/sandbox/claude-pin.md deleted file mode 100644 index 774ca1b8..00000000 --- a/quest/m0/kix/sandbox/claude-pin.md +++ /dev/null @@ -1,19 +0,0 @@ -# [XS] Bump the runner's pinned Claude Code - -## Goal - -The runner's Nix closure pins Claude Code 2.1.305 or later instead of -2.1.291, and the sandbox's behaviour the README documents matches the pinned -version. - -## Plan - -Decided 2026-10-24 (user) as a follow-up of -[kixelated/quest#212](https://github.com/kixelated/quest/pull/212), whose live -boundary test passed on both 2.1.291 and 2.1.305: - -- Bump the pin in `flake.nix` (`QUEST_RUNNER_CLAUDE`) and rerun the runner - tests. Check the sandbox settings the runner passes (`dontAsk`, - `--setting-sources user`, the MCP tool timeout) against the new version's - docs and changelog. -- Ranked first in the epic: the later children build on the pinned version. diff --git a/src/runner/claude.ts b/src/runner/claude.ts index 2116b97a..9a760a95 100644 --- a/src/runner/claude.ts +++ b/src/runner/claude.ts @@ -73,6 +73,8 @@ export function claudeSettings(sandbox: Sandbox): Record { enabled: true, failIfUnavailable: true, allowUnsandboxedCommands: false, + // Added in 2.1.305; enabling this lets commands escape filesystem isolation. + allowNestedUserNamespaces: false, autoAllowBashIfSandboxed: true, filesystem: { denyRead: sandbox.deny, diff --git a/test/runner.test.ts b/test/runner.test.ts index 0e370d98..51e95b01 100644 --- a/test/runner.test.ts +++ b/test/runner.test.ts @@ -1589,6 +1589,7 @@ describe("quest runner", () => { enabled: true, failIfUnavailable: true, allowUnsandboxedCommands: false, + allowNestedUserNamespaces: false, filesystem: { allowRead: [checkout, repository, launch.tmp], allowWrite: [launch.tmp] }, // A trusted revision's commands reach the package registries and nothing else. network: { allowedDomains: REGISTRIES, strictAllowlist: true },