Upgrade to Zig 0.17

Build fx with Zig 0.17.0. build.zig.zon requires it, every workflow
installs it, and PGSO qualification uses LLVM 22.1.8 to match.

Zig 0.17 removes `**`, `errdefer |err|`, `@cImport`, and the old type
info field lists. The source moves to `@splat`, a comptime
`text_utils.repeat` helper, split cleanup functions, and the new
field_names and field_types arrays. The Node-API addon translates
node_api.h with a build step. Test fakes checked with `@hasDecl` become
pub, because `@hasDecl` no longer sees private declarations.

`zig build test` gives the test binary its fx and source paths through
a generated test_paths module, since the build no longer exposes install
paths for environment variables.

Zig 0.17's SafeAllocator grows a small allocation in place only when it
sits at the end of a bucket, and buckets are never reused, so an
allocation-failure sweep could count a different number of allocations
on each run. Sweeps and allocation-count tests now use
testing_allocator.no_resize, which never resizes in place.

allocRemaining now treats its limit as inclusive. readFileToEnd keeps
rejecting a file of exactly max_bytes by reading through
appendRemaining. Direct allocRemaining callers for remote responses now
accept a body exactly at their limit.

Zig 0.17's memset stores a full vector per iteration, so the Linux
memset replacement is removed. macOS release binaries now require macOS
15, Zig 0.17's default minimum.
This commit is contained in:
Pranit committed 2026-10-05 19:44:53 -04:00
1 parent e4915028b8
commit b1d515851a
301 files changed
+2121 -1759

No files matched your search

+5 -5
View File
@@ -7,7 +7,7 @@ inputs:
required: false
default: "false"
llvm:
description: Install LLVM 21
description: Install LLVM 22
required: false
default: "false"
bun:
@@ -37,7 +37,7 @@ runs:
if: inputs.zig == 'true' || inputs.llvm == 'true'
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2
with:
version: "0.16.0"
version: "0.17.0"
cache-key: pgso-macos-arm64
- name: Install pinned LLVM
@@ -45,9 +45,9 @@ runs:
if: inputs.llvm == 'true'
shell: bash
run: |
brew install llvm@21
llvm_bin="$(brew --prefix llvm@21)/bin"
"$llvm_bin/opt" --version | grep -F "version 21.1.8"
brew install llvm@22
llvm_bin="$(brew --prefix llvm@22)/bin"
"$llvm_bin/opt" --version | grep -F "version 22.1.8"
echo "path=$llvm_bin" >> "$GITHUB_OUTPUT"
- name: Setup Bun
+4 -4
View File
@@ -46,7 +46,7 @@ jobs:
id: zig
uses: ./.github/actions/setup-zig
with:
version: "0.16.0"
version: "0.17.0"
cache-key: bench-ReleaseSafe
- name: Build (ReleaseSafe)
@@ -105,7 +105,7 @@ jobs:
id: zig
uses: ./.github/actions/setup-zig
with:
version: "0.16.0"
version: "0.17.0"
cache-key: bench-ReleaseSafe
- name: Build (ReleaseSafe)
@@ -139,7 +139,7 @@ jobs:
- name: Setup Zig
uses: ./.github/actions/setup-zig
with:
version: "0.16.0"
version: "0.17.0"
cache-key: bench-ReleaseSafe
- name: Build (ReleaseSafe)
@@ -176,7 +176,7 @@ jobs:
id: zig
uses: ./.github/actions/setup-zig
with:
version: "0.16.0"
version: "0.17.0"
cache-key: bench-ReleaseSafe
- name: Setup Node
+3 -3
View File
@@ -54,7 +54,7 @@ jobs:
- name: Setup Zig
uses: mlugg/setup-zig@v2
with:
version: "0.16.0"
version: "0.17.0"
use-cache: false
- name: Test size reporter
@@ -96,7 +96,7 @@ jobs:
uses: actions/cache/restore@v4
with:
path: ${{ runner.temp }}/fx-binary-size-base-bin
key: binary-size-base-v1-${{ matrix.target }}-zig-0.16.0-${{ env.BASE_SHA }}
key: binary-size-base-v1-${{ matrix.target }}-zig-0.17.0-${{ env.BASE_SHA }}
- name: Build base binary
if: steps.base-cache.outputs.cache-hit != 'true'
@@ -126,7 +126,7 @@ jobs:
uses: actions/cache/save@v4
with:
path: ${{ runner.temp }}/fx-binary-size-base-bin
key: binary-size-base-v1-${{ matrix.target }}-zig-0.16.0-${{ env.BASE_SHA }}
key: binary-size-base-v1-${{ matrix.target }}-zig-0.17.0-${{ env.BASE_SHA }}
- name: Measure base binary
shell: bash
+7 -7
View File
@@ -37,7 +37,7 @@ jobs:
id: zig
uses: ./.github/actions/setup-zig
with:
version: "0.16.0"
version: "0.17.0"
cache-key: ci-unit-${{ matrix.optimize }}
- name: Check formatting
@@ -101,7 +101,7 @@ jobs:
id: zig
uses: ./.github/actions/setup-zig
with:
version: "0.16.0"
version: "0.17.0"
cache-key: ci-build-ReleaseSafe
# Other runners execute this binary, and their CPUs can differ from this
@@ -138,7 +138,7 @@ jobs:
id: zig
uses: ./.github/actions/setup-zig
with:
version: "0.16.0"
version: "0.17.0"
cache-key: ci-sdk-wasm
- name: Build WASM surfaces (ReleaseSmall)
@@ -191,7 +191,7 @@ jobs:
id: zig
uses: ./.github/actions/setup-zig
with:
version: "0.16.0"
version: "0.17.0"
cache-key: ci-sdk-napi-${{ matrix.platform }}
- name: Build N-API addon (ReleaseSafe)
@@ -466,7 +466,7 @@ jobs:
id: zig
uses: ./.github/actions/setup-zig
with:
version: "0.16.0"
version: "0.17.0"
cache-key: ci-e2e-fixtures
- name: Setup Bun
@@ -569,7 +569,7 @@ jobs:
id: zig
uses: ./.github/actions/setup-zig
with:
version: "0.16.0"
version: "0.17.0"
cache-key: ci-mcp-fixtures
- name: Setup Bun
@@ -647,7 +647,7 @@ jobs:
id: zig
uses: ./.github/actions/setup-zig
with:
version: "0.16.0"
version: "0.17.0"
cache-key: ci-unit-ReleaseSafe
- name: Build
+2 -2
View File
@@ -41,7 +41,7 @@ jobs:
- name: Setup Zig
uses: mlugg/setup-zig@v2
with:
version: "0.16.0"
version: "0.17.0"
- name: Build dev binary
run: zig build -Doptimize=ReleaseSafe -Dtarget=${{ matrix.target }} -Dupdate-channel=dev
@@ -77,7 +77,7 @@ jobs:
- name: Setup Zig
uses: mlugg/setup-zig@v2
with:
version: "0.16.0"
version: "0.17.0"
- name: Build terminal WASM
run: zig build -Doptimize=ReleaseSafe -Dwasm-surface=term
+3 -3
View File
@@ -67,7 +67,7 @@ jobs:
- name: Setup Zig
uses: mlugg/setup-zig@v2
with:
version: "0.16.0"
version: "0.17.0"
use-cache: false
- name: Test signing and release routing
@@ -91,7 +91,7 @@ jobs:
- name: Setup Zig
uses: mlugg/setup-zig@v2
with:
version: "0.16.0"
version: "0.17.0"
use-cache: false
# Both E2E shards run this binary on other runners, so build for the
@@ -147,7 +147,7 @@ jobs:
- name: Setup Zig
uses: mlugg/setup-zig@v2
with:
version: "0.16.0"
version: "0.17.0"
use-cache: false
- name: Setup Bun
+2 -2
View File
@@ -134,7 +134,7 @@ jobs:
- name: Setup Zig
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2
with:
version: "0.16.0"
version: "0.17.0"
- name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
@@ -191,7 +191,7 @@ jobs:
- name: Setup Zig
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2
with:
version: "0.16.0"
version: "0.17.0"
- name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
+2 -2
View File
@@ -75,7 +75,7 @@ jobs:
- name: Setup Zig
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2
with:
version: "0.16.0"
version: "0.17.0"
- name: Build
run: zig build -Doptimize=ReleaseSafe -Dtarget=${{ matrix.target }}
@@ -109,7 +109,7 @@ jobs:
- name: Setup Zig
uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2
with:
version: "0.16.0"
version: "0.17.0"
- name: Build
run: zig build -Doptimize=ReleaseSafe -Dtarget=x86_64-macos
+5 -1
View File
@@ -30,7 +30,7 @@ When running fx for verification, **always use the freshly-built binary at** **`
## Language and Toolchain
This project is written in **Zig 0.16+**. There is no Node.js runtime, no `package.json` at the root, and no JavaScript build step for the main binary.
This project is written in **Zig 0.17+**. There is no Node.js runtime, no `package.json` at the root, and no JavaScript build step for the main binary.
Build and test commands:
@@ -180,6 +180,8 @@ Do not bypass the permission system for new tools.
* Zig strings are `[]const u8`. There is no implicit null termination.
* Zig 0.17 removed the `**` operator. Fill arrays with `@splat` and build repeated comptime strings with `text_utils.repeat` from `src/core/shared/text_utils.zig`.
* For JSON serialization, use `std.json.Stringify.value` with an allocating writer (`std.Io.Writer.Allocating`).
* For JSON string escaping (writing raw JSON), use the project's `writeJsonStr` helper in `src/acp/jsonrpc.zig` rather than assuming `std.json.encodeJsonString` exists.
@@ -224,6 +226,8 @@ Do not bypass the permission system for new tools.
* Use `io_mod.dirRealpathAlloc(alloc, dir, sub_path)` to resolve paths within `std.testing.tmpDir()`.
* Back `std.testing.checkAllAllocationFailures`, hand-written `FailingAllocator` sweeps, and allocation-count comparisons with `testing_allocator.no_resize` from `src/core/shared/testing_allocator.zig`. `std.testing.allocator` may grow an allocation in place on one run and not the next, which changes the allocation sequence and makes those tests flaky.
## Testing (TypeScript)
Two test suites live under `tests/`, both using Bun:
+1 -1
View File
@@ -20,7 +20,7 @@ Contributions should preserve that direction:
Requirements:
* Zig `0.16.0+`
* Zig `0.17.0+`
* interactive terminal for manual shell testing
+1 -1
View File
@@ -245,7 +245,7 @@ live message test.
## Build from source
Building fx requires [Zig 0.16.0+](https://ziglang.org/download/):
Building fx requires [Zig 0.17.0+](https://ziglang.org/download/):
```bash
git clone https://github.com/vercel-labs/fx.git
+3 -2
View File
@@ -40,8 +40,9 @@ const Scenario = enum {
edge,
fn parse(value: []const u8) !Scenario {
inline for (std.meta.fields(Scenario)) |field| {
if (std.mem.eql(u8, value, field.name)) return @enumFromInt(field.value);
const scenario_info = @typeInfo(Scenario).@"enum";
inline for (scenario_info.field_names, scenario_info.field_values) |field_name, field_value| {
if (std.mem.eql(u8, value, field_name)) return @fromBackingInt(@intCast(field_value));
}
return error.InvalidScenario;
}
+1 -1
View File
@@ -348,7 +348,7 @@ function provenanceFixture(): [Provenance, Provenance] {
head_sha: sha, head_branch: branch, event, repository: { id: 1, full_name: "vercel-labs/fx" }, head_repository: { id: 1, full_name: "vercel-labs/fx" } },
source: { sha: source, parents: source === merge ? [{ sha: base }, { sha: head }] : [] },
manifest: { stage: "complete", status: "passed", eligible: true, evidence: {
identity: { source_sha: source, target: "aarch64-macos", host_arch: "arm64", zig_version: "0.16.0", llvm_version: "21.1.8" },
identity: { source_sha: source, target: "aarch64-macos", host_arch: "arm64", zig_version: "0.17.0", llvm_version: "22.1.8" },
profile: { sha256: "d".repeat(64) },
artifacts: { candidate: { sha256: "e".repeat(64), size: { size_bytes: 6_000_000 }, architecture: "arm64", signature_valid: true, version: "0.0.9", minimum_macos: "13.0" } },
} },
+43 -34
View File
@@ -61,7 +61,7 @@ pub fn build(b: *std.Build) void {
.omit_frame_pointer = true,
.unwind_tables = .none,
.error_tracing = false,
.strip = optimize != .Debug,
.strip = optimize != .debug,
}),
});
exe.root_module.addImport("build_options", build_options.createModule());
@@ -71,9 +71,7 @@ pub fn build(b: *std.Build) void {
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}
run_cmd.addPassthruArgs();
const run_step = b.step("run", "Run fx");
run_step.dependOn(&run_cmd.step);
@@ -83,12 +81,12 @@ pub fn build(b: *std.Build) void {
});
const run_exe_tests = b.addRunArtifact(exe_tests);
run_exe_tests.step.dependOn(b.getInstallStep());
run_exe_tests.setEnvironmentVariable(
"FX_TEST_PRODUCT_EXE",
b.getInstallPath(.bin, "fx"),
);
// The session boundary test walks the source tree from here.
run_exe_tests.setEnvironmentVariable("FX_TEST_SOURCE_ROOT", b.pathFromRoot("src"));
// Tests launch the installed fx binary, and the session boundary test
// walks the source tree.
const test_paths = b.addOptions();
test_paths.addOptionPathUntracked("product_exe", b.graph.path(.install_bin, "fx"));
test_paths.addOptionPathUntracked("source_root", b.path("src"));
exe.root_module.addImport("test_paths", test_paths.createModule());
// The session manager's own tests, as fx compiles it (no hooks).
const session_manager_tests = b.addTest(.{ .root_module = session_manager });
@@ -131,7 +129,7 @@ pub fn build(b: *std.Build) void {
mcp_test_exports,
);
const run_mcp_dispatcher_e2e = b.addRunArtifact(mcp_dispatcher_e2e);
if (b.args) |args| run_mcp_dispatcher_e2e.addArgs(args);
run_mcp_dispatcher_e2e.addPassthruArgs();
const mcp_dispatcher_e2e_step = b.step(
"run-mcp-stdio-dispatcher-e2e",
"Run the MCP stdio dispatcher E2E driver",
@@ -161,7 +159,7 @@ pub fn build(b: *std.Build) void {
const run_bench = b.addRunArtifact(file_index_bench);
run_bench.step.dependOn(&install_bench.step);
if (b.args) |args| run_bench.addArgs(args);
run_bench.addPassthruArgs();
const run_bench_step = b.step("run-bench-file-index", "Build and run file_index search benchmark");
run_bench_step.dependOn(&run_bench.step);
@@ -239,7 +237,7 @@ pub fn build(b: *std.Build) void {
const run_approval_review_bench = b.addRunArtifact(approval_review_bench);
run_approval_review_bench.step.dependOn(&install_approval_review_bench.step);
if (b.args) |args| run_approval_review_bench.addArgs(args);
run_approval_review_bench.addPassthruArgs();
const run_approval_review_bench_step = b.step(
"run-bench-approval-review",
"Build and run the file-diff approval review benchmark",
@@ -336,7 +334,7 @@ fn addWasmArtifact(
.root_module = b.createModule(.{
.root_source_file = b.path(wasm_root),
.target = wasm_target,
.optimize = .ReleaseSmall,
.optimize = .small,
.single_threaded = true,
.link_libc = true,
.stack_check = false,
@@ -349,7 +347,7 @@ fn addWasmArtifact(
});
if (surface == .core) wasm_exe.stack_size = 1024 * 1024;
wasm_exe.root_module.addImport("build_options", wasm_options.createModule());
_ = addSessionManager(b, wasm_exe.root_module, wasm_target, .ReleaseSmall, true);
_ = addSessionManager(b, wasm_exe.root_module, wasm_target, .small, true);
const install_wasm = b.addInstallArtifact(wasm_exe, .{});
const wasm_step = b.step(name ++ "-wasm", description);
@@ -381,19 +379,25 @@ fn addNapiArtifact(
.root_module = b.createModule(.{
.root_source_file = b.path(root),
.target = target,
.optimize = .ReleaseSafe,
.optimize = .safe,
.link_libc = true,
.strip = true,
}),
});
lib.root_module.addImport("build_options", napi_options.createModule());
_ = addSessionManager(b, lib.root_module, target, .ReleaseSafe, null);
_ = addSessionManager(b, lib.root_module, target, .safe, null);
const node_include = b.option(
[]const u8,
"node-include-dir",
"Directory containing node_api.h for the N-API addon",
) orelse discoverNodeIncludeDir(b);
lib.root_module.addSystemIncludePath(.{ .cwd_relative = node_include });
const node_api = b.addTranslateC(.{
.root_source_file = .{ .cwd_relative = b.pathJoin(&.{ node_include, "node_api.h" }) },
.target = target,
.optimize = .safe,
});
node_api.addSystemIncludePath(.{ .cwd_relative = node_include });
lib.root_module.addImport("node_api", node_api.createModule());
lib.linker_allow_shlib_undefined = true;
const install = b.addInstallArtifact(lib, .{ .dest_sub_path = "libfx.node" });
@@ -403,25 +407,28 @@ fn addNapiArtifact(
}
fn discoverNodeIncludeDir(b: *std.Build) []const u8 {
var code: u8 = 0;
const out = b.runAllowFail(
const out = switch (b.runFallible(
&.{ "node", "-p", "require('node:path').join(require('node:path').dirname(process.execPath), '..', 'include', 'node')" },
&code,
.ignore,
) catch std.process.fatal("Node.js is required to locate node_api.h; pass -Dnode-include-dir=<path>", .{});
if (code != 0) std.process.fatal("could not locate node_api.h; pass -Dnode-include-dir=<path>", .{});
.{ .stderr_behavior = .ignore },
)) {
.success => |stdout| stdout,
.spawn_failed, .crashed => std.process.fatal("Node.js is required to locate node_api.h; pass -Dnode-include-dir=<path>", .{}),
.bad_exit_code => std.process.fatal("could not locate node_api.h; pass -Dnode-include-dir=<path>", .{}),
};
const trimmed = std.mem.trim(u8, out, " \t\r\n");
return b.allocator.dupe(u8, trimmed) catch std.process.fatal("could not allocate Node include path", .{});
}
fn readGitCommit(b: *std.Build) []const u8 {
var code: u8 = 0;
const out = b.runAllowFail(
// The configure cache cannot see a new commit, so rerun configure.
b.graph.poisonCache();
const out = switch (b.runFallible(
&.{ "git", "rev-parse", "--short=12", "HEAD" },
&code,
.ignore,
) catch return "unknown";
if (code != 0) return "unknown";
.{ .stderr_behavior = .ignore },
)) {
.success => |stdout| stdout,
.spawn_failed, .bad_exit_code, .crashed => return "unknown",
};
const trimmed = std.mem.trim(u8, out, " \t\r\n");
return b.allocator.dupe(u8, trimmed) catch "unknown";
}
@@ -433,13 +440,13 @@ fn addSessionManager(
b: *std.Build,
importer: *std.Build.Module,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
optimize: std.lang.Optimize,
single_threaded: ?bool,
) *std.Build.Module {
const options = b.addOptions();
options.addOption(bool, "hooks", false);
options.addOption([]const u8, "trace_dir", b.getInstallPath(.prefix, "session-manager-traces"));
options.addOption([]const u8, "src_dir", b.pathFromRoot("src/core/session_manager"));
options.addOptionPathUntracked("trace_dir", b.graph.path(.install_prefix, "session-manager-traces"));
options.addOptionPathUntracked("src_dir", b.path("src/core/session_manager"));
const module = b.createModule(.{
.root_source_file = b.path("src/core/session_manager/api.zig"),
.target = target,
@@ -453,7 +460,9 @@ fn addSessionManager(
}
fn readAppVersion(b: *std.Build) []const u8 {
const bytes = std.Io.Dir.cwd().readFileAlloc(b.graph.io, "src/main.zig", b.allocator, .limited(1024 * 1024)) catch
b.dependOnFileContents(b.path("src/main.zig"));
const main_path = b.root.joinString(b.allocator, "src/main.zig") catch @panic("OOM");
const bytes = std.Io.Dir.cwd().readFileAlloc(b.graph.io, main_path, b.allocator, .limited(1024 * 1024)) catch
@panic("could not read src/main.zig to resolve app version");
defer b.allocator.free(bytes);
+1 -1
View File
@@ -25,7 +25,7 @@
.fingerprint = 0x2ca027d00bcd652c, // Changing this has security and trust implications.
// Tracks the earliest Zig version that the package considers to be a
// supported use case.
.minimum_zig_version = "0.16.0",
.minimum_zig_version = "0.17.0",
// This field is optional.
// Each dependency must either provide a `url` and `hash`, or a `path`.
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
+6 -6
View File
@@ -10,8 +10,8 @@ The driver requires:
- macOS on an arm64 host
- generic `aarch64-macos` output
- Zig `0.16.0`
- LLVM `21.1.8` tools and profile runtime from one configured LLVM root
- Zig `0.17.0`
- LLVM `22.1.8` tools and profile runtime from one configured LLVM root
- the selected Xcode macOS SDK and native Apple linker with arm64 support
- Bun `1.3.14`
- Hyperfine `1.20.0`
@@ -37,18 +37,18 @@ output fail closed.
Every mutating command requires a fresh or empty output directory. State from separate runs is never merged implicitly.
```bash
brew install llvm@21
brew install llvm@22
python3 -m scripts.pgso build \
--llvm-bin "$(brew --prefix llvm@21)/bin" \
--llvm-bin "$(brew --prefix llvm@22)/bin" \
--output-dir /tmp/fx-pgso-build
python3 -m scripts.pgso train \
--llvm-bin "$(brew --prefix llvm@21)/bin" \
--llvm-bin "$(brew --prefix llvm@22)/bin" \
--output-dir /tmp/fx-pgso-train
python3 -m scripts.pgso all \
--llvm-bin "$(brew --prefix llvm@21)/bin" \
--llvm-bin "$(brew --prefix llvm@22)/bin" \
--output-dir /tmp/fx-pgso-candidate \
--target aarch64-macos \
--update-channel stable \
+2 -2
View File
@@ -158,8 +158,8 @@ class PgsoCliTests(unittest.TestCase):
class ToolchainInfo:
host_arch: str = "arm64"
target: str = "aarch64-macos"
zig_version: str = "0.16.0"
llvm_version: str = "21.1.8"
zig_version: str = "0.17.0"
llvm_version: str = "22.1.8"
@dataclasses.dataclass(frozen=True)
class CorpusInfo:
+2 -2
View File
@@ -337,8 +337,8 @@ class ShardAggregationTests(unittest.TestCase):
source_sha="a" * 40,
target="aarch64-macos",
host_arch="arm64",
zig_version="0.16.0",
llvm_version="21.1.8",
zig_version="0.17.0",
llvm_version="22.1.8",
bitcode_sha256="b" * 64,
corpus_sha256="c" * 64,
update_channel="stable",
+3 -3
View File
@@ -23,8 +23,8 @@ class PgsoModelTests(unittest.TestCase):
source_sha="a" * 40,
target="aarch64-macos",
host_arch="arm64",
zig_version="0.16.0",
llvm_version="21.1.8",
zig_version="0.17.0",
llvm_version="22.1.8",
bitcode_sha256="b" * 64,
corpus_sha256="c" * 64,
update_channel="stable",
@@ -44,7 +44,7 @@ class PgsoModelTests(unittest.TestCase):
"source_sha": "d" * 40,
"target": "x86_64-macos",
"host_arch": "x86_64",
"zig_version": "0.16.1",
"zig_version": "0.17.1",
"llvm_version": "21.1.9",
"bitcode_sha256": "e" * 64,
"corpus_sha256": "f" * 64,
+2 -2
View File
@@ -224,8 +224,8 @@ class PgsoPipelineTests(unittest.TestCase):
"zig_darwin_sdk": self.root / "ZigDarwin.sdk",
"zig_sdk_version": "26.4",
"profile_runtime": self.root / "libclang_rt.profile_osx.a",
"zig_version": "0.16.0",
"llvm_version": "21.1.8",
"zig_version": "0.17.0",
"llvm_version": "22.1.8",
"target": "aarch64-macos",
"host_arch": "arm64",
}
+10 -10
View File
@@ -39,7 +39,7 @@ class PgsoToolchainTests(unittest.TestCase):
(self.zig_sdk / "SDKSettings.json").write_text('{"MinimalDisplayName":"26.4"}')
zig_env = f'.{{\n .lib_dir = {json.dumps(str(self.zig_lib))},\n}}\n'
self.write_executable(self.zig, f"""case "$1" in
version) printf '0.16.0\\n' ;;
version) printf '0.17.0\\n' ;;
env) printf '%s' {shlex.quote(zig_env)} ;;
*) exit 2 ;;
esac""")
@@ -74,13 +74,13 @@ esac""",
path.write_text(f"#!/bin/sh\n{body}\n")
path.chmod(0o755)
def write_llvm_tool(self, name: str, version: str = "21.1.8") -> None:
def write_llvm_tool(self, name: str, version: str = "22.1.8") -> None:
self.write_executable(
self.llvm_bin / name,
f"printf 'LLVM version {version}\\n'",
)
def write_clang(self, version: str = "21.1.8") -> None:
def write_clang(self, version: str = "22.1.8") -> None:
body = f"""case "$1" in
--print-resource-dir) printf '%s\\n' {shlex.quote(str(self.resource_dir))} ;;
--version) printf 'clang version {version}\\n' ;;
@@ -136,8 +136,8 @@ esac"""
self.assertEqual(self.zig_sdk.resolve(), toolchain.zig_darwin_sdk)
self.assertEqual("26.4", toolchain.zig_sdk_version)
self.assertEqual(self.profile_runtime.resolve(), toolchain.profile_runtime)
self.assertEqual("0.16.0", toolchain.zig_version)
self.assertEqual("21.1.8", toolchain.llvm_version)
self.assertEqual("0.17.0", toolchain.zig_version)
self.assertEqual("22.1.8", toolchain.llvm_version)
self.assertEqual("aarch64-macos", toolchain.target)
self.assertEqual("arm64", toolchain.host_arch)
@@ -166,15 +166,15 @@ esac"""
)
def test_discover_rejects_the_wrong_zig_version(self) -> None:
self.write_executable(self.zig, "printf '0.16.1\\n'")
self.write_executable(self.zig, "printf '0.17.1\\n'")
with self.assertRaisesRegex(PgsoError, "requires Zig 0.16.0"):
with self.assertRaisesRegex(PgsoError, "requires Zig 0.17.0"):
self.discover()
def test_discover_rejects_a_mixed_llvm_version(self) -> None:
self.write_llvm_tool("llc", version="21.1.9")
with self.assertRaisesRegex(PgsoError, "requires LLVM 21.1.8"):
with self.assertRaisesRegex(PgsoError, "requires LLVM 22.1.8"):
self.discover()
def test_discover_rejects_a_missing_executable(self) -> None:
@@ -185,7 +185,7 @@ esac"""
def test_discover_rejects_an_llvm_tool_outside_the_root(self) -> None:
outside = self.root / "outside-opt"
self.write_executable(outside, "printf 'LLVM version 21.1.8\\n'")
self.write_executable(outside, "printf 'LLVM version 22.1.8\\n'")
(self.llvm_bin / "opt").unlink()
(self.llvm_bin / "opt").symlink_to(outside)
@@ -223,7 +223,7 @@ esac"""
def test_discover_rejects_unusable_zig_library_metadata(self) -> None:
for env_text in (".{}", '.{\n.lib_dir = "relative",\n}', '.{\n.lib_dir = "a",\n.lib_dir = "b",\n}'):
self.write_executable(self.zig, f"""if [ "$1" = version ]; then printf '0.16.0\\n'; else printf '%s' {shlex.quote(env_text)}; fi""")
self.write_executable(self.zig, f"""if [ "$1" = version ]; then printf '0.17.0\\n'; else printf '%s' {shlex.quote(env_text)}; fi""")
with self.subTest(env_text=env_text), self.assertRaises(PgsoError):
self.discover()
+2 -2
View File
@@ -12,8 +12,8 @@ import subprocess
from scripts.pgso.model import PgsoError
REQUIRED_ZIG_VERSION = "0.16.0"
REQUIRED_LLVM_VERSION = "21.1.8"
REQUIRED_ZIG_VERSION = "0.17.0"
REQUIRED_LLVM_VERSION = "22.1.8"
SUPPORTED_TARGET = "aarch64-macos"
+2 -1
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../core/shared/testing_allocator.zig");
const io_mod = @import("../core/shared/io.zig");
const host_target = @import("../core/hosts/target.zig");
@@ -468,7 +469,7 @@ test "Reader propagates allocation failure without publishing a partial frame" {
};
var reader = Reader.initForTesting(&source, ChunkedTestSource.read);
var failing = std.testing.FailingAllocator.init(
std.testing.allocator,
testing_allocator.no_resize,
.{ .fail_index = 0 },
);
try std.testing.expectError(
+2 -1
View File
@@ -2,6 +2,7 @@ const std = @import("std");
const debug_trace = @import("../core/shared/debug_trace.zig");
const io_mod = @import("../core/shared/io.zig");
const jsonrpc = @import("jsonrpc.zig");
const text_utils = @import("../core/shared/text_utils.zig");
const Allocator = std.mem.Allocator;
const RequestId = jsonrpc.RequestId;
@@ -272,7 +273,7 @@ test "libfx steering runtime bounds each message and total queue" {
@memset(oversized, 'x');
try std.testing.expectError(error.SteeringMessageTooLarge, runtime.enqueue(alloc, oversized));
const message = "x" ** (max_queued_bytes / max_messages);
const message = text_utils.repeat("x", max_queued_bytes / max_messages);
for (0..max_messages) |_| try runtime.enqueue(alloc, message);
try std.testing.expectError(error.SteeringQueueFull, runtime.enqueue(alloc, "overflow"));
runtime.clear(alloc, "test");
+2 -1
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../core/shared/testing_allocator.zig");
const acp_types = @import("types.zig");
const builtin_tools = @import("../builtins/tools.zig");
const mcp_contract = @import("../core/mcp/mcp_contract.zig");
@@ -564,7 +565,7 @@ fn checkPreparationOwnershipAllocFailures(alloc: Allocator) !void {
test "ACP MCP preparation releases heap runtime ownership on allocation failure" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
checkPreparationOwnershipAllocFailures,
.{},
);
+14 -7
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../core/shared/testing_allocator.zig");
const skill_contract = @import("../core/skills/skill_contract.zig");
const std_builtin = @import("builtin");
const command_admission = @import("../core/permissions/command_admission.zig");
@@ -2573,9 +2574,15 @@ fn setRecoveryCheckpoint(
checkpoint: session_codec.RecoveryCheckpoint,
) !void {
const ctx: *AcpContext = @ptrCast(@alignCast(raw_ctx));
errdefer |err| if (err == error.SessionPersistenceUncertain) {
if (ctx.current_prompt_input) |input| input.retainImageSnapshots();
writeRecoveryCheckpoint(ctx, checkpoint) catch |err| {
if (err == error.SessionPersistenceUncertain) {
if (ctx.current_prompt_input) |input| input.retainImageSnapshots();
}
return err;
};
}
fn writeRecoveryCheckpoint(ctx: *AcpContext, checkpoint: session_codec.RecoveryCheckpoint) !void {
const session = if (ctx.state.active_session) |*value| value else return error.SessionPersistenceUnavailable;
session.session_write_mutex.lockUncancelable(io_mod.getIo());
defer session.session_write_mutex.unlock(io_mod.getIo());
@@ -2714,9 +2721,9 @@ fn pushHttpError(raw_ctx: *anyopaque, status: std.http.Status, detail: []const u
null;
defer if (owned_message) |message| ctx.alloc.free(message);
const msg = owned_message orelse if (detail.len > 0)
std.fmt.bufPrint(&buf, "HTTP {d}: {s}", .{ @intFromEnum(status), detail }) catch "HTTP error"
std.fmt.bufPrint(&buf, "HTTP {d}: {s}", .{ @backingInt(status), detail }) catch "HTTP error"
else
std.fmt.bufPrint(&buf, "HTTP {d}", .{@intFromEnum(status)}) catch "HTTP error";
std.fmt.bufPrint(&buf, "HTTP {d}", .{@backingInt(status)}) catch "HTTP error";
ctx.sendAgentText(ctx.operationalMessageId(), msg) catch {};
}
@@ -3866,8 +3873,8 @@ test "parsePromptInput bounds remote resource omissions with a stable summary" {
try std.testing.expect(std.mem.find(u8, parsed.omissions[parsed.omissions.len - 1].source, "/31") != null);
const summary = parsed.omission_summary orelse return error.TestExpectedEqual;
try std.testing.expectEqual(@as(usize, 96), summary.omitted_count);
try std.testing.expectEqual(@as(usize, 96), summary.reason_counts[@intFromEnum(context_contract.OmissionReason.unsafe_target)]);
const zero_digest = [_]u8{0} ** std.crypto.hash.sha2.Sha256.digest_length;
try std.testing.expectEqual(@as(usize, 96), summary.reason_counts[@backingInt(context_contract.OmissionReason.unsafe_target)]);
const zero_digest: [std.crypto.hash.sha2.Sha256.digest_length]u8 = @splat(0);
try std.testing.expect(!std.mem.eql(u8, &summary.digest, &zero_digest));
}
@@ -3896,7 +3903,7 @@ test "localFileTargetPath canonicalizes a local symlink target" {
}
test "parsePromptInput releases partial resource state across allocation failures" {
const backing = std.testing.allocator;
const backing = testing_allocator.no_resize;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
var file = try tmp.dir.createFile(std.testing.io, "local.txt", .{});
+4 -3
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../core/shared/testing_allocator.zig");
const managed_execution = @import("../core/execution/managed_execution.zig");
const acp_runner = @import("../core/cli/acp_runner.zig");
const config_runtime = @import("../core/config/config_runtime.zig");
@@ -3545,7 +3546,7 @@ test "ACP legacy URL publication owns partial allocations" {
try std.testing.expect(reserved);
}
};
try std.testing.checkAllAllocationFailures(std.testing.allocator, Case.run, .{});
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, Case.run, .{});
}
test "ACP legacy URL state requires consent and completion" {
@@ -3734,7 +3735,7 @@ fn acpModelTestState(
}
test "ACP ultrafast writes preserve v2 baselines across other preference changes" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const home = try io_mod.dirRealpathAlloc(alloc, tmp.dir, ".");
@@ -3943,7 +3944,7 @@ test "ACP rejects refreshed Codex tokens for another account" {
}
test "ACP usage flush preserves snapshot ownership on allocation failure" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var runtime: session_runtime.SessionRuntime = .{ .max_history_turns = 8 };
var runtime_owned = true;
defer if (runtime_owned) runtime.deinit(alloc);
+1 -1
View File
@@ -2248,7 +2248,7 @@ fn formatIso8601(alloc: Allocator, timestamp_ms: i64) ![]u8 {
return std.fmt.allocPrint(alloc, "{d:0>4}-{d:0>2}-{d:0>2}T{d:0>2}:{d:0>2}:{d:0>2}Z", .{
year_day.year,
@intFromEnum(month_day.month),
@backingInt(month_day.month),
month_day.day_index + 1,
day.getHoursIntoDay(),
day.getMinutesIntoHour(),
+1 -1
View File
@@ -162,7 +162,7 @@ test "describeToolCall flags internal discovery and names MCP identity" {
}
test "toolUpdateContentText clips long output and guards unsafe bytes" {
const long = "x" ** 500;
const long = text_utils.repeat("x", 500);
const clipped = toolUpdateContentText(false, long);
try std.testing.expectEqual(@as(usize, 200), clipped.len);
+9 -8
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../core/shared/testing_allocator.zig");
const debug_trace = @import("../core/shared/debug_trace.zig");
const host = @import("../core/hosts/host.zig");
const host_target = @import("../core/hosts/target.zig");
@@ -684,7 +685,7 @@ fn renderCandidateLessThan(_: void, lhs: *RuleCandidate, rhs: *RuleCandidate) bo
fn omissionLessThan(_: void, lhs: context_contract.ContextOmissionInput, rhs: context_contract.ContextOmissionInput) bool {
if (!std.mem.eql(u8, lhs.source, rhs.source)) return std.mem.lessThan(u8, lhs.source, rhs.source);
return @intFromEnum(lhs.reason) < @intFromEnum(rhs.reason);
return @backingInt(lhs.reason) < @backingInt(rhs.reason);
}
fn stringLessThan(_: void, lhs: []const u8, rhs: []const u8) bool {
@@ -774,7 +775,7 @@ fn appendOmissionSummaryNotice(scratch: *SelectionScratch) !void {
.{ scratch.omission_summary.omitted_count, digest },
);
const hidden_count = scratch.omission_summary.reason_counts[@intFromEnum(context_contract.OmissionReason.home_outside_workspace)];
const hidden_count = scratch.omission_summary.reason_counts[@backingInt(context_contract.OmissionReason.home_outside_workspace)];
const actionable_count = scratch.omission_summary.omitted_count - hidden_count;
if (actionable_count == 0) return;
@@ -791,12 +792,12 @@ fn appendOmissionSummaryNotice(scratch: *SelectionScratch) !void {
fn writeOmissionReasonCounts(
writer: *std.Io.Writer,
counts: [std.meta.fields(context_contract.OmissionReason).len]usize,
counts: [@typeInfo(context_contract.OmissionReason).@"enum".field_names.len]usize,
actionable_only: bool,
) !void {
var wrote_any = false;
for (counts, 0..) |count, index| {
const reason: context_contract.OmissionReason = @enumFromInt(index);
const reason: context_contract.OmissionReason = @fromBackingInt(@intCast(index));
if (count == 0 or (actionable_only and reason == .home_outside_workspace)) continue;
if (wrote_any) try writer.writeAll(", ");
try writer.print("{s}:{d}", .{ reason.label(), count });
@@ -1251,9 +1252,9 @@ test "combined project instruction cap keeps global and closest whole sections i
defer arena_state.deinit();
const arena = arena_state.allocator();
var scratch = SelectionScratch{ .arena = arena };
const full = "GUIDE\n\nGLOBAL_BODY\n" ++ ("g" ** 160) ++
"\n\nMIDDLE_BODY\n" ++ ("m" ** 400) ++
"\n\nCLOSE_BODY\n" ++ ("c" ** 160) ++
const full = "GUIDE\n\nGLOBAL_BODY\n" ++ text_utils.repeat("g", 160) ++
"\n\nMIDDLE_BODY\n" ++ text_utils.repeat("m", 400) ++
"\n\nCLOSE_BODY\n" ++ text_utils.repeat("c", 160) ++
"\n\n<project-rules-omitted from=\"/unsafe/AGENTS.md\" reason=\"unsafe target\" />";
const global_start = std.mem.find(u8, full, "\n\nGLOBAL_BODY").?;
const middle_start = std.mem.find(u8, full, "\n\nMIDDLE_BODY").?;
@@ -2038,7 +2039,7 @@ fn checkLaterSelectionAllocationFailures(alloc: Allocator, workspace: []const u8
}
test "later applicable selection cleans partial allocation failures" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
+3 -2
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../core/shared/testing_allocator.zig");
const builtin = @import("builtin");
pub const permission_reviewer = @import("gateway/permission_reviewer.zig");
@@ -1752,11 +1753,11 @@ test "gateway worker returns one bounded error for malformed provider result ide
}
test "gateway worker rejects unstorable provider identities without returning search results" {
try std.testing.checkAllAllocationFailures(std.testing.allocator, expectUnstorableProviderIdentity, .{});
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, expectUnstorableProviderIdentity, .{});
}
fn expectUnstorableProviderIdentity(alloc: Allocator) !void {
const oversized = [_]u8{'i'} ** 257;
const oversized: [257]u8 = @splat('i');
var cancel_flag = std.atomic.Value(bool).init(false);
var response = try normalizeGatewayCompletion(alloc, .{
.backend = perplexity_search_backend_id,
+4 -4
View File
@@ -220,7 +220,7 @@ fn sendGatewayReview(
debug_trace.logf(
"permission",
"event=auto_review_transport result={s} http_status={d}",
.{ @tagName(std.meta.activeTag(outcome)), @intFromEnum(stream.status) },
.{ @tagName(std.meta.activeTag(outcome)), @backingInt(stream.status) },
);
return outcome;
}
@@ -310,7 +310,7 @@ fn mapTransportError(
}
fn mapHttpStatus(status: std.http.Status) permission_auto_classifier.TransportOutcome {
const code: u16 = @intFromEnum(status);
const code: u16 = @backingInt(status);
if (code == 408 or code == 425 or code == 429 or code >= 500) {
return .transient_failure;
}
@@ -399,8 +399,8 @@ const FakeStream = struct {
},
.timeout => error.Timeout,
.cancelled => error.Cancelled,
.transient_http => .{ .status = @enumFromInt(429) },
.permanent_http => .{ .status = @enumFromInt(400) },
.transient_http => .{ .status = @fromBackingInt(@intCast(429)) },
.permanent_http => .{ .status = @fromBackingInt(@intCast(400)) },
};
}
};
@@ -383,7 +383,7 @@ fn sendReview(
};
if (cancel_flag.load(.seq_cst)) return .cancelled;
const status_code: u16 = @intFromEnum(result.status);
const status_code: u16 = @backingInt(result.status);
if (result.status != .ok) {
const outcome: permission_auto_classifier.TransportOutcome =
if (status_code == 408 or status_code == 425 or status_code == 429 or status_code >= 500)
+4 -3
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../core/shared/testing_allocator.zig");
const builtin_tools = @import("tools.zig");
const debug_trace = @import("../core/shared/debug_trace.zig");
@@ -1429,7 +1430,7 @@ test "MCP config diagnostic preserves the startup parser error" {
}
test "MCP config diagnostic propagates allocation failure" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
const test_home = try TestHome.install(alloc, "/tmp");
defer test_home.deinit();
@@ -1445,7 +1446,7 @@ test "MCP config diagnostic propagates allocation failure" {
test "MCP config diagnostic preserves parser allocation failure" {
var failing = std.testing.FailingAllocator.init(
std.testing.allocator,
testing_allocator.no_resize,
.{ .fail_index = 0 },
);
try std.testing.expectError(
@@ -1577,7 +1578,7 @@ test "built-in MCP runtime loading leaves enabled servers disconnected" {
}
test "built-in MCP runtime config transfer cleans its unvisited suffix on append failure" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var configs = try loadConfigFromJson(alloc,
\\{"mcp":{"first":{"type":"local","command":["node"],"enabled":false},"second":{"type":"local","command":["node"],"environment":{"TOKEN":"value"},"enabled":false}}}
);
+12 -10
View File
@@ -1,10 +1,12 @@
const std = @import("std");
const testing_allocator = @import("../core/shared/testing_allocator.zig");
const debug_trace = @import("../core/shared/debug_trace.zig");
const io_mod = @import("../core/shared/io.zig");
const skill_commands = @import("../core/skills/skill_commands.zig");
const skill_contract = @import("../core/skills/skill_contract.zig");
const skill_runtime = @import("../core/skills/skill_runtime.zig");
const text_utils = @import("../core/shared/text_utils.zig");
const Allocator = std.mem.Allocator;
@@ -886,7 +888,7 @@ test "install transaction payloads are not immediate skill candidates" {
defer alloc.free(source_dir);
const skills_dir = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "dest");
defer alloc.free(skills_dir);
var transaction = try InstallTransactionPaths.init(alloc, skills_dir, [_]u8{1} ** 16);
var transaction = try InstallTransactionPaths.init(alloc, skills_dir, @as([16]u8, @splat(1)));
defer transaction.deinit(alloc);
defer std.Io.Dir.cwd().deleteTree(io_mod.getIo(), transaction.root) catch {};
@@ -904,8 +906,8 @@ test "install transaction creation retries collisions without reusing roots" {
const skills_dir = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "dest");
defer alloc.free(skills_dir);
const collision_token = [_]u8{1} ** 16;
const fresh_token = [_]u8{2} ** 16;
const collision_token: [16]u8 = @splat(1);
const fresh_token: [16]u8 = @splat(2);
var collision = try InstallTransactionPaths.init(alloc, skills_dir, collision_token);
defer collision.deinit(alloc);
try std.Io.Dir.createDirAbsolute(io_mod.getIo(), collision.root, .default_dir);
@@ -937,7 +939,7 @@ test "install transaction collision exhaustion leaves the destination unchanged"
defer alloc.free(skills_dir);
const installed_path = try std.fs.path.join(alloc, &.{ skills_dir, "review", "SKILL.md" });
defer alloc.free(installed_path);
const collision_token = [_]u8{3} ** 16;
const collision_token: [16]u8 = @splat(3);
var collision = try InstallTransactionPaths.init(alloc, skills_dir, collision_token);
defer collision.deinit(alloc);
try std.Io.Dir.createDirAbsolute(io_mod.getIo(), collision.root, .default_dir);
@@ -945,7 +947,7 @@ test "install transaction collision exhaustion leaves the destination unchanged"
var token_state = TestTransactionTokenState{
.collision_token = collision_token,
.fresh_token = [_]u8{4} ** 16,
.fresh_token = @as([16]u8, @splat(4)),
.collision_count = install_transaction_attempts,
};
try std.testing.expectError(
@@ -1003,7 +1005,7 @@ test "skill install lock leaves do not expand valid destination names" {
const skills_dir = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "dest");
defer alloc.free(skills_dir);
const long_name = "n" ** 240;
const long_name = text_utils.repeat("n", 240);
try skill_contract.validateManagedSkillName(long_name);
var long_lock = try acquireSkillInstallLock(skills_dir, long_name, 0, .{});
long_lock.release();
@@ -1135,7 +1137,7 @@ test "skill install coordination stays outside discovery" {
}
test "copySkillDir preserves the installed skill across allocation failures" {
const backing = std.testing.allocator;
const backing = testing_allocator.no_resize;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
@@ -1230,7 +1232,7 @@ fn writeLargeTempSkill(tmp: *std.testing.TmpDir, sub_path: []const u8, name: []c
try file.writeStreamingAll(io_mod.getIo(), "---\nname: ");
try file.writeStreamingAll(io_mod.getIo(), name);
try file.writeStreamingAll(io_mod.getIo(), "\ndescription: valid large skill\n---\n\n");
const body_chunk = [_]u8{'x'} ** (16 * 1024);
const body_chunk: [16 * 1024]u8 = @splat('x');
for (0..257) |_| try file.writeStreamingAll(io_mod.getIo(), &body_chunk);
try file.writeStreamingAll(io_mod.getIo(), "\nLARGE_SKILL_TAIL\n");
}
@@ -1658,7 +1660,7 @@ test "installFromDirectory propagates root and nested metadata operational failu
}
test "installFromDirectory propagates nested metadata allocation failures" {
const backing = std.testing.allocator;
const backing = testing_allocator.no_resize;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
@@ -2047,7 +2049,7 @@ test "built-in skills command installs from a local pack" {
}
test "built-in skills install cleans empty result before notice allocation failure" {
const backing = std.testing.allocator;
const backing = testing_allocator.no_resize;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
+5 -4
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const Allocator = std.mem.Allocator;
const display_width = @import("../shared/display_width.zig");
@@ -2175,7 +2176,7 @@ fn checkSetextLookaheadAllocationFailures(alloc: Allocator) !void {
test "setext lookahead frees allocation failures" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
checkSetextLookaheadAllocationFailures,
.{},
);
@@ -2212,7 +2213,7 @@ fn checkDefinitionListAllocationFailures(alloc: Allocator) !void {
test "definition lists free allocation failures" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
checkDefinitionListAllocationFailures,
.{},
);
@@ -2379,7 +2380,7 @@ fn checkFootnoteAllocationFailures(alloc: Allocator) !void {
test "footnotes free allocation failures" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
checkFootnoteAllocationFailures,
.{},
);
@@ -3221,7 +3222,7 @@ test "reset clears an unfinished indented code block" {
}
test "code block payload clone frees language if code allocation fails" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var source = CodeBlockPayload{
.language = try alloc.dupe(u8, "zig"),
.code = try alloc.dupe(u8, "const value = 1;\n"),
+7 -6
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const debug_trace = @import("../shared/debug_trace.zig");
const io_mod = @import("../shared/io.zig");
const message = @import("../shared/message.zig");
@@ -1103,7 +1104,7 @@ test "durable execution memory preserves committed file presentation verbatim an
}
test "file evidence does not parse unknown tool arguments" {
var failing_allocator = std.testing.FailingAllocator.init(std.testing.allocator, .{});
var failing_allocator = std.testing.FailingAllocator.init(testing_allocator.no_resize, .{});
const alloc = failing_allocator.allocator();
var files: std.ArrayList(types.FileEvidence) = .empty;
defer files.deinit(alloc);
@@ -1114,7 +1115,7 @@ test "file evidence does not parse unknown tool arguments" {
.{
.id = "large_command",
.name = "run_command",
.arguments_json = "{\"command\":\"" ++ ("x\\n" ** 20_000) ++ "\"}",
.arguments_json = "{\"command\":\"" ++ text_utils.repeat("x\\n", 20_000) ++ "\"}",
},
.success,
null,
@@ -1609,12 +1610,12 @@ test "normal execution-memory builders clean every allocation failure" {
};
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
ChatCheck.run,
.{},
);
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
MessageCheck.run,
.{},
);
@@ -1632,11 +1633,11 @@ test "normal execution-memory builders skip non-tool messages without allocation
message.Message.assistantBorrowed("ignored", &.{}),
};
var chat_failing = std.testing.FailingAllocator.init(
std.testing.allocator,
testing_allocator.no_resize,
.{ .fail_index = 0 },
);
var message_failing = std.testing.FailingAllocator.init(
std.testing.allocator,
testing_allocator.no_resize,
.{ .fail_index = 0 },
);
@@ -353,7 +353,7 @@ fn matchDelimiters(alloc: Allocator, tokens: []Token, matches: *std.ArrayList(Ma
var next_seq: u32 = 1;
// Sequence number of the top entry when a search for this closer kind
// last failed; entries at or below it stay unusable for that kind.
var openers_bottom: [3][2][3]u32 = .{.{.{ 0, 0, 0 }} ** 2} ** 3;
var openers_bottom: [3][2][3]u32 = @splat(@splat(@splat(0)));
for (tokens, 0..) |*token, token_index| {
const closer = switch (token.*) {
@@ -474,7 +474,7 @@ fn emitTokens(
}
fn emitStyle(alloc: Allocator, out: *std.ArrayList(u8), style: Style, opening: bool, depth: *[3]usize, options: InlineOptions) !void {
const slot = @intFromEnum(style);
const slot = @backingInt(style);
const open_seq: []const u8 = switch (style) {
.bold => ansi.bold_open,
.italic => ansi.italic_open,
+2 -1
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const types = @import("../shared/types.zig");
const Allocator = std.mem.Allocator;
@@ -115,7 +116,7 @@ test "question answer codec exposes structural and allocation failures" {
);
var failing = std.testing.FailingAllocator.init(
std.testing.allocator,
testing_allocator.no_resize,
.{ .fail_index = 0 },
);
try std.testing.expectError(
+5 -4
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const debug_trace = @import("../shared/debug_trace.zig");
const edit_contract = @import("../input/editor_state.zig");
const io_mod = @import("../shared/io.zig");
@@ -815,7 +816,7 @@ fn checkQuestionPromptSyncAllocationFailures(alloc: Allocator) !void {
test "question prompt frees partial owned-entry allocations" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
checkQuestionPromptSyncAllocationFailures,
.{},
);
@@ -828,7 +829,7 @@ test "question prompt freeform allocation failure preserves prompt state" {
prompt.moveChoice(-1);
var failing = std.testing.FailingAllocator.init(
std.testing.allocator,
testing_allocator.no_resize,
.{ .fail_index = 0 },
);
try std.testing.expectError(
@@ -849,7 +850,7 @@ test "question prompt submission allocation failure preserves the draft" {
prompt.applyFreeformCursorMove(1, 7);
var failing = std.testing.FailingAllocator.init(
std.testing.allocator,
testing_allocator.no_resize,
.{ .fail_index = 0 },
);
try std.testing.expectError(
@@ -1681,7 +1682,7 @@ test "compact submission keeps input distinct from actions and choice prompts cl
}
test "compact submission allocation failure preserves the unanswered input" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var prompt: QuestionPrompt = .{};
defer prompt.deinit(alloc);
try prompt.syncFrom(alloc, &.{.{ .question = "Input", .options = &.{}, .submission = .input }});
+4 -2
View File
@@ -1,8 +1,10 @@
const std = @import("std");
const testing_allocator = @import("../../shared/testing_allocator.zig");
const types = @import("../../shared/types.zig");
const checkpoint_codec = @import("checkpoint.zig");
const runtime_prompt_context = @import("prompt_context.zig");
const debug_trace = @import("../../shared/debug_trace.zig");
const text_utils = @import("../../shared/text_utils.zig");
const Allocator = std.mem.Allocator;
@@ -182,7 +184,7 @@ test "Agent request token calibration survives startTurn and dies with cleared h
try std.testing.expect(agent.request_token_calibration != null);
// An unmatchable model id clears rather than stores.
agent.storeRequestTokenCalibration("x" ** (max_request_calibration_model_bytes + 1), cost);
agent.storeRequestTokenCalibration(text_utils.repeat("x", max_request_calibration_model_bytes + 1), cost);
try std.testing.expectEqual(@as(?RequestTokenCalibrationState, null), agent.request_token_calibration);
agent.storeRequestTokenCalibration("fixture/model", cost);
@@ -220,7 +222,7 @@ test "Agent accumulates per-turn usage with saturation" {
}
test "Agent history restore is transactional" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var agent: Agent = .{};
defer agent.deinit(alloc);
+24 -5
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../../shared/testing_allocator.zig");
const command_admission = @import("../../permissions/command_admission.zig");
const permission_auto_classifier = @import("../../permissions/auto_classifier.zig");
const types = @import("../../shared/types.zig");
@@ -1425,10 +1426,28 @@ test "streamed presentation preserves ANSI OSC 8 code fence and table spans" {
defer environ.deinit();
try environ.put(ansi_span_fixture_env, "1");
// The fixture reaches the shell tool, which imports the `test_paths`
// module that `zig build test` generates, so the nested compile needs the
// same module.
const test_paths = @import("test_paths");
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const test_paths_source = try std.fmt.allocPrint(
alloc,
"pub const product_exe: []const u8 = \"{f}\";\npub const source_root: []const u8 = \"{f}\";\n",
.{ std.zig.fmtString(test_paths.product_exe), std.zig.fmtString(test_paths.source_root) },
);
defer alloc.free(test_paths_source);
try tmp.dir.writeFile(std.testing.io, .{ .sub_path = "test_paths.zig", .data = test_paths_source });
const test_paths_path = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "test_paths.zig");
defer alloc.free(test_paths_path);
const test_paths_module = try std.fmt.allocPrint(alloc, "-Mtest_paths={s}", .{test_paths_path});
defer alloc.free(test_paths_module);
const cwd = try std.process.currentPathAlloc(std.testing.io, alloc);
defer alloc.free(cwd);
const result = try std.process.run(alloc, std.testing.io, .{
.argv = &.{ "zig", "test", "-lc", "-Mroot=src/main.zig", "--test-filter", ansi_span_test_name },
.argv = &.{ "zig", "test", "-lc", "--dep", "test_paths", "-Mroot=src/main.zig", test_paths_module, "--test-filter", ansi_span_test_name },
.cwd = .{ .path = cwd },
.environ_map = &environ,
.stdout_limit = .limited(64 * 1024),
@@ -1989,7 +2008,7 @@ test "stream callbacks publish absolute raw output token progress" {
}
test "streamed presentation suppresses callback-time failures" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
{
var capture = StreamCapture{};
@@ -2171,7 +2190,7 @@ fn checkSemanticCodeBlockCaptureAllocationFailures(alloc: Allocator) !void {
test "streamed presentation cleans up every allocation failure" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
checkStreamPresentationAllocationFailures,
.{},
);
@@ -2179,7 +2198,7 @@ test "streamed presentation cleans up every allocation failure" {
test "semantic table capture cleans up every allocation failure" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
checkSemanticTableCaptureAllocationFailures,
.{},
);
@@ -2187,7 +2206,7 @@ test "semantic table capture cleans up every allocation failure" {
test "semantic code block capture cleans up every allocation failure" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
checkSemanticCodeBlockCaptureAllocationFailures,
.{},
);
+5 -5
View File
@@ -863,7 +863,7 @@ test "request image notices keep clipped source references as JSON text" {
var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena_state.deinit();
const arena = arena_state.allocator();
const source_ref = "é" ** 256;
const source_ref = text_utils.repeat("é", 256);
var image = try testPngHeaderToolImage(arena, 8001, 1);
image.source_ref = try arena.dupe(u8, source_ref);
const images = [_]types.ToolImage{ image, image };
@@ -1481,7 +1481,7 @@ test "saved preparation stores complete output verbatim on cap loss" {
defer arena_state.deinit();
const arena = arena_state.allocator();
var cancel = std.atomic.Value(bool).init(false);
const raw = "MY_UNIT_TOKEN=abcdefgh" ** 47;
const raw = text_utils.repeat("MY_UNIT_TOKEN=abcdefgh", 47);
try std.testing.expect(raw.len > tool_result_limits.min_configured_tool_result_bytes);
const prepared = try prepareToolModelOutput(
@@ -1520,7 +1520,7 @@ test "no-save preparation preserves capped output verbatim without a result hand
defer arena_state.deinit();
const arena = arena_state.allocator();
var cancel = std.atomic.Value(bool).init(false);
const raw = "MY_UNIT_TOKEN=abcdefgh" ** 47;
const raw = text_utils.repeat("MY_UNIT_TOKEN=abcdefgh", 47);
const prepared = try prepareToolModelOutput(
arena,
@@ -1651,7 +1651,7 @@ test "retrieved output storage failure does not publish an unbacked result" {
.cancel_flag = &cancel,
.max_tool_result_bytes = 1024,
.session_child_capability = &readonly,
}, toolCall("retrieval-store-error", "read_tool_result", "{}"), "x" ** 1025));
}, toolCall("retrieval-store-error", "read_tool_result", "{}"), text_utils.repeat("x", 1025)));
}
test "saved tool output preparation keeps builtins and dynamic tools compactable" {
@@ -1667,7 +1667,7 @@ test "saved tool output preparation keeps builtins and dynamic tools compactable
var arena_state = std.heap.ArenaAllocator.init(alloc);
defer arena_state.deinit();
const arena = arena_state.allocator();
const raw = "COMPLETE_TOOL_BODY\n" ++ "x" ** 8192;
const raw = "COMPLETE_TOOL_BODY\n" ++ text_utils.repeat("x", 8192);
const prepared = try prepareToolModelOutput(arena, .{
.system_prompt = "",
.gateway_retry_count = 0,
+1 -1
View File
@@ -438,7 +438,7 @@ pub fn gatewayHttpErrorDetail(
model: []const u8,
capabilities: model_capabilities.Capabilities,
) ![]const u8 {
if (@intFromEnum(status) != 413) return detail;
if (@backingInt(status) != 413) return detail;
var out: std.Io.Writer.Allocating = .init(alloc);
defer out.deinit();
if (detail.len > 0) try out.writer.print("{s}\n\n", .{detail});
+3 -2
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../../shared/testing_allocator.zig");
const hooks = @import("../../hooks/hooks.zig");
const types = @import("../../shared/types.zig");
const tool_result_errors = @import("../../tooling/tool_result_errors.zig");
@@ -193,7 +194,7 @@ fn checkNonObjectPreparationAllocationFailures(alloc: Allocator) !void {
}
test "non-object preparation cleans every failed allocation" {
try std.testing.checkAllAllocationFailures(std.testing.allocator, checkNonObjectPreparationAllocationFailures, .{});
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, checkNonObjectPreparationAllocationFailures, .{});
}
fn expectMalformedFeedback(model_output: []const u8, failure: []const u8, raw: []const u8) !void {
@@ -250,7 +251,7 @@ fn checkMalformedPreparationAllocationFailures(alloc: Allocator) !void {
}
test "malformed preparation cleans every failed allocation" {
try std.testing.checkAllAllocationFailures(std.testing.allocator, checkMalformedPreparationAllocationFailures, .{});
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, checkMalformedPreparationAllocationFailures, .{});
}
pub noinline fn prepareToolCallForLifecycle(
+26 -14
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../../shared/testing_allocator.zig");
const skill_runtime = @import("../../skills/skill_runtime.zig");
const skill_contract = @import("../../skills/skill_contract.zig");
const skill_invocation = @import("../../skills/skill_invocation.zig");
@@ -593,7 +594,7 @@ test "legacy exec argument projection releases temporary allocations" {
try std.testing.expectEqualStrings(":", request.object.get("command").?.string);
}
};
try std.testing.checkAllAllocationFailures(std.testing.allocator, Probe.run, .{});
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, Probe.run, .{});
}
fn findLegacyCall(
@@ -1289,7 +1290,7 @@ fn check_subagent_history_projection_allocation_failures(alloc: Allocator) !void
test "subagent history projection cleans every partial allocation failure" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
check_subagent_history_projection_allocation_failures,
.{},
);
@@ -2078,7 +2079,7 @@ fn check_legacy_replay_projection_allocations(alloc: Allocator) !void {
}
test "legacy request projection preserves replay ownership and source binding on failure" {
try std.testing.checkAllAllocationFailures(std.testing.allocator, check_legacy_replay_projection_allocations, .{});
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, check_legacy_replay_projection_allocations, .{});
}
test "mixed legacy terminal batches become inert in every order" {
@@ -2261,7 +2262,7 @@ fn check_terminal_request_projection_allocation_failures(alloc: Allocator) !void
test "terminal request projection cleans every partial allocation failure" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
check_terminal_request_projection_allocation_failures,
.{},
);
@@ -2449,7 +2450,7 @@ fn check_terminal_request_normalization_allocation_failures(alloc: Allocator) !v
test "terminal request normalization cleans every partial allocation failure" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
check_terminal_request_normalization_allocation_failures,
.{},
);
@@ -4107,7 +4108,7 @@ test "recovery checkpoints do not accumulate temporary history copies in the tur
calls[0] = .{ .id = id, .name = "read_file", .arguments_json = "{\"path\":\"evidence.txt\"}" };
try messages.appendSlice(alloc, &.{
.{ .role = .assistant, .tool_calls = calls },
.{ .role = .tool, .tool_call_id = id, .tool_name = "read_file", .tool_result_status = .success, .content = "current evidence" ** 16 },
.{ .role = .tool, .tool_call_id = id, .tool_name = "read_file", .tool_result_status = .success, .content = text_utils.repeat("current evidence", 16) },
});
const retained_bytes = turn.queryCapacity();
try persistRecoveryCheckpoint(&deps, &finalization, fixture.job(), messages.items, "partial", "model", false, false, 10, 1, false, .transport_interrupted, .retry_request, .confirmed, .{});
@@ -4400,7 +4401,7 @@ test "discarded completion prose preserves ownership and fails atomically" {
}
};
try Probe.run(std.testing.allocator);
try std.testing.checkAllAllocationFailures(std.testing.allocator, Probe.run, .{});
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, Probe.run, .{});
}
fn isRetryableModelFailure(kind: agent_stream_provider.FailureKind) bool {
@@ -4757,7 +4758,7 @@ test "deferred retry cleanup clears status and contains sink errors" {
clear_deferred_retry_status(&deps);
try std.testing.expectEqual(@as(usize, 1), normal.route_recovery_clear_count);
var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{ .fail_index = 0 });
var failing = std.testing.FailingAllocator.init(testing_allocator.no_resize, .{ .fail_index = 0 });
var failed = support.FakeAgentRuntimeDeps.init(failing.allocator());
defer failed.deinit();
const failed_deps = failed.deps();
@@ -6219,7 +6220,7 @@ pub fn compactContext(
.before_summary = request.before_summary,
};
if (progress.operation_id) |id| deps.compaction_activity.?.running(deps.ctx, id, .preparation);
errdefer |err| {
return compactWithProgress(alloc, deps, request, &progress) catch |err| {
if (err == error.Cancelled) {
compactor.traceEvent(trace_ctx, .transaction_failed, "stage={s} origin={s} err={s}", .{ @tagName(progress.stage), @tagName(request.activity_origin), @errorName(err) });
} else {
@@ -6229,7 +6230,18 @@ pub fn compactContext(
deps.compaction_activity.?.settle(deps.ctx, id, compaction_activity.failure(err, progress.stage, cancel_flag.load(.seq_cst)));
if (request.failure_provenance) |out| out.* = .{ .operation_id = id, .turn_id = trace_ctx.turn_id, .err = err };
}
}
return err;
};
}
fn compactWithProgress(
alloc: Allocator,
deps: *const AgentRuntimeDeps,
request: ContextCompactionRequest,
progress: *CompactionProgress,
) !?compactor.Result {
const trace_ctx = request.compactor.trace_ctx;
const cancel_flag = request.compactor.cancel_flag;
var compactor_request = request.compactor;
compactor_request.progress = progress.interface();
var result = try compactor.compact(alloc, compactor_request) orelse {
@@ -6326,7 +6338,7 @@ test "compaction activity automatic error provenance excludes secondary finaliza
var job = fixture.job();
job.turn_id = 31;
job.model = @constCast(model);
var steps = [_]types.ToolExecutionStep{.{ .assistant = @constCast("history " ** 19_000) }};
var steps = [_]types.ToolExecutionStep{.{ .assistant = @constCast(text_utils.repeat("history ", 19_000)) }};
var history = [_]HistoryTurn{.{ .assistant = .{
.user = .{ .text = @constCast("earlier request") },
.assistant = @constCast("earlier answer"),
@@ -6444,7 +6456,7 @@ test "automatic compaction interruption persists transport cancellation and pres
var job = fixture.job();
job.turn_id = 37;
job.model = @constCast(model);
var steps = [_]types.ToolExecutionStep{.{ .assistant = @constCast("history " ** 19_000) }};
var steps = [_]types.ToolExecutionStep{.{ .assistant = @constCast(text_utils.repeat("history ", 19_000)) }};
var history = [_]HistoryTurn{.{ .assistant = .{
.user = .{ .text = @constCast("earlier request") },
.assistant = @constCast("earlier answer"),
@@ -6595,7 +6607,7 @@ test "compaction activity transaction settles only after publication and preserv
// A tool call leaves work to summarize, so the model is asked. Its
// output is larger than the kept budget, so the turn is compacted,
// while its user message still fits word for word.
const notes = "recorded notes " ** 400;
const notes = text_utils.repeat("recorded notes ", 400);
const calls = [_]types.ToolCall{.{ .id = "record-1", .name = "read_file", .arguments_json = "{\"path\":\"notes.md\"}" }};
const results = [_]types.PersistedToolResult{.{
.tool_call_id = @constCast("record-1"),
@@ -6607,7 +6619,7 @@ test "compaction activity transaction settles only after publication and preserv
}};
const steps = [_]types.ToolExecutionStep{.{ .tool_calls = @constCast(&calls), .tool_results = @constCast(&results) }};
const turns = [_]HistoryTurn{.{ .assistant = .{
.user = .{ .text = @constCast("recorded source " ** 100) },
.user = .{ .text = @constCast(text_utils.repeat("recorded source ", 100)) },
.assistant = @constCast("Recorded it."),
.execution = .{ .tool_steps = @constCast(&steps) },
} }};
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../../shared/testing_allocator.zig");
const skill_contract = @import("../../skills/skill_contract.zig");
const types = @import("../../shared/types.zig");
const tool_dispatch = @import("../../tooling/tool_dispatch.zig");
@@ -904,7 +905,7 @@ fn checkParallelRunAllocationFailures(alloc: Allocator) !void {
test "parallel run cleans up every allocation failure" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
checkParallelRunAllocationFailures,
.{},
);
@@ -975,7 +976,7 @@ fn checkParallelResultDuplicationAllocationFailures(alloc: Allocator) !void {
test "parallel result duplication cleans up every allocation failure" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
checkParallelResultDuplicationAllocationFailures,
.{},
);
+6 -4
View File
@@ -1,7 +1,9 @@
const std = @import("std");
const testing_allocator = @import("../../shared/testing_allocator.zig");
const token_estimate = @import("../../shared/token_estimate.zig");
const types = @import("../../shared/types.zig");
const stream_provider = @import("../stream_provider.zig");
const text_utils = @import("../../shared/text_utils.zig");
const Allocator = std.mem.Allocator;
const ChatMessage = types.ChatMessage;
@@ -392,7 +394,7 @@ test "provider request image accounting excludes encoded payload length" {
};
inline for (cases) |case| {
const small = try measureProviderRequest(std.testing.allocator, case.prefix ++ "AAAA" ++ case.suffix, measurement_test_request(true));
const large = try measureProviderRequest(std.testing.allocator, case.prefix ++ ("AAAA" ** 1000) ++ case.suffix, measurement_test_request(true));
const large = try measureProviderRequest(std.testing.allocator, case.prefix ++ text_utils.repeat("AAAA", 1000) ++ case.suffix, measurement_test_request(true));
try std.testing.expect(large.serialized_bytes > small.serialized_bytes);
try std.testing.expectEqual(small.text_tokens, large.text_tokens);
try std.testing.expectEqual(small.estimated_input_tokens, large.estimated_input_tokens);
@@ -511,7 +513,7 @@ test "provider request measurement degrades to text estimate on unknown envelope
}
test "provider request image calibration uses exact usage plus text growth without compounding" {
const first = RequestCost{ .serialized_bytes = 4_000_000, .text_tokens = 100, .image_identity = [_]u8{1} ** 32, .estimated_input_tokens = 100 };
const first = RequestCost{ .serialized_bytes = 4_000_000, .text_tokens = 100, .image_identity = @as([32]u8, @splat(1)), .estimated_input_tokens = 100 };
var next = first;
next.text_tokens = 150;
next.estimated_input_tokens = 150;
@@ -526,7 +528,7 @@ test "provider request image calibration uses exact usage plus text growth witho
try std.testing.expectEqual(@as(usize, 1100), repeated.estimated_input_tokens);
try std.testing.expectEqual(@as(usize, 1000), calibrateProviderRequest(first, .{ .request = calibrated, .exact_input_tokens = 1050 }).estimated_input_tokens);
next.image_identity = [_]u8{2} ** 32;
next.image_identity = @as([32]u8, @splat(2));
try std.testing.expectEqual(next, calibrateProviderRequest(next, .{ .request = first, .exact_input_tokens = 1000 }));
next.image_identity = null;
try std.testing.expectEqual(next, calibrateProviderRequest(next, .{ .request = first, .exact_input_tokens = 1000 }));
@@ -577,7 +579,7 @@ test "provider request image accounting handles allocation and malformed input f
_ = try measureProviderRequest(alloc, "{\"input\":[{\"role\":\"user\",\"content\":[{\"type\":\"input_image\",\"image_url\":\"data:image/png;base64,AAAA\"}]}]}", measurement_test_request(true));
}
};
try std.testing.checkAllAllocationFailures(std.testing.allocator, Probe.run, .{});
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, Probe.run, .{});
for ([_][]const u8{
"{",
"{\"input\":[],\"prompt\":[]}",
+2 -1
View File
@@ -1,5 +1,6 @@
const std = @import("std");
const language_script = @import("../../shared/language_script.zig");
const text_utils = @import("../../shared/text_utils.zig");
const minimum_letters: usize = 5;
const minimum_unexpected_prose_letters: usize = 8;
@@ -167,7 +168,7 @@ test "response language evidence distinguishes clear scripts" {
);
try std.testing.expectEqual(
Script.latin,
evidence("English transcript payload with sparse 界 markers. " ** 16).script.?,
evidence(text_utils.repeat("English transcript payload with sparse 界 markers. ", 16)).script.?,
);
}
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../../../shared/testing_allocator.zig");
const types = @import("../../../shared/types.zig");
const debug_trace = @import("../../../shared/debug_trace.zig");
const io_mod = @import("../../../shared/io.zig");
@@ -1996,7 +1997,7 @@ test "common Stop finish event failure wins and is not retried" {
}
test "common Stop terminal payload construction failure leaves guard open for one fallback" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
const calls = [_]ToolCall{
toolCall("call_read", "read_file", "{\"path\":\"README.md\"}"),
};
+39 -37
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../../../shared/testing_allocator.zig");
const agent_stream_provider = @import("../../stream_provider.zig");
const builtin_tools = @import("../../../../builtins/tools.zig");
const types = @import("../../../shared/types.zig");
@@ -28,6 +29,7 @@ const prompt_context = @import("../prompt_context.zig");
const runtime_orchestrator = @import("../orchestrator.zig");
const test_support = @import("support.zig");
const text_utils = @import("../../../shared/text_utils.zig");
const Allocator = std.mem.Allocator;
const HistoryTurn = types.HistoryTurn;
@@ -1004,7 +1006,7 @@ test "processQueuedPrompt delivers dense under-budget Vision evidence unchanged"
const persisted = hooks.history_turns.items[0].assistant.execution.tool_steps[0].tool_results[0];
try std.testing.expectEqual(types.PersistedToolStatus.success, persisted.status);
try std.testing.expectEqual(@as(usize, 40), std.mem.count(u8, persisted.output, "visible item "));
try std.testing.expect(std.mem.find(u8, persisted.output, "x" ** 5000) != null);
try std.testing.expect(std.mem.find(u8, persisted.output, text_utils.repeat("x", 5000)) != null);
try expectGatewayToolResultOutput(&gateway, 2, "call_vision_dense_evidence", persisted.output);
try expectBodyNotContains(&gateway, 2, image_path);
}
@@ -2051,10 +2053,10 @@ test "processQueuedPrompt recovers from Vision capacity failure with a narrower
try std.testing.expectEqual(@as(usize, 1), countNeedle(gateway.request_bodies.items[3], "\"type\":\"file\""));
try expectBodyContains(&gateway, 2, "output_limit_exceeded");
try expectBodyContains(&gateway, 2, "narrower focus or fewer images");
try expectBodyNotContains(&gateway, 2, "a" ** 1024);
try expectBodyNotContains(&gateway, 2, text_utils.repeat("a", 1024));
try expectBodyContains(&gateway, 4, "narrow validated evidence");
try expectBodyContains(&gateway, 4, "READY");
try expectBodyNotContains(&gateway, 4, "a" ** 1024);
try expectBodyNotContains(&gateway, 4, text_utils.repeat("a", 1024));
const execution = hooks.history_turns.items[0].assistant.execution;
try std.testing.expectEqual(@as(usize, 2), execution.tool_steps.len);
try std.testing.expectEqual(types.PersistedToolStatus.failure, execution.tool_steps[0].tool_results[0].status);
@@ -2304,7 +2306,7 @@ test "vision propagates cancellation raised on either provider attempt" {
}
test "vision surfaces allocation failure on either provider attempt" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const image_path = try writeTestImagePath(alloc, &tmp);
@@ -3169,7 +3171,7 @@ test "processQueuedPrompt semantically compacts history at eighty percent and co
.truncated = true,
}};
var restored_steps = [_]types.ToolExecutionStep{.{
.assistant = @constCast("AUTO_HISTORY_ASSISTANT_SENTINEL\n" ++ ("h" ** 150_000)),
.assistant = @constCast("AUTO_HISTORY_ASSISTANT_SENTINEL\n" ++ text_utils.repeat("h", 150_000)),
.tool_calls = &restored_calls,
.tool_results = &restored_results,
}};
@@ -3290,7 +3292,7 @@ test "processQueuedPrompt delivers steering queued during in-turn compaction wit
var history = [_]HistoryTurn{
.{ .assistant = .{
.user = .{ .text = @constCast("COMPACT_STEER_HISTORY_USER") },
.assistant = @constCast("COMPACT_STEER_HISTORY_ASSISTANT\n" ++ ("h" ** 150_000)),
.assistant = @constCast("COMPACT_STEER_HISTORY_ASSISTANT\n" ++ text_utils.repeat("h", 150_000)),
.execution = .{ .tool_steps = &restored_steps },
} },
.{ .assistant = .{
@@ -3344,14 +3346,14 @@ fn compactBehindSystemPrompt(comptime system_bytes: usize) !usize {
hooks.available_capability_overrides = &overrides;
var fixture = PromptFixture{};
var config = fixture.config();
config.system_prompt = "i" ** system_bytes;
config.system_prompt = text_utils.repeat("i", system_bytes);
var job = fixture.job();
job.model = @constCast(model);
var old_steps = [_]types.ToolExecutionStep{.{ .assistant = @constCast("OLD_BUDGET_FACT " ++ ("h" ** 380_000)) }};
var old_steps = [_]types.ToolExecutionStep{.{ .assistant = @constCast("OLD_BUDGET_FACT " ++ text_utils.repeat("h", 380_000)) }};
var history = [_]HistoryTurn{
.{ .assistant = .{ .user = .{ .text = @constCast("older") }, .assistant = @constCast("older done"), .execution = .{ .tool_steps = &old_steps } } },
.{ .assistant = .{ .user = .{ .text = @constCast("middle") }, .assistant = @constCast("MIDDLE_BUDGET_FACT " ++ ("m" ** 8_000)) } },
.{ .assistant = .{ .user = .{ .text = @constCast("recent") }, .assistant = @constCast("RECENT_BUDGET_FACT " ++ ("r" ** 8_000)) } },
.{ .assistant = .{ .user = .{ .text = @constCast("middle") }, .assistant = @constCast("MIDDLE_BUDGET_FACT " ++ text_utils.repeat("m", 8_000)) } },
.{ .assistant = .{ .user = .{ .text = @constCast("recent") }, .assistant = @constCast("RECENT_BUDGET_FACT " ++ text_utils.repeat("r", 8_000)) } },
};
job.history = &history;
try runFakePrompt(&gateway, &hooks, config, job);
@@ -3394,7 +3396,7 @@ test "compaction remeasures its rebuilt continuation after calibrated preflight"
var completions: [6]FakeCompletion = undefined;
for (ids, 0..) |id, index| {
calls[index] = .{toolCall(id, "read_file", "{\"path\":\"fixture.txt\"}")};
states[index] = try std.fmt.allocPrint(alloc, "[{{\"type\":\"reasoning\",\"text\":\"\",\"providerOptions\":{{\"openai\":{{\"reasoningEncryptedContent\":\"{s}\"}}}}}},{{\"type\":\"tool-call\",\"toolCallId\":\"{s}\"}}]", .{ "r" ** 200_000, id });
states[index] = try std.fmt.allocPrint(alloc, "[{{\"type\":\"reasoning\",\"text\":\"\",\"providerOptions\":{{\"openai\":{{\"reasoningEncryptedContent\":\"{s}\"}}}}}},{{\"type\":\"tool-call\",\"toolCallId\":\"{s}\"}}]", .{ text_utils.repeat("r", 200_000), id });
initialized += 1;
completions[index] = .{ .tool_calls = &calls[index], .provider_state_json = states[index], .usage = .{ .input_tokens = inputs[index] } };
}
@@ -3430,7 +3432,7 @@ test "compaction can summarize the newest exchange when fixed context prevents r
defer tmp.cleanup();
const result_dir = try io_mod.dirRealpathAlloc(alloc, tmp.dir, ".");
defer alloc.free(result_dir);
const calls = [_]ToolCall{toolCall("completed-large-write", "write_file", "{\"path\":\"large.txt\",\"content\":\"" ++ ("x" ** 68_000) ++ "\"}")};
const calls = [_]ToolCall{toolCall("completed-large-write", "write_file", "{\"path\":\"large.txt\",\"content\":\"" ++ text_utils.repeat("x", 68_000) ++ "\"}")};
var gateway = FakeGateway.init(alloc, &.{
.{ .tool_calls = &calls },
.{ .content = "The large write completed. Do not repeat it." },
@@ -3446,7 +3448,7 @@ test "compaction can summarize the newest exchange when fixed context prevents r
hooks.exec_plans = &.{.{ .result = .{ .model_output = "Write completed." } }};
var fixture = PromptFixture{};
var config = fixture.config();
config.system_prompt = "i" ** 16_000;
config.system_prompt = text_utils.repeat("i", 16_000);
config.tool_result_dir = result_dir;
var job = fixture.job();
job.model = @constCast(model);
@@ -3456,7 +3458,7 @@ test "compaction can summarize the newest exchange when fixed context prevents r
try std.testing.expectEqual(@as(usize, 3), gateway.request_bodies.items.len);
try expectBodyContains(&gateway, 1, "[Tool call T1: write_file]");
try expectBodyContains(&gateway, 2, "compacted_conversation");
try expectBodyNotContains(&gateway, 2, "x" ** 68_000);
try expectBodyNotContains(&gateway, 2, text_utils.repeat("x", 68_000));
try std.testing.expectEqual(@as(usize, 1), hooks.successful_effect_count.load(.seq_cst));
}
}
@@ -3743,7 +3745,7 @@ test "cancelled automatic compaction is retried by the next prompt" {
var fixture = PromptFixture{};
var config = fixture.config();
config.tool_result_dir = result_dir;
var old_steps = [_]types.ToolExecutionStep{.{ .assistant = @constCast("CANCELLED_AUTO_HISTORY_ASSISTANT\n" ++ ("h" ** 150_000)) }};
var old_steps = [_]types.ToolExecutionStep{.{ .assistant = @constCast("CANCELLED_AUTO_HISTORY_ASSISTANT\n" ++ text_utils.repeat("h", 150_000)) }};
var history = [_]HistoryTurn{
.{ .assistant = .{
.user = .{ .text = @constCast("CANCELLED_AUTO_HISTORY_USER") },
@@ -3814,9 +3816,9 @@ test "retained context automatic compaction archives oversized parallel results
defer tmp.cleanup();
const result_dir = try io_mod.dirRealpathAlloc(alloc, tmp.dir, ".");
defer alloc.free(result_dir);
const old_text = "OLDER_HISTORY_SENTINEL " ++ ("o" ** 52_000);
const result_a = "RECENT_EXACT_A\n" ++ ("a" ** 13_000);
const result_b = "RECENT_EXACT_B\n" ++ ("b" ** 13_000);
const old_text = "OLDER_HISTORY_SENTINEL " ++ text_utils.repeat("o", 52_000);
const result_a = "RECENT_EXACT_A\n" ++ text_utils.repeat("a", 13_000);
const result_b = "RECENT_EXACT_B\n" ++ text_utils.repeat("b", 13_000);
var old_steps = [_]types.ToolExecutionStep{.{ .assistant = @constCast(old_text) }};
const history = [_]HistoryTurn{.{ .assistant = .{
.user = .{ .text = @constCast("previous work") },
@@ -3863,8 +3865,8 @@ test "retained context automatic compaction archives oversized parallel results
try expectBodyContains(&gateway, 1, "RECENT_EXACT_B");
try expectBodyContains(&gateway, 2, "compacted_conversation");
try expectBodyContains(&gateway, 2, "Search them by text, or open one by its ID");
try expectBodyNotContains(&gateway, 2, "a" ** 13_000);
try expectBodyNotContains(&gateway, 2, "b" ** 13_000);
try expectBodyNotContains(&gateway, 2, text_utils.repeat("a", 13_000));
try expectBodyNotContains(&gateway, 2, text_utils.repeat("b", 13_000));
try expectBodyNotContains(&gateway, 2, "OLDER_HISTORY_SENTINEL");
try std.testing.expectEqual(@as(usize, 2), hooks.successful_effect_count.load(.seq_cst));
const completed = hooks.history_turns.items[hooks.history_turns.items.len - 1].assistant;
@@ -3891,8 +3893,8 @@ test "retained context compaction preserves recovered historical replay" {
defer tmp.cleanup();
const result_dir = try io_mod.dirRealpathAlloc(alloc, tmp.dir, ".");
defer alloc.free(result_dir);
const old_text = "OLDER_HISTORY_SENTINEL " ++ ("o" ** 52_000);
const recent_result = "RECENT_RECOVERED_RESULT\n" ++ ("r" ** 1_000);
const old_text = "OLDER_HISTORY_SENTINEL " ++ text_utils.repeat("o", 52_000);
const recent_result = "RECENT_RECOVERED_RESULT\n" ++ text_utils.repeat("r", 1_000);
const model = "provider/retained-recovery";
const state = "[{\"type\":\"reasoning\",\"text\":\"retained_reasoning\"},{\"type\":\"text\",\"offset\":0,\"length\":42,\"providerOptions\":{\"fixture\":{\"id\":\"discarded_text\"}}}]";
var calls = [_]ToolCall{toolCall("old_read", "read_file", "{\"path\":\"a.txt\"}")};
@@ -3946,7 +3948,7 @@ test "retained context compaction preserves recovered historical replay" {
test "compaction summarizes an oversized only recent exchange without repeating it" {
const alloc = std.testing.allocator;
const original_result = "RECENT_ONLY\n" ++ ("r" ** 10_000);
const original_result = "RECENT_ONLY\n" ++ text_utils.repeat("r", 10_000);
var gateway = FakeGateway.init(alloc, &.{
.{ .tool_calls = &.{toolCall("recent-only", "read_file", "{\"path\":\"large.txt\"}")} },
.{ .content = "Turn in progress\nIn between: The read completed with RECENT_ONLY." },
@@ -3968,7 +3970,7 @@ test "compaction summarizes an oversized only recent exchange without repeating
try expectBodyContains(&gateway, 1, "\"toolChoice\":{\"type\":\"none\"}");
try expectBodyContains(&gateway, 1, "user prompt");
try expectBodyContains(&gateway, 2, "RECENT_ONLY");
try expectBodyNotContains(&gateway, 2, "r" ** 10_000);
try expectBodyNotContains(&gateway, 2, text_utils.repeat("r", 10_000));
try expectBodyContains(&gateway, 2, "compacted_conversation");
// The current request stays as the live user message and is not copied
// into the compacted user messages as well.
@@ -3990,7 +3992,7 @@ test "automatic compaction summarizes a newest exchange larger than the input wi
defer tmp.cleanup();
const result_dir = try io_mod.dirRealpathAlloc(alloc, tmp.dir, ".");
defer alloc.free(result_dir);
const arguments = "{\"path\":\"large.txt\",\"content\":\"" ++ ("x" ** 40_000) ++ "\"}";
const arguments = "{\"path\":\"large.txt\",\"content\":\"" ++ text_utils.repeat("x", 40_000) ++ "\"}";
var gateway = FakeGateway.init(alloc, &.{
.{ .tool_calls = &.{toolCall("oversized-newest", "write_file", arguments)} },
.{ .content = "The large file was written successfully. Do not repeat the write." },
@@ -4014,7 +4016,7 @@ test "automatic compaction summarizes a newest exchange larger than the input wi
try expectBodyContains(&gateway, 1, "[Tool call T1: write_file]");
try expectBodyContains(&gateway, 1, "[Tool result T1: write_file]");
try expectBodyContains(&gateway, 2, "compacted_conversation");
try expectBodyNotContains(&gateway, 2, "x" ** 40_000);
try expectBodyNotContains(&gateway, 2, text_utils.repeat("x", 40_000));
try std.testing.expectEqual(@as(usize, 1), hooks.successful_effect_count.load(.seq_cst));
try std.testing.expectEqualStrings("Continued after the large write.", hooks.finish_assistant_text.?);
}
@@ -4044,7 +4046,7 @@ test "interruption after automatic compaction retains the recent and new executi
hooks.available_capability_overrides = &overrides;
hooks.permission_decisions = &.{ .once, .once };
hooks.exec_plans = &.{
.{ .result = .{ .model_output = "x" ** (10 * 1024) } },
.{ .result = .{ .model_output = text_utils.repeat("x", 10 * 1024) } },
.{ .result = .{ .model_output = "later result" } },
};
var fixture = PromptFixture{};
@@ -4055,7 +4057,7 @@ test "interruption after automatic compaction retains the recent and new executi
var history = [_]HistoryTurn{.{ .assistant = .{
.user = .{ .text = @constCast("earlier work") },
.assistant = @constCast("o" ** 40_000),
.assistant = @constCast(text_utils.repeat("o", 40_000)),
} }};
job.history = &history;
@@ -4066,7 +4068,7 @@ test "interruption after automatic compaction retains the recent and new executi
const interrupted = hooks.history_turns.items[1].interrupted;
try std.testing.expectEqual(@as(usize, 1), interrupted.execution.tool_steps.len);
try std.testing.expectEqualStrings("after-checkpoint", interrupted.execution.tool_steps[0].tool_calls[0].id);
try std.testing.expectEqualStrings("x" ** (10 * 1024), hooks.compaction_prefixes.items[0].?.assistant.execution.tool_steps[0].tool_results[0].output);
try std.testing.expectEqualStrings(text_utils.repeat("x", 10 * 1024), hooks.compaction_prefixes.items[0].?.assistant.execution.tool_steps[0].tool_results[0].output);
try std.testing.expectEqual(@as(usize, 2), interrupted.execution.files.len);
try std.testing.expectEqual(@as(usize, 2), hooks.successful_effect_count.load(.seq_cst));
try std.testing.expectEqualStrings("before-checkpoint", hooks.compaction_prefixes.items[0].?.assistant.execution.tool_steps[0].tool_calls[0].id);
@@ -4099,7 +4101,7 @@ test "context overflow after automatic compaction retries with new execution" {
hooks.available_capability_overrides = &overrides;
hooks.permission_decisions = &.{ .once, .once };
hooks.exec_plans = &.{
.{ .result = .{ .model_output = "x" ** (10 * 1024) } },
.{ .result = .{ .model_output = text_utils.repeat("x", 10 * 1024) } },
.{ .result = .{ .model_output = "later result" } },
};
var fixture = PromptFixture{};
@@ -4110,7 +4112,7 @@ test "context overflow after automatic compaction retries with new execution" {
var history = [_]HistoryTurn{.{ .assistant = .{
.user = .{ .text = @constCast("earlier work") },
.assistant = @constCast("o" ** 40_000),
.assistant = @constCast(text_utils.repeat("o", 40_000)),
} }};
job.history = &history;
@@ -4151,7 +4153,7 @@ test "compaction summarizes a truncated result that has no saved copy" {
defer hooks.deinit();
hooks.permission_decisions = &.{.once};
hooks.exec_plans = &.{.{ .result = .{
.model_output = "ineligible result\n" ++ ("x" ** (70 * 1024)),
.model_output = "ineligible result\n" ++ text_utils.repeat("x", 70 * 1024),
} }};
var fixture = PromptFixture{};
var job = fixture.job();
@@ -4163,7 +4165,7 @@ test "compaction summarizes a truncated result that has no saved copy" {
try expectBodyContains(&gateway, 1, "ineligible result");
try expectBodyContains(&gateway, 1, "The tool calls will not be available later");
try expectBodyContains(&gateway, 2, "compacted_conversation");
try expectBodyNotContains(&gateway, 2, "x" ** 1024);
try expectBodyNotContains(&gateway, 2, text_utils.repeat("x", 1024));
try expectBodyNotContains(&gateway, 2, "read_tool_result");
try std.testing.expectEqualStrings("Done after compaction.", hooks.finish_assistant_text.?);
try std.testing.expectEqual(@as(usize, 1), hooks.successful_effect_count.load(.seq_cst));
@@ -4899,7 +4901,7 @@ test "processQueuedPrompt retains complete skill content without granting ordina
hooks.tool_registry = builtin_tools.registry;
const result: runtime_tool_contracts.ToolExecutionResult = .{
.model_content_kind = case.kind,
.model_output = "SKILL START\n" ++ ("required instruction\n" ** 1400) ++ "COMPLETE_SKILL_TAIL",
.model_output = "SKILL START\n" ++ text_utils.repeat("required instruction\n", 1400) ++ "COMPLETE_SKILL_TAIL",
};
hooks.exec_plans = &.{ .{ .result = result }, .{ .result = result } };
var fixture = PromptFixture{};
@@ -5090,7 +5092,7 @@ test "processQueuedPrompt skill catalog is invariant under harmless request expa
.{ .name = "alpha", .description = "Unrelated instructions", .path = "/tmp/skills/alpha", .source = .global_fx },
.{ .name = "release", .description = "Release the package", .path = "/tmp/skills/release", .source = .global_fx },
};
const prompts = [_][]const u8{ "Release the package", "Release the package." ++ (" Keep existing behavior unchanged." ** 24) };
const prompts = [_][]const u8{ "Release the package", "Release the package." ++ text_utils.repeat(" Keep existing behavior unchanged.", 24) };
var first: ?[]u8 = null;
defer if (first) |value| alloc.free(value);
for (prompts) |prompt| {
@@ -5122,7 +5124,7 @@ test "processQueuedPrompt skill catalog is invariant under harmless request expa
test "processQueuedPrompt prepares each origin skill catalog with its supplied model and workspace" {
const alloc = std.testing.allocator;
const Skill = @import("../../../skills/skill_runtime.zig").Skill;
const description = "Instruction detail. " ** 50;
const description = text_utils.repeat("Instruction detail. ", 50);
const names = [_][]const u8{ "alpha", "beta", "gamma", "delta" };
const cases = [_]struct { model: []const u8, workspace: []const u8, context_window: u32, child: bool }{
.{ .model = "fixture/parent-model", .workspace = "/tmp/parent-workspace", .context_window = 64_000, .child = false },
@@ -5210,7 +5212,7 @@ test "processQueuedPrompt keeps supplied system prompt components in stable orde
}};
config.skill_catalog = .{ .skills = &skills };
config.context_limits.skill_catalog_bytes = .{ .value = .{ .bytes = 1024 }, .source = .command_line };
config.host_instructions = "host guidance-order instructions\n" ++ ("Required host instruction.\n" ** 80);
config.host_instructions = "host guidance-order instructions\n" ++ text_utils.repeat("Required host instruction.\n", 80);
config.model_prompt_overlay = "model guidance-order overlay";
var job = fixture.job();
job.history = history[0..];
+8 -6
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../../../shared/testing_allocator.zig");
const builtin = @import("builtin");
const builtin_context = @import("../../../../builtins/context.zig");
const builtin_tools = @import("../../../../builtins/tools.zig");
@@ -35,6 +36,7 @@ const permission_auto_classifier = @import("../../../permissions/auto_classifier
const auto_classifier_context = @import("../../../permissions/auto_classifier_context.zig");
const test_support = @import("support.zig");
const text_utils = @import("../../../shared/text_utils.zig");
const ChatMessage = types.ChatMessage;
const PermissionGrant = types.PermissionGrant;
@@ -2062,7 +2064,7 @@ fn expectRejectedPrompt(completion: FakeCompletion, expected_error: anyerror) !v
}
test "processQueuedPrompt rejects unstorable tool batches before permissions or execution" {
const oversized = [_]u8{'i'} ** 257;
const oversized: [257]u8 = @splat('i');
const invalid = [_]ToolCall{
.{ .id = "bad", .name = "", .arguments_json = "{}" },
.{ .id = &oversized, .name = "read_file", .arguments_json = "{}" },
@@ -2264,7 +2266,7 @@ test "vision uses generic lifecycle execution memory persistence and reprojectio
}
test "vision OOM propagates through assembled orchestrator without a tool result" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
const calls = [_]ToolCall{toolCall(
"call_vision_oom",
"vision",
@@ -5314,7 +5316,7 @@ test "processQueuedPrompt caps chatty grep_files model output" {
defer gateway.deinit();
var hooks = FakeAgentRuntimeDeps.init(alloc);
hooks.exec_plans = &.{.{ .result = .{
.model_output = "match\n" ++ ("x" ** 2048),
.model_output = "match\n" ++ text_utils.repeat("x", 2048),
} }};
defer hooks.deinit();
var fixture = PromptFixture{};
@@ -5338,7 +5340,7 @@ test "processQueuedPrompt caps chatty terminal exec result with explicit marker"
defer gateway.deinit();
var hooks = FakeAgentRuntimeDeps.init(alloc);
hooks.exec_plans = &.{.{ .result = .{
.model_output = "{\"truncated\":true,\"stdout\":\"" ++ ("x" ** 2048) ++ "\",\"stderr\":\"\",\"stdout_bytes\":2048}",
.model_output = "{\"truncated\":true,\"stdout\":\"" ++ text_utils.repeat("x", 2048) ++ "\",\"stderr\":\"\",\"stdout_bytes\":2048}",
} }};
defer hooks.deinit();
var fixture = PromptFixture{};
@@ -5544,7 +5546,7 @@ test "PreToolUse rewrite is authoritative for validation permission execution hi
}
test "PreToolUse allocation failure escapes before history validation permission or execution" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
const calls = [_]ToolCall{
toolCall("call_read", "read_file", "{\"path\":\"src/main.zig\"}"),
};
@@ -5918,7 +5920,7 @@ test "owner cancellation wins over PreToolUse outcome allocation failure" {
}
};
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var cancel_flag = std.atomic.Value(bool).init(false);
var handler = CancelRewriteHandler{ .cancel_flag = &cancel_flag };
var lifecycle_runtime = lifecycle_hooks.Runtime.init(alloc);
+2 -1
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../../shared/testing_allocator.zig");
const command_admission = @import("../../permissions/command_admission.zig");
const managed_execution = @import("../../execution/managed_execution.zig");
const permission_auto_classifier = @import("../../permissions/auto_classifier.zig");
@@ -2352,7 +2353,7 @@ fn checkProvisionalLifecycleAllocationFailures(alloc: Allocator) !void {
test "provisional lifecycle cleans up every copied-id allocation failure" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
checkProvisionalLifecycleAllocationFailures,
.{},
);
+17 -15
View File
@@ -1,9 +1,11 @@
const std = @import("std");
const testing_allocator = @import("../../shared/testing_allocator.zig");
const stream_provider = @import("../stream_provider.zig");
const model_provider = @import("../../config/model_provider.zig");
const image_attachments = @import("../../images/image_attachments.zig");
const types = @import("../../shared/types.zig");
const tool_result_errors = @import("../../tooling/tool_result_errors.zig");
const text_utils = @import("../../shared/text_utils.zig");
const Allocator = std.mem.Allocator;
@@ -1080,7 +1082,7 @@ test "Vision request parsing enforces nonempty ids and focus bounds" {
error.FocusTooLong,
parse_vision_request(
std.testing.allocator,
"{\"image_ids\":[1],\"focus\":\"" ++ ("x" ** (max_focus_bytes + 1)) ++ "\"}",
"{\"image_ids\":[1],\"focus\":\"" ++ text_utils.repeat("x", max_focus_bytes + 1) ++ "\"}",
),
);
}
@@ -1160,7 +1162,7 @@ test "Vision provider parsing accepts forty evidence items within the total byte
test "Vision provider parsing accepts one long evidence string within the total byte limit" {
const provider_json =
"{\"images\":[{\"image_id\":3,\"status\":\"ok\",\"summary\":\"long text\",\"visible_text\":[\"" ++
("x" ** 5000) ++
text_utils.repeat("x", 5000) ++
"\"],\"details\":[]}]}";
const parsed = try parse_vision_provider_result(
std.testing.allocator,
@@ -1319,17 +1321,17 @@ test "Vision provider parsing has no hidden twenty kibibyte ceiling" {
const evidence_chunk_bytes = 4096;
const large_provider_json =
"{\"images\":[{\"image_id\":3,\"status\":\"ok\",\"summary\":\"ok\",\"visible_text\":[\"" ++
("x" ** evidence_chunk_bytes) ++
text_utils.repeat("x", evidence_chunk_bytes) ++
"\",\"" ++
("x" ** evidence_chunk_bytes) ++
text_utils.repeat("x", evidence_chunk_bytes) ++
"\",\"" ++
("x" ** evidence_chunk_bytes) ++
text_utils.repeat("x", evidence_chunk_bytes) ++
"\",\"" ++
("x" ** evidence_chunk_bytes) ++
text_utils.repeat("x", evidence_chunk_bytes) ++
"\",\"" ++
("x" ** evidence_chunk_bytes) ++
text_utils.repeat("x", evidence_chunk_bytes) ++
"\",\"" ++
("x" ** evidence_chunk_bytes) ++
text_utils.repeat("x", evidence_chunk_bytes) ++
"\"],\"details\":[]}]}";
try std.testing.expect(large_provider_json.len > 20 * 1024);
@@ -1755,7 +1757,7 @@ fn check_native_replay_projection_allocations(alloc: Allocator) !void {
test "native message projection preserves selected replay and releases failed allocations" {
try check_native_replay_projection_allocations(std.testing.allocator);
try std.testing.checkAllAllocationFailures(std.testing.allocator, check_native_replay_projection_allocations, .{});
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, check_native_replay_projection_allocations, .{});
}
test "native message projection leaves mismatched replay to the provider adapter" {
@@ -1849,12 +1851,12 @@ test "Vision owned parsing and merge clean up every allocation failure" {
"{\"image_ids\":[1,2],\"focus\":\"compare both images\"}",
"{\"paths\":[\"first.png\",\"second.png\"],\"focus\":\"compare both images\"}",
}) |request_json| {
var request_probe = std.testing.FailingAllocator.init(std.testing.allocator, .{});
var request_probe = std.testing.FailingAllocator.init(testing_allocator.no_resize, .{});
const probed_request = try parse_vision_request(request_probe.allocator(), request_json);
probed_request.deinit(request_probe.allocator());
for (0..request_probe.alloc_index) |fail_index| {
var failing = std.testing.FailingAllocator.init(
std.testing.allocator,
testing_allocator.no_resize,
.{ .fail_index = fail_index },
);
try std.testing.expectError(
@@ -1866,7 +1868,7 @@ test "Vision owned parsing and merge clean up every allocation failure" {
const provider_json =
"{\"images\":[{\"image_id\":1,\"status\":\"ok\",\"summary\":\"summary\",\"visible_text\":[\"visible\"],\"details\":[\"detail\"]}]}";
var provider_probe = std.testing.FailingAllocator.init(std.testing.allocator, .{});
var provider_probe = std.testing.FailingAllocator.init(testing_allocator.no_resize, .{});
const probed_provider = try parse_vision_provider_result(
provider_probe.allocator(),
provider_json,
@@ -1875,7 +1877,7 @@ test "Vision owned parsing and merge clean up every allocation failure" {
probed_provider.deinit(provider_probe.allocator());
for (0..provider_probe.alloc_index) |fail_index| {
var failing = std.testing.FailingAllocator.init(
std.testing.allocator,
testing_allocator.no_resize,
.{ .fail_index = fail_index },
);
try std.testing.expectError(
@@ -1889,7 +1891,7 @@ test "Vision owned parsing and merge clean up every allocation failure" {
}
const provider_records = [_]VisionImageResult{ test_ok_result(2), test_ok_result(1) };
var merge_probe = std.testing.FailingAllocator.init(std.testing.allocator, .{});
var merge_probe = std.testing.FailingAllocator.init(testing_allocator.no_resize, .{});
const probed_merge = try merge_vision_results(
merge_probe.allocator(),
&.{ 1, 2 },
@@ -1898,7 +1900,7 @@ test "Vision owned parsing and merge clean up every allocation failure" {
probed_merge.deinit(merge_probe.allocator());
for (0..merge_probe.alloc_index) |fail_index| {
var failing = std.testing.FailingAllocator.init(
std.testing.allocator,
testing_allocator.no_resize,
.{ .fail_index = fail_index },
);
try std.testing.expectError(
+3 -2
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const model_capabilities = @import("../config/model_capabilities.zig");
const image_attachments = @import("../images/image_attachments.zig");
const debug_trace = @import("../shared/debug_trace.zig");
@@ -492,7 +493,7 @@ test "owned stream result copies preserve service tier metadata" {
try std.testing.expectEqualStrings("account", reference.account_id.?);
}
};
try std.testing.checkAllAllocationFailures(std.testing.allocator, Copy.check, .{source});
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, Copy.check, .{source});
}
test "owned failure copies preserve diagnostics after source teardown and allocation failures" {
@@ -517,7 +518,7 @@ test "owned failure copies preserve diagnostics after source teardown and alloca
try std.testing.expectEqual(@as(?u64, 3), copied.failed.retry_after_seconds);
}
};
try std.testing.checkAllAllocationFailures(std.testing.allocator, Copy.check, .{});
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, Copy.check, .{});
}
pub inline fn failResult(err: anytype) @TypeOf(err)!Result {
+3 -2
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const context_contract = @import("../workspace/context_contract.zig");
const io_mod = @import("../shared/io.zig");
const permissions = @import("../permissions/permissions.zig");
@@ -669,7 +670,7 @@ test "retained context targets use bounded typed history without executing calls
.execution = .{ .tool_steps = @constCast(&steps) },
} }};
try checkRetainedTargetsAllocations(alloc, &history, workspace, registry);
try std.testing.checkAllAllocationFailures(alloc, checkRetainedTargetsAllocations, .{ &history, workspace, registry });
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, checkRetainedTargetsAllocations, .{ &history, workspace, registry });
var cancelled = std.atomic.Value(bool).init(true);
try std.testing.expectError(error.Cancelled, retainedContextTargets(alloc, &history, null, workspace, registry, &cancelled));
@@ -1145,7 +1146,7 @@ test "preparation cancellation and allocation failures clean owned state" {
const workspace = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "workspace");
defer alloc.free(workspace);
try std.testing.checkAllAllocationFailures(
alloc,
testing_allocator.no_resize,
checkPreparationAllocationFailures,
.{workspace},
);
+33 -19
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const credentials = @import("../auth/credentials.zig");
const secret = @import("../auth/secret.zig");
const io_mod = @import("../shared/io.zig");
@@ -20,6 +21,7 @@ const types = @import("../shared/types.zig");
const model_provider = @import("../config/model_provider.zig");
const assistant_presentation = @import("assistant_presentation.zig");
const compaction_activity = @import("../output/compaction_activity.zig");
const text_utils = @import("../shared/text_utils.zig");
pub const AgentTurnSettings = struct {
max_tool_result_bytes: usize = tool_result_limits.default_max_tool_result_bytes,
@@ -2121,11 +2123,23 @@ pub const WorkerRuntime = struct {
max_history_turns: usize,
publication: ?HistoryPublication,
) !void {
errdefer |err| if (err == error.SessionPersistenceUncertain) {
if (self.active_prompt_snapshot_ownership) |ownership| {
_ = ownership.preserve();
self.propagateHistoryTurnToQueue(alloc, turn, max_history_turns, publication) catch |err| {
if (err == error.SessionPersistenceUncertain) {
if (self.active_prompt_snapshot_ownership) |ownership| {
_ = ownership.preserve();
}
}
return err;
};
}
fn propagateHistoryTurnToQueue(
self: *WorkerRuntime,
alloc: std.mem.Allocator,
turn: types.HistoryTurn,
max_history_turns: usize,
publication: ?HistoryPublication,
) !void {
if (self.queued_prompts.items.len == 0) {
if (publication) |value| try value.commit();
return;
@@ -3424,7 +3438,7 @@ test "compaction preserves active snapshots only after persistence succeeds" {
}
test "failed queued prompt dequeue retains borrowed snapshot ownership" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
{
@@ -3960,7 +3974,7 @@ test "finish ownership handoff preserves allocator and filesystem ownership" {
defer alloc.free(snapshot_path);
try std.testing.checkAllAllocationFailures(
alloc,
testing_allocator.no_resize,
checkFinishOwnershipHandoffAllocation,
.{snapshot_path},
);
@@ -4409,7 +4423,7 @@ test "direct steering applies at the boundary without cancellation and seals bef
}
test "direct steering output allocation failure preserves feedback and cancellation discards it" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var receipt = SteeringReceipt{};
var runtime = WorkerRuntime{};
try std.testing.expect(runtime.beginDirectProcessing(41));
@@ -4427,7 +4441,7 @@ test "direct steering output allocation failure preserves feedback and cancellat
}
test "direct steering rejects inactive cancelled and full-allocation admission without taking ownership" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var runtime = WorkerRuntime{};
defer runtime.deinit(alloc);
const prompt = try makePrompt(alloc, "feedback", "model");
@@ -4883,7 +4897,7 @@ test "failed steering admission does not cancel the active turn" {
runtime.active_turn_id = 41;
var failing = std.testing.FailingAllocator.init(
std.testing.allocator,
testing_allocator.no_resize,
.{ .fail_index = 0 },
);
const prompt = QueuedPrompt{
@@ -5044,7 +5058,7 @@ test "steer retraction requires an active processing turn" {
}
test "failed steer retraction preserves the queue" {
const backing = std.testing.allocator;
const backing = testing_allocator.no_resize;
var runtime = WorkerRuntime{};
defer runtime.deinit(backing);
runtime.worker_processing = true;
@@ -5120,7 +5134,7 @@ test "steering presentation survives queue to feedback transfer without duplicat
}
test "steering snapshot allocation failure preserves queue and feedback ownership" {
try std.testing.checkAllAllocationFailures(std.testing.allocator, struct {
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, struct {
fn run(alloc: std.mem.Allocator) !void {
const backing = std.testing.allocator;
var runtime = WorkerRuntime{};
@@ -5457,7 +5471,7 @@ fn checkStateSnapshotFailurePreservesPendingEvents(alloc: std.mem.Allocator) !vo
test "state snapshot allocation failures preserve pending event ownership" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
checkStateSnapshotFailurePreservesPendingEvents,
.{},
);
@@ -6284,7 +6298,7 @@ test "submitted text only queues while a prompt is active" {
}
test "prompt take and grant append allocation failures preserve state" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var runtime = WorkerRuntime{};
defer runtime.deinit(alloc);
@@ -6447,7 +6461,7 @@ fn checkToolLifecycleDupAllocationFailure(alloc: std.mem.Allocator) !void {
test "typed lifecycle duplication frees every partial allocation" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
checkToolLifecycleDupAllocationFailure,
.{},
);
@@ -6725,7 +6739,7 @@ fn checkSemanticWorkerEventDuplicationAllocationFailure(alloc: std.mem.Allocator
test "ordinary and error semantic worker events free partial duplication allocations" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
checkSemanticWorkerEventDuplicationAllocationFailure,
.{},
);
@@ -6784,7 +6798,7 @@ fn checkSemanticNoticeEnqueueAllocationFailure(alloc: std.mem.Allocator) !void {
test "semantic notice enqueue frees partial topic body and queue allocations" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
checkSemanticNoticeEnqueueAllocationFailure,
.{},
);
@@ -7112,7 +7126,7 @@ test "approval cancellation leaves admitted steering available for the next turn
}
test "text enqueue allocation failure preserves active interactive state" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var runtime = WorkerRuntime{};
defer runtime.deinit(alloc);
runtime.worker_processing = true;
@@ -7301,7 +7315,7 @@ test "question request rolls back pending state when its boundary event cannot b
const options = [_]types.QuestionOption{.{ .label = "Yes", .description = null }};
const entries = [_]types.QuestionBatchEntry{.{ .question = "Continue?", .options = &options }};
var probe = std.testing.FailingAllocator.init(std.testing.allocator, .{});
var probe = std.testing.FailingAllocator.init(testing_allocator.no_resize, .{});
var probe_runtime = WorkerRuntime{};
probe_runtime.worker_stop_requested = true;
const probe_result = try probe_runtime.requestQuestionBatchAnswerBlocking(probe.allocator(), &entries);
@@ -7310,7 +7324,7 @@ test "question request rolls back pending state when its boundary event cannot b
probe_runtime.deinit(probe.allocator());
try std.testing.expectEqual(probe.allocated_bytes, probe.freed_bytes);
var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{
var failing = std.testing.FailingAllocator.init(testing_allocator.no_resize, .{
.fail_index = marker_append_alloc_index,
});
var runtime = WorkerRuntime{};
@@ -7338,7 +7352,7 @@ test "discarding queued recovery releases metadata without deleting saved images
.path = @constCast("/missing/source.png"),
.media_type = @constCast("image/png"),
.snapshot_path = path,
.snapshot_sha256 = @constCast("a" ** 64),
.snapshot_sha256 = @constCast(text_utils.repeat("a", 64)),
}};
var runtime = WorkerRuntime{};
defer runtime.deinit(alloc);
+16 -7
View File
@@ -1087,10 +1087,19 @@ pub fn Runtime(comptime App: type) type {
defer if (app.worker.isCancelRequested()) {
app.worker.settleCompactionActivity(operation_id, .{ .outcome = .cancelled });
};
errdefer |err| {
compactWithActivity(app, job, gateway_retry_count, operation_id) catch |err| {
app.worker.settleCompactionActivity(operation_id, compaction_activity.failure(err, .preparation, app.worker.isCancelRequested()));
if (failure_provenance) |out| out.* = .{ .operation_id = operation_id, .turn_id = job.turn_id, .err = err };
}
return err;
};
}
fn compactWithActivity(
app: *App,
job: worker_runtime.ContextCompactionTask,
gateway_retry_count: usize,
operation_id: compaction_activity.OperationId,
) anyerror!void {
var arena_state = std.heap.ArenaAllocator.init(std.heap.c_allocator);
defer arena_state.deinit();
const arena = arena_state.allocator();
@@ -1577,7 +1586,7 @@ const RefreshContextApp = struct {
self.session.deinit(self.alloc);
}
fn writeDomainNotice(self: *RefreshContextApp, notice: types.SemanticNotice, _: bool) !void {
pub fn writeDomainNotice(self: *RefreshContextApp, notice: types.SemanticNotice, _: bool) !void {
self.context_notice_tone = notice.tone;
self.context_notice_visibility = notice.visibility;
try self.context_notices.appendSlice(self.alloc, notice.body);
@@ -1672,7 +1681,7 @@ const FakeApp = struct {
return app;
}
fn toolRegistry(self: *const FakeApp) tool_dispatch.Registry {
pub fn toolRegistry(self: *const FakeApp) tool_dispatch.Registry {
return self.tool_registry;
}
@@ -1680,7 +1689,7 @@ const FakeApp = struct {
return self.context_registry;
}
fn snapshotMcpModelCatalog(
pub fn snapshotMcpModelCatalog(
self: *FakeApp,
alloc: Allocator,
_: types.PermissionRuleSet,
@@ -2336,7 +2345,7 @@ test "app agent runtime bounds a large multiline run command activity" {
var app = try FakeApp.init(alloc);
defer app.deinit();
const arguments_json = "{\"action\":\"run\",\"command\":\"" ++ ("x\\n" ** 20_000) ++ "\"}";
const arguments_json = "{\"action\":\"run\",\"command\":\"" ++ text_utils.repeat("x\\n", 20_000) ++ "\"}";
const label = try app.describeToolAction(arena, .{
.id = "large_command",
.name = "shell",
@@ -2880,7 +2889,7 @@ test "manual compaction worker call commits a checkpoint without a continuation"
const steps = [_]types.ToolExecutionStep{.{ .tool_calls = @constCast(&calls), .tool_results = @constCast(&results) }};
job.history[0] = try types.dupeHistoryTurn(alloc, .{ .assistant = .{
.user = .{ .text = @constCast("exact user request") },
.assistant = @constCast("exact completed response\n" ++ ("evidence " ** 1_000)),
.assistant = @constCast("exact completed response\n" ++ text_utils.repeat("evidence ", 1_000)),
.execution = .{ .tool_steps = @constCast(&steps) },
} });
job.history[1] = try types.dupeHistoryTurn(alloc, .{ .assistant = .{
+12 -11
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const config_runtime = @import("../config/config_runtime.zig");
const debug_trace = @import("../shared/debug_trace.zig");
const host = @import("../hosts/host.zig");
@@ -2435,7 +2436,7 @@ const TestAuth = struct {
};
}
fn beginPromptCredentialRefresh(self: *TestAuth) auth_runtime.PromptCredentialRefreshStart {
pub fn beginPromptCredentialRefresh(self: *TestAuth) auth_runtime.PromptCredentialRefreshStart {
self.prompt_refresh_start_count += 1;
return self.prompt_refresh_start;
}
@@ -2444,7 +2445,7 @@ const TestAuth = struct {
return .idle;
}
fn cancelPromptCredentialRefresh(_: *TestAuth) void {}
pub fn cancelPromptCredentialRefresh(_: *TestAuth) void {}
fn selectSource(self: *TestAuth, _: std.mem.Allocator, source: credentials.Source) !?bool {
self.selected_source = source;
@@ -2480,7 +2481,7 @@ const TestAuth = struct {
return self.openSignInPicker(alloc);
}
fn startupCredentialPending(self: *const TestAuth) bool {
pub fn startupCredentialPending(self: *const TestAuth) bool {
return self.startup_pending;
}
@@ -2540,7 +2541,7 @@ const TestAuth = struct {
return false;
}
fn pickerView(_: *const TestAuth) auth_runtime.PickerView {
pub fn pickerView(_: *const TestAuth) auth_runtime.PickerView {
return .{
.active = false,
.available_sources = .empty,
@@ -2620,7 +2621,7 @@ const TestAuth = struct {
.{ .ready = action };
}
fn recordCredentialFailure(
pub fn recordCredentialFailure(
self: *TestAuth,
failure: auth_runtime.CredentialFailure,
options: struct { notify: bool = true },
@@ -2638,7 +2639,7 @@ const TestAuth = struct {
return true;
}
fn credentialFailure(self: *const TestAuth) ?auth_runtime.CredentialFailure {
pub fn credentialFailure(self: *const TestAuth) ?auth_runtime.CredentialFailure {
return self.credential_failure;
}
@@ -2765,7 +2766,7 @@ const TestApp = struct {
self.transcript.deinit(self.alloc);
}
fn startModelCacheWarmup(self: *TestApp) void {
pub fn startModelCacheWarmup(self: *TestApp) void {
self.model_cache_warmup_count += 1;
}
@@ -2785,12 +2786,12 @@ const TestApp = struct {
return self.test_url_opener.opener();
}
fn persistCredentialSourcePreference(self: *TestApp, source: credentials.Source) void {
pub fn persistCredentialSourcePreference(self: *TestApp, source: credentials.Source) void {
self.preference_write_count += 1;
if (self.preference_write_succeeds) self.last_preference_source = source;
}
fn providerCatalog(self: *TestApp, _: model_provider.ProviderId) ?model_catalog.Provider {
pub fn providerCatalog(self: *TestApp, _: model_provider.ProviderId) ?model_catalog.Provider {
return .{ .context = self, .fetch_fn = fetchTestCatalog };
}
@@ -2880,7 +2881,7 @@ test "provider inventory completion does not reclaim a changed composer" {
}
test "provider picker preparation failure starts no inventory task" {
var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{ .fail_index = 0 });
var failing = std.testing.FailingAllocator.init(testing_allocator.no_resize, .{ .fail_index = 0 });
var app: TestApp = .{ .alloc = failing.allocator() };
defer app.deinit();
@@ -3675,7 +3676,7 @@ const StartupCredentialApp = struct {
self.notices += 1;
}
fn startModelCacheWarmup(self: *@This()) void {
pub fn startModelCacheWarmup(self: *@This()) void {
self.warmups += 1;
}
+4 -3
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const skill_contract = @import("../skills/skill_contract.zig");
const agent_runtime = @import("../agent/agent_runtime.zig");
const tool_mcp_runtime = @import("../tooling/tool_mcp_runtime.zig");
@@ -191,7 +192,7 @@ test "prepared diff payload keeps compact output when full formatting is unavail
.lifecycle_id = .{ .turn_id = 40, .call_id = "full-format-fallback" },
};
var failing = std.testing.FailingAllocator.init(
std.testing.allocator,
testing_allocator.no_resize,
.{ .fail_index = 0 },
);
@@ -2092,7 +2093,7 @@ const FakeApp = struct {
self.command_output_count += 1;
}
fn writeCommandOutputChunkForLifecycle(self: *FakeApp, lifecycle_id: ?types.ToolLifecycleId, stream: command_output_content.Stream, text: []const u8, record: bool) !void {
pub fn writeCommandOutputChunkForLifecycle(self: *FakeApp, lifecycle_id: ?types.ToolLifecycleId, stream: command_output_content.Stream, text: []const u8, record: bool) !void {
storeLifecycleId(
&self.last_command_output_lifecycle_turn_id,
&self.last_command_output_lifecycle_call_id,
@@ -2105,7 +2106,7 @@ const FakeApp = struct {
try self.flushCommandOutputSummaryForLifecycle(null, record);
}
fn flushCommandOutputSummaryForLifecycle(self: *FakeApp, lifecycle_id: ?types.ToolLifecycleId, record: bool) !void {
pub fn flushCommandOutputSummaryForLifecycle(self: *FakeApp, lifecycle_id: ?types.ToolLifecycleId, record: bool) !void {
_ = record;
storeLifecycleId(
&self.last_command_output_complete_lifecycle_turn_id,
+20 -19
View File
@@ -3087,7 +3087,7 @@ fn writeModelCatalogSummary(writer: *std.Io.Writer, app: anytype) !void {
}
if (snapshot.failure_category) |category| {
try writer.print(" failure={s} retryable={s}", .{ category, boolLabel(snapshot.failure_retryable) });
if (snapshot.failure_http_status) |status| try writer.print(" status={d}", .{@intFromEnum(status)});
if (snapshot.failure_http_status) |status| try writer.print(" status={d}", .{@backingInt(status)});
if (snapshot.anonymous_fallback) try writer.writeAll(" anonymous_fallback=true");
}
try writer.writeByte('\n');
@@ -3631,7 +3631,7 @@ test "workspace list refresh waits for an idle turn" {
active: bool = true,
index_refresh_count: usize = 0,
fn refreshWorkspaceAccess(self: *@This()) !bool {
pub fn refreshWorkspaceAccess(self: *@This()) !bool {
self.available = false;
self.active = false;
self.index_refresh_count += 1;
@@ -3689,11 +3689,11 @@ test "workspace list reports refresh rejection without replacing access" {
self.transcript.deinit(self.alloc);
}
fn refreshWorkspaceAccess(_: *@This()) !bool {
pub fn refreshWorkspaceAccess(_: *@This()) !bool {
return error.TooManyDirectories;
}
fn workspaceAccess(self: *@This()) *const Access {
pub fn workspaceAccess(self: *@This()) *const Access {
return &self.access;
}
@@ -3862,8 +3862,9 @@ const StatuslineFeedback = enum { announce, silent };
fn parseStatuslineItem(raw: []const u8) ?config_runtime.StatuslineItem {
const trimmed = std.mem.trim(u8, raw, " \t");
inline for (std.meta.fields(config_runtime.StatuslineItem)) |field| {
if (std.mem.eql(u8, trimmed, field.name)) return @enumFromInt(field.value);
const statusline_item_info = @typeInfo(config_runtime.StatuslineItem).@"enum";
inline for (statusline_item_info.field_names, statusline_item_info.field_values) |field_name, field_value| {
if (std.mem.eql(u8, trimmed, field_name)) return @fromBackingInt(@intCast(field_value));
}
return null;
}
@@ -4302,7 +4303,7 @@ const McpCommandFakeApp = struct {
self.notice_body.deinit(self.alloc);
}
fn openMcpMenu(self: *McpCommandFakeApp) !void {
pub fn openMcpMenu(self: *McpCommandFakeApp) !void {
self.menu_open_count += 1;
}
@@ -4352,7 +4353,7 @@ const McpCommandFakeApp = struct {
self.reload_pending = true;
}
fn takeMcpReloadCompletion(self: *McpCommandFakeApp) !?app_mcp_runtime.ReloadCompletion {
pub fn takeMcpReloadCompletion(self: *McpCommandFakeApp) !?app_mcp_runtime.ReloadCompletion {
if (!self.reload_pending) return null;
self.reload_pending = false;
return switch (self.reload_behavior) {
@@ -4391,11 +4392,11 @@ const McpCommandFakeApp = struct {
};
}
fn mcpReloadCompletionOrigin(self: *const McpCommandFakeApp) app_mcp_runtime.PresentationOrigin {
pub fn mcpReloadCompletionOrigin(self: *const McpCommandFakeApp) app_mcp_runtime.PresentationOrigin {
return self.completion_origin;
}
fn applyMcpMenuReloadCompletion(
pub fn applyMcpMenuReloadCompletion(
self: *McpCommandFakeApp,
generation: u64,
_: *const app_mcp_runtime.ReloadCompletion,
@@ -4404,7 +4405,7 @@ const McpCommandFakeApp = struct {
self.menu_reload_completions += 1;
}
fn takeMcpAuthenticationCompletion(
pub fn takeMcpAuthenticationCompletion(
self: *McpCommandFakeApp,
) !?app_mcp_runtime.AuthenticationCompletion {
if (!self.authentication_pending) return null;
@@ -4416,11 +4417,11 @@ const McpCommandFakeApp = struct {
};
}
fn mcpAuthenticationCompletionOrigin(self: *const McpCommandFakeApp) app_mcp_runtime.PresentationOrigin {
pub fn mcpAuthenticationCompletionOrigin(self: *const McpCommandFakeApp) app_mcp_runtime.PresentationOrigin {
return self.completion_origin;
}
fn applyMcpMenuAuthenticationCompletion(
pub fn applyMcpMenuAuthenticationCompletion(
self: *McpCommandFakeApp,
generation: u64,
_: *const app_mcp_runtime.AuthenticationCompletion,
@@ -4573,7 +4574,7 @@ const SkillsInstallReplayApp = struct {
self.shell.deinit(self.alloc);
}
fn requestSkillsRefresh(self: *SkillsInstallReplayApp) !u64 {
pub fn requestSkillsRefresh(self: *SkillsInstallReplayApp) !u64 {
self.reload_count += 1;
self.skills.fresh_through_generation = self.reload_count;
return self.reload_count;
@@ -5250,13 +5251,13 @@ test "app_commands exposes active handler API surface" {
const HandlerSurface = Handlers(SurfaceOnlyApp);
const route_info = @typeInfo(@TypeOf(HandlerSurface.route)).@"fn";
try std.testing.expectEqual(@as(usize, 2), route_info.params.len);
try std.testing.expect(route_info.params[0].type.? == *SurfaceOnlyApp);
try std.testing.expect(route_info.params[1].type.? == []const u8);
try std.testing.expectEqual(@as(usize, 2), route_info.param_types.len);
try std.testing.expect(route_info.param_types[0].? == *SurfaceOnlyApp);
try std.testing.expect(route_info.param_types[1].? == []const u8);
const handlers_info = @typeInfo(@TypeOf(HandlerSurface.commandHandlers)).@"fn";
try std.testing.expectEqual(@as(usize, 1), handlers_info.params.len);
try std.testing.expect(handlers_info.params[0].type.? == *SurfaceOnlyApp);
try std.testing.expectEqual(@as(usize, 1), handlers_info.param_types.len);
try std.testing.expect(handlers_info.param_types[0].? == *SurfaceOnlyApp);
try std.testing.expect(handlers_info.return_type.? == command_router.CommandHandlers);
}
+5 -5
View File
@@ -847,7 +847,7 @@ const TestApp = struct {
return .{ .handoff = handoff, .failure = active_capture.?.shutdown_failure };
}
fn takeUpgradeRelaunchRequest(_: *TestApp) ?auto_upgrade.RelaunchRequest {
pub fn takeUpgradeRelaunchRequest(_: *TestApp) ?auto_upgrade.RelaunchRequest {
const path = active_capture.?.upgrade_relaunch_path orelse return null;
var request = auto_upgrade.RelaunchRequest{
.executable_path_len = path.len,
@@ -870,19 +870,19 @@ const TestApp = struct {
appendTestEvent("resume-reconciliation");
}
fn startAutoUpgrade(_: *TestApp) void {
pub fn startAutoUpgrade(_: *TestApp) void {
appendTestEvent("auto-upgrade");
}
fn startFileIndex(_: *TestApp) void {
pub fn startFileIndex(_: *TestApp) void {
appendTestEvent("file-index");
}
fn startMcpDiscovery(_: *TestApp) void {
pub fn startMcpDiscovery(_: *TestApp) void {
appendTestEvent("mcp-discovery");
}
fn rebindAfterInit(_: *TestApp) void {
pub fn rebindAfterInit(_: *TestApp) void {
appendTestEvent("rebind-after-init");
}
+29 -28
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const file_picker_path = @import("../input/file_picker_path.zig");
const question_prompt = @import("../agent/question_prompt.zig");
const app_auth_runtime = @import("app_auth_runtime.zig");
@@ -3966,7 +3967,7 @@ const RoutingFakeApp = struct {
var count: usize = 0;
for (self.model_completion_values) |value| {
if (count == out.len) break;
if (query.len > 0 and std.ascii.indexOfIgnoreCase(value, query) == null) continue;
if (query.len > 0 and std.ascii.findIgnoreCase(value, query) == null) continue;
out[count] = value;
count += 1;
}
@@ -4607,7 +4608,7 @@ test "app_input_runtime workspace add handoff dismisses argument completion" {
}
test "help menu whole replacement preserves the prior draft when staging fails" {
var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{});
var failing = std.testing.FailingAllocator.init(testing_allocator.no_resize, .{});
const alloc = failing.allocator();
var app = try RoutingFakeApp.init(alloc);
defer app.deinit();
@@ -4631,7 +4632,7 @@ test "help menu whole replacement preserves the prior draft when staging fails"
}
test "workspace menu whole replacement preserves the prior draft when staging fails" {
var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{});
var failing = std.testing.FailingAllocator.init(testing_allocator.no_resize, .{});
const alloc = failing.allocator();
var app = try RoutingFakeApp.init(alloc);
defer app.deinit();
@@ -5602,7 +5603,7 @@ test "app_input_runtime redraws a closed skill picker when the input limit notic
}
test "app_input_runtime keeps the skill picker open when binding allocation fails" {
var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{});
var failing = std.testing.FailingAllocator.init(testing_allocator.no_resize, .{});
const alloc = failing.allocator();
var app = try RoutingFakeApp.init(alloc);
defer app.deinit();
@@ -5863,7 +5864,7 @@ test "app_input_runtime ctrl-l preserves an active inline picker" {
}
test "app_input_runtime Enter keeps backslash newline semantics when history allocation fails" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var app = try RoutingFakeApp.init(alloc);
defer app.deinit();
try app.input_runtime.textReplacementState().replace(alloc, "draft\\");
@@ -6926,7 +6927,7 @@ test "app_input_runtime bare model Tab keeps current selection across catalog re
test "app_input_runtime bare model Tab keeps current selection beyond completion window" {
const alloc = std.testing.allocator;
const current_model = "anthropic/claude-opus-4.8";
var completions = [_][]const u8{"anthropic/claude-opus-4.8-preview"} ** 33;
var completions: [33][]const u8 = @splat("anthropic/claude-opus-4.8-preview");
completions[completions.len - 1] = current_model;
var app = try RoutingFakeApp.init(alloc);
@@ -9063,7 +9064,7 @@ test "app_input_runtime ctrl+p model stage never submits the borrowed composer"
}
test "app_input_runtime ctrl+p catalog enter failure restores the draft without later input" {
var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{});
var failing = std.testing.FailingAllocator.init(testing_allocator.no_resize, .{});
const alloc = failing.allocator();
var app = try RoutingFakeApp.init(alloc);
defer app.deinit();
@@ -9185,7 +9186,7 @@ test "app_input_runtime ctrl+d while the picker borrows the composer closes inst
}
test "app_input_runtime ctrl+p catalog enter failure still closes and restores the draft" {
var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{});
var failing = std.testing.FailingAllocator.init(testing_allocator.no_resize, .{});
const alloc = failing.allocator();
var app = try RoutingFakeApp.init(alloc);
defer app.deinit();
@@ -9963,7 +9964,7 @@ test "app_input_runtime keeps composer and decision input limits independent" {
}
test "app_input_runtime approval amendment paste finalization resets state after allocation failure" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var app = try RoutingFakeApp.init(alloc);
defer app.deinit();
try std.testing.expect(try app.approval_prompt.syncRequest(alloc, .{ .label = "shell.run npm test" }));
@@ -10197,7 +10198,7 @@ test "app_input_runtime question freeform rejects paste beyond input limit" {
app.question_prompt.moveChoice(-1);
try feedRoutingBytes(&app, "\x1b[200~");
try feedRoutingBytes(&app, "x" ** 4097);
try feedRoutingBytes(&app, text_utils.repeat("x", 4097));
try feedRoutingBytes(&app, "\x1b[201~");
try std.testing.expectEqual(paste_framing.Owner.none, app.input_runtime.paste.owner);
@@ -11965,7 +11966,7 @@ test "file approval digit submits the mapped choice when the modal is ready" {
}
test "file approval keeps the active review when feedback materialization fails" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var app = try RoutingFakeApp.init(alloc);
defer app.deinit();
try installReadyRoutingFileApproval(&app);
@@ -13554,7 +13555,7 @@ test "app_input_runtime rejected second image keeps the first pending image and
}
test "app_input_runtime repeated image command releases every allocation on failure" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
try writeTestImage(&tmp, "second.png");
@@ -13835,7 +13836,7 @@ test "app_input_runtime disabled prompt history skips composer recall recording"
test "app_input_runtime accepted large paste recalls compact and editable" {
const alloc = std.testing.allocator;
const placeholder = "[Pasted text #7, 1 line]";
const backing = "x" ** (paste_blocks.large_paste_char_threshold + 1);
const backing = text_utils.repeat("x", paste_blocks.large_paste_char_threshold + 1);
var app = FakeSubmitApp{ .alloc = alloc };
defer app.deinit();
@@ -14278,14 +14279,14 @@ fn checkAcceptedPromptCleanupAcrossAllocationFailures(failing: *std.testing.Fail
}
test "app_input_runtime accepted prompt cleanup survives allocation failures" {
var probe = std.testing.FailingAllocator.init(std.testing.allocator, .{});
var probe = std.testing.FailingAllocator.init(testing_allocator.no_resize, .{});
try checkAcceptedPromptCleanupAcrossAllocationFailures(&probe);
const allocation_count = probe.alloc_index;
try std.testing.expectEqual(probe.allocated_bytes, probe.freed_bytes);
for (0..allocation_count) |fail_index| {
var failing = std.testing.FailingAllocator.init(
std.testing.allocator,
testing_allocator.no_resize,
.{ .fail_index = fail_index },
);
checkAcceptedPromptCleanupAcrossAllocationFailures(&failing) catch |err| {
@@ -14464,11 +14465,11 @@ test "app_input_runtime inline image edits roll back across allocation failures"
defer std.testing.allocator.free(root);
const snapshot_dir = try std.fs.path.join(std.testing.allocator, &.{ root, "snapshots" });
defer std.testing.allocator.free(snapshot_dir);
var probe = std.testing.FailingAllocator.init(std.testing.allocator, .{});
var probe = std.testing.FailingAllocator.init(testing_allocator.no_resize, .{});
try check_inline_image_edit_allocation_failure(&probe, root, snapshot_dir);
try std.testing.expectEqual(probe.allocated_bytes, probe.freed_bytes);
for (0..probe.alloc_index) |index| {
var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{ .fail_index = index });
var failing = std.testing.FailingAllocator.init(testing_allocator.no_resize, .{ .fail_index = index });
check_inline_image_edit_allocation_failure(&failing, root, snapshot_dir) catch |err| {
try std.testing.expectEqual(error.OutOfMemory, err);
};
@@ -15530,14 +15531,14 @@ test "app_input_runtime image path paste stays atomic across allocation failures
const path = try realTmpPath(std.testing.allocator, &tmp, "oom.png");
defer std.testing.allocator.free(path);
var probe = std.testing.FailingAllocator.init(std.testing.allocator, .{});
var probe = std.testing.FailingAllocator.init(testing_allocator.no_resize, .{});
try checkImagePathPasteAllocationFailureIsAtomic(&probe, path);
const allocation_count = probe.alloc_index;
try std.testing.expectEqual(probe.allocated_bytes, probe.freed_bytes);
for (0..allocation_count) |fail_index| {
var failing = std.testing.FailingAllocator.init(
std.testing.allocator,
testing_allocator.no_resize,
.{ .fail_index = fail_index },
);
checkImagePathPasteAllocationFailureIsAtomic(&failing, path) catch |err| {
@@ -15608,7 +15609,7 @@ test "app_input_runtime finalizePastedBlock registers large text placeholder ato
var app = FakeSubmitApp{ .alloc = alloc };
defer app.deinit();
const large_paste = "x" ** (paste_blocks.large_paste_char_threshold + 1);
const large_paste = text_utils.repeat("x", paste_blocks.large_paste_char_threshold + 1);
try app.input_runtime.insertionState().insertByte(alloc, 'a', .clear);
try app.input_runtime.paste.buffer.appendSlice(alloc, large_paste);
@@ -15626,7 +15627,7 @@ test "app_input_runtime finalizePastedBlock registers large text placeholder ato
test "app_input_runtime large paste replaces selection within its expanded byte budget" {
const alloc = std.testing.allocator;
const large_paste = "x" ** (paste_blocks.large_paste_char_threshold + 1);
const large_paste = text_utils.repeat("x", paste_blocks.large_paste_char_threshold + 1);
var app = FakeSubmitApp{ .alloc = alloc };
defer app.deinit();
try app.input_runtime.textReplacementState().replace(alloc, "abcde");
@@ -15647,7 +15648,7 @@ test "app_input_runtime rejects a large paste when its backing text does not fit
const alloc = std.testing.allocator;
var app = FakeSubmitApp{ .alloc = alloc };
defer app.deinit();
const large_paste = "x" ** (paste_blocks.large_paste_char_threshold + 1);
const large_paste = text_utils.repeat("x", paste_blocks.large_paste_char_threshold + 1);
try app.input_runtime.textReplacementState().replace(alloc, "draft");
try app.input_runtime.paste.buffer.appendSlice(alloc, large_paste);
@@ -15664,7 +15665,7 @@ test "app_input_runtime rejects a large paste when its backing text does not fit
test "app_input_runtime multiple large pastes share one expanded byte budget" {
const alloc = std.testing.allocator;
const large_paste = "x" ** (paste_blocks.large_paste_char_threshold + 1);
const large_paste = text_utils.repeat("x", paste_blocks.large_paste_char_threshold + 1);
var app = FakeSubmitApp{ .alloc = alloc };
defer app.deinit();
@@ -15682,8 +15683,8 @@ test "app_input_runtime multiple large pastes share one expanded byte budget" {
}
test "app_input_runtime finalizePastedBlock leaves large paste unchanged on allocation failure" {
const base = std.testing.allocator;
const large_paste = "x" ** (paste_blocks.large_paste_char_threshold + 1);
const base = testing_allocator.no_resize;
const large_paste = text_utils.repeat("x", paste_blocks.large_paste_char_threshold + 1);
var saw_failure = false;
var fail_index: usize = 0;
@@ -15712,8 +15713,8 @@ test "app_input_runtime finalizePastedBlock leaves large paste unchanged on allo
}
test "app_input_runtime finishPaste leaves no orphan placeholder after late finalization failure" {
const base = std.testing.allocator;
const large_paste = "x" ** (paste_blocks.large_paste_char_threshold + 1);
const base = testing_allocator.no_resize;
const large_paste = text_utils.repeat("x", paste_blocks.large_paste_char_threshold + 1);
var saw_failure = false;
var fail_index: usize = 0;
@@ -15767,7 +15768,7 @@ test "app_input_runtime finishPaste leaves no orphan placeholder after late fina
}
test "app_input_runtime composer paste finalization failure clears inactive state" {
const base = std.testing.allocator;
const base = testing_allocator.no_resize;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const root = try io_mod.dirRealpathAlloc(base, tmp.dir, ".");
+3 -3
View File
@@ -2440,10 +2440,10 @@ test "shutdown cleanup erases from footer frame top after frame commit" {
}
test "startup credential modes select a refresh policy, never a narrower source set" {
const modes = std.meta.fields(CredentialLoadMode);
const modes = @typeInfo(CredentialLoadMode).@"enum".field_names;
try std.testing.expectEqual(@as(usize, 2), modes.len);
try std.testing.expectEqualStrings("stored", modes[0].name);
try std.testing.expectEqualStrings("refresh_if_needed", modes[1].name);
try std.testing.expectEqualStrings("stored", modes[0]);
try std.testing.expectEqualStrings("refresh_if_needed", modes[1]);
}
test "loadStartupState applies core env overrides" {
+2 -1
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const builtin = @import("builtin");
const io_mod = @import("../shared/io.zig");
const debug_trace = @import("../shared/debug_trace.zig");
@@ -3069,7 +3070,7 @@ test "project prompt display escapes repository control bytes" {
}
test "project prompt allocation failure releases the runtime lease" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var state: State = .{};
const runtime = try alloc.create(mcp_runtime.McpRuntime);
runtime.* = mcp_runtime.McpRuntime.init(alloc);
+13 -3
View File
@@ -41,8 +41,18 @@ pub fn Runtime(comptime App: type) type {
const work = (try app.worker.tryTakeNextWork(std.heap.c_allocator)) orelse return;
defer worker_runtime.freeWorkItem(std.heap.c_allocator, work);
defer app.worker.finishProcessing();
errdefer |err| settleCompactionWorkFailure(&app.worker, work, err);
processCooperativeWork(app, event_handlers, flush_frame, work) catch |err| {
settleCompactionWorkFailure(&app.worker, work, err);
return err;
};
}
fn processCooperativeWork(
app: *App,
event_handlers: app_worker_runtime.WorkerEventHandlers,
flush_frame: *const fn (*App) anyerror!void,
work: worker_runtime.WorkItem,
) !void {
try app_worker_runtime.Runtime(App).tick(
app,
event_handlers,
@@ -164,7 +174,7 @@ pub fn Runtime(comptime App: type) type {
}
test "compaction activity error routing requires exact operation and turn provenance" {
const id: compaction_activity.OperationId = @enumFromInt(1);
const id: compaction_activity.OperationId = @fromBackingInt(@intCast(1));
const task: worker_runtime.WorkItem = .{ .compact_context = .{
.operation_id = id,
.turn_id = 7,
@@ -177,7 +187,7 @@ test "compaction activity error routing requires exact operation and turn proven
try std.testing.expect(!compactionErrorHandled(task, null, error.ModelFailed));
try std.testing.expect(!compactionErrorHandled(task, provenance, error.OutOfMemory));
var stale = provenance;
stale.operation_id = @enumFromInt(2);
stale.operation_id = @fromBackingInt(@intCast(2));
try std.testing.expect(!compactionErrorHandled(task, stale, provenance.err));
stale = provenance;
stale.turn_id = 8;
+25 -23
View File
@@ -943,7 +943,17 @@ pub fn Runtime(comptime App: type) type {
render_requests.animation_next_deadline_ms,
},
);
errdefer |err| {
defer attempt.deinit();
const resize_commit = if (comptime has_resize_lifecycle)
shell_runtime.pendingResizeFrameCommit(
&app.shell,
snapshot.reasons.contains(.resize),
)
else
shell_runtime.ResizeFrameCommit.none;
const attempted = attemptRequestedFrame(app, snapshot) catch |err| {
attempt.restore();
if (comptime @hasField(App, "terminal")) {
_ = app_lifecycle.closeFullTranscriptIfActive(
app.alloc,
@@ -965,17 +975,9 @@ pub fn Runtime(comptime App: type) type {
render_requests.animation_next_deadline_ms,
},
);
}
defer attempt.deinit();
const resize_commit = if (comptime has_resize_lifecycle)
shell_runtime.pendingResizeFrameCommit(
&app.shell,
snapshot.reasons.contains(.resize),
)
else
shell_runtime.ResizeFrameCommit.none;
const result = (try attemptRequestedFrame(app, snapshot)) orelse {
return err;
};
const result = attempted orelse {
attempt.restore();
render_requests.noteInputPendingAbort();
debug_trace.logf(
@@ -2615,7 +2617,7 @@ test "pending prompt at an occupied band bottom preserves the summary through ad
var history: std.ArrayList(u8) = .empty;
defer history.deinit(alloc);
var offset: u64 = 0;
_ = try app.shell.appendRawTranscriptEntryClassified(alloc, "previous answer\n" ** 235, .unknown_raw);
_ = try app.shell.appendRawTranscriptEntryClassified(alloc, text_utils.repeat("previous answer\n", 235), .unknown_raw);
const summary = " 10m 31s (↑177 ↓21k)";
_ = try app.shell.appendRawTranscriptEntryClassified(alloc, summary, .turn_summary);
app.shell.render_requests.request(.first_frame);
@@ -2633,7 +2635,7 @@ test "pending prompt at an occupied band bottom preserves the summary through ad
try std.testing.expectEqual(@as(usize, 1), std.mem.count(u8, initial, summary));
const last_line = "- evidence gaps that could still change the design.";
const prompt = ("PASTE_BODY\n" ** 383) ++ last_line;
const prompt = text_utils.repeat("PASTE_BODY\n", 383) ++ last_line;
app.submission.pending = .{ .draft = .{
.turn_id = 1,
.prompt = try alloc.dupe(u8, prompt),
@@ -2668,7 +2670,7 @@ test "pending prompt at an occupied band bottom preserves the summary through ad
try std.testing.expectEqual(@as(usize, 1), std.mem.count(u8, adopted, "┃ - evidence gaps"));
try std.testing.expectEqual(@as(usize, 1), std.mem.count(u8, adopted, last_line));
try std.testing.expectEqual(@as(usize, 0), publicationLineCount(adopted, "┃ - evidence gaps tha"));
_ = try app.shell.appendRawTranscriptEntryClassified(alloc, "FOLLOWUP\n" ** 80, .unknown_raw);
_ = try app.shell.appendRawTranscriptEntryClassified(alloc, text_utils.repeat("FOLLOWUP\n", 80), .unknown_raw);
_ = try flushRebasedPublicationFrame(&app, file, &physical, &history, &offset);
try std.testing.expectEqual(@as(usize, 1), std.mem.count(u8, history.items, summary));
try std.testing.expectEqual(@as(usize, 1), std.mem.count(u8, history.items, last_line));
@@ -2693,7 +2695,7 @@ test "pending prompt preview preserves blank bottom rows and canonical leading s
.alloc = alloc,
.submission = .{ .pending = .{ .draft = .{
.turn_id = 1,
.prompt = try alloc.dupe(u8, "line\n" ** 30),
.prompt = try alloc.dupe(u8, text_utils.repeat("line\n", 30)),
.images = &.{},
.skill_display_spans = &.{},
} } },
@@ -3049,11 +3051,11 @@ const CoordinatorFaultTestApp = struct {
file_picker_receipt: ?input_completion_runtime.FilePickerReceipt = null,
last_snapshot: ?render_request.AttemptSnapshot = null,
fn flushNotifications(self: *CoordinatorFaultTestApp) void {
pub fn flushNotifications(self: *CoordinatorFaultTestApp) void {
self.notification_flushes += 1;
}
fn renderFrameAttemptForTest(
pub fn renderFrameAttemptForTest(
self: *CoordinatorFaultTestApp,
snapshot: render_request.AttemptSnapshot,
) !FrameAttemptResult {
@@ -4538,9 +4540,9 @@ fn checkRewritePublicationRetry(case: RewritePublicationCase, retry: Publication
try app.selected_model.appendSlice(alloc, "test-model");
try app.shell.initBacking(alloc);
try app.shell.enableShadowVt(alloc);
const prefix_id = try app.shell.appendRawTranscriptEntryClassified(alloc, ("\x1b[0m" ** 256) ++ ("prefix padding\n" ** 2), .subagent_status);
const prefix_id = try app.shell.appendRawTranscriptEntryClassified(alloc, text_utils.repeat("\x1b[0m", 256) ++ text_utils.repeat("prefix padding\n", 2), .subagent_status);
const text = "1. earlier context\n2. earlier context\n3. earlier context\n4. earlier context\n5. RW_OLD_00\n6. RW_OLD_01\n7. RW_OLD_02\n8. RW_OLD_03\n9. RW_OLD_04\n10. RW_OLD_05\n\n";
const assistant_id = try app.shell.streamAssistantChunk(alloc, &app.metrics, if (retry.utf8) text ++ ("界é" ** 48) ++ "\n\nRW_ARTIFACTS\n" else text ++ "RW_ARTIFACTS\n");
const assistant_id = try app.shell.streamAssistantChunk(alloc, &app.metrics, if (retry.utf8) text ++ text_utils.repeat("界é", 48) ++ "\n\nRW_ARTIFACTS\n" else text ++ "RW_ARTIFACTS\n");
const status_id = if (retry.cancel) blk: {
const id: types.ToolLifecycleId = .{ .turn_id = 41, .call_id = "publication-cancel" };
_ = try app.shell.applyToolLifecycle(alloc, .{ .authoritative_started = .{ .id = id, .reconciles_provisional_call_id = null, .tool_name = "run_command", .activity_kind = .command } });
@@ -6143,7 +6145,7 @@ test "core.app_render_runtime lifecycle rewrite recovers normal buffer after fil
try app.shell.writeTranscript(
alloc,
&app.metrics,
"retained assistant history\n" ** 64,
text_utils.repeat("retained assistant history\n", 64),
true,
);
app.shell.render_requests.request(.first_frame);
@@ -6247,7 +6249,7 @@ test "core.app_render_runtime full transcript defers repaint until its page is r
try app.shell.writeTranscript(
alloc,
&app.metrics,
("historical transcript row\n" ** 512) ++ "FULL_ASYNC_SENTINEL\n",
text_utils.repeat("historical transcript row\n", 512) ++ "FULL_ASYNC_SENTINEL\n",
true,
);
app.shell.render_requests.request(.first_frame);
@@ -6311,7 +6313,7 @@ test "core.app_render_runtime full transcript resize defers history reset until
try app.shell.writeTranscript(
alloc,
&app.metrics,
"old primary history\n" ** 256,
text_utils.repeat("old primary history\n", 256),
true,
);
app.shell.render_requests.request(.first_frame);
+2 -1
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const io_mod = @import("../shared/io.zig");
const profile_paths = @import("../shared/profile_paths.zig");
const skill_contract = @import("../skills/skill_contract.zig");
@@ -194,7 +195,7 @@ test "loadSkills canonicalizes a symlinked HOME before discovering optional root
}
test "loadSkills propagates allocation failure instead of returning an empty inventory" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
try tmp.dir.createDirPath(io_mod.getIo(), "home/workspace");
+43 -33
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const worker_runtime = @import("../agent/worker_runtime.zig");
const agent_runtime = @import("../agent/agent_runtime.zig");
const auto_classifier_context = @import("../permissions/auto_classifier_context.zig");
@@ -1319,7 +1320,7 @@ pub const Persistence = struct {
/// in a static release-binary template.
pub fn initInto(storage: *Persistence) void {
comptime {
if (std.meta.fields(Persistence).len != 30) {
if (@typeInfo(Persistence).@"struct".field_names.len != 30) {
@compileError("update Persistence.initInto for the changed field set");
}
}
@@ -2955,11 +2956,20 @@ pub fn Runtime(comptime App: type) type {
app: *App,
checkpoint: session_codec.RecoveryCheckpoint,
) !void {
errdefer |err| if (err == error.SessionPersistenceUncertain) {
if (comptime @hasDecl(@TypeOf(app.worker), "preservePromptSnapshots")) {
app.worker.preservePromptSnapshots(checkpoint.turn_id, checkpoint.user.images);
writeRecoveryCheckpoint(app, checkpoint) catch |err| {
if (err == error.SessionPersistenceUncertain) {
if (comptime @hasDecl(@TypeOf(app.worker), "preservePromptSnapshots")) {
app.worker.preservePromptSnapshots(checkpoint.turn_id, checkpoint.user.images);
}
}
return err;
};
}
fn writeRecoveryCheckpoint(
app: *App,
checkpoint: session_codec.RecoveryCheckpoint,
) !void {
if (comptime !@hasField(App, "session_persistence")) {
return error.SessionPersistenceUnavailable;
}
@@ -6484,7 +6494,7 @@ test "session transition retires queued and running compaction only after worker
.history = try app.alloc.alloc(types.HistoryTurn, 0),
});
const next = app.worker.compactionActivitySnapshot();
try std.testing.expect(@intFromEnum(next.operation.?.id) > @intFromEnum(old.operation.?.id));
try std.testing.expect(@backingInt(next.operation.?.id) > @backingInt(old.operation.?.id));
try std.testing.expect(next.revision > retired.revision);
app.worker.settleCompactionActivity(old.operation.?.id, .{ .outcome = .failed });
try std.testing.expect(!app.worker.dismissCompactionActivity(old.operation.?.id, terminal.revision));
@@ -6703,7 +6713,7 @@ const TestApp = struct {
);
}
fn snapshotMcpToolNames(self: *TestApp, alloc: Allocator) ![][]u8 {
pub fn snapshotMcpToolNames(self: *TestApp, alloc: Allocator) ![][]u8 {
const names = try alloc.alloc([]u8, self.mcp_tool_names.items.len);
var initialized: usize = 0;
errdefer {
@@ -6717,7 +6727,7 @@ const TestApp = struct {
return names;
}
fn terminalTitle(self: *TestApp) host_capability.TerminalTitle {
pub fn terminalTitle(self: *TestApp) host_capability.TerminalTitle {
return .{
.context = self,
.set_fn = setTerminalTitleLabelForTest,
@@ -6784,7 +6794,7 @@ const TestApp = struct {
self.* = undefined;
}
fn writeDomainNotice(self: *TestApp, notice: types.SemanticNotice, _: bool) !void {
pub fn writeDomainNotice(self: *TestApp, notice: types.SemanticNotice, _: bool) !void {
if (self.fail_system_notice) {
self.writable_seen_during_notice_failure =
self.session_persistence.writable != null;
@@ -6858,7 +6868,7 @@ const TestApp = struct {
try self.replay_events.append(self.alloc, .completed_tool_status);
}
fn writeCompletedToolStatusReturningEntryId(
pub fn writeCompletedToolStatusReturningEntryId(
self: *TestApp,
kind: types.ToolOutcomeKind,
text: []const u8,
@@ -6868,7 +6878,7 @@ const TestApp = struct {
return self.allocateTranscriptEntryId();
}
fn writeHistoricalQuestionResolution(
pub fn writeHistoricalQuestionResolution(
self: *TestApp,
answers: []const types.QuestionAnswer,
) !u32 {
@@ -6878,7 +6888,7 @@ const TestApp = struct {
return entry_id;
}
fn attachHistoricalToolDetail(
pub fn attachHistoricalToolDetail(
self: *TestApp,
entry_id: u32,
_: types.ToolCall,
@@ -6887,7 +6897,7 @@ const TestApp = struct {
try self.historical_tool_detail_entry_ids.append(self.alloc, entry_id);
}
fn attachHistoricalCancelledCommandDetail(
pub fn attachHistoricalCancelledCommandDetail(
self: *TestApp,
entry_id: u32,
_: types.ToolCall,
@@ -7769,7 +7779,7 @@ test "resume handoff requires no derived cache write" {
const session_dir = (try Runtime(TestApp).activeSessionDisplayPath(&app, alloc)) orelse
return error.TestExpectedSessionDirectory;
defer alloc.free(session_dir);
const session_dir_z = try alloc.dupeZ(u8, session_dir);
const session_dir_z = try alloc.dupeSentinel(u8, session_dir, 0);
defer alloc.free(session_dir_z);
if (std.c.chmod(session_dir_z.ptr, 0o500) != 0) return error.TestChmodFailed;
defer _ = std.c.chmod(session_dir_z.ptr, 0o700);
@@ -7782,7 +7792,7 @@ test "resume handoff requires no derived cache write" {
}
test "resume handoff suppresses session id allocation failure" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const paths = try testPaths(alloc, &tmp);
@@ -8198,7 +8208,7 @@ test "resume projection stores reflow metadata for session action rows" {
.labels = &labels,
};
const command = "bun run " ++ ("pipeline-stage-" ** 10);
const command = "bun run " ++ text_utils.repeat("pipeline-stage-", 10);
const run_output = "{\"session_id\":\"shell-4\",\"state\":\"running\",\"backend\":\"captured\"}";
var run_calls = [_]types.ToolCall{.{
.id = "call_run",
@@ -8292,7 +8302,7 @@ test "resume projection restores session action rows with labels seeded from the
.labels = &labels,
};
const command = "cd /workspace/packages/cli && " ++ ("printf relative-path " ** 6);
const command = "cd /workspace/packages/cli && " ++ text_utils.repeat("printf relative-path ", 6);
const run_output = "{\"session_id\":\"shell-4\",\"state\":\"running\",\"backend\":\"captured\"}";
var run_calls = [_]types.ToolCall{.{
.id = "call_run",
@@ -8369,7 +8379,7 @@ test "resume projection stores reflow metadata after the session moved workspace
.labels = &labels,
};
const command = "cd /workspace/packages/cli && " ++ ("printf relative-path " ** 6);
const command = "cd /workspace/packages/cli && " ++ text_utils.repeat("printf relative-path ", 6);
var run_calls = [_]types.ToolCall{.{
.id = "call_run",
.name = "shell",
@@ -8684,7 +8694,7 @@ test "resume falls back to saved command output when replay contains an empty fr
"exit_code=0\n<stdout>\nFALLBACK_STDOUT_MARKER\n</stdout>\n" ++
"<stderr>\n</stderr>\n";
const replay_handle = "fx-command-replay-empty-frame.bin";
var empty_replay = [_]u8{0} ** ("FXRPLY01".len + 9);
var empty_replay: ["FXRPLY01".len + 9]u8 = @splat(0);
@memcpy(empty_replay[0.."FXRPLY01".len], "FXRPLY01");
var calls = [_]types.ToolCall{.{
@@ -9761,7 +9771,7 @@ test "subagent host publication requires successful registry recovery" {
return error.HostAuthorityUnavailable;
}
};
try std.testing.checkAllAllocationFailures(alloc, AllocationCheck.run, .{ &app.session_persistence.store.?, parent_id });
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, AllocationCheck.run, .{ &app.session_persistence.store.?, parent_id });
}
test "interactive session resume uses the live transition and shared restore path" {
@@ -10229,7 +10239,7 @@ test "cancelled command presentation survives a persisted session restart" {
}
test "cancelled command metadata allocation failure preserves core presentation" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var failing = std.testing.FailingAllocator.init(alloc, .{});
const app_alloc = failing.allocator();
var app = try TestApp.init(app_alloc, "/workspace");
@@ -10567,7 +10577,7 @@ test "cancelled command replay rejects unsafe handles without partial output" {
}
test "authoritative cancelled replay survives history insertion failure" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const paths = try testPaths(alloc, &tmp);
@@ -10957,7 +10967,7 @@ test "appendFinishedPrompt transfers snapshot ownership after history acceptance
}
test "appendFinishedPrompt does not transfer snapshot ownership when history insertion fails" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var app = try TestApp.init(alloc, "/tmp/workspace");
defer app.deinit();
var probe = SnapshotOwnershipProbe{};
@@ -11842,7 +11852,7 @@ test "session picker clears selection feedback when the target context changes"
}
test "session picker summary append releases ownership when cloning fails" {
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var summary = session_store.SessionSummary{
.id = try alloc.dupe(u8, "allocation-failure"),
.created_at_ms = 1,
@@ -12191,7 +12201,7 @@ test "validateSessionTitle accepts printable text and rejects malformed input" {
try std.testing.expectError(error.InvalidTitle, V("bad\ttitle"));
try std.testing.expectError(error.InvalidTitle, V("bad\x07title"));
const long = "x" ** (session_display_metadata.max_title_bytes + 1);
const long = text_utils.repeat("x", session_display_metadata.max_title_bytes + 1);
try std.testing.expectError(error.TitleTooLong, V(long));
}
@@ -12288,7 +12298,7 @@ const ReconciliationOriginUsage = struct {
replaced_provider: ?model_provider.ProviderId = null,
replaced_source: ?types.CredentialSource = null,
fn replaceProviderReconciliationCredential(
pub fn replaceProviderReconciliationCredential(
self: *@This(),
_: Allocator,
provider: model_provider.ProviderId,
@@ -12304,7 +12314,7 @@ const ReconciliationOriginUsage = struct {
const ReconciliationOriginAuth = struct {
source: types.CredentialSource,
fn credentialSource(self: *const @This()) ?types.CredentialSource {
pub fn credentialSource(self: *const @This()) ?types.CredentialSource {
return self.source;
}
@@ -12312,7 +12322,7 @@ const ReconciliationOriginAuth = struct {
return "origin-bound-token";
}
fn accountId(_: *const @This()) ?[]const u8 {
pub fn accountId(_: *const @This()) ?[]const u8 {
return null;
}
@@ -12427,7 +12437,7 @@ const TitleGenerationFakeApp = struct {
return .{ .api_key = "test-key", .gateway_team = null, .source = .ai_gateway_api_key };
}
fn accountId(_: *const TitleTestAuth) ?[]const u8 {
pub fn accountId(_: *const TitleTestAuth) ?[]const u8 {
return null;
}
};
@@ -12447,11 +12457,11 @@ const TitleGenerationFakeApp = struct {
self.alloc.free(self.workspace_root);
}
fn agentStreamProvider(self: *TitleGenerationFakeApp) @import("../agent/stream_provider.zig").Provider {
pub fn agentStreamProvider(self: *TitleGenerationFakeApp) @import("../agent/stream_provider.zig").Provider {
return .{ .context = self, .stream_fn = titleStream };
}
fn sessionTitleModel(_: *TitleGenerationFakeApp) ?[]const u8 {
pub fn sessionTitleModel(_: *TitleGenerationFakeApp) ?[]const u8 {
return "test/title-model";
}
@@ -12774,10 +12784,10 @@ test "terminal title shows the session title once cached and keeps model context
var app = try TestApp.init(alloc, paths.workspace);
defer app.deinit();
try app.selected_model.appendSlice(alloc, "provider/" ++ ("model" ** 20));
try Runtime(TestApp).setCachedSessionTitle(&app, "session-" ++ ("title" ** 20));
try app.selected_model.appendSlice(alloc, "provider/" ++ text_utils.repeat("model", 20));
try Runtime(TestApp).setCachedSessionTitle(&app, "session-" ++ text_utils.repeat("title", 20));
const expected = "session-" ++ ("title" ** 20);
const expected = "session-" ++ text_utils.repeat("title", 20);
try std.testing.expectEqualStrings(expected, app.terminalTitleLabelText());
try std.testing.expect(std.mem.find(u8, app.terminalTitleLabelText(), "model") == null);
}
+2 -2
View File
@@ -185,7 +185,7 @@ const TestUpgrader = struct {
self.stop_count += 1;
}
fn takeRenderDirty(self: *TestUpgrader) bool {
pub fn takeRenderDirty(self: *TestUpgrader) bool {
const dirty = self.render_dirty;
self.render_dirty = false;
return dirty;
@@ -196,7 +196,7 @@ const TestWorker = struct {
queued_count: usize = 0,
shutdown_count: usize = 0,
fn queuedPromptCount(self: *const TestWorker) usize {
pub fn queuedPromptCount(self: *const TestWorker) usize {
return self.queued_count;
}
+16 -14
View File
@@ -460,12 +460,14 @@ pub fn Runtime(comptime App: type) type {
}
fn persistFinishedPrompt(app: *App, finished: types.FinishedPrompt) !void {
errdefer |err| app_session_runtime.Runtime(App).recordFailedHistoryDelivery(app, err);
if (comptime @hasField(App, "session")) {
try app_session_runtime.Runtime(App).appendFinishedPrompt(app, finished);
} else {
try app.appendFinishedPrompt(finished);
}
const appended = if (comptime @hasField(App, "session"))
app_session_runtime.Runtime(App).appendFinishedPrompt(app, finished)
else
app.appendFinishedPrompt(finished);
appended catch |err| {
app_session_runtime.Runtime(App).recordFailedHistoryDelivery(app, err);
return err;
};
}
fn settleDeferredFinish(app: *App) !void {
@@ -1492,12 +1494,12 @@ const FakeWorker = struct {
reset_cancel_after_take_events: bool = false,
admission_snapshot: worker_runtime.InteractiveAdmissionSnapshot = .open,
fn compactionActivitySnapshot(self: *FakeWorker) @import("../output/compaction_activity.zig").Snapshot {
pub fn compactionActivitySnapshot(self: *FakeWorker) @import("../output/compaction_activity.zig").Snapshot {
self.compaction_reads += 1;
return self.compaction.snapshot;
}
fn expireCompactionActivity(self: *FakeWorker, id: @import("../output/compaction_activity.zig").OperationId, revision: u64, now_ms: i64) bool {
pub fn expireCompactionActivity(self: *FakeWorker, id: @import("../output/compaction_activity.zig").OperationId, revision: u64, now_ms: i64) bool {
return self.compaction.expire(id, revision, now_ms);
}
@@ -1572,7 +1574,7 @@ const FakeWorker = struct {
};
}
fn interactiveAdmissionSnapshot(self: *FakeWorker) worker_runtime.InteractiveAdmissionSnapshot {
pub fn interactiveAdmissionSnapshot(self: *FakeWorker) worker_runtime.InteractiveAdmissionSnapshot {
return self.admission_snapshot;
}
@@ -1605,14 +1607,14 @@ const FakeWorker = struct {
return self.active_turn_id;
}
fn propagateHistoryTurn(self: *FakeWorker, alloc: std.mem.Allocator, turn: types.HistoryTurn, max_history_turns: usize) !void {
pub fn propagateHistoryTurn(self: *FakeWorker, alloc: std.mem.Allocator, turn: types.HistoryTurn, max_history_turns: usize) !void {
_ = alloc;
_ = turn;
_ = max_history_turns;
self.propagated_history_turns += 1;
}
fn propagateGrant(self: *FakeWorker, alloc: std.mem.Allocator, tool_name: []const u8, target_path: []const u8) !void {
pub fn propagateGrant(self: *FakeWorker, alloc: std.mem.Allocator, tool_name: []const u8, target_path: []const u8) !void {
_ = alloc;
_ = tool_name;
_ = target_path;
@@ -1652,7 +1654,7 @@ const FakeApprovalPrompt = struct {
return changed;
}
fn syncReview(self: *FakeApprovalPrompt, review: ?*const diff_mod.FileReview) bool {
pub fn syncReview(self: *FakeApprovalPrompt, review: ?*const diff_mod.FileReview) bool {
const request = self.request orelse {
const changed = self.review != null;
self.review = null;
@@ -1849,7 +1851,7 @@ const FakeShell = struct {
self.command_output_display = .{};
}
fn openCommandOutputLifecycleId(self: *const FakeShell) ?types.ToolLifecycleId {
pub fn openCommandOutputLifecycleId(self: *const FakeShell) ?types.ToolLifecycleId {
return self.lifecycle.openCommandOutputLifecycleId();
}
};
@@ -1996,7 +1998,7 @@ const FakeApp = struct {
return false;
}
fn dispatchAttentionRequired(
pub fn dispatchAttentionRequired(
self: *FakeApp,
turn_id: u64,
kind: @import("../hooks/hooks.zig").AttentionKind,
+2 -1
View File
@@ -17,6 +17,7 @@ const session = @import("../session/session.zig");
const interaction_state = @import("../../ui/footer/interaction_state.zig");
const approval_prompt = @import("../permissions/approval_prompt.zig");
const render_request = @import("../../ui/render_request.zig");
const text_utils = @import("../shared/text_utils.zig");
const ToolPermissionDecision = types.ToolPermissionDecision;
@@ -489,7 +490,7 @@ test "approval wheel keeps scrolling a committed command review after review syn
defer app.deinit();
const request: permission_request.PermissionRequest = .{
.id = 42,
.label = "shell.run " ++ ("x" ** 2_400),
.label = "shell.run " ++ text_utils.repeat("x", 2_400),
};
try std.testing.expect(try app.approval_prompt.syncRequest(alloc, request));
try std.testing.expect(try approval_screen.needsScreen(
+19 -14
View File
@@ -195,6 +195,15 @@ pub fn PasteEditRuntime(comptime App: type) type {
return true;
}
/// Resets the paste after `owner` failed to take it and returns `err`.
fn failPaste(app: *App, owner: paste_framing.Owner, err: anytype) @TypeOf(err) {
app.input_runtime.paste.resetWithTrace(.{ .finalize_failed = .{
.owner = owner,
.error_name = @errorName(err),
} });
return err;
}
pub fn finishPaste(app: *App, max_input_len: usize) !void {
if (app.input_runtime.paste.overflow_bytes > 0) {
const attempted_bytes = app.input_runtime.paste.attemptedBytes();
@@ -259,15 +268,12 @@ pub fn PasteEditRuntime(comptime App: type) type {
.decision_prompt => app.input_runtime.paste.resetWithTrace(.decision_prompt_active),
.question_freeform => {
if (comptime @hasField(App, "question_prompt")) {
errdefer |err| app.input_runtime.paste.resetWithTrace(.{ .finalize_failed = .{
.owner = .question_freeform,
.error_name = @errorName(err),
} });
switch (try app.question_prompt.insertFreeformSlice(
const outcome = app.question_prompt.insertFreeformSlice(
app.alloc,
app.input_runtime.paste.buffer.items,
max_input_len,
)) {
) catch |err| return failPaste(app, .question_freeform, err);
switch (outcome) {
.inserted => {
app.input_runtime.input_limit_rejection = input_limit_rejection.clear();
app.input_runtime.paste.finishHandled();
@@ -277,7 +283,8 @@ pub fn PasteEditRuntime(comptime App: type) type {
.limit_exceeded => {
const attempted_bytes = app.input_runtime.paste.buffer.items.len;
app.input_runtime.paste.resetWithTrace(.{ .input_limit = .question_freeform });
try input_limit_feedback.report(App, app, .question_freeform, attempted_bytes);
input_limit_feedback.report(App, app, .question_freeform, attempted_bytes) catch |err|
return failPaste(app, .question_freeform, err);
},
}
} else {
@@ -298,15 +305,12 @@ pub fn PasteEditRuntime(comptime App: type) type {
},
.approval_amendment => {
if (comptime @hasField(App, "approval_prompt")) {
errdefer |err| app.input_runtime.paste.resetWithTrace(.{ .finalize_failed = .{
.owner = .approval_amendment,
.error_name = @errorName(err),
} });
switch (try app.approval_prompt.decision.insertAmendmentSlice(
const outcome = app.approval_prompt.decision.insertAmendmentSlice(
app.alloc,
app.input_runtime.paste.buffer.items,
max_input_len,
)) {
) catch |err| return failPaste(app, .approval_amendment, err);
switch (outcome) {
.inserted => app.input_runtime.input_limit_rejection = input_limit_rejection.clear(),
.inactive => {
app.input_runtime.paste.resetWithTrace(.session_reset);
@@ -315,7 +319,8 @@ pub fn PasteEditRuntime(comptime App: type) type {
.limit_exceeded => {
const attempted_bytes = app.input_runtime.paste.buffer.items.len;
app.input_runtime.paste.resetWithTrace(.{ .input_limit = .approval_amendment });
try input_limit_feedback.report(App, app, .approval_amendment, attempted_bytes);
input_limit_feedback.report(App, app, .approval_amendment, attempted_bytes) catch |err|
return failPaste(app, .approval_amendment, err);
return;
},
}
+2 -1
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const paste_blocks = @import("../input/pasted_blocks.zig");
const registered_entities = @import("../input/registered_entities.zig");
const image_attachments = @import("../images/image_attachments.zig");
@@ -2004,7 +2005,7 @@ fn checkPendingDraftConstructionAllocationFailure(alloc: std.mem.Allocator) !voi
test "pending draft construction frees every partial allocation" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
checkPendingDraftConstructionAllocationFailure,
.{},
);
+13 -12
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const credentials = @import("../auth/credentials.zig");
const secret = @import("../auth/secret.zig");
const collections = @import("../shared/collections.zig");
@@ -113,7 +114,7 @@ pub const ModelProviderFilter = enum {
others,
};
pub const model_provider_filter_count = std.meta.fields(ModelProviderFilter).len;
pub const model_provider_filter_count = @typeInfo(ModelProviderFilter).@"enum".field_names.len;
pub const ModelMenuItem = struct {
id: []u8,
@@ -154,7 +155,7 @@ pub const ModelMenu = struct {
}
pub fn providerFilter(self: *const ModelMenu) ModelProviderFilter {
return @enumFromInt(@min(self.provider_index, model_provider_filter_count - 1));
return @fromBackingInt(@intCast(@min(self.provider_index, model_provider_filter_count - 1)));
}
pub fn filteredItemCount(self: *const ModelMenu) usize {
@@ -195,7 +196,7 @@ pub const ModelMenu = struct {
next += direction;
if (next < 0) next = @as(i32, @intCast(filter_count)) - 1;
if (next >= @as(i32, @intCast(filter_count))) next = 0;
const filter: ModelProviderFilter = @enumFromInt(@as(usize, @intCast(next)));
const filter: ModelProviderFilter = @fromBackingInt(@intCast(next));
if (!modelProviderFilterAvailable(self.items.items, filter)) continue;
if (next == current) return false;
self.provider_index = @intCast(next);
@@ -276,12 +277,12 @@ fn providerFilter(provider: []const u8) ModelProviderFilter {
pub fn modelProviderFilterAvailable(items: []const ModelMenuItem, filter: ModelProviderFilter) bool {
if (filter == .all) return true;
var seen = [_]bool{false} ** model_provider_filter_count;
for (items) |item| seen[@intFromEnum(providerFilter(item.provider))] = true;
var seen: [model_provider_filter_count]bool = @splat(false);
for (items) |item| seen[@backingInt(providerFilter(item.provider))] = true;
var specific_count: usize = 0;
for (seen[1..]) |available| specific_count += @intFromBool(available);
return specific_count > 1 and seen[@intFromEnum(filter)];
return specific_count > 1 and seen[@backingInt(filter)];
}
fn providerMatchesFilter(provider: []const u8, filter: ModelProviderFilter) bool {
@@ -760,7 +761,7 @@ pub const Runtime = struct {
self.mutex.unlock(io_mod.getIo());
diagnostics.recordModelCatalogEvent(true, .load, "outcome=failed category={s} status={d} retryable={s} anonymous_fallback={s} kept_previous_catalog={s}", .{
@tagName(failure.failure.category),
if (failure.failure.http_status) |status| @intFromEnum(status) else 0,
if (failure.failure.http_status) |status| @backingInt(status) else 0,
boolLabel(failure.failure.retryable),
boolLabel(failure.anonymous_fallback_used),
boolLabel(kept_previous),
@@ -1301,7 +1302,7 @@ test "model cache access copies clean up every induced allocation failure" {
const access = authenticatedCatalogAccess("copied-secret", "copied-team");
for (0..2) |fail_index| {
var failing = std.testing.FailingAllocator.init(
std.testing.allocator,
testing_allocator.no_resize,
.{ .fail_index = fail_index },
);
@@ -1323,7 +1324,7 @@ test "model cache access owns Grok account identity with its credential" {
);
for (0..2) |fail_index| {
var failing = std.testing.FailingAllocator.init(
std.testing.allocator,
testing_allocator.no_resize,
.{ .fail_index = fail_index },
);
try std.testing.expectError(
@@ -1495,11 +1496,11 @@ test "model menu owns resolved catalog state and filters without changing catalo
try std.testing.expect(runtime.menu.moveProvider(-1));
try std.testing.expectEqual(ModelProviderFilter.all, runtime.menu.providerFilter());
runtime.menu.provider_index = @intFromEnum(ModelProviderFilter.others);
runtime.menu.provider_index = @backingInt(ModelProviderFilter.others);
try std.testing.expectEqual(@as(usize, 2), runtime.menu.filteredItemCount());
try std.testing.expectEqualStrings("private/blue-hornbill", runtime.menu.itemAt(0).?.id);
try std.testing.expectEqualStrings("standalone", runtime.menu.itemAt(1).?.id);
runtime.menu.provider_index = @intFromEnum(ModelProviderFilter.all);
runtime.menu.provider_index = @backingInt(ModelProviderFilter.all);
try std.testing.expect(runtime.menu.moveVisibleItems(-1, 2));
try std.testing.expectEqual(@as(usize, 3), runtime.menu.selected_index);
@@ -1541,7 +1542,7 @@ test "model menu provider navigation skips absent and redundant filters" {
}
test "model menu snapshot construction cleans every allocation failure" {
const backing = std.testing.allocator;
const backing = testing_allocator.no_resize;
const entries = [_]model_catalog.ModelCatalogEntry{
.{ .id = @constCast("openai/gpt-5"), .model_type = @constCast("language") },
.{ .id = @constCast("anthropic/claude-opus-4.8"), .model_type = @constCast("language") },
+2 -2
View File
@@ -99,8 +99,8 @@ pub fn Runtime(comptime App: type) type {
// `current` always means "used for inference right now",
// so a key is only current while the gateway is active.
const active = if (active_provider == .gateway) app.auth.credentialSource() else null;
inline for (@typeInfo(provider_picker_catalog.KeySource).@"enum".fields) |field| {
const key_source = @field(provider_picker_catalog.KeySource, field.name);
inline for (@typeInfo(provider_picker_catalog.KeySource).@"enum".field_names) |field_name| {
const key_source = @field(provider_picker_catalog.KeySource, field_name);
const credential = provider_picker_catalog.keySourceCredential(key_source);
const detected = if (credential) |value| view.available_sources.contains(value) else true;
if (detected) {
+3 -2
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const io_mod = @import("../shared/io.zig");
const profile_usage_runtime = @import("../session/profile_usage_runtime.zig");
const usage_recovery = @import("../session/usage_recovery.zig");
@@ -79,7 +80,7 @@ pub const Runtime = struct {
/// a static release-binary template.
pub fn initInto(storage: *Self, alloc: Allocator) void {
comptime {
if (std.meta.fields(Self).len != 8) {
if (@typeInfo(Self).@"struct".field_names.len != 8) {
@compileError("update Runtime.initInto for the changed field set");
}
}
@@ -376,7 +377,7 @@ test "usage dashboard reports home-copy allocation failure without starting work
var context: u8 = 0;
var failing = std.testing.FailingAllocator.init(
std.testing.allocator,
testing_allocator.no_resize,
.{ .fail_index = 0 },
);
var runtime = Runtime.init(failing.allocator());
+13 -13
View File
@@ -153,7 +153,7 @@ pub const FailureSnapshot = struct {
},
});
if (self.http_status) |status| {
try out.writer.print(" · HTTP {d}", .{@intFromEnum(status)});
try out.writer.print(" · HTTP {d}", .{@backingInt(status)});
}
return try out.toOwnedSlice();
}
@@ -173,7 +173,7 @@ pub const FailureSnapshot = struct {
try writer.writeAll(",\"reason\":");
try std.json.Stringify.value(@tagName(self.reason), .{}, writer);
if (self.http_status) |status| {
try writer.print(",\"http_status\":{d}", .{@intFromEnum(status)});
try writer.print(",\"http_status\":{d}", .{@backingInt(status)});
}
try writer.writeByte('}');
}
@@ -197,7 +197,7 @@ fn stampNowMs() u32 {
}
fn noteRequestPathCredentialVerified(source: credentials.Source) void {
request_path_verified_source.store(@intFromEnum(source), .seq_cst);
request_path_verified_source.store(@backingInt(source), .seq_cst);
request_path_verified_ms.store(stampNowMs(), .seq_cst);
}
@@ -207,7 +207,7 @@ fn noteRequestPathCredentialVerified(source: credentials.Source) void {
pub fn requestPathCredentialVerifiedRecently(source: credentials.Source) bool {
const verified_ms = request_path_verified_ms.load(.seq_cst);
if (verified_ms == 0) return false;
if (request_path_verified_source.load(.seq_cst) != @intFromEnum(source)) return false;
if (request_path_verified_source.load(.seq_cst) != @backingInt(source)) return false;
return stampNowMs() -% verified_ms < request_path_verified_window_ms;
}
@@ -1799,7 +1799,7 @@ pub const Runtime = struct {
auth_mode: credentials.AuthMode,
) void {
comptime {
if (std.meta.fields(Self).len != 33) {
if (@typeInfo(Self).@"struct".field_names.len != 33) {
@compileError("update Runtime.initInto for the changed field set");
}
}
@@ -4902,7 +4902,7 @@ test "api key stage zeroes its allocation on every exit path" {
const sentinel = "FX_API_KEY_ZERO_SENTINEL";
{
var backing: [16384]u8 = [_]u8{0xa5} ** 16384;
var backing: [16384]u8 = @splat(0xa5);
var fixed = std.heap.FixedBufferAllocator.init(&backing);
const alloc = fixed.allocator();
var runtime: Runtime = .{};
@@ -4914,7 +4914,7 @@ test "api key stage zeroes its allocation on every exit path" {
}
{
var backing: [16384]u8 = [_]u8{0xa5} ** 16384;
var backing: [16384]u8 = @splat(0xa5);
var fixed = std.heap.FixedBufferAllocator.init(&backing);
const alloc = fixed.allocator();
var runtime: Runtime = .{};
@@ -4943,7 +4943,7 @@ test "api key stage zeroes its allocation on every exit path" {
}
{
var backing: [16384]u8 = [_]u8{0xa5} ** 16384;
var backing: [16384]u8 = @splat(0xa5);
var fixed = std.heap.FixedBufferAllocator.init(&backing);
const alloc = fixed.allocator();
var runtime: Runtime = .{};
@@ -4972,7 +4972,7 @@ test "api key stage zeroes its allocation on every exit path" {
}
{
var backing: [16384]u8 = [_]u8{0xa5} ** 16384;
var backing: [16384]u8 = @splat(0xa5);
var fixed = std.heap.FixedBufferAllocator.init(&backing);
const alloc = fixed.allocator();
var runtime: Runtime = .{};
@@ -5001,7 +5001,7 @@ test "api key stage zeroes its allocation on every exit path" {
}
{
var backing: [16384]u8 = [_]u8{0xa5} ** 16384;
var backing: [16384]u8 = @splat(0xa5);
var fixed = std.heap.FixedBufferAllocator.init(&backing);
const alloc = fixed.allocator();
var runtime: Runtime = .{};
@@ -5030,7 +5030,7 @@ test "api key stage zeroes its allocation on every exit path" {
}
{
var backing: [16384]u8 = [_]u8{0xa5} ** 16384;
var backing: [16384]u8 = @splat(0xa5);
var fixed = std.heap.FixedBufferAllocator.init(&backing);
const alloc = fixed.allocator();
var runtime: Runtime = .{};
@@ -5058,7 +5058,7 @@ test "api key stage zeroes its allocation on every exit path" {
}
{
var backing: [16384]u8 = [_]u8{0xa5} ** 16384;
var backing: [16384]u8 = @splat(0xa5);
var fixed = std.heap.FixedBufferAllocator.init(&backing);
const alloc = fixed.allocator();
var runtime: Runtime = .{};
@@ -5070,7 +5070,7 @@ test "api key stage zeroes its allocation on every exit path" {
}
{
var backing: [16384]u8 = [_]u8{0xa5} ** 16384;
var backing: [16384]u8 = @splat(0xa5);
var fixed = std.heap.FixedBufferAllocator.init(&backing);
const alloc = fixed.allocator();
var runtime: Runtime = .{};
+2 -1
View File
@@ -7,6 +7,7 @@ const profile_paths = @import("../shared/profile_paths.zig");
const types = @import("../shared/types.zig");
const secret = @import("secret.zig");
const session_presence = @import("session_presence.zig");
const text_utils = @import("../shared/text_utils.zig");
const Allocator = std.mem.Allocator;
const schema_version: i64 = 1;
@@ -288,7 +289,7 @@ test "Grok account identity is bounded and safe for HTTP headers" {
try std.testing.expect(validAccountId("acct_123"));
try std.testing.expect(!validAccountId(""));
try std.testing.expect(!validAccountId("acct\r\ninjected"));
try std.testing.expect(!validAccountId("a" ** 1025));
try std.testing.expect(!validAccountId(text_utils.repeat("a", 1025)));
const invalid =
\\{"version":1,"access_token":"access","refresh_token":"refresh","expires_at_ms":1234,"account_id":"acct\ninjected"}
+5 -4
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const builtin = @import("builtin");
const credentials = @import("credentials.zig");
const chatgpt_session = @import("chatgpt_session.zig");
@@ -1575,14 +1576,14 @@ fn checkBrowserPreparedAllocationFailures(alloc: Allocator) !void {
test "browser login preparation cleans up allocation failures" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
checkBrowserPreparedAllocationFailures,
.{},
);
}
test "team parsing cleans up allocation failures" {
try std.testing.checkAllAllocationFailures(std.testing.allocator, check_parse_teams_allocation_failures, .{});
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, check_parse_teams_allocation_failures, .{});
}
test "single team selection does not allocate" {
@@ -1594,7 +1595,7 @@ test "single team selection does not allocate" {
.slug = &slug,
.name = &name,
}};
var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{ .fail_index = 0 });
var failing = std.testing.FailingAllocator.init(testing_allocator.no_resize, .{ .fail_index = 0 });
try std.testing.expect((try selectTeam(failing.allocator(), &teams, null)) != null);
}
@@ -1665,7 +1666,7 @@ test "team validation failure prevents the durable selection commit" {
}
test "login session transfer cleans up allocation failures" {
try std.testing.checkAllAllocationFailures(std.testing.allocator, check_take_login_session_allocation_failures, .{});
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, check_take_login_session_allocation_failures, .{});
}
fn readLine(alloc: Allocator) ![]u8 {
+6 -5
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const browser_callback = @import("browser_callback.zig");
const io_mod = @import("../shared/io.zig");
const oauth_transport = @import("oauth_transport.zig");
@@ -713,7 +714,7 @@ fn checkQueryValueAllocationFailures(alloc: Allocator) !void {
test "OAuth query decoding cleans up allocation failures" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
checkQueryValueAllocationFailures,
.{},
);
@@ -737,7 +738,7 @@ fn checkBrowserTokenAllocationFailures(alloc: Allocator) !void {
test "browser token parsing and transfer clean up allocation failures" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
checkBrowserTokenAllocationFailures,
.{},
);
@@ -930,9 +931,9 @@ test "oauth parses token set" {
}
test "oauth parsers clean up allocation failures" {
try std.testing.checkAllAllocationFailures(std.testing.allocator, check_metadata_allocation_failures, .{});
try std.testing.checkAllAllocationFailures(std.testing.allocator, check_device_authorization_allocation_failures, .{});
try std.testing.checkAllAllocationFailures(std.testing.allocator, check_token_set_allocation_failures, .{});
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, check_metadata_allocation_failures, .{});
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, check_device_authorization_allocation_failures, .{});
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, check_token_set_allocation_failures, .{});
}
test "oauth expiry timestamps reject invalid durations" {
+3 -2
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const builtin = @import("builtin");
const debug_trace = @import("../shared/debug_trace.zig");
const host_contract = @import("../hosts/host.zig");
@@ -1373,7 +1374,7 @@ test "JS host OAuth session revision conflict does not take session ownership" {
}
test "oauth session parse cleans up allocation failures" {
try std.testing.checkAllAllocationFailures(std.testing.allocator, check_parse_allocation_failures, .{});
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, check_parse_allocation_failures, .{});
}
test "oauth session parse rejects non-object JSON" {
@@ -1394,7 +1395,7 @@ test "oauth session loading propagates allocation failures" {
file.close(std.testing.io);
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
check_load_allocation_failures,
.{&tmp.dir},
);
+4 -4
View File
@@ -81,8 +81,8 @@ pub fn keySourceAnnotation(source: KeySource, current: bool) []const u8 {
}
pub fn parseKeySource(value: []const u8) ?KeySource {
inline for (@typeInfo(KeySource).@"enum".fields) |field| {
const source = @field(KeySource, field.name);
inline for (@typeInfo(KeySource).@"enum".field_names) |field_name| {
const source = @field(KeySource, field_name);
if (std.ascii.eqlIgnoreCase(value, keySourceSlug(source))) return source;
}
return null;
@@ -190,8 +190,8 @@ test "key field shows a placeholder when empty and one bullet per byte" {
}
test "key source slugs round trip and map to their credentials" {
inline for (@typeInfo(KeySource).@"enum".fields) |field| {
const source = @field(KeySource, field.name);
inline for (@typeInfo(KeySource).@"enum".field_names) |field_name| {
const source = @field(KeySource, field_name);
try std.testing.expectEqual(source, parseKeySource(keySourceSlug(source)).?);
}
try std.testing.expectEqual(types.CredentialSource.ai_gateway_api_key, keySourceCredential(.env).?);
+22 -17
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const std_builtin = @import("builtin");
const command_admission = @import("../permissions/command_admission.zig");
const agent_runtime = @import("../agent/agent_runtime.zig");
@@ -138,7 +139,7 @@ const headless_interrupt = if (supports_headless_interrupt) struct {
fn handle(signal: std.posix.SIG) callconv(.c) void {
_ = requested_signal.cmpxchgStrong(
0,
@intCast(@intFromEnum(signal)),
@intCast(@backingInt(signal)),
.seq_cst,
.seq_cst,
);
@@ -146,7 +147,7 @@ const headless_interrupt = if (supports_headless_interrupt) struct {
}
fn exitCode() u8 {
return switch (@as(std.posix.SIG, @enumFromInt(requested_signal.load(.seq_cst)))) {
return switch (@as(std.posix.SIG, @fromBackingInt(@intCast(requested_signal.load(.seq_cst))))) {
std.posix.SIG.TERM => headless_termination_exit_code,
else => headless_interrupt_exit_code,
};
@@ -192,7 +193,7 @@ const headless_interrupt = if (supports_headless_interrupt) struct {
if (test_after_restore) |hook| hook();
}
if (redeliver and cancel_requested.load(.seq_cst)) {
const signal: std.posix.SIG = @enumFromInt(requested_signal.load(.seq_cst));
const signal: std.posix.SIG = @fromBackingInt(@intCast(requested_signal.load(.seq_cst)));
_ = std.c.raise(signal);
}
coordinator_active = false;
@@ -3394,9 +3395,13 @@ fn setRecoveryCheckpoint(
checkpoint: session_codec.RecoveryCheckpoint,
) !void {
const ctx: *AskContext = @ptrCast(@alignCast(raw_ctx));
errdefer |err| if (err == error.SessionPersistenceUncertain) {
ctx.prompt_snapshot_committed = true;
writeRecoveryCheckpoint(ctx, checkpoint) catch |err| {
if (err == error.SessionPersistenceUncertain) ctx.prompt_snapshot_committed = true;
return err;
};
}
fn writeRecoveryCheckpoint(ctx: *AskContext, checkpoint: session_codec.RecoveryCheckpoint) !void {
ctx.session_write_mutex.lockUncancelable(io_mod.getIo());
defer ctx.session_write_mutex.unlock(io_mod.getIo());
const writable = if (ctx.writable) |*value| value else return error.SessionPersistenceUnavailable;
@@ -6039,9 +6044,9 @@ test "runWithDeps reports a missing image before startup after cleaning prior at
}
const valid_path = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "valid.png");
defer alloc.free(valid_path);
const valid_path_z = try alloc.dupeZ(u8, valid_path);
const valid_path_z = try alloc.dupeSentinel(u8, valid_path, 0);
defer alloc.free(valid_path_z);
const missing_path_z = try alloc.dupeZ(u8, "/tmp/fx-ask-missing-image.png");
const missing_path_z = try alloc.dupeSentinel(u8, "/tmp/fx-ask-missing-image.png", 0);
defer alloc.free(missing_path_z);
var stdout_capture: TestCapture = .{};
@@ -6079,7 +6084,7 @@ test "runWithDeps reports unsupported images in JSON before startup" {
}
const invalid_path = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "not-image.txt");
defer alloc.free(invalid_path);
const invalid_path_z = try alloc.dupeZ(u8, invalid_path);
const invalid_path_z = try alloc.dupeSentinel(u8, invalid_path, 0);
defer alloc.free(invalid_path_z);
var stdout_capture: TestCapture = .{};
@@ -6118,7 +6123,7 @@ test "runWithDeps assigns image ids and owns the authorized catalog before proce
}
const image_path = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "valid.png");
defer alloc.free(image_path);
const image_path_z = try alloc.dupeZ(u8, image_path);
const image_path_z = try alloc.dupeSentinel(u8, image_path, 0);
defer alloc.free(image_path_z);
var stdout_capture: TestCapture = .{};
@@ -6229,7 +6234,7 @@ test "stdin prompt resource limit accepts exact bytes and rejects one over witho
test "stdin prompt reader propagates allocation failure" {
var failing = std.testing.FailingAllocator.init(
std.testing.allocator,
testing_allocator.no_resize,
.{ .fail_index = 0 },
);
var reader = std.Io.Reader.fixed("prompt");
@@ -7702,7 +7707,7 @@ test "runWithDeps retains full terminal projection with saved capability" {
test "exec-only terminal projection propagates view allocation failure" {
var failing = std.testing.FailingAllocator.init(
std.testing.allocator,
testing_allocator.no_resize,
.{ .fail_index = 0 },
);
try std.testing.expectError(
@@ -7889,7 +7894,7 @@ fn exerciseSavedAskSessionStoreAllocation(
}
test "saved ask allocation failures keep managed borrows owned" {
const backing = std.testing.allocator;
const backing = testing_allocator.no_resize;
var counting = std.testing.FailingAllocator.init(backing, .{});
try exerciseSavedAskSessionStoreAllocation(counting.allocator(), false);
@@ -7980,7 +7985,7 @@ test "saved ask classifies unsafe store failure by request mode" {
}
test "saved ask propagates store allocation failure" {
const setup_alloc = std.testing.allocator;
const setup_alloc = testing_allocator.no_resize;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
try tmp.dir.createDirPath(io_mod.getIo(), "home");
@@ -9077,7 +9082,7 @@ test "shell diagnostic codes remain bounded semantic identifiers" {
test "fx ask JSON permission-denied capture is best effort under allocation failure" {
var failing = std.testing.FailingAllocator.init(
std.testing.allocator,
testing_allocator.no_resize,
.{ .fail_index = 0 },
);
var ctx = AskContext.init(
@@ -9242,7 +9247,7 @@ fn checkAskJsonCaptureAllocationFailures(alloc: Allocator) !void {
test "fx ask JSON capture cleans up every allocation failure" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
testing_allocator.no_resize,
checkAskJsonCaptureAllocationFailures,
.{},
);
@@ -9376,7 +9381,7 @@ test "resumed ask preserves user and image identity after a retained mid-turn ch
.path = @constCast("/missing/original.png"),
.media_type = @constCast("image/png"),
.snapshot_path = @constCast("images/image-7-aaaaaaaaaaaaaaaa.bin"),
.snapshot_sha256 = @constCast("a" ** 64),
.snapshot_sha256 = @constCast(text_utils.repeat("a", 64)),
}};
const old_user = types.UserTurn{
.text = @constCast("original request"),
@@ -9578,7 +9583,7 @@ test "default fx ask passes canonical image paths as initial context targets" {
}
const image_path = try io_mod.dirRealpathAlloc(alloc, tmp.dir, "target.png");
defer alloc.free(image_path);
const image_path_z = try alloc.dupeZ(u8, image_path);
const image_path_z = try alloc.dupeSentinel(u8, image_path, 0);
defer alloc.free(image_path_z);
var stdout_capture: TestCapture = .{};
+19 -18
View File
@@ -228,9 +228,10 @@ fn runWithOutput(alloc: Allocator, args: []const [:0]const u8, output: anytype)
}
fn frameKindName(kind: record_tape.Kind) []const u8 {
const raw = @intFromEnum(kind);
inline for (std.meta.fields(record_tape.Kind)) |field| {
if (raw == field.value) return field.name;
const raw = @backingInt(kind);
const kind_info = @typeInfo(record_tape.Kind).@"enum";
inline for (kind_info.field_names, kind_info.field_values) |field_name, field_value| {
if (raw == field_value) return field_name;
}
return "unknown";
}
@@ -501,7 +502,7 @@ fn buildTape(alloc: Allocator, cols: u16, rows: u16, version: []const u8, frames
for (frames) |frame| {
var frame_header: [9]u8 = undefined;
std.mem.writeInt(i32, frame_header[0..4], frame.delta_ms, .little);
frame_header[4] = @intFromEnum(frame.kind);
frame_header[4] = @backingInt(frame.kind);
std.mem.writeInt(u32, frame_header[5..9], @intCast(frame.payload.len), .little);
try out.writer.writeAll(&frame_header);
try out.writer.writeAll(frame.payload);
@@ -524,7 +525,7 @@ fn resizePayload(cols: u16, rows: u16) [4]u8 {
}
fn unknownKind(raw: u8) record_tape.Kind {
return @enumFromInt(raw);
return @fromBackingInt(@intCast(raw));
}
fn writeTestFile(path: []const u8, content: []const u8) !void {
@@ -588,7 +589,7 @@ test "minimal stdout tape replays to final grid snapshot" {
});
defer alloc.free(tape);
try writeTestFile(tape_path, tape);
const tape_arg = try alloc.dupeZ(u8, tape_path);
const tape_arg = try alloc.dupeSentinel(u8, tape_path, 0);
defer alloc.free(tape_arg);
var capture = CaptureOutput.init(alloc);
@@ -614,7 +615,7 @@ test "frames mode prints non-marker frame snapshots only" {
});
defer alloc.free(tape);
try writeTestFile(tape_path, tape);
const tape_arg = try alloc.dupeZ(u8, tape_path);
const tape_arg = try alloc.dupeSentinel(u8, tape_path, 0);
defer alloc.free(tape_arg);
var capture = CaptureOutput.init(alloc);
@@ -642,7 +643,7 @@ test "json summary reports frame resize and stdout metadata" {
});
defer alloc.free(tape);
try writeTestFile(tape_path, tape);
const tape_arg = try alloc.dupeZ(u8, tape_path);
const tape_arg = try alloc.dupeSentinel(u8, tape_path, 0);
defer alloc.free(tape_arg);
var capture = CaptureOutput.init(alloc);
@@ -669,7 +670,7 @@ test "json output escapes metadata strings" {
});
defer alloc.free(tape);
try writeTestFile(tape_path, tape);
const tape_arg = try alloc.dupeZ(u8, tape_path);
const tape_arg = try alloc.dupeSentinel(u8, tape_path, 0);
defer alloc.free(tape_arg);
var capture = CaptureOutput.init(alloc);
@@ -695,7 +696,7 @@ test "json output includes unknown frame metadata without altering grid" {
});
defer alloc.free(tape);
try writeTestFile(tape_path, tape);
const tape_arg = try alloc.dupeZ(u8, tape_path);
const tape_arg = try alloc.dupeSentinel(u8, tape_path, 0);
defer alloc.free(tape_arg);
var capture = CaptureOutput.init(alloc);
@@ -730,7 +731,7 @@ test "json replay recovers an incomplete final frame through stderr" {
try truncated.appendSlice(alloc, &.{ 1, 2, 3 });
try writeTestFile(tape_path, truncated.items);
const tape_arg = try alloc.dupeZ(u8, tape_path);
const tape_arg = try alloc.dupeSentinel(u8, tape_path, 0);
defer alloc.free(tape_arg);
var capture = CaptureOutput.init(alloc);
defer capture.deinit();
@@ -756,7 +757,7 @@ test "frames mode prints unknown frame snapshots without trapping" {
});
defer alloc.free(tape);
try writeTestFile(tape_path, tape);
const tape_arg = try alloc.dupeZ(u8, tape_path);
const tape_arg = try alloc.dupeSentinel(u8, tape_path, 0);
defer alloc.free(tape_arg);
var capture = CaptureOutput.init(alloc);
@@ -782,9 +783,9 @@ test "golden path writes final snapshot and suppresses final stdout" {
});
defer alloc.free(tape);
try writeTestFile(tape_path, tape);
const tape_arg = try alloc.dupeZ(u8, tape_path);
const tape_arg = try alloc.dupeSentinel(u8, tape_path, 0);
defer alloc.free(tape_arg);
const golden_arg = try alloc.dupeZ(u8, golden_path);
const golden_arg = try alloc.dupeSentinel(u8, golden_path, 0);
defer alloc.free(golden_arg);
var capture = CaptureOutput.init(alloc);
@@ -817,9 +818,9 @@ test "frames-dir writes manifest and per-frame grid artifacts" {
});
defer alloc.free(tape);
try writeTestFile(tape_path, tape);
const tape_arg = try alloc.dupeZ(u8, tape_path);
const tape_arg = try alloc.dupeSentinel(u8, tape_path, 0);
defer alloc.free(tape_arg);
const frames_dir_arg = try alloc.dupeZ(u8, frames_dir);
const frames_dir_arg = try alloc.dupeSentinel(u8, frames_dir, 0);
defer alloc.free(frames_dir_arg);
var capture = CaptureOutput.init(alloc);
@@ -898,7 +899,7 @@ test "json failures use stdout for missing arguments files and malformed tapes"
const path = try testPath(alloc, tmp.dir, "bad-json.fxtape");
defer alloc.free(path);
try writeTestFile(path, "not a tape");
const path_arg: [:0]u8 = try alloc.dupeZ(u8, path);
const path_arg: [:0]u8 = try alloc.dupeSentinel(u8, path, 0);
defer alloc.free(path_arg);
var malformed = CaptureOutput.init(alloc);
defer malformed.deinit();
@@ -925,7 +926,7 @@ test "run malformed tape returns bad tape stderr" {
const tape_path = try testPath(alloc, tmp.dir, "malformed.fxtape");
defer alloc.free(tape_path);
try writeTestFile(tape_path, "not a tape");
const tape_arg = try alloc.dupeZ(u8, tape_path);
const tape_arg = try alloc.dupeSentinel(u8, tape_path, 0);
defer alloc.free(tape_arg);
var capture = CaptureOutput.init(alloc);
+1 -1
View File
@@ -704,7 +704,7 @@ fn pathExists(path: []const u8) bool {
fn accessPath(path: []const u8) !bool {
var path_buf: [std.fs.max_path_bytes]u8 = undefined;
const path_z = std.fmt.bufPrintZ(&path_buf, "{s}", .{path}) catch return false;
const path_z = std.fmt.bufPrintSentinel(&path_buf, "{s}", .{path}, 0) catch return false;
const rc = std.c.access(path_z.ptr, std.c.F_OK);
if (rc == 0) return true;
return switch (std.posix.errno(rc)) {
+2 -2
View File
@@ -860,7 +860,7 @@ test "conversation outranks tool output, and both a saved ledger, on equal score
}
test "excerpts of one long line never split a UTF-8 character" {
const line = ("é" ** 2000) ++ " needle " ++ ("ü" ** 2000);
const line = text_utils.repeat("é", 2000) ++ " needle " ++ text_utils.repeat("ü", 2000);
var arena_state = std.heap.ArenaAllocator.init(testing.allocator);
defer arena_state.deinit();
const arena = arena_state.allocator();
@@ -875,7 +875,7 @@ test "long shell results show the matching part of their one JSON line" {
var arena_state = std.heap.ArenaAllocator.init(testing.allocator);
defer arena_state.deinit();
const arena = arena_state.allocator();
const body = "{\"session_id\":null,\"output_delta\":\"" ++ ("noise row here\\n" ** 400) ++ "total sessions: 11,355\\n" ++ ("more noise\\n" ** 400) ++ "\"}";
const body = "{\"session_id\":null,\"output_delta\":\"" ++ text_utils.repeat("noise row here\\n", 400) ++ "total sessions: 11,355\\n" ++ text_utils.repeat("more noise\\n", 400) ++ "\"}";
const query = try prepareQuery(arena, &.{"total sessions"});
// A word right after an escaped newline is still that word.
var counts: [max_terms]u32 = @splat(0);
+14 -13
View File
@@ -24,6 +24,7 @@
//! the store, so every compaction and every test runs this same code.
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const checkpoint = @import("checkpoint.zig");
const ledger = @import("ledger.zig");
const lint = @import("lint.zig");
@@ -1555,7 +1556,7 @@ test "a follow-up after the conversation lists only the skipped turns, findable"
}
test "the conversation serves only a request for every turn" {
const output = "x" ** 4000;
const output = text_utils.repeat("x", 4000);
const turns = [_]Turn{ workedTurn("first", "call-1", output), workedTurn("second", "call-2", output) };
var model = FakeModel{ .replies = &.{ "Turn 1\nIn between: Ran the first build.", "Turn 2\nIn between: Ran the second build." } };
defer model.deinit();
@@ -1618,7 +1619,7 @@ test "the tool line says what code knows: the call, how it ended and its size" {
try testing.expectEqualStrings("read_file src/a.zig (3 lines)", try codeLine(arena, .{ .number = 2, .name = "read_file", .call = .{ .id = "r", .name = "read_file", .arguments = "{\"path\":\"src/a.zig\"}" }, .result = .{ .call_id = "r", .name = "read_file", .output = "a\nb\nc" } }));
try testing.expectEqualStrings("shell zig build test (no result)", try codeLine(arena, .{ .number = 3, .name = "shell", .call = call }));
// Long arguments are cut; the record keeps them whole.
const long = try codeLine(arena, .{ .number = 4, .name = "write_file", .call = .{ .id = "w", .name = "write_file", .arguments = "{\"content\":\"" ++ ("é" ** 200) ++ "\"}" } });
const long = try codeLine(arena, .{ .number = 4, .name = "write_file", .call = .{ .id = "w", .name = "write_file", .arguments = "{\"content\":\"" ++ text_utils.repeat("é", 200) ++ "\"}" } });
try testing.expect(std.mem.endsWith(u8, long, "… (no result)"));
try testing.expect(std.unicode.utf8ValidateSlice(long));
try testing.expectEqual(@as(?i64, -1), exitCode("{\"exit_code\":-1}"));
@@ -2005,8 +2006,8 @@ test "every turn stays, each long user message and final reply whole" {
defer model.deinit();
var store = MemoryStore{ .alloc = testing.allocator };
defer store.deinit();
const pasted = "PASTE_START " ++ ("log line " ** 2000) ++ "PASTE_END";
const plan = "PLAN " ** 400;
const pasted = "PASTE_START " ++ text_utils.repeat("log line ", 2000) ++ "PASTE_END";
const plan = text_utils.repeat("PLAN ", 400);
var turns: [12]Turn = undefined;
const items = [_]Item{
.{ .tool_call = .{ .id = "c", .name = "shell", .arguments = "{\"command\":\"make\"}" } },
@@ -2035,7 +2036,7 @@ test "only a text over its room clips its longest messages, whole in their saved
defer model.deinit();
var store = MemoryStore{ .alloc = testing.allocator };
defer store.deinit();
const long_reply = "REPLY_START " ++ ("detail " ** 3000) ++ "REPLY_END";
const long_reply = "REPLY_START " ++ text_utils.repeat("detail ", 3000) ++ "REPLY_END";
const turns = [_]Turn{
.{ .user = "write the plan", .items = &.{.{ .assistant = long_reply }} },
.{ .user = "thanks", .items = &.{.{ .assistant = "Sure." }} },
@@ -2294,7 +2295,7 @@ test "the index line is built the same way for any tool" {
try testing.expectEqualStrings("not json at all", try indexLine(arena, "not json\nat all"));
// Long arguments are cut to one short line without splitting a character.
const long = try indexLine(arena, "{\"content\":\"" ++ ("é" ** 300) ++ "\"}");
const long = try indexLine(arena, "{\"content\":\"" ++ text_utils.repeat("é", 300) ++ "\"}");
try testing.expect(long.len <= max_index_bytes);
try testing.expect(std.unicode.utf8ValidateSlice(long));
}
@@ -2380,7 +2381,7 @@ test "compaction survives every allocation failure" {
}
}
};
try testing.checkAllAllocationFailures(testing.allocator, Run.run, .{});
try testing.checkAllAllocationFailures(testing_allocator.no_resize, Run.run, .{});
}
test "without a store nothing is saved and the notes keep tool details" {
@@ -2408,7 +2409,7 @@ test "turns too large for one request go oldest first, each part adding to the o
defer model.deinit();
var store = MemoryStore{ .alloc = testing.allocator };
defer store.deinit();
const output = "build output line " ** 400;
const output = text_utils.repeat("build output line ", 400);
const turns = [_]Turn{ workedTurn("first", "call-1", output), workedTurn("second", "call-2", output), workedTurn("third", "call-3", output) };
// Room for one turn per request.
const one = turnTokens(turns[0]);
@@ -2452,7 +2453,7 @@ test "a turn too large for one request keeps the start and end of its long texts
defer model.deinit();
var store = MemoryStore{ .alloc = testing.allocator };
defer store.deinit();
const output = "START_OF_OUTPUT " ++ ("filler " ** 20_000) ++ "END_OF_OUTPUT";
const output = "START_OF_OUTPUT " ++ text_utils.repeat("filler ", 20_000) ++ "END_OF_OUTPUT";
const turns = [_]Turn{workedTurn("Run the build.", "call-1", output)};
const room = 4000;
var result = try compact(testing.allocator, .{ .model = "m", .turns = &turns, .max_prompt_tokens = room }, model.model(), store.store());
@@ -2490,7 +2491,7 @@ test "a request fits even when a turn has many texts too short to clip" {
var arena_state: std.heap.ArenaAllocator = .init(testing.allocator);
defer arena_state.deinit();
const arena = arena_state.allocator();
const output = "one short line of build output " ** 48;
const output = text_utils.repeat("one short line of build output ", 48);
var items: std.ArrayList(Item) = .empty;
for (0..300) |index| {
const id = try std.fmt.allocPrint(arena, "call-{d}", .{index});
@@ -2516,9 +2517,9 @@ test "a request fits even when a turn has many texts too short to clip" {
}
test "clipping the turns keeps the earlier summary whole when that is enough" {
const previous = "EARLIER_START " ++ ("older work " ** 1500) ++ "EARLIER_END";
const previous = "EARLIER_START " ++ text_utils.repeat("older work ", 1500) ++ "EARLIER_END";
const earlier: Compacted = .{ .earlier = previous, .turn_count = 2, .tool_count = 1, .saved = true };
const turns = [_]Turn{workedTurn("Run it again.", "call-1", "build output line " ** 3000)};
const turns = [_]Turn{workedTurn("Run it again.", "call-1", text_utils.repeat("build output line ", 3000))};
var model = FakeModel{};
defer model.deinit();
var store = MemoryStore{ .alloc = testing.allocator };
@@ -2535,7 +2536,7 @@ test "clipping the turns keeps the earlier summary whole when that is enough" {
}
test "the earlier summary is clipped only when the turns alone cannot make the request fit" {
const previous = "EARLIER_START " ++ ("older work " ** 8000) ++ "EARLIER_END";
const previous = "EARLIER_START " ++ text_utils.repeat("older work ", 8000) ++ "EARLIER_END";
const earlier: Compacted = .{ .earlier = previous, .turn_count = 2, .tool_count = 1, .saved = true };
const turns = [_]Turn{workedTurn("Run it again.", "call-1", "ok")};
var model = FakeModel{};
+2 -2
View File
@@ -75,7 +75,7 @@ pub const Event = struct {
kind: Kind = .log,
detail_len: u16 = 0,
truncated: bool = false,
detail_buf: [max_detail_bytes]u8 = [_]u8{0} ** max_detail_bytes,
detail_buf: [max_detail_bytes]u8 = @splat(0),
pub fn name(self: *const Event) []const u8 {
return @tagName(self.kind);
@@ -175,7 +175,7 @@ test "compaction diagnostics stay bounded and reset without file tracing" {
reset();
defer reset();
record(.decision, 7, 3, 0, false, "automatic_threshold tokens={d}/{d}", .{ 279466, 280000 });
const oversized = [_]u8{'x'} ** (max_detail_bytes + 10);
const oversized: [max_detail_bytes + 10]u8 = @splat('x');
record(.retention_exhausted, 7, 4, 0, true, "{s}", .{oversized});
var events: [2]Event = undefined;
try std.testing.expectEqual(@as(usize, 2), snapshot(&events));
+6 -5
View File
@@ -13,6 +13,7 @@ const model_provider = @import("../config/model_provider.zig");
const trace = @import("trace.zig");
const debug_trace = @import("../shared/debug_trace.zig");
const types = @import("../shared/types.zig");
const text_utils = @import("../shared/text_utils.zig");
const Allocator = std.mem.Allocator;
const HistoryTurn = types.HistoryTurn;
@@ -370,7 +371,7 @@ test "compactor input budget follows normal model capacity" {
}
test "retained context budgets provider replay on completed exchanges" {
const replay = types.ProviderReplay{ .source = .{ .provider = .gateway, .model = "fixture/model" }, .parts_json = "[{\"type\":\"reasoning\",\"text\":\"\",\"providerOptions\":{\"openai\":{\"reasoningEncryptedContent\":\"" ++ ("r" ** 80_000) ++ "\"}}}]" };
const replay = types.ProviderReplay{ .source = .{ .provider = .gateway, .model = "fixture/model" }, .parts_json = "[{\"type\":\"reasoning\",\"text\":\"\",\"providerOptions\":{\"openai\":{\"reasoningEncryptedContent\":\"" ++ text_utils.repeat("r", 80_000) ++ "\"}}}]" };
var steps = [_]types.ToolExecutionStep{
.{ .assistant = @constCast("one"), .provider_replay = replay },
.{ .assistant = @constCast("two"), .provider_replay = replay },
@@ -396,7 +397,7 @@ test "retained context budgets replay on standalone assistant replies" {
const turn = types.AssistantHistoryTurn{
.user = .{ .text = @constCast("continue") },
.assistant = @constCast("small reply"),
.provider_replay = .{ .source = .{ .provider = .gateway, .model = "fixture/model" }, .parts_json = "[{\"type\":\"reasoning\",\"text\":\"\",\"providerOptions\":{\"openai\":{\"reasoningEncryptedContent\":\"" ++ ("r" ** 80_000) ++ "\"}}}]" },
.provider_replay = .{ .source = .{ .provider = .gateway, .model = "fixture/model" }, .parts_json = "[{\"type\":\"reasoning\",\"text\":\"\",\"providerOptions\":{\"openai\":{\"reasoningEncryptedContent\":\"" ++ text_utils.repeat("r", 80_000) ++ "\"}}}]" },
};
const history = [_]HistoryTurn{ .{ .assistant = turn }, .{ .assistant = turn }, .{ .assistant = turn } };
const same_model: model_provider.ProviderSelection = .{ .provider = .gateway, .model = "fixture/model" };
@@ -404,7 +405,7 @@ test "retained context budgets replay on standalone assistant replies" {
}
test "retained context keeps or compacts a parallel tool exchange whole without shortening results" {
const body = "large output " ** 2000;
const body = text_utils.repeat("large output ", 2000);
const calls = [_]types.ToolCall{
.{ .id = "one", .name = "read_file", .arguments_json = "{}" },
.{ .id = "two", .name = "read_file", .arguments_json = "{}" },
@@ -508,7 +509,7 @@ test "the window keeps the newest turns and compacts the rest" {
test "the window ends an unfinished turn at its completed exchange" {
var arena_state = std.heap.ArenaAllocator.init(testing.allocator);
defer arena_state.deinit();
const calls = [_]types.ToolCall{.{ .id = "large-write", .name = "write_file", .arguments_json = "x" ** 32_000 }};
const calls = [_]types.ToolCall{.{ .id = "large-write", .name = "write_file", .arguments_json = text_utils.repeat("x", 32_000) }};
const results = [_]types.PersistedToolResult{.{
.tool_call_id = @constCast("large-write"),
.tool_name = @constCast("write_file"),
@@ -536,7 +537,7 @@ test "what stays after the cut includes the rest of an unfinished turn" {
var arena_state = std.heap.ArenaAllocator.init(testing.allocator);
defer arena_state.deinit();
const calls = [_]types.ToolCall{
.{ .id = "large-write", .name = "write_file", .arguments_json = "x" ** 32_000 },
.{ .id = "large-write", .name = "write_file", .arguments_json = text_utils.repeat("x", 32_000) },
.{ .id = "small-read", .name = "read_file", .arguments_json = "{\"path\":\"src/a.zig\"}" },
};
const results = [_]types.PersistedToolResult{
+11 -9
View File
@@ -1,4 +1,6 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const text_utils = @import("../shared/text_utils.zig");
const Allocator = std.mem.Allocator;
const max_providers = 32;
@@ -470,7 +472,7 @@ fn test_allocations(alloc: Allocator) !void {
}
test "configured provider allocation failures release partial registry and URLs" {
try std.testing.checkAllAllocationFailures(std.testing.allocator, test_allocations, .{});
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, test_allocations, .{});
}
const test_required_fields = "\"protocol\":\"openai-chat-completions\",\"base_url\":\"https://example.com/v1\",\"auth\":{\"type\":\"none\"}";
@@ -547,23 +549,23 @@ test "configured provider auth admits only explicit none or a portable environme
}
test "configured provider scalar bounds and minimum input budget" {
try validate_id("a" ** max_id_bytes);
try validate_id(text_utils.repeat("a", max_id_bytes));
try validate_id("Local_2-test");
try std.testing.expectError(error.LimitExceeded, validate_id("a" ** (max_id_bytes + 1)));
try validate_model_id("m" ** max_model_bytes);
try std.testing.expectError(error.LimitExceeded, validate_id(text_utils.repeat("a", max_id_bytes + 1)));
try validate_model_id(text_utils.repeat("m", max_model_bytes));
try validate_model_id("vendor/model:tag");
try validate_model_id("model with internal spaces");
try std.testing.expectError(error.InvalidModelId, validate_model_id(" leading"));
try std.testing.expectError(error.InvalidModelId, validate_model_id("trailing "));
try std.testing.expectError(error.LimitExceeded, validate_model_id("m" ** (max_model_bytes + 1)));
try std.testing.expectError(error.LimitExceeded, validate_model_id(text_utils.repeat("m", max_model_bytes + 1)));
try std.testing.expectError(error.InvalidModelId, validate_model_id("bad\xff"));
const prefix = "https://example.com/";
_ = try validate_url(prefix ++ "a" ** (max_url_bytes - prefix.len));
try std.testing.expectError(error.LimitExceeded, validate_url(prefix ++ "a" ** (max_url_bytes - prefix.len + 1)));
_ = try validate_url(prefix ++ text_utils.repeat("a", max_url_bytes - prefix.len));
try std.testing.expectError(error.LimitExceeded, validate_url(prefix ++ text_utils.repeat("a", max_url_bytes - prefix.len + 1)));
const alloc = std.testing.allocator;
for ([_]usize{ max_env_bytes, max_env_bytes + 1 }) |length| {
const env = "E" ** (max_env_bytes + 1);
const env = text_utils.repeat("E", max_env_bytes + 1);
const json = try std.fmt.allocPrint(alloc, "{{\"local\":{{\"protocol\":\"openai-chat-completions\",\"base_url\":\"https://example.com\",\"auth\":{{\"type\":\"bearer\",\"env\":\"{s}\"}}}}}}", .{env[0..length]});
defer alloc.free(json);
if (length == max_env_bytes) {
@@ -634,5 +636,5 @@ fn test_invalid_allocations(alloc: Allocator) !void {
}
test "configured provider validation failures release earlier definitions and models" {
try std.testing.checkAllAllocationFailures(std.testing.allocator, test_invalid_allocations, .{});
try std.testing.checkAllAllocationFailures(testing_allocator.no_resize, test_invalid_allocations, .{});
}
+13 -11
View File
@@ -32,8 +32,9 @@ pub const Name = enum {
}
pub fn parse(raw: []const u8) ?Name {
inline for (std.meta.fields(Name)) |field| {
if (std.mem.eql(u8, raw, field.name)) return @enumFromInt(field.value);
const name_info = @typeInfo(Name).@"enum";
inline for (name_info.field_names, name_info.field_values) |field_name, field_value| {
if (std.mem.eql(u8, raw, field_name)) return @fromBackingInt(@intCast(field_value));
}
return null;
}
@@ -105,9 +106,10 @@ pub const Values = struct {
}
pub fn apply(self: *Values, overrides: Overrides) void {
inline for (std.meta.fields(Name)) |field| {
const name: Name = @enumFromInt(field.value);
if (overrides.get(name)) |value| @field(self, field.name) = value;
const name_info = @typeInfo(Name).@"enum";
inline for (name_info.field_names, name_info.field_values) |field_name, field_value| {
const name: Name = @fromBackingInt(@intCast(field_value));
if (overrides.get(name)) |value| @field(self, field_name) = value;
}
}
@@ -150,14 +152,14 @@ pub const Overrides = struct {
}
pub fn retag(self: *Overrides, source: Source) void {
inline for (std.meta.fields(Name)) |field| {
if (@field(self, field.name)) |*value| value.source = source;
inline for (@typeInfo(Name).@"enum".field_names) |field_name| {
if (@field(self, field_name)) |*value| value.source = source;
}
}
pub fn merge(self: *Overrides, incoming: Overrides) void {
inline for (std.meta.fields(Name)) |field| {
if (@field(incoming, field.name)) |value| @field(self, field.name) = value;
inline for (@typeInfo(Name).@"enum".field_names) |field_name| {
if (@field(incoming, field_name)) |value| @field(self, field_name) = value;
}
}
};
@@ -223,8 +225,8 @@ pub fn lineSafePrefixLength(bytes: []const u8, max_bytes: usize) usize {
test "defaults match the public context limit contract" {
const values = Values{};
inline for (std.meta.fields(Name)) |field| {
const name: Name = @enumFromInt(field.value);
inline for (@typeInfo(Name).@"enum".field_values) |field_value| {
const name: Name = @fromBackingInt(@intCast(field_value));
try std.testing.expectEqual(name.defaultBytes(), values.get(name).effectiveBytes());
try std.testing.expectEqual(Source.compiled_default, values.get(name).source);
}
+7 -7
View File
@@ -20,7 +20,7 @@ pub const Category = enum {
}
};
pub const category_count = std.meta.fields(Category).len;
pub const category_count = @typeInfo(Category).@"enum".field_names.len;
pub const SettingId = enum {
statusline_context,
@@ -213,10 +213,10 @@ pub const Menu = struct {
pub fn cycleCategory(self: *Menu, delta: i32) bool {
if (!self.active) return false;
const count: i32 = @intCast(category_count);
var next: i32 = @intFromEnum(self.category) + delta;
var next: i32 = @backingInt(self.category) + delta;
while (next < 0) next += count;
while (next >= count) next -= count;
self.category = @enumFromInt(next);
self.category = @fromBackingInt(@intCast(next));
self.selected_index = 0;
self.window_start = 0;
return true;
@@ -389,10 +389,10 @@ fn matchesQuery(snapshot: Snapshot, spec: Spec, query: []const u8) bool {
}
fn tokenMatches(snapshot: Snapshot, spec: Spec, token: []const u8) bool {
return std.ascii.indexOfIgnoreCase(spec.label, token) != null or
std.ascii.indexOfIgnoreCase(spec.description, token) != null or
std.ascii.indexOfIgnoreCase(snapshot.value(spec.id), token) != null or
std.ascii.indexOfIgnoreCase(@tagName(spec.id), token) != null;
return std.ascii.findIgnoreCase(spec.label, token) != null or
std.ascii.findIgnoreCase(spec.description, token) != null or
std.ascii.findIgnoreCase(snapshot.value(spec.id), token) != null or
std.ascii.findIgnoreCase(@tagName(spec.id), token) != null;
}
fn onOff(value: bool) []const u8 {
+1 -1
View File
@@ -230,7 +230,7 @@ const UserPreferenceField = enum(u4) {
session_titles,
fn mask(self: UserPreferenceField) u16 {
return @as(u16, 1) << @intFromEnum(self);
return @as(u16, 1) << @backingInt(self);
}
fn snapshotName(self: UserPreferenceField) []const u8 {
+21 -22
View File
@@ -104,11 +104,11 @@ const ForegroundSessionTerminationRequest = enum(std.c.sig_atomic_t) {
force,
};
var foreground_session_termination_request: std.c.sig_atomic_t =
@intFromEnum(ForegroundSessionTerminationRequest.none);
@backingInt(ForegroundSessionTerminationRequest.none);
const foreground_session_replace_error_name_bytes = blk: {
var max_len: usize = 0;
for (std.meta.fields(std.process.ReplaceError)) |field| {
max_len = @max(max_len, field.name.len);
for (@typeInfo(std.process.ReplaceError).error_set.error_names.?) |error_name| {
max_len = @max(max_len, error_name.len);
}
break :blk max_len;
};
@@ -187,7 +187,7 @@ pub fn runForegroundSessionBootstrap(args: []const [:0]const u8) !void {
};
@as(*volatile std.c.sig_atomic_t, &foreground_session_termination_request).* =
@intFromEnum(ForegroundSessionTerminationRequest.none);
@backingInt(ForegroundSessionTerminationRequest.none);
const supervisor_action: std.posix.Sigaction = .{
.handler = .{ .handler = recordForegroundSessionTermination },
.mask = foregroundSupervisorSignalMask(),
@@ -293,8 +293,8 @@ fn writeForegroundTargetScript(target_input: std.Io.File, script: []const u8) vo
fn recordForegroundSessionTermination(signal: std.posix.SIG) callconv(.c) void {
const request = @as(*volatile std.c.sig_atomic_t, &foreground_session_termination_request);
request.* = @intFromEnum(mergeForegroundSessionTerminationRequest(
@enumFromInt(request.*),
request.* = @backingInt(mergeForegroundSessionTerminationRequest(
@fromBackingInt(@intCast(request.*)),
signal,
));
}
@@ -316,9 +316,9 @@ fn mergeForegroundSessionTerminationRequest(
}
fn foregroundSessionTerminationRequest() ForegroundSessionTerminationRequest {
return @enumFromInt(
return @fromBackingInt(@intCast(
@as(*volatile std.c.sig_atomic_t, &foreground_session_termination_request).*,
);
));
}
const ForegroundTerminationAction = enum {
@@ -463,7 +463,7 @@ fn waitForForegroundTarget(
);
if (request == .force) {
@as(*volatile std.c.sig_atomic_t, &foreground_session_termination_request).* =
@intFromEnum(ForegroundSessionTerminationRequest.force);
@backingInt(ForegroundSessionTerminationRequest.force);
}
try refreshForegroundTargetTree(&descendants, target_pid);
advanceForegroundTargetTermination(
@@ -683,7 +683,7 @@ fn exitForegroundSessionSupervisor(term: std.process.Child.Term) noreturn {
std.posix.sigaddset(&signal_mask, signal);
std.posix.sigprocmask(std.posix.SIG.UNBLOCK, &signal_mask, null);
std.posix.raise(signal) catch {};
std.process.exit(128 + @as(u8, @truncate(@intFromEnum(signal))));
std.process.exit(128 + @as(u8, @truncate(@backingInt(signal))));
},
.stopped, .unknown => std.process.exit(127),
}
@@ -1504,9 +1504,7 @@ pub fn spawnDetachedSession(
fn foregroundSessionExecutable(scratch: Allocator) ![]const u8 {
if (comptime builtin.is_test) {
const path_z = std.c.getenv("FX_TEST_PRODUCT_EXE") orelse
return error.TestProductExecutableMissing;
return std.mem.sliceTo(path_z, 0);
return (try self_exe.testProductExe(scratch)) orelse error.TestProductExecutableMissing;
}
return self_exe.pathForReexec(scratch);
}
@@ -2006,7 +2004,7 @@ test "zsh user profile reports natural SIGTERM after alias-safe startup" {
try std.testing.expect(std.mem.startsWith(u8, signaled.output, "signal=15\n"));
const foreground = signaled.command_result.?;
try std.testing.expectEqual(@as(?i64, null), foreground.exit_code);
try std.testing.expectEqual(@as(?u32, @intFromEnum(std.posix.SIG.TERM)), foreground.signal);
try std.testing.expectEqual(@as(?u32, @backingInt(std.posix.SIG.TERM)), foreground.signal);
// The startup files ran once into the snapshot. The aliased `builtin`
// could not intercept the alias-safe `\builtin eval` that runs the
@@ -2316,7 +2314,7 @@ fn formatOutputWithStatus(
fn commandStatusFromTerm(term: std.process.Child.Term) command_contract.CommandStatus {
return switch (term) {
.exited => |code| .{ .exit_code = @intCast(code) },
.signal => |sig| .{ .signal = @intFromEnum(sig) },
.signal => |sig| .{ .signal = @backingInt(sig) },
.stopped, .unknown => .indeterminate,
};
}
@@ -2726,9 +2724,9 @@ const ForegroundLaunchFailureProbe = struct {
};
fn parseReplaceError(name: []const u8) ?std.process.ReplaceError {
inline for (std.meta.fields(std.process.ReplaceError)) |field| {
if (std.mem.eql(u8, name, field.name)) {
return @field(std.process.ReplaceError, field.name);
inline for (comptime @typeInfo(std.process.ReplaceError).error_set.error_names.?) |error_name| {
if (std.mem.eql(u8, name, error_name)) {
return @field(std.process.ReplaceError, error_name);
}
}
return null;
@@ -3456,7 +3454,7 @@ fn terminateRemainingProcessGroup(pid: std.posix.pid_t) void {
fn remainingProcessGroupAlive(process_group_id: ?std.posix.pid_t) bool {
if (comptime builtin.os.tag == .windows or builtin.os.tag == .wasi) return false;
const pid = process_group_id orelse return false;
std.posix.kill(-pid, @enumFromInt(0)) catch |err| return switch (err) {
std.posix.kill(-pid, @fromBackingInt(@intCast(0))) catch |err| return switch (err) {
error.ProcessNotFound => false,
else => true,
};
@@ -3583,6 +3581,7 @@ fn spawnForegroundSessionBootstrapForTest(
target_script: []const u8,
) !std.process.Child {
const product_path = try foregroundSessionExecutable(std.testing.allocator);
defer std.testing.allocator.free(product_path);
const argv = [_][]const u8{
product_path,
foreground_session_token,
@@ -3668,13 +3667,13 @@ fn spawnUnreadyForegroundSessionChildForTest(argv: []const []const u8) !std.proc
fn expectReapedChildForTest(child: *std.process.Child, pid: std.posix.pid_t) !void {
try std.testing.expect(child.id == null);
try std.testing.expectError(error.ProcessNotFound, std.posix.kill(pid, @enumFromInt(0)));
try std.testing.expectError(error.ProcessNotFound, std.posix.kill(pid, @fromBackingInt(@intCast(0))));
}
fn expectProcessGoneWithinForTest(pid: std.posix.pid_t, timeout_ms: i64) !void {
const deadline_ms = io_mod.milliTimestamp() + timeout_ms;
while (io_mod.milliTimestamp() < deadline_ms) {
std.posix.kill(pid, @enumFromInt(0)) catch |err| switch (err) {
std.posix.kill(pid, @fromBackingInt(@intCast(0))) catch |err| switch (err) {
error.ProcessNotFound => return,
else => return err,
};
@@ -5144,7 +5143,7 @@ test "timeout terminates foreground process group descendants" {
const started_ms = io_mod.milliTimestamp();
while (true) {
std.posix.kill(pid, @enumFromInt(0)) catch |err| switch (err) {
std.posix.kill(pid, @fromBackingInt(@intCast(0))) catch |err| switch (err) {
error.ProcessNotFound => break,
else => return err,
};
+2 -1
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const builtin = @import("builtin");
const command_admission = @import("../permissions/command_admission.zig");
const command_contract = @import("command_contract.zig");
@@ -1612,7 +1613,7 @@ fn testAuthority(input: StartCapturedInput) command_admission.CommandExecutionAu
test "captured managed execution yields one handle and delivers ordered output once" {
if (comptime builtin.os.tag == .wasi) return;
const alloc = std.testing.allocator;
const alloc = testing_allocator.no_resize;
var runtime = Runtime.init(alloc);
defer runtime.deinit();
var input = StartCapturedInput{
+2 -2
View File
@@ -77,7 +77,7 @@ pub fn failureForHttpStatus(status: std.http.Status) Failure {
return .{ .category = .rate_limited, .http_status = status, .retryable = true };
}
const code = @intFromEnum(status);
const code = @backingInt(status);
if (code >= 500 and code < 600) {
return .{
.category = .gateway_unavailable,
@@ -298,7 +298,7 @@ fn traceCatalogLoadOutcome(
"model_catalog_load",
.{},
common_format ++ " http_status={d} retryable={s}",
common_args ++ .{ @intFromEnum(status), retryable },
common_args ++ .{ @backingInt(status), retryable },
);
} else {
debug_trace.eventf(
+1 -1
View File
@@ -32,7 +32,7 @@ test "fromCatalogEntry preserves catalog capability metadata" {
.id = @constCast("provider/model"),
.model_type = @constCast("language"),
.has_reasoning = true,
.reasoning_efforts = .{ .items = @constCast(efforts[0..]), .capacity = efforts.len },
.reasoning_efforts = .fromOwnedSlice(@constCast(efforts[0..])),
.supports_fast_mode = true,
.has_tool_use = true,
.has_vision = true,
+2 -2
View File
@@ -20,7 +20,7 @@ pub const Version = struct {
pub fn parse(raw: []const u8) ?Version {
const value = update_target.normalizeVersion(std.mem.trim(u8, raw, " \r\n\t"));
if (!update_target.isValidVersion(value)) return null;
var version: Version = .{ .bytes = [_]u8{0} ** update_target.max_version_bytes, .len = @intCast(value.len) };
var version: Version = .{ .bytes = @as([update_target.max_version_bytes]u8, @splat(0)), .len = @intCast(value.len) };
@memcpy(version.bytes[0..value.len], value);
return version;
}
@@ -202,7 +202,7 @@ test "provider version cache rejects directories symlinks and hardlinks" {
.directory => try tmp.dir.createDir(std.testing.io, name, .fromMode(0o700)),
.symlink => try tmp.dir.symLink(std.testing.io, "target", name, .{}),
.hardlink => {
const name_z = try alloc.dupeZ(u8, name);
const name_z = try alloc.dupeSentinel(u8, name, 0);
defer alloc.free(name_z);
try std.testing.expectEqual(@as(c_int, 0), std.c.linkat(tmp.dir.handle, "target", tmp.dir.handle, name_z, 0));
},
+2 -1
View File
@@ -1,4 +1,5 @@
const std = @import("std");
const testing_allocator = @import("../shared/testing_allocator.zig");
const io_mod = @import("../shared/io.zig");
const Allocator = std.mem.Allocator;
@@ -174,7 +175,7 @@ test "git argv: read-only commands disable optional locks" {
}
test "git repository detection treats allocation failure as unavailable" {
var failing = std.testing.FailingAllocator.init(std.testing.allocator, .{ .fail_index = 0 });
var failing = std.testing.FailingAllocator.init(testing_allocator.no_resize, .{ .fail_index = 0 });
try std.testing.expect(!isGitRepository(failing.allocator()));
}
Loaded 100 of 301 files, more files were not shown because too many files have changed in this diff. Show more