@sandagent/manager 0.1.0-beta.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.
- package/README.md +191 -0
- package/dist/__tests__/sand-agent.test.d.ts +2 -0
- package/dist/__tests__/sand-agent.test.d.ts.map +1 -0
- package/dist/__tests__/sand-agent.test.js +284 -0
- package/dist/__tests__/sand-agent.test.js.map +1 -0
- package/dist/__tests__/signal-integration.test.d.ts +2 -0
- package/dist/__tests__/signal-integration.test.d.ts.map +1 -0
- package/dist/__tests__/signal-integration.test.js +136 -0
- package/dist/__tests__/signal-integration.test.js.map +1 -0
- package/dist/__tests__/transcript.test.d.ts +2 -0
- package/dist/__tests__/transcript.test.d.ts.map +1 -0
- package/dist/__tests__/transcript.test.js +144 -0
- package/dist/__tests__/transcript.test.js.map +1 -0
- package/dist/__tests__/types.test.d.ts +2 -0
- package/dist/__tests__/types.test.d.ts.map +1 -0
- package/dist/__tests__/types.test.js +148 -0
- package/dist/__tests__/types.test.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/sand-agent.d.ts +50 -0
- package/dist/sand-agent.d.ts.map +1 -0
- package/dist/sand-agent.js +187 -0
- package/dist/sand-agent.js.map +1 -0
- package/dist/transcript.d.ts +112 -0
- package/dist/transcript.d.ts.map +1 -0
- package/dist/transcript.js +176 -0
- package/dist/transcript.js.map +1 -0
- package/dist/types.d.ts +175 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# @sandagent/manager
|
|
2
|
+
|
|
3
|
+
Core manager package for SandAgent - manages sandbox and runner lifecycle, defines core interfaces.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`@sandagent/manager` is the foundational package that provides:
|
|
8
|
+
|
|
9
|
+
- **SandAgent**: Main class for managing sandboxed agent instances
|
|
10
|
+
- **Core Interfaces**: `SandboxAdapter`, `SandboxHandle`, `RunnerSpec`, etc.
|
|
11
|
+
- **Transcript Writers**: Tools for logging agent execution (JSONL, Memory, Console, Multi)
|
|
12
|
+
- **Type Definitions**: Shared types for messages, streams, and sandbox operations
|
|
13
|
+
|
|
14
|
+
This package is typically used as a dependency by higher-level packages like `@sandagent/ai-provider` and sandbox adapters.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @sandagent/manager
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
### Basic Usage
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { SandAgent } from '@sandagent/manager';
|
|
28
|
+
import { E2BSandbox } from '@sandagent/sandbox-e2b';
|
|
29
|
+
|
|
30
|
+
const agent = new SandAgent({
|
|
31
|
+
sandbox: new E2BSandbox({ apiKey: 'xxx' }),
|
|
32
|
+
runner: {
|
|
33
|
+
kind: 'claude-agent-sdk',
|
|
34
|
+
model: 'claude-sonnet-4-20250514',
|
|
35
|
+
outputFormat: 'stream',
|
|
36
|
+
},
|
|
37
|
+
env: {
|
|
38
|
+
ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Stream a task
|
|
43
|
+
const stream = await agent.stream({
|
|
44
|
+
messages: [
|
|
45
|
+
{ role: 'user', content: 'Create a hello world program' }
|
|
46
|
+
],
|
|
47
|
+
workspace: {
|
|
48
|
+
path: '/workspace',
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Read the stream
|
|
53
|
+
const reader = stream.getReader();
|
|
54
|
+
while (true) {
|
|
55
|
+
const { done, value } = await reader.read();
|
|
56
|
+
if (done) break;
|
|
57
|
+
process.stdout.write(value);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Cleanup
|
|
61
|
+
await agent.destroy();
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### With Transcript Logging
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { SandAgent, JsonlTranscriptWriter } from '@sandagent/manager';
|
|
68
|
+
|
|
69
|
+
const transcriptWriter = new JsonlTranscriptWriter('./transcript.jsonl');
|
|
70
|
+
|
|
71
|
+
const stream = await agent.stream({
|
|
72
|
+
messages: [{ role: 'user', content: 'Do something' }],
|
|
73
|
+
workspace: { path: '/workspace' },
|
|
74
|
+
transcriptWriter,
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Upload Files
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
await agent.uploadFiles([
|
|
82
|
+
{ path: 'hello.txt', content: 'Hello World!' },
|
|
83
|
+
{ path: 'data.json', content: JSON.stringify({ key: 'value' }) },
|
|
84
|
+
], '/workspace');
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Core Interfaces
|
|
88
|
+
|
|
89
|
+
### SandboxAdapter
|
|
90
|
+
|
|
91
|
+
Interface that sandbox implementations must follow:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
interface SandboxAdapter {
|
|
95
|
+
attach(): Promise<SandboxHandle>;
|
|
96
|
+
getHandle(): SandboxHandle | null;
|
|
97
|
+
getEnv?(): Record<string, string>;
|
|
98
|
+
getWorkdir?(): string;
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### SandboxHandle
|
|
103
|
+
|
|
104
|
+
Interface for interacting with an attached sandbox:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
interface SandboxHandle {
|
|
108
|
+
exec(command: string[], opts?: ExecOptions): AsyncIterable<Uint8Array>;
|
|
109
|
+
upload(files: Array<{ path: string; content: Uint8Array | string }>, targetDir: string): Promise<void>;
|
|
110
|
+
readFile?(filePath: string): Promise<string>;
|
|
111
|
+
runCommand?(command: string): Promise<{ stdout: string; stderr: string; exitCode: number }>;
|
|
112
|
+
destroy(): Promise<void>;
|
|
113
|
+
getWorkdir?(): string;
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### RunnerSpec
|
|
118
|
+
|
|
119
|
+
Specification for the runner to execute inside the sandbox:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
interface RunnerSpec {
|
|
123
|
+
kind: 'claude-agent-sdk' | string;
|
|
124
|
+
model: string;
|
|
125
|
+
systemPrompt?: string;
|
|
126
|
+
maxTurns?: number;
|
|
127
|
+
allowedTools?: string[];
|
|
128
|
+
approvalDir?: string;
|
|
129
|
+
outputFormat?: 'stream' | 'json';
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Transcript Writers
|
|
134
|
+
|
|
135
|
+
Available transcript writers:
|
|
136
|
+
|
|
137
|
+
- **JsonlTranscriptWriter**: Write to JSONL file
|
|
138
|
+
- **MemoryTranscriptWriter**: Store in memory
|
|
139
|
+
- **ConsoleTranscriptWriter**: Log to console
|
|
140
|
+
- **MultiTranscriptWriter**: Write to multiple writers
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import {
|
|
144
|
+
JsonlTranscriptWriter,
|
|
145
|
+
MemoryTranscriptWriter,
|
|
146
|
+
MultiTranscriptWriter
|
|
147
|
+
} from '@sandagent/manager';
|
|
148
|
+
|
|
149
|
+
const transcriptWriter = new MultiTranscriptWriter([
|
|
150
|
+
new JsonlTranscriptWriter('./transcript.jsonl'),
|
|
151
|
+
new ConsoleTranscriptWriter(),
|
|
152
|
+
]);
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## API Reference
|
|
156
|
+
|
|
157
|
+
### SandAgent
|
|
158
|
+
|
|
159
|
+
#### Constructor
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
new SandAgent(options: SandAgentOptions)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Options:**
|
|
166
|
+
- `sandbox` (required): A SandboxAdapter instance
|
|
167
|
+
- `runner` (required): RunnerSpec configuration
|
|
168
|
+
- `env`: Environment variables for the sandbox
|
|
169
|
+
|
|
170
|
+
#### Methods
|
|
171
|
+
|
|
172
|
+
**stream(input: StreamInput): Promise<ReadableStream<Uint8Array>>**
|
|
173
|
+
|
|
174
|
+
Stream a task through the agent. Returns a ReadableStream of AI SDK UI messages.
|
|
175
|
+
|
|
176
|
+
**uploadFiles(files: Array<{ path: string; content: Uint8Array | string }>, targetDir?: string): Promise<void>**
|
|
177
|
+
|
|
178
|
+
Upload files to the agent's workspace.
|
|
179
|
+
|
|
180
|
+
**destroy(): Promise<void>**
|
|
181
|
+
|
|
182
|
+
Destroy the sandbox and release resources.
|
|
183
|
+
|
|
184
|
+
## Requirements
|
|
185
|
+
|
|
186
|
+
- Node.js 20+
|
|
187
|
+
- A sandbox adapter (@sandagent/sandbox-e2b, @sandagent/sandbox-local, etc.)
|
|
188
|
+
|
|
189
|
+
## License
|
|
190
|
+
|
|
191
|
+
Apache-2.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sand-agent.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/sand-agent.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { SandAgent } from "../sand-agent.js";
|
|
3
|
+
/**
|
|
4
|
+
* Create an async iterable from data
|
|
5
|
+
*/
|
|
6
|
+
function createAsyncIterable(data) {
|
|
7
|
+
return {
|
|
8
|
+
[Symbol.asyncIterator]: () => {
|
|
9
|
+
let index = 0;
|
|
10
|
+
return {
|
|
11
|
+
async next() {
|
|
12
|
+
if (index < data.length) {
|
|
13
|
+
return { value: data[index++], done: false };
|
|
14
|
+
}
|
|
15
|
+
return { value: undefined, done: true };
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Mock sandbox adapter for testing
|
|
23
|
+
*/
|
|
24
|
+
function createMockSandbox() {
|
|
25
|
+
const handle = {
|
|
26
|
+
exec: vi
|
|
27
|
+
.fn()
|
|
28
|
+
.mockReturnValue(createAsyncIterable([new TextEncoder().encode("test output")])),
|
|
29
|
+
upload: vi.fn().mockResolvedValue(undefined),
|
|
30
|
+
readFile: vi.fn().mockResolvedValue(""),
|
|
31
|
+
destroy: vi.fn().mockResolvedValue(undefined),
|
|
32
|
+
};
|
|
33
|
+
const adapter = {
|
|
34
|
+
attach: vi.fn().mockResolvedValue(handle),
|
|
35
|
+
getHandle: vi.fn().mockReturnValue(handle),
|
|
36
|
+
handle,
|
|
37
|
+
};
|
|
38
|
+
return adapter;
|
|
39
|
+
}
|
|
40
|
+
describe("SandAgent", () => {
|
|
41
|
+
describe("stream", () => {
|
|
42
|
+
it("should attach to sandbox and execute command", async () => {
|
|
43
|
+
const sandbox = createMockSandbox();
|
|
44
|
+
const agent = new SandAgent({
|
|
45
|
+
sandbox,
|
|
46
|
+
runner: {
|
|
47
|
+
kind: "claude-agent-sdk",
|
|
48
|
+
model: "claude-sonnet-4-20250514",
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
const stream = await agent.stream({
|
|
52
|
+
messages: [{ role: "user", content: "Hello" }],
|
|
53
|
+
});
|
|
54
|
+
expect(sandbox.attach).toHaveBeenCalledWith();
|
|
55
|
+
expect(stream).toBeInstanceOf(ReadableStream);
|
|
56
|
+
});
|
|
57
|
+
it("should use default workspace path", async () => {
|
|
58
|
+
const sandbox = createMockSandbox();
|
|
59
|
+
const agent = new SandAgent({
|
|
60
|
+
sandbox,
|
|
61
|
+
runner: {
|
|
62
|
+
kind: "claude-agent-sdk",
|
|
63
|
+
model: "claude-sonnet-4-20250514",
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
await agent.stream({
|
|
67
|
+
messages: [{ role: "user", content: "Hello" }],
|
|
68
|
+
});
|
|
69
|
+
expect(sandbox.handle.exec).toHaveBeenCalledWith(expect.arrayContaining(["--cwd", "/workspace"]), expect.objectContaining({ cwd: "/workspace" }));
|
|
70
|
+
});
|
|
71
|
+
it("should use custom workspace path", async () => {
|
|
72
|
+
const sandbox = createMockSandbox();
|
|
73
|
+
const agent = new SandAgent({
|
|
74
|
+
sandbox,
|
|
75
|
+
runner: {
|
|
76
|
+
kind: "claude-agent-sdk",
|
|
77
|
+
model: "claude-sonnet-4-20250514",
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
await agent.stream({
|
|
81
|
+
messages: [{ role: "user", content: "Hello" }],
|
|
82
|
+
workspace: { path: "/custom/path" },
|
|
83
|
+
});
|
|
84
|
+
expect(sandbox.handle.exec).toHaveBeenCalledWith(expect.arrayContaining(["--cwd", "/custom/path"]), expect.objectContaining({ cwd: "/custom/path" }));
|
|
85
|
+
});
|
|
86
|
+
it("should include user message in command", async () => {
|
|
87
|
+
const sandbox = createMockSandbox();
|
|
88
|
+
const agent = new SandAgent({
|
|
89
|
+
sandbox,
|
|
90
|
+
runner: {
|
|
91
|
+
kind: "claude-agent-sdk",
|
|
92
|
+
model: "claude-sonnet-4-20250514",
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
await agent.stream({
|
|
96
|
+
messages: [{ role: "user", content: "Create a file" }],
|
|
97
|
+
});
|
|
98
|
+
expect(sandbox.handle.exec).toHaveBeenCalledWith(expect.arrayContaining(["Create a file"]), expect.any(Object));
|
|
99
|
+
});
|
|
100
|
+
it("should pass through stdout without modification", async () => {
|
|
101
|
+
const testData = "test streaming data";
|
|
102
|
+
const sandbox = createMockSandbox();
|
|
103
|
+
// Override exec to return test data using proper async iterable
|
|
104
|
+
sandbox.handle.exec = vi
|
|
105
|
+
.fn()
|
|
106
|
+
.mockReturnValue(createAsyncIterable([new TextEncoder().encode(testData)]));
|
|
107
|
+
const agent = new SandAgent({
|
|
108
|
+
sandbox,
|
|
109
|
+
runner: {
|
|
110
|
+
kind: "claude-agent-sdk",
|
|
111
|
+
model: "claude-sonnet-4-20250514",
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
const stream = await agent.stream({
|
|
115
|
+
messages: [{ role: "user", content: "Hello" }],
|
|
116
|
+
});
|
|
117
|
+
const reader = stream.getReader();
|
|
118
|
+
const { value } = await reader.read();
|
|
119
|
+
const text = new TextDecoder().decode(value);
|
|
120
|
+
expect(text).toBe(testData);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
describe("uploadFiles", () => {
|
|
124
|
+
it("should upload files to the sandbox", async () => {
|
|
125
|
+
const sandbox = createMockSandbox();
|
|
126
|
+
const agent = new SandAgent({
|
|
127
|
+
sandbox,
|
|
128
|
+
runner: {
|
|
129
|
+
kind: "claude-agent-sdk",
|
|
130
|
+
model: "claude-sonnet-4-20250514",
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
await agent.uploadFiles([{ path: "test.txt", content: "Hello, World!" }]);
|
|
134
|
+
expect(sandbox.handle.upload).toHaveBeenCalledWith([{ path: "test.txt", content: "Hello, World!" }], "/workspace");
|
|
135
|
+
});
|
|
136
|
+
it("should upload files to custom directory", async () => {
|
|
137
|
+
const sandbox = createMockSandbox();
|
|
138
|
+
const agent = new SandAgent({
|
|
139
|
+
sandbox,
|
|
140
|
+
runner: {
|
|
141
|
+
kind: "claude-agent-sdk",
|
|
142
|
+
model: "claude-sonnet-4-20250514",
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
await agent.uploadFiles([{ path: "test.txt", content: "Hello, World!" }], "/custom/dir");
|
|
146
|
+
expect(sandbox.handle.upload).toHaveBeenCalledWith([{ path: "test.txt", content: "Hello, World!" }], "/custom/dir");
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
describe("destroy", () => {
|
|
150
|
+
it("should destroy the sandbox", async () => {
|
|
151
|
+
const sandbox = createMockSandbox();
|
|
152
|
+
const agent = new SandAgent({
|
|
153
|
+
sandbox,
|
|
154
|
+
runner: {
|
|
155
|
+
kind: "claude-agent-sdk",
|
|
156
|
+
model: "claude-sonnet-4-20250514",
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
// First attach to sandbox
|
|
160
|
+
await agent.stream({
|
|
161
|
+
messages: [{ role: "user", content: "Hello" }],
|
|
162
|
+
});
|
|
163
|
+
// Then destroy
|
|
164
|
+
await agent.destroy();
|
|
165
|
+
expect(sandbox.handle.destroy).toHaveBeenCalled();
|
|
166
|
+
});
|
|
167
|
+
it("should do nothing if not attached", async () => {
|
|
168
|
+
const sandbox = createMockSandbox();
|
|
169
|
+
const agent = new SandAgent({
|
|
170
|
+
sandbox,
|
|
171
|
+
runner: {
|
|
172
|
+
kind: "claude-agent-sdk",
|
|
173
|
+
model: "claude-sonnet-4-20250514",
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
// Destroy without attaching first
|
|
177
|
+
await agent.destroy();
|
|
178
|
+
expect(sandbox.handle.destroy).not.toHaveBeenCalled();
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
describe("AbortSignal support", () => {
|
|
182
|
+
it("should pass signal to exec()", async () => {
|
|
183
|
+
const sandbox = createMockSandbox();
|
|
184
|
+
const agent = new SandAgent({
|
|
185
|
+
sandbox,
|
|
186
|
+
runner: {
|
|
187
|
+
kind: "claude-agent-sdk",
|
|
188
|
+
model: "claude-sonnet-4-20250514",
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
const controller = new AbortController();
|
|
192
|
+
const signal = controller.signal;
|
|
193
|
+
await agent.stream({
|
|
194
|
+
messages: [{ role: "user", content: "Hello" }],
|
|
195
|
+
signal,
|
|
196
|
+
});
|
|
197
|
+
expect(sandbox.handle.exec).toHaveBeenCalledWith(expect.any(Array), expect.objectContaining({ signal }));
|
|
198
|
+
});
|
|
199
|
+
it("should throw error if signal is already aborted", async () => {
|
|
200
|
+
const sandbox = createMockSandbox();
|
|
201
|
+
const agent = new SandAgent({
|
|
202
|
+
sandbox,
|
|
203
|
+
runner: {
|
|
204
|
+
kind: "claude-agent-sdk",
|
|
205
|
+
model: "claude-sonnet-4-20250514",
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
const controller = new AbortController();
|
|
209
|
+
controller.abort();
|
|
210
|
+
const signal = controller.signal;
|
|
211
|
+
await expect(agent.stream({
|
|
212
|
+
messages: [{ role: "user", content: "Hello" }],
|
|
213
|
+
signal,
|
|
214
|
+
})).rejects.toThrow("Operation was aborted");
|
|
215
|
+
});
|
|
216
|
+
it("should pass signal to exec()", async () => {
|
|
217
|
+
const sandbox = createMockSandbox();
|
|
218
|
+
const controller = new AbortController();
|
|
219
|
+
sandbox.handle.exec = vi
|
|
220
|
+
.fn()
|
|
221
|
+
.mockReturnValue(createAsyncIterable([new TextEncoder().encode("test output")]));
|
|
222
|
+
const agent = new SandAgent({
|
|
223
|
+
sandbox,
|
|
224
|
+
runner: {
|
|
225
|
+
kind: "claude-agent-sdk",
|
|
226
|
+
model: "claude-sonnet-4-20250514",
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
await agent.stream({
|
|
230
|
+
messages: [{ role: "user", content: "Hello" }],
|
|
231
|
+
signal: controller.signal,
|
|
232
|
+
});
|
|
233
|
+
// Verify signal was passed to exec
|
|
234
|
+
expect(sandbox.handle.exec).toHaveBeenCalledWith(expect.any(Array), expect.objectContaining({
|
|
235
|
+
signal: controller.signal,
|
|
236
|
+
}));
|
|
237
|
+
});
|
|
238
|
+
it("should handle AbortError from exec() gracefully", async () => {
|
|
239
|
+
const sandbox = createMockSandbox();
|
|
240
|
+
const transcriptEntries = [];
|
|
241
|
+
const mockTranscriptWriter = {
|
|
242
|
+
write: vi.fn().mockImplementation((entry) => {
|
|
243
|
+
transcriptEntries.push(entry);
|
|
244
|
+
return Promise.resolve();
|
|
245
|
+
}),
|
|
246
|
+
};
|
|
247
|
+
// Create a mock that throws AbortError
|
|
248
|
+
sandbox.handle.exec = vi.fn().mockReturnValue({
|
|
249
|
+
[Symbol.asyncIterator]: async function* () {
|
|
250
|
+
yield new TextEncoder().encode("chunk1");
|
|
251
|
+
const error = new Error("The operation was aborted");
|
|
252
|
+
error.name = "AbortError";
|
|
253
|
+
throw error;
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
const agent = new SandAgent({
|
|
257
|
+
sandbox,
|
|
258
|
+
runner: {
|
|
259
|
+
kind: "claude-agent-sdk",
|
|
260
|
+
model: "claude-sonnet-4-20250514",
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
const stream = await agent.stream({
|
|
264
|
+
messages: [{ role: "user", content: "Hello" }],
|
|
265
|
+
transcriptWriter: mockTranscriptWriter,
|
|
266
|
+
});
|
|
267
|
+
const reader = stream.getReader();
|
|
268
|
+
// Read first chunk
|
|
269
|
+
await reader.read();
|
|
270
|
+
// Try to read next chunk - should throw
|
|
271
|
+
await expect(reader.read()).rejects.toThrow();
|
|
272
|
+
// Check that start entry was written
|
|
273
|
+
const startEntry = transcriptEntries.find((entry) => entry.type === "start");
|
|
274
|
+
expect(startEntry).toBeDefined();
|
|
275
|
+
// Check that at least one chunk was written
|
|
276
|
+
const chunkEntry = transcriptEntries.find((entry) => entry.type === "chunk");
|
|
277
|
+
expect(chunkEntry).toBeDefined();
|
|
278
|
+
// AbortError should NOT create an error entry - just stops cleanly
|
|
279
|
+
const errorEntry = transcriptEntries.find((entry) => entry.type === "error");
|
|
280
|
+
expect(errorEntry).toBeUndefined();
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
//# sourceMappingURL=sand-agent.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sand-agent.test.js","sourceRoot":"","sources":["../../src/__tests__/sand-agent.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signal-integration.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/signal-integration.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { SandAgent } from "../sand-agent.js";
|
|
3
|
+
describe("Signal Integration Tests", () => {
|
|
4
|
+
let mockSandbox;
|
|
5
|
+
let mockHandle;
|
|
6
|
+
let execSignalReceived;
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
execSignalReceived = undefined;
|
|
9
|
+
mockHandle = {
|
|
10
|
+
exec: vi.fn((command, opts) => {
|
|
11
|
+
// Capture the signal that was passed
|
|
12
|
+
execSignalReceived = opts?.signal;
|
|
13
|
+
// Return an async iterable that respects the signal
|
|
14
|
+
return {
|
|
15
|
+
async *[Symbol.asyncIterator]() {
|
|
16
|
+
const chunks = [
|
|
17
|
+
new TextEncoder().encode("chunk1"),
|
|
18
|
+
new TextEncoder().encode("chunk2"),
|
|
19
|
+
new TextEncoder().encode("chunk3"),
|
|
20
|
+
];
|
|
21
|
+
for (const chunk of chunks) {
|
|
22
|
+
// Check if signal was aborted
|
|
23
|
+
if (opts?.signal?.aborted) {
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
yield chunk;
|
|
27
|
+
// Small delay to allow abort to happen
|
|
28
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}),
|
|
33
|
+
upload: vi.fn(),
|
|
34
|
+
readFile: vi.fn(),
|
|
35
|
+
destroy: vi.fn(),
|
|
36
|
+
};
|
|
37
|
+
mockSandbox = {
|
|
38
|
+
attach: vi.fn(async () => mockHandle),
|
|
39
|
+
getHandle: vi.fn(() => mockHandle),
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
it("should pass signal from StreamInput to sandbox exec", async () => {
|
|
43
|
+
const agent = new SandAgent({
|
|
44
|
+
sandbox: mockSandbox,
|
|
45
|
+
runner: {
|
|
46
|
+
kind: "claude-agent-sdk",
|
|
47
|
+
model: "claude-3-5-sonnet-20241022",
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
const controller = new AbortController();
|
|
51
|
+
const input = {
|
|
52
|
+
messages: [{ role: "user", content: "test" }],
|
|
53
|
+
signal: controller.signal,
|
|
54
|
+
};
|
|
55
|
+
const stream = await agent.stream(input);
|
|
56
|
+
expect(stream).toBeInstanceOf(ReadableStream);
|
|
57
|
+
// Verify that the signal was passed to exec
|
|
58
|
+
expect(execSignalReceived).toBe(controller.signal);
|
|
59
|
+
});
|
|
60
|
+
it("should stop streaming when signal is aborted", async () => {
|
|
61
|
+
const agent = new SandAgent({
|
|
62
|
+
sandbox: mockSandbox,
|
|
63
|
+
runner: {
|
|
64
|
+
kind: "claude-agent-sdk",
|
|
65
|
+
model: "claude-3-5-sonnet-20241022",
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
const controller = new AbortController();
|
|
69
|
+
const input = {
|
|
70
|
+
messages: [{ role: "user", content: "test" }],
|
|
71
|
+
signal: controller.signal,
|
|
72
|
+
};
|
|
73
|
+
const stream = await agent.stream(input);
|
|
74
|
+
const reader = stream.getReader();
|
|
75
|
+
expect(reader).toBeDefined();
|
|
76
|
+
// Read first chunk
|
|
77
|
+
const chunk1 = await reader.read();
|
|
78
|
+
expect(chunk1.done).toBe(false);
|
|
79
|
+
// Abort the signal
|
|
80
|
+
controller.abort();
|
|
81
|
+
// Try to read more - should complete quickly
|
|
82
|
+
const chunks = [];
|
|
83
|
+
while (true) {
|
|
84
|
+
const result = await reader.read();
|
|
85
|
+
if (result.done)
|
|
86
|
+
break;
|
|
87
|
+
chunks.push(result.value);
|
|
88
|
+
}
|
|
89
|
+
// Should have received fewer than all 3 chunks
|
|
90
|
+
expect(chunks.length).toBeLessThan(2);
|
|
91
|
+
});
|
|
92
|
+
it("should handle pre-aborted signal", async () => {
|
|
93
|
+
const agent = new SandAgent({
|
|
94
|
+
sandbox: mockSandbox,
|
|
95
|
+
runner: {
|
|
96
|
+
kind: "claude-agent-sdk",
|
|
97
|
+
model: "claude-3-5-sonnet-20241022",
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
const controller = new AbortController();
|
|
101
|
+
controller.abort(); // Abort before streaming
|
|
102
|
+
const input = {
|
|
103
|
+
messages: [{ role: "user", content: "test" }],
|
|
104
|
+
signal: controller.signal,
|
|
105
|
+
};
|
|
106
|
+
await expect(agent.stream(input)).rejects.toThrow("Operation was aborted");
|
|
107
|
+
});
|
|
108
|
+
it("should work without signal (backward compatibility)", async () => {
|
|
109
|
+
const agent = new SandAgent({
|
|
110
|
+
sandbox: mockSandbox,
|
|
111
|
+
runner: {
|
|
112
|
+
kind: "claude-agent-sdk",
|
|
113
|
+
model: "claude-3-5-sonnet-20241022",
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
const input = {
|
|
117
|
+
messages: [{ role: "user", content: "test" }],
|
|
118
|
+
// No signal provided
|
|
119
|
+
};
|
|
120
|
+
const stream = await agent.stream(input);
|
|
121
|
+
expect(stream).toBeInstanceOf(ReadableStream);
|
|
122
|
+
// Verify that exec was called without signal
|
|
123
|
+
expect(execSignalReceived).toBeUndefined();
|
|
124
|
+
// Should be able to read all chunks
|
|
125
|
+
const reader = stream.getReader();
|
|
126
|
+
const chunks = [];
|
|
127
|
+
while (true) {
|
|
128
|
+
const result = await reader.read();
|
|
129
|
+
if (result.done)
|
|
130
|
+
break;
|
|
131
|
+
chunks.push(result.value);
|
|
132
|
+
}
|
|
133
|
+
expect(chunks.length).toBe(3);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
//# sourceMappingURL=signal-integration.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signal-integration.test.js","sourceRoot":"","sources":["../../src/__tests__/signal-integration.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/transcript.test.ts"],"names":[],"mappings":""}
|