@aether-agent/evals 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +137 -0
  3. package/dist/Agent.d.ts +6 -0
  4. package/dist/Agent.d.ts.map +1 -0
  5. package/dist/Agent.js +2 -0
  6. package/dist/Agent.js.map +1 -0
  7. package/dist/DockerAgent.d.ts +24 -0
  8. package/dist/DockerAgent.d.ts.map +1 -0
  9. package/dist/DockerAgent.js +101 -0
  10. package/dist/DockerAgent.js.map +1 -0
  11. package/dist/FakeAgent.d.ts +21 -0
  12. package/dist/FakeAgent.d.ts.map +1 -0
  13. package/dist/FakeAgent.js +69 -0
  14. package/dist/FakeAgent.js.map +1 -0
  15. package/dist/containers/container.d.ts +51 -0
  16. package/dist/containers/container.d.ts.map +1 -0
  17. package/dist/containers/container.js +237 -0
  18. package/dist/containers/container.js.map +1 -0
  19. package/dist/containers/image.d.ts +20 -0
  20. package/dist/containers/image.d.ts.map +1 -0
  21. package/dist/containers/image.js +43 -0
  22. package/dist/containers/image.js.map +1 -0
  23. package/dist/containers/index.d.ts +5 -0
  24. package/dist/containers/index.d.ts.map +1 -0
  25. package/dist/containers/index.js +3 -0
  26. package/dist/containers/index.js.map +1 -0
  27. package/dist/diff.d.ts +11 -0
  28. package/dist/diff.d.ts.map +1 -0
  29. package/dist/diff.js +18 -0
  30. package/dist/diff.js.map +1 -0
  31. package/dist/errors.d.ts +7 -0
  32. package/dist/errors.d.ts.map +1 -0
  33. package/dist/errors.js +11 -0
  34. package/dist/errors.js.map +1 -0
  35. package/dist/generate.d.ts +54 -0
  36. package/dist/generate.d.ts.map +1 -0
  37. package/dist/generate.js +100 -0
  38. package/dist/generate.js.map +1 -0
  39. package/dist/git.d.ts +10 -0
  40. package/dist/git.d.ts.map +1 -0
  41. package/dist/git.js +37 -0
  42. package/dist/git.js.map +1 -0
  43. package/dist/index.d.ts +20 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +11 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/judge.d.ts +47 -0
  48. package/dist/judge.d.ts.map +1 -0
  49. package/dist/judge.js +177 -0
  50. package/dist/judge.js.map +1 -0
  51. package/dist/task.d.ts +5 -0
  52. package/dist/task.d.ts.map +1 -0
  53. package/dist/task.js +7 -0
  54. package/dist/task.js.map +1 -0
  55. package/dist/transcript.d.ts +24 -0
  56. package/dist/transcript.d.ts.map +1 -0
  57. package/dist/transcript.js +96 -0
  58. package/dist/transcript.js.map +1 -0
  59. package/dist/workspace.d.ts +45 -0
  60. package/dist/workspace.d.ts.map +1 -0
  61. package/dist/workspace.js +135 -0
  62. package/dist/workspace.js.map +1 -0
  63. package/package.json +41 -0
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2026 Joshua Carver
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,137 @@
1
+ <!-- START doctoc generated TOC please keep comment here to allow auto update -->
2
+ <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
3
+
4
+ - [@aether-agent/evals](#aether-agentevals)
5
+ - [Install](#install)
6
+ - [Writing evals with vitest](#writing-evals-with-vitest)
7
+ - [Grading a run with an LLM judge](#grading-a-run-with-an-llm-judge)
8
+
9
+ <!-- END doctoc generated TOC please keep comment here to allow auto update -->
10
+
11
+ # @aether-agent/evals
12
+
13
+ This package facilitates writing evals for Aether agents.
14
+
15
+ ## Install
16
+
17
+ ```sh
18
+ npm install --save-dev @aether-agent/evals
19
+ ```
20
+
21
+ ## Writing evals with vitest
22
+
23
+ `@aether-agent/evals` runs a single Dockerized eval per test:
24
+
25
+ ```ts
26
+ import { test, expect } from "vitest";
27
+ import {
28
+ Transcript,
29
+ DockerAgent,
30
+ Container,
31
+ Image,
32
+ Task,
33
+ Workspace,
34
+ } from "@aether-agent/evals";
35
+ import { readFile } from "node:fs/promises";
36
+ import { join } from "node:path";
37
+
38
+ test("agent edits notes.txt", async () => {
39
+ await using workspace = await Workspace.fromFiles({ "notes.txt": "alpha\nalpha\n" });
40
+ const task = new Task(
41
+ "Change the first line of notes.txt from alpha to beta"
42
+ );
43
+
44
+ await using container = await Container.builder(
45
+ Image.parse("aether-sandbox:latest")
46
+ ).start(workspace);
47
+
48
+ const agent = new DockerAgent({
49
+ container,
50
+ command: ["node", "/app/eval-agent.js"]
51
+ });
52
+
53
+ const trace = await Transcript.fromStream(agent.run(task));
54
+ expect(trace.messages.at(-1)?.type).toBe("done");
55
+
56
+ // Assert against the retained workspace and the recorded tool calls.
57
+ expect(await readFile(join(workspace.path, "notes.txt"), "utf8")).toBe(
58
+ "beta\nalpha\n",
59
+ );
60
+
61
+ const tests = await container.exec({ command: ["pnpm", "test"] });
62
+ expect(tests.exitCode).toBe(0);
63
+
64
+ // Workspace is removed when `workspace` goes out of scope; run container exec assertions first.
65
+ });
66
+ ```
67
+
68
+ ### Grading a run with an LLM judge
69
+
70
+ ```ts
71
+ import { readFile } from "node:fs/promises";
72
+ import { join } from "node:path";
73
+ import { test, expect } from "vitest";
74
+ import {
75
+ Transcript,
76
+ DockerAgent,
77
+ Container,
78
+ Image,
79
+ generate,
80
+ judge,
81
+ Task,
82
+ Workspace,
83
+ } from "@aether-agent/evals";
84
+
85
+ test("agent makes a maintainer-quality edit", async () => {
86
+ await using workspace = await Workspace.fromFiles({ "notes.txt": "alpha\nalpha\n" });
87
+ const task = new Task(
88
+ "Change the first line of notes.txt from alpha to beta",
89
+ );
90
+
91
+ await using container = await Container.builder(
92
+ Image.parse("aether-sandbox:latest"),
93
+ ).start(workspace);
94
+
95
+ const agent = new DockerAgent({
96
+ container,
97
+ command: ["node", "/app/eval-agent.js"],
98
+ });
99
+
100
+ const trace = await Transcript.fromStream(agent.run(task));
101
+ const grader = judge({
102
+ instructions: "Grade strictly using only the provided transcript and files.",
103
+ task: "Change the first line of notes.txt from alpha to beta",
104
+ context: {
105
+ transcript: trace.messages,
106
+ files: {
107
+ "notes.txt": await readFile(join(workspace.path, "notes.txt"), "utf8"),
108
+ },
109
+ },
110
+ criteria: [
111
+ {
112
+ id: "correct-edit",
113
+ description: "The final notes.txt content is exactly 'beta\\nalpha\\n'.",
114
+ blocking: true,
115
+ threshold: 1,
116
+ weight: 3,
117
+ },
118
+ {
119
+ id: "minimal-change",
120
+ description: "No unrelated files or lines were changed.",
121
+ blocking: true,
122
+ threshold: 0.8,
123
+ weight: 2,
124
+ },
125
+ ],
126
+ });
127
+
128
+ const response = await generate(grader.prompt, {
129
+ model: "anthropic:claude-sonnet-4-5", // or set AETHER_LLM_MODEL
130
+ temperature: 0,
131
+ schema: grader.schema,
132
+ });
133
+
134
+ const summary = grader.summarize(response);
135
+ expect(summary.passed, summary.reason).toBe(true);
136
+ });
137
+ ```
@@ -0,0 +1,6 @@
1
+ import type { AgentMessage } from "@aether-agent/sdk";
2
+ import type { Task } from "./task.js";
3
+ export interface Agent {
4
+ run(task: Task): AsyncIterable<AgentMessage>;
5
+ }
6
+ //# sourceMappingURL=Agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Agent.d.ts","sourceRoot":"","sources":["../src/Agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,WAAW,KAAK;IACpB,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC;CAC9C"}
package/dist/Agent.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Agent.js","sourceRoot":"","sources":["../src/Agent.ts"],"names":[],"mappings":""}
@@ -0,0 +1,24 @@
1
+ import type { AgentMessage } from "@aether-agent/sdk";
2
+ import { Container } from "./containers/container.js";
3
+ import type { Agent } from "./Agent.js";
4
+ import type { Task } from "./task.js";
5
+ export declare const CONTAINER_AETHER_HOME = "/root/.aether";
6
+ export declare const AETHER_EVAL_TASK_PROMPT_ENV = "AETHER_EVAL_TASK_PROMPT";
7
+ export declare const AETHER_EVAL_WORKSPACE_ROOT_ENV = "AETHER_EVAL_WORKSPACE_ROOT";
8
+ export declare const AETHER_EVAL_CWD_ENV = "AETHER_EVAL_CWD";
9
+ export interface DockerAgentOptions {
10
+ container: Container;
11
+ command: string[];
12
+ env?: Record<string, string>;
13
+ }
14
+ export declare class DockerAgent implements Agent {
15
+ readonly container: Container;
16
+ readonly command: string[];
17
+ readonly env: Record<string, string>;
18
+ constructor(options: DockerAgentOptions);
19
+ withEnvVar(key: string, value: string): DockerAgent;
20
+ withEnvVars(env: Record<string, string>): DockerAgent;
21
+ run(task: Task): AsyncIterable<AgentMessage>;
22
+ }
23
+ export declare function defaultEvalEnvVars(hostEnv?: Record<string, string | undefined>): Record<string, string>;
24
+ //# sourceMappingURL=DockerAgent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DockerAgent.d.ts","sourceRoot":"","sources":["../src/DockerAgent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAEtD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGtC,eAAO,MAAM,qBAAqB,kBAAkB,CAAC;AACrD,eAAO,MAAM,2BAA2B,4BAA4B,CAAC;AACrE,eAAO,MAAM,8BAA8B,+BAA+B,CAAC;AAC3E,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAWrD,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,qBAAa,WAAY,YAAW,KAAK;IACvC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAEzB,OAAO,EAAE,kBAAkB;IAevC,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW;IAQnD,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,WAAW;IAQ9C,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,aAAa,CAAC,YAAY,CAAC;CAiCpD;AAED,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAc,GACvD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAexB"}
@@ -0,0 +1,101 @@
1
+ import { env as processEnv } from "node:process";
2
+ import { AetherEvalError } from "./errors.js";
3
+ import { isTerminalMessage } from "./transcript.js";
4
+ export const CONTAINER_AETHER_HOME = "/root/.aether";
5
+ export const AETHER_EVAL_TASK_PROMPT_ENV = "AETHER_EVAL_TASK_PROMPT";
6
+ export const AETHER_EVAL_WORKSPACE_ROOT_ENV = "AETHER_EVAL_WORKSPACE_ROOT";
7
+ export const AETHER_EVAL_CWD_ENV = "AETHER_EVAL_CWD";
8
+ const PROVIDER_ENV_VARS = new Set([
9
+ "ANTHROPIC_API_KEY",
10
+ "DEEPSEEK_API_KEY",
11
+ "GEMINI_API_KEY",
12
+ "MOONSHOT_API_KEY",
13
+ "OPENAI_API_KEY",
14
+ "OPENROUTER_API_KEY",
15
+ "ZAI_API_KEY",
16
+ ]);
17
+ export class DockerAgent {
18
+ container;
19
+ command;
20
+ env;
21
+ constructor(options) {
22
+ if (options.command.length === 0) {
23
+ throw new AetherEvalError("configuration_error", "DockerAgent command must not be empty");
24
+ }
25
+ this.container = options.container;
26
+ this.command = [...options.command];
27
+ this.env = {
28
+ ...defaultEvalEnvVars(processEnv),
29
+ ...(options.env ?? {}),
30
+ };
31
+ }
32
+ withEnvVar(key, value) {
33
+ return new DockerAgent({
34
+ container: this.container,
35
+ command: this.command,
36
+ env: { ...this.env, [key]: value },
37
+ });
38
+ }
39
+ withEnvVars(env) {
40
+ return new DockerAgent({
41
+ container: this.container,
42
+ command: this.command,
43
+ env: { ...this.env, ...env },
44
+ });
45
+ }
46
+ async *run(task) {
47
+ const env = {
48
+ ...this.env,
49
+ [AETHER_EVAL_WORKSPACE_ROOT_ENV]: this.container.workspaceRoot,
50
+ [AETHER_EVAL_CWD_ENV]: this.container.cwd,
51
+ [AETHER_EVAL_TASK_PROMPT_ENV]: task.prompt,
52
+ };
53
+ let stderr = "";
54
+ let finished = false;
55
+ for await (const line of this.container.execStreaming({
56
+ command: this.command,
57
+ cwd: this.container.cwd,
58
+ env,
59
+ onStderr: (chunk) => {
60
+ stderr += chunk;
61
+ },
62
+ })) {
63
+ const trimmed = line.trim();
64
+ if (!trimmed)
65
+ continue;
66
+ const message = parseAgentMessage(trimmed);
67
+ finished = isTerminalMessage(message);
68
+ yield message;
69
+ if (finished)
70
+ break;
71
+ }
72
+ if (!finished) {
73
+ throw new AetherEvalError("command_exit_without_terminal", `agent command exited without emitting a terminal AgentMessage.\nstderr:\n${stderr}`);
74
+ }
75
+ }
76
+ }
77
+ export function defaultEvalEnvVars(hostEnv = processEnv) {
78
+ const env = {};
79
+ for (const [key, value] of Object.entries(hostEnv)) {
80
+ if (value === undefined)
81
+ continue;
82
+ if (key === "AETHER_HOME")
83
+ continue;
84
+ if (PROVIDER_ENV_VARS.has(key) ||
85
+ key === "OLLAMA_HOST" ||
86
+ key.startsWith("AETHER_")) {
87
+ env[key] = value;
88
+ }
89
+ }
90
+ env.AETHER_HOME = CONTAINER_AETHER_HOME;
91
+ return env;
92
+ }
93
+ function parseAgentMessage(line) {
94
+ try {
95
+ return JSON.parse(line);
96
+ }
97
+ catch (err) {
98
+ throw new AetherEvalError("agent_message_json_line", `agent emitted an invalid AgentMessage JSON line: ${line}`, err);
99
+ }
100
+ }
101
+ //# sourceMappingURL=DockerAgent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DockerAgent.js","sourceRoot":"","sources":["../src/DockerAgent.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { AgentMessage } from "@aether-agent/sdk";
2
+ import type { Agent } from "./Agent.js";
3
+ import type { Task } from "./task.js";
4
+ import type { Workspace } from "./workspace.js";
5
+ /**
6
+ * In-memory {@link Agent} for testing eval harnesses without Docker. Streams a scripted transcript
7
+ * and optionally writes files into the workspace, mirroring how a real agent is observed.
8
+ */
9
+ export declare class FakeAgent implements Agent {
10
+ readonly messages: AgentMessage[];
11
+ readonly fileWrites: ReadonlyArray<readonly [string, string]>;
12
+ readonly workspace: Workspace | undefined;
13
+ constructor(messages: AgentMessage[], fileWrites?: ReadonlyArray<readonly [string, string]>, workspace?: Workspace | undefined);
14
+ static success(): FakeAgent;
15
+ static withToolCall(toolName: string, result: string): FakeAgent;
16
+ static writesFile(path: string, contents: string): FakeAgent;
17
+ withFileWrite(path: string, contents: string): FakeAgent;
18
+ withWorkspace(workspace: Workspace): FakeAgent;
19
+ run(_task: Task): AsyncIterable<AgentMessage>;
20
+ }
21
+ //# sourceMappingURL=FakeAgent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FakeAgent.d.ts","sourceRoot":"","sources":["../src/FakeAgent.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD;;;GAGG;AACH,qBAAa,SAAU,YAAW,KAAK;IAEnC,QAAQ,CAAC,QAAQ,EAAE,YAAY,EAAE;IACjC,QAAQ,CAAC,UAAU,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7D,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS;gBAFhC,QAAQ,EAAE,YAAY,EAAE,EACxB,UAAU,GAAE,aAAa,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,EACzD,SAAS,GAAE,SAAS,GAAG,SAAqB;IAGvD,MAAM,CAAC,OAAO,IAAI,SAAS;IAa3B,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS;IAkBhE,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS;IAI5D,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS;IAQxD,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS;IAIvC,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,aAAa,CAAC,YAAY,CAAC;CAkBrD"}
@@ -0,0 +1,69 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import { AetherEvalError } from "./errors.js";
4
+ /**
5
+ * In-memory {@link Agent} for testing eval harnesses without Docker. Streams a scripted transcript
6
+ * and optionally writes files into the workspace, mirroring how a real agent is observed.
7
+ */
8
+ export class FakeAgent {
9
+ messages;
10
+ fileWrites;
11
+ workspace;
12
+ constructor(messages, fileWrites = [], workspace = undefined) {
13
+ this.messages = messages;
14
+ this.fileWrites = fileWrites;
15
+ this.workspace = workspace;
16
+ }
17
+ static success() {
18
+ return new FakeAgent([
19
+ {
20
+ type: "text",
21
+ message_id: "fake_1",
22
+ chunk: "Task completed successfully",
23
+ is_complete: true,
24
+ model_name: "fake",
25
+ },
26
+ { type: "done" },
27
+ ]);
28
+ }
29
+ static withToolCall(toolName, result) {
30
+ return new FakeAgent([
31
+ {
32
+ type: "tool_result",
33
+ model_name: "fake",
34
+ result: { id: "fake_call_1", name: toolName, arguments: "{}", result },
35
+ },
36
+ {
37
+ type: "text",
38
+ message_id: "fake_2",
39
+ chunk: "Task completed using tools",
40
+ is_complete: true,
41
+ model_name: "fake",
42
+ },
43
+ { type: "done" },
44
+ ]);
45
+ }
46
+ static writesFile(path, contents) {
47
+ return FakeAgent.success().withFileWrite(path, contents);
48
+ }
49
+ withFileWrite(path, contents) {
50
+ return new FakeAgent(this.messages, [...this.fileWrites, [path, contents]], this.workspace);
51
+ }
52
+ withWorkspace(workspace) {
53
+ return new FakeAgent(this.messages, this.fileWrites, workspace);
54
+ }
55
+ async *run(_task) {
56
+ if (this.fileWrites.length > 0 && !this.workspace) {
57
+ throw new AetherEvalError("configuration_error", "FakeAgent file writes require a bound workspace");
58
+ }
59
+ for (const [relativePath, contents] of this.fileWrites) {
60
+ const target = join(this.workspace.path, relativePath);
61
+ await mkdir(dirname(target), { recursive: true });
62
+ await writeFile(target, contents);
63
+ }
64
+ for (const message of this.messages) {
65
+ yield message;
66
+ }
67
+ }
68
+ }
69
+ //# sourceMappingURL=FakeAgent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FakeAgent.js","sourceRoot":"","sources":["../src/FakeAgent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAG1C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAK9C;;;GAGG;AACH,MAAM,OAAO,SAAS;IAET;IACA;IACA;IAHX,YACW,QAAwB,EACxB,aAAuD,EAAE,EACzD,YAAmC,SAAS;QAF5C,aAAQ,GAAR,QAAQ,CAAgB;QACxB,eAAU,GAAV,UAAU,CAA+C;QACzD,cAAS,GAAT,SAAS,CAAmC;IACpD,CAAC;IAEJ,MAAM,CAAC,OAAO;QACZ,OAAO,IAAI,SAAS,CAAC;YACnB;gBACE,IAAI,EAAE,MAAM;gBACZ,UAAU,EAAE,QAAQ;gBACpB,KAAK,EAAE,6BAA6B;gBACpC,WAAW,EAAE,IAAI;gBACjB,UAAU,EAAE,MAAM;aACnB;YACD,EAAE,IAAI,EAAE,MAAM,EAAE;SACjB,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,QAAgB,EAAE,MAAc;QAClD,OAAO,IAAI,SAAS,CAAC;YACnB;gBACE,IAAI,EAAE,aAAa;gBACnB,UAAU,EAAE,MAAM;gBAClB,MAAM,EAAE,EAAE,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE;aACvE;YACD;gBACE,IAAI,EAAE,MAAM;gBACZ,UAAU,EAAE,QAAQ;gBACpB,KAAK,EAAE,4BAA4B;gBACnC,WAAW,EAAE,IAAI;gBACjB,UAAU,EAAE,MAAM;aACnB;YACD,EAAE,IAAI,EAAE,MAAM,EAAE;SACjB,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,UAAU,CAAC,IAAY,EAAE,QAAgB;QAC9C,OAAO,SAAS,CAAC,OAAO,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC3D,CAAC;IAED,aAAa,CAAC,IAAY,EAAE,QAAgB;QAC1C,OAAO,IAAI,SAAS,CAClB,IAAI,CAAC,QAAQ,EACb,CAAC,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,EACtC,IAAI,CAAC,SAAS,CACf,CAAC;IACJ,CAAC;IAED,aAAa,CAAC,SAAoB;QAChC,OAAO,IAAI,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAClE,CAAC;IAED,KAAK,CAAC,CAAC,GAAG,CAAC,KAAW;QACpB,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAClD,MAAM,IAAI,eAAe,CACvB,qBAAqB,EACrB,iDAAiD,CAClD,CAAC;QACJ,CAAC;QAED,KAAK,MAAM,CAAC,YAAY,EAAE,QAAQ,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACvD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,SAAU,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;YACxD,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAClD,MAAM,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACpC,CAAC;QAED,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpC,MAAM,OAAO,CAAC;QAChB,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,51 @@
1
+ import { type StartedTestContainer } from "testcontainers";
2
+ import { Image } from "./image.js";
3
+ import type { Workspace } from "../workspace.js";
4
+ export interface BindMount {
5
+ source: string;
6
+ target: string;
7
+ mode?: "rw" | "ro" | "z" | "Z";
8
+ }
9
+ export interface ContainerExecOptions {
10
+ command: string[];
11
+ cwd?: string;
12
+ env?: Record<string, string>;
13
+ signal?: AbortSignal;
14
+ }
15
+ export interface ExecOutput {
16
+ exitCode: number;
17
+ stdout: string;
18
+ stderr: string;
19
+ }
20
+ export interface ContainerStreamingOptions {
21
+ command: string[];
22
+ cwd?: string;
23
+ env?: Record<string, string>;
24
+ signal?: AbortSignal;
25
+ onStderr?: (chunk: string) => void;
26
+ }
27
+ export declare class Container {
28
+ private readonly container;
29
+ private readonly ephemeralTempdirs;
30
+ readonly workspaceRoot = "/workspace";
31
+ readonly cwd: string;
32
+ constructor(container: StartedTestContainer, ephemeralTempdirs: string[], cwd: string);
33
+ static builder(image: Image): ContainerBuilder;
34
+ exec(options: ContainerExecOptions): Promise<ExecOutput>;
35
+ execShell(script: string): Promise<ExecOutput>;
36
+ execStreaming(options: ContainerStreamingOptions): AsyncIterable<string>;
37
+ [Symbol.asyncDispose](): Promise<void>;
38
+ }
39
+ export declare class ContainerBuilder {
40
+ private readonly image;
41
+ private envVars;
42
+ private mounts;
43
+ private ephemeralMounts;
44
+ constructor(image: Image);
45
+ withEnvVar(key: string, value: string): this;
46
+ withEnvVars(envVars: Record<string, string>): this;
47
+ withMount(mount: BindMount): this;
48
+ withEphemeralMount(target: string): this;
49
+ start(workspace: Workspace): Promise<Container>;
50
+ }
51
+ //# sourceMappingURL=container.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../src/containers/container.ts"],"names":[],"mappings":"AAMA,OAAO,EAGL,KAAK,oBAAoB,EAE1B,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,GAAG,GAAG,GAAG,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAED,qBAAa,SAAS;IAKlB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IALpC,QAAQ,CAAC,aAAa,gBAAgB;IACtC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;gBAGF,SAAS,EAAE,oBAAoB,EAC/B,iBAAiB,EAAE,MAAM,EAAE,EAC5C,GAAG,EAAE,MAAM;IAKb,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,gBAAgB;IAIxC,IAAI,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,UAAU,CAAC;IAYxD,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAM7C,aAAa,CAClB,OAAO,EAAE,yBAAyB,GACjC,aAAa,CAAC,MAAM,CAAC;IA6ClB,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7C;AAED,qBAAa,gBAAgB;IAKf,OAAO,CAAC,QAAQ,CAAC,KAAK;IAJlC,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,eAAe,CAAgB;gBAEV,KAAK,EAAE,KAAK;IAEzC,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAK5C,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAKlD,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAKjC,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAKlC,KAAK,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;CAuCtD"}