@giselles-ai/sandbox-agent 0.1.11 → 0.1.13

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/dist/index.d.ts CHANGED
@@ -1,6 +1,30 @@
1
1
  import { z } from 'zod';
2
2
  import { Sandbox } from '@vercel/sandbox';
3
3
 
4
+ type AgentType = "codex" | "gemini";
5
+ declare class Agent {
6
+ private _type;
7
+ private _snapshotId;
8
+ private _pendingOps;
9
+ private constructor();
10
+ static create(type: AgentType, options: {
11
+ snapshotId: string;
12
+ }): Agent;
13
+ get type(): AgentType;
14
+ get snapshotId(): string;
15
+ get dirty(): boolean;
16
+ addFiles(files: Array<{
17
+ path: string;
18
+ content: Buffer;
19
+ }>): this;
20
+ setAgentMd(content: string | Buffer): this;
21
+ runCommands(commands: Array<{
22
+ cmd: string;
23
+ args?: string[];
24
+ }>): this;
25
+ prepare(): Promise<void>;
26
+ }
27
+
4
28
  type BaseChatRequest = {
5
29
  message: string;
6
30
  session_id?: string;
@@ -78,4 +102,4 @@ type GeminiAgentOptions = {
78
102
  };
79
103
  declare function createGeminiAgent(options?: GeminiAgentOptions): ChatAgent<GeminiAgentRequest>;
80
104
 
81
- export { type BaseChatRequest, type ChatAgent, type ChatCommand, type RunChatInput, type StdoutMapper, createCodexAgent, createCodexStdoutMapper, createGeminiAgent, runChat };
105
+ export { Agent, type BaseChatRequest, type ChatAgent, type ChatCommand, type RunChatInput, type StdoutMapper, createCodexAgent, createCodexStdoutMapper, createGeminiAgent, runChat };
package/dist/index.js CHANGED
@@ -1,3 +1,87 @@
1
+ // src/agent.ts
2
+ import { Sandbox } from "@vercel/sandbox";
3
+ var Agent = class _Agent {
4
+ _type;
5
+ _snapshotId;
6
+ _pendingOps = [];
7
+ constructor(type, snapshotId) {
8
+ this._type = type;
9
+ this._snapshotId = snapshotId;
10
+ }
11
+ static create(type, options) {
12
+ const trimmed = options.snapshotId.trim();
13
+ if (!trimmed) {
14
+ throw new Error("snapshotId is required.");
15
+ }
16
+ return new _Agent(type, trimmed);
17
+ }
18
+ get type() {
19
+ return this._type;
20
+ }
21
+ get snapshotId() {
22
+ return this._snapshotId;
23
+ }
24
+ get dirty() {
25
+ return this._pendingOps.length > 0;
26
+ }
27
+ addFiles(files) {
28
+ if (files.length === 0) {
29
+ return this;
30
+ }
31
+ this._pendingOps.push({
32
+ kind: "writeFiles",
33
+ files
34
+ });
35
+ return this;
36
+ }
37
+ setAgentMd(content) {
38
+ const buffer = typeof content === "string" ? Buffer.from(content) : content;
39
+ console.log(`[agent] setAgentMd called, content length=${buffer.length}`);
40
+ return this.addFiles([
41
+ { path: "/home/vercel-sandbox/AGENTS.md", content: buffer }
42
+ ]);
43
+ }
44
+ runCommands(commands) {
45
+ for (const command of commands) {
46
+ this._pendingOps.push({
47
+ kind: "runCommand",
48
+ cmd: command.cmd,
49
+ args: command.args ?? []
50
+ });
51
+ }
52
+ return this;
53
+ }
54
+ async prepare() {
55
+ console.log(
56
+ `[agent] prepare called, dirty=${this.dirty}, pendingOps=${this._pendingOps.length}`
57
+ );
58
+ if (!this.dirty) {
59
+ return;
60
+ }
61
+ const ops = this._pendingOps;
62
+ const sandbox = await Sandbox.create({
63
+ source: { type: "snapshot", snapshotId: this._snapshotId }
64
+ });
65
+ console.log(
66
+ `[sandbox] created sandbox=${sandbox.sandboxId} from snapshot=${this._snapshotId}`
67
+ );
68
+ for (const op of ops) {
69
+ switch (op.kind) {
70
+ case "writeFiles":
71
+ await sandbox.writeFiles(op.files);
72
+ break;
73
+ case "runCommand":
74
+ await sandbox.runCommand(op.cmd, op.args);
75
+ break;
76
+ }
77
+ }
78
+ const snapshot = await sandbox.snapshot();
79
+ console.log(`[agent] prepare done, new snapshotId=${snapshot.snapshotId}`);
80
+ this._snapshotId = snapshot.snapshotId;
81
+ this._pendingOps = [];
82
+ }
83
+ };
84
+
1
85
  // src/agents/codex-agent.ts
2
86
  import * as TOML from "@iarna/toml";
3
87
  import { z } from "zod";
@@ -39,6 +123,18 @@ function mapEvent(event) {
39
123
  content: typeof event.message === "string" ? event.message : JSON.stringify(event)
40
124
  };
41
125
  }
126
+ if (type === "item.completed") {
127
+ const item = event.item;
128
+ if (item?.type === "agent_message" && typeof item.text === "string") {
129
+ return {
130
+ type: "message",
131
+ role: "assistant",
132
+ content: item.text,
133
+ delta: false
134
+ };
135
+ }
136
+ return null;
137
+ }
42
138
  if (type === "response.completed") {
43
139
  return null;
44
140
  }
@@ -158,18 +254,11 @@ function assertBrowserToolRelayCredentials(parsed) {
158
254
  function createCodexAgent(options = {}) {
159
255
  const env = options.env ?? {};
160
256
  const snapshotId = options.snapshotId?.trim() || requiredEnv(env, "SANDBOX_SNAPSHOT_ID");
161
- const apiKey = env.CODEX_API_KEY?.trim() || env.OPENAI_API_KEY?.trim();
162
- if (!apiKey) {
163
- throw new Error(
164
- "Missing required environment variable: CODEX_API_KEY or OPENAI_API_KEY"
165
- );
166
- }
257
+ const apiKey = requiredEnv(env, "CODEX_API_KEY");
167
258
  const browserToolEnabled = options.tools?.browser !== void 0;
168
259
  const browserToolRelayUrl = options.tools?.browser?.relayUrl?.trim();
169
260
  if (browserToolEnabled) {
170
261
  requiredEnv(env, "BROWSER_TOOL_RELAY_URL");
171
- requiredEnv(env, "BROWSER_TOOL_RELAY_SESSION_ID");
172
- requiredEnv(env, "BROWSER_TOOL_RELAY_TOKEN");
173
262
  }
174
263
  if (browserToolEnabled && !browserToolRelayUrl) {
175
264
  throw new Error("tools.browser.relayUrl is empty.");
@@ -181,9 +270,13 @@ function createCodexAgent(options = {}) {
181
270
  if (!browserToolEnabled) {
182
271
  return;
183
272
  }
184
- requiredEnv(env, "VERCEL_OIDC_TOKEN");
185
273
  assertBrowserToolRelayCredentials(_input.input);
186
- await patchCodexConfigTransportEnv(_input.sandbox, env);
274
+ const patchEnv = {
275
+ ...env,
276
+ BROWSER_TOOL_RELAY_SESSION_ID: _input.input.relay_session_id,
277
+ BROWSER_TOOL_RELAY_TOKEN: _input.input.relay_token
278
+ };
279
+ await patchCodexConfigTransportEnv(_input.sandbox, patchEnv);
187
280
  },
188
281
  createCommand({ input }) {
189
282
  const args = ["exec"];
@@ -194,9 +287,7 @@ function createCodexAgent(options = {}) {
194
287
  return {
195
288
  cmd: "codex",
196
289
  args,
197
- env: {
198
- OPENAI_API_KEY: apiKey
199
- }
290
+ env: { CODEX_API_KEY: apiKey }
200
291
  };
201
292
  },
202
293
  createStdoutMapper() {
@@ -283,8 +374,6 @@ function createGeminiAgent(options = {}) {
283
374
  const browserToolRelayUrl = options.tools?.browser?.relayUrl?.trim();
284
375
  if (browserToolEnabled) {
285
376
  requiredEnv2(env, "BROWSER_TOOL_RELAY_URL");
286
- requiredEnv2(env, "BROWSER_TOOL_RELAY_SESSION_ID");
287
- requiredEnv2(env, "BROWSER_TOOL_RELAY_TOKEN");
288
377
  }
289
378
  if (browserToolEnabled && !browserToolRelayUrl) {
290
379
  throw new Error("tools.browser.relayUrl is empty.");
@@ -296,9 +385,13 @@ function createGeminiAgent(options = {}) {
296
385
  if (!browserToolEnabled) {
297
386
  return;
298
387
  }
299
- requiredEnv2(env, "VERCEL_OIDC_TOKEN");
300
388
  assertBrowserToolRelayCredentials2(input);
301
- await patchGeminiSettingsTransportEnv(sandbox, env);
389
+ const patchEnv = {
390
+ ...env,
391
+ BROWSER_TOOL_RELAY_SESSION_ID: input.relay_session_id,
392
+ BROWSER_TOOL_RELAY_TOKEN: input.relay_token
393
+ };
394
+ await patchGeminiSettingsTransportEnv(sandbox, patchEnv);
302
395
  },
303
396
  createCommand({ input }) {
304
397
  const args = [
@@ -325,7 +418,7 @@ function createGeminiAgent(options = {}) {
325
418
 
326
419
  // src/chat-run.ts
327
420
  import { Writable } from "stream";
328
- import { Sandbox } from "@vercel/sandbox";
421
+ import { Sandbox as Sandbox2 } from "@vercel/sandbox";
329
422
  function emitText(controller, text, encoder) {
330
423
  if (text.length === 0) {
331
424
  return;
@@ -379,19 +472,23 @@ function runChat(input) {
379
472
  const mapper = input.agent.createStdoutMapper?.();
380
473
  void (async () => {
381
474
  try {
382
- const sandbox = parsed.sandbox_id ? await Sandbox.get({ sandboxId: parsed.sandbox_id }) : await (async () => {
475
+ const sandbox = parsed.sandbox_id ? await Sandbox2.get({ sandboxId: parsed.sandbox_id }) : await (async () => {
383
476
  const snapshotId = input.agent.snapshotId?.trim();
384
477
  if (!snapshotId) {
385
478
  throw new Error(
386
479
  "Agent must provide snapshotId when sandbox_id is not provided."
387
480
  );
388
481
  }
389
- return Sandbox.create({
482
+ const created = await Sandbox2.create({
390
483
  source: {
391
484
  type: "snapshot",
392
485
  snapshotId
393
486
  }
394
487
  });
488
+ console.log(
489
+ `[sandbox] created sandbox=${created.sandboxId} from snapshot=${snapshotId}`
490
+ );
491
+ return created;
395
492
  })();
396
493
  enqueueEvent({ type: "sandbox", sandbox_id: sandbox.sandboxId });
397
494
  await input.agent.prepareSandbox({
@@ -408,6 +505,7 @@ function runChat(input) {
408
505
  stdout: new Writable({
409
506
  write(chunk, _encoding, callback) {
410
507
  const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
508
+ console.log("[sandbox-agent] raw stdout:", text);
411
509
  if (mapper) {
412
510
  for (const line of mapper.push(text)) {
413
511
  enqueueStdout(line);
@@ -455,6 +553,7 @@ function runChat(input) {
455
553
  );
456
554
  }
457
555
  export {
556
+ Agent,
458
557
  createCodexAgent,
459
558
  createCodexStdoutMapper,
460
559
  createGeminiAgent,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@giselles-ai/sandbox-agent",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "license": "Apache-2.0",