sandbox-agent 0.3.2 → 0.4.0-rc.2
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/chunk-LSO36KRX.js +15 -0
- package/dist/chunk-LSO36KRX.js.map +1 -0
- package/dist/index.d.ts +31 -20
- package/dist/index.js +180 -57
- package/dist/index.js.map +1 -1
- package/dist/providers/cloudflare.d.ts +21 -0
- package/dist/providers/cloudflare.js +60 -0
- package/dist/providers/cloudflare.js.map +1 -0
- package/dist/providers/computesdk.d.ts +11 -0
- package/dist/providers/computesdk.js +56 -0
- package/dist/providers/computesdk.js.map +1 -0
- package/dist/providers/daytona.d.ts +15 -0
- package/dist/providers/daytona.js +59 -0
- package/dist/providers/daytona.js.map +1 -0
- package/dist/providers/docker.d.ts +13 -0
- package/dist/providers/docker.js +75 -0
- package/dist/providers/docker.js.map +1 -0
- package/dist/providers/e2b.d.ts +10 -0
- package/dist/providers/e2b.js +59 -0
- package/dist/providers/e2b.js.map +1 -0
- package/dist/providers/local.d.ts +14 -0
- package/dist/{spawn-ROM6CN74.js → providers/local.js} +67 -3
- package/dist/providers/local.js.map +1 -0
- package/dist/providers/modal.d.ts +13 -0
- package/dist/providers/modal.js +58 -0
- package/dist/providers/modal.js.map +1 -0
- package/dist/providers/vercel.d.ts +9 -0
- package/dist/providers/vercel.js +62 -0
- package/dist/providers/vercel.js.map +1 -0
- package/dist/spawn-76JDF5d3.d.ts +13 -0
- package/dist/types-DLlJOfyX.d.ts +28 -0
- package/package.json +81 -4
- package/dist/spawn-ROM6CN74.js.map +0 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/providers/shared.ts
|
|
2
|
+
var DEFAULT_SANDBOX_AGENT_IMAGE = "rivetdev/sandbox-agent:0.4.0-rc.2-full";
|
|
3
|
+
var SANDBOX_AGENT_INSTALL_SCRIPT = "https://releases.rivet.dev/sandbox-agent/0.3.x/install.sh";
|
|
4
|
+
var DEFAULT_AGENTS = ["claude", "codex"];
|
|
5
|
+
function buildServerStartCommand(port) {
|
|
6
|
+
return `nohup sandbox-agent server --no-token --host 0.0.0.0 --port ${port} >/tmp/sandbox-agent.log 2>&1 &`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
DEFAULT_SANDBOX_AGENT_IMAGE,
|
|
11
|
+
SANDBOX_AGENT_INSTALL_SCRIPT,
|
|
12
|
+
DEFAULT_AGENTS,
|
|
13
|
+
buildServerStartCommand
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=chunk-LSO36KRX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/shared.ts"],"sourcesContent":["export const DEFAULT_SANDBOX_AGENT_IMAGE = \"rivetdev/sandbox-agent:0.4.0-rc.2-full\";\nexport const SANDBOX_AGENT_INSTALL_SCRIPT = \"https://releases.rivet.dev/sandbox-agent/0.3.x/install.sh\";\nexport const DEFAULT_AGENTS = [\"claude\", \"codex\"] as const;\n\nexport function buildServerStartCommand(port: number): string {\n return `nohup sandbox-agent server --no-token --host 0.0.0.0 --port ${port} >/tmp/sandbox-agent.log 2>&1 &`;\n}\n"],"mappings":";AAAO,IAAM,8BAA8B;AACpC,IAAM,+BAA+B;AACrC,IAAM,iBAAiB,CAAC,UAAU,OAAO;AAEzC,SAAS,wBAAwB,MAAsB;AAC5D,SAAO,+DAA+D,IAAI;AAC5E;","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,7 @@
|
|
|
1
1
|
import { NewSessionRequest, SessionConfigOption, SessionModeState, AnyMessage, AcpEnvelopeDirection, RequestPermissionRequest, RequestPermissionResponse, NewSessionResponse, PermissionOptionKind, PromptRequest, PromptResponse, SetSessionModeResponse, SetSessionConfigOptionResponse } from 'acp-http-client';
|
|
2
2
|
export { AcpRpcError } from 'acp-http-client';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
type SandboxAgentSpawnOptions = {
|
|
6
|
-
enabled?: boolean;
|
|
7
|
-
host?: string;
|
|
8
|
-
port?: number;
|
|
9
|
-
token?: string;
|
|
10
|
-
binaryPath?: string;
|
|
11
|
-
timeoutMs?: number;
|
|
12
|
-
log?: SandboxAgentSpawnLogMode;
|
|
13
|
-
env?: Record<string, string>;
|
|
14
|
-
};
|
|
3
|
+
import { S as SandboxProvider } from './types-DLlJOfyX.js';
|
|
4
|
+
export { S as SandboxAgentSpawnLogMode, a as SandboxAgentSpawnOptions } from './spawn-76JDF5d3.js';
|
|
15
5
|
|
|
16
6
|
interface components {
|
|
17
7
|
schemas: {
|
|
@@ -1368,6 +1358,7 @@ interface SessionRecord {
|
|
|
1368
1358
|
lastConnectionId: string;
|
|
1369
1359
|
createdAt: number;
|
|
1370
1360
|
destroyedAt?: number;
|
|
1361
|
+
sandboxId?: string;
|
|
1371
1362
|
sessionInit?: Omit<NewSessionRequest, "_meta">;
|
|
1372
1363
|
configOptions?: SessionConfigOption[];
|
|
1373
1364
|
modes?: SessionModeState | null;
|
|
@@ -1394,11 +1385,11 @@ interface ListEventsRequest extends ListPageRequest {
|
|
|
1394
1385
|
sessionId: string;
|
|
1395
1386
|
}
|
|
1396
1387
|
interface SessionPersistDriver {
|
|
1397
|
-
getSession(id: string): Promise<SessionRecord |
|
|
1388
|
+
getSession(id: string): Promise<SessionRecord | undefined>;
|
|
1398
1389
|
listSessions(request?: ListPageRequest): Promise<ListPage<SessionRecord>>;
|
|
1399
1390
|
updateSession(session: SessionRecord): Promise<void>;
|
|
1400
1391
|
listEvents(request: ListEventsRequest): Promise<ListPage<SessionEvent>>;
|
|
1401
|
-
insertEvent(event: SessionEvent): Promise<void>;
|
|
1392
|
+
insertEvent(sessionId: string, event: SessionEvent): Promise<void>;
|
|
1402
1393
|
}
|
|
1403
1394
|
interface InMemorySessionPersistDriverOptions {
|
|
1404
1395
|
maxSessions?: number;
|
|
@@ -1410,11 +1401,11 @@ declare class InMemorySessionPersistDriver implements SessionPersistDriver {
|
|
|
1410
1401
|
private readonly sessions;
|
|
1411
1402
|
private readonly eventsBySession;
|
|
1412
1403
|
constructor(options?: InMemorySessionPersistDriverOptions);
|
|
1413
|
-
getSession(id: string): Promise<SessionRecord |
|
|
1404
|
+
getSession(id: string): Promise<SessionRecord | undefined>;
|
|
1414
1405
|
listSessions(request?: ListPageRequest): Promise<ListPage<SessionRecord>>;
|
|
1415
1406
|
updateSession(session: SessionRecord): Promise<void>;
|
|
1416
1407
|
listEvents(request: ListEventsRequest): Promise<ListPage<SessionEvent>>;
|
|
1417
|
-
insertEvent(event: SessionEvent): Promise<void>;
|
|
1408
|
+
insertEvent(sessionId: string, event: SessionEvent): Promise<void>;
|
|
1418
1409
|
}
|
|
1419
1410
|
type ResponsesOf<T> = T extends {
|
|
1420
1411
|
responses: infer R;
|
|
@@ -1451,6 +1442,8 @@ interface SandboxAgentConnectCommonOptions {
|
|
|
1451
1442
|
replayMaxChars?: number;
|
|
1452
1443
|
signal?: AbortSignal;
|
|
1453
1444
|
token?: string;
|
|
1445
|
+
skipHealthCheck?: boolean;
|
|
1446
|
+
/** @deprecated Use skipHealthCheck instead. */
|
|
1454
1447
|
waitForHealth?: boolean | SandboxAgentHealthWaitOptions;
|
|
1455
1448
|
}
|
|
1456
1449
|
type SandboxAgentConnectOptions = (SandboxAgentConnectCommonOptions & {
|
|
@@ -1461,16 +1454,23 @@ type SandboxAgentConnectOptions = (SandboxAgentConnectCommonOptions & {
|
|
|
1461
1454
|
baseUrl?: string;
|
|
1462
1455
|
});
|
|
1463
1456
|
interface SandboxAgentStartOptions {
|
|
1457
|
+
sandbox: SandboxProvider;
|
|
1458
|
+
sandboxId?: string;
|
|
1459
|
+
skipHealthCheck?: boolean;
|
|
1464
1460
|
fetch?: typeof fetch;
|
|
1465
1461
|
headers?: HeadersInit;
|
|
1466
1462
|
persist?: SessionPersistDriver;
|
|
1467
1463
|
replayMaxEvents?: number;
|
|
1468
1464
|
replayMaxChars?: number;
|
|
1469
|
-
|
|
1465
|
+
signal?: AbortSignal;
|
|
1466
|
+
token?: string;
|
|
1470
1467
|
}
|
|
1471
1468
|
interface SessionCreateRequest {
|
|
1472
1469
|
id?: string;
|
|
1473
1470
|
agent: string;
|
|
1471
|
+
/** Shorthand for `sessionInit.cwd`. Ignored when `sessionInit` is provided. */
|
|
1472
|
+
cwd?: string;
|
|
1473
|
+
/** Full session init. When omitted, built from `cwd` (or default) with empty `mcpServers`. */
|
|
1474
1474
|
sessionInit?: Omit<NewSessionRequest, "_meta">;
|
|
1475
1475
|
model?: string;
|
|
1476
1476
|
mode?: string;
|
|
@@ -1479,6 +1479,9 @@ interface SessionCreateRequest {
|
|
|
1479
1479
|
interface SessionResumeOrCreateRequest {
|
|
1480
1480
|
id: string;
|
|
1481
1481
|
agent: string;
|
|
1482
|
+
/** Shorthand for `sessionInit.cwd`. Ignored when `sessionInit` is provided. */
|
|
1483
|
+
cwd?: string;
|
|
1484
|
+
/** Full session init. When omitted, built from `cwd` (or default) with empty `mcpServers`. */
|
|
1482
1485
|
sessionInit?: Omit<NewSessionRequest, "_meta">;
|
|
1483
1486
|
model?: string;
|
|
1484
1487
|
mode?: string;
|
|
@@ -1647,10 +1650,12 @@ declare class SandboxAgent {
|
|
|
1647
1650
|
private readonly defaultHeaders?;
|
|
1648
1651
|
private readonly healthWait;
|
|
1649
1652
|
private readonly healthWaitAbortController;
|
|
1653
|
+
private sandboxProvider?;
|
|
1654
|
+
private sandboxProviderId?;
|
|
1655
|
+
private sandboxProviderRawId?;
|
|
1650
1656
|
private readonly persist;
|
|
1651
1657
|
private readonly replayMaxEvents;
|
|
1652
1658
|
private readonly replayMaxChars;
|
|
1653
|
-
private spawnHandle?;
|
|
1654
1659
|
private healthPromise?;
|
|
1655
1660
|
private healthError?;
|
|
1656
1661
|
private disposed;
|
|
@@ -1662,10 +1667,15 @@ declare class SandboxAgent {
|
|
|
1662
1667
|
private readonly pendingPermissionRequests;
|
|
1663
1668
|
private readonly nextSessionEventIndexBySession;
|
|
1664
1669
|
private readonly seedSessionEventIndexBySession;
|
|
1670
|
+
private readonly pendingObservedEnvelopePersistenceBySession;
|
|
1665
1671
|
constructor(options: SandboxAgentConnectOptions);
|
|
1666
1672
|
static connect(options: SandboxAgentConnectOptions): Promise<SandboxAgent>;
|
|
1667
|
-
static start(options
|
|
1673
|
+
static start(options: SandboxAgentStartOptions): Promise<SandboxAgent>;
|
|
1674
|
+
get sandboxId(): string | undefined;
|
|
1675
|
+
get sandbox(): SandboxProvider | undefined;
|
|
1676
|
+
get inspectorUrl(): string;
|
|
1668
1677
|
dispose(): Promise<void>;
|
|
1678
|
+
destroySandbox(): Promise<void>;
|
|
1669
1679
|
listSessions(request?: ListPageRequest): Promise<ListPage<Session>>;
|
|
1670
1680
|
getSession(id: string): Promise<Session | null>;
|
|
1671
1681
|
getEvents(request: ListEventsRequest): Promise<ListPage<SessionEvent>>;
|
|
@@ -1740,6 +1750,7 @@ declare class SandboxAgent {
|
|
|
1740
1750
|
connectProcessTerminal(id: string, options?: ProcessTerminalSessionOptions): ProcessTerminalSession;
|
|
1741
1751
|
private getLiveConnection;
|
|
1742
1752
|
private persistObservedEnvelope;
|
|
1753
|
+
private enqueueObservedEnvelopePersistence;
|
|
1743
1754
|
private persistSessionStateFromEvent;
|
|
1744
1755
|
private allocateSessionEventIndex;
|
|
1745
1756
|
private ensureSessionEventIndexSeeded;
|
|
@@ -1781,4 +1792,4 @@ interface InspectorUrlOptions {
|
|
|
1781
1792
|
*/
|
|
1782
1793
|
declare function buildInspectorUrl(options: InspectorUrlOptions): string;
|
|
1783
1794
|
|
|
1784
|
-
export { type AcpEnvelope, type AcpServerInfo, type AcpServerListResponse, type AgentInfo, type AgentInstallRequest, type AgentInstallResponse, type AgentListResponse, type AgentQuery, type AgentQueryOptions, type FsActionResponse, type FsDeleteQuery, type FsEntriesQuery, type FsEntry, type FsMoveRequest, type FsMoveResponse, type FsPathQuery, type FsStat, type FsUploadBatchQuery, type FsUploadBatchResponse, type FsWriteResponse, type HealthResponse, InMemorySessionPersistDriver, type InMemorySessionPersistDriverOptions, type InspectorUrlOptions, type ListEventsRequest, type ListPage, type ListPageRequest, LiveAcpConnection, type McpConfigQuery, type McpServerConfig, type PermissionReply, type PermissionRequestListener, type ProblemDetails, type ProcessConfig, type ProcessCreateRequest, type ProcessInfo, type ProcessInputRequest, type ProcessInputResponse, type ProcessListResponse, type ProcessLogEntry, type ProcessLogFollowQuery, type ProcessLogListener, type ProcessLogSubscription, type ProcessLogsQuery, type ProcessLogsResponse, type ProcessLogsStream, type ProcessRunRequest, type ProcessRunResponse, type ProcessSignalQuery, type ProcessState, type ProcessTerminalClientFrame, type ProcessTerminalConnectOptions, type ProcessTerminalErrorFrame, type ProcessTerminalExitFrame, type ProcessTerminalReadyFrame, type ProcessTerminalResizeRequest, type ProcessTerminalResizeResponse, type ProcessTerminalServerFrame, ProcessTerminalSession, type ProcessTerminalSessionOptions, type ProcessTerminalWebSocketUrlOptions, SandboxAgent, type SandboxAgentConnectOptions, SandboxAgentError, type SandboxAgentHealthWaitOptions, type
|
|
1795
|
+
export { type AcpEnvelope, type AcpServerInfo, type AcpServerListResponse, type AgentInfo, type AgentInstallRequest, type AgentInstallResponse, type AgentListResponse, type AgentQuery, type AgentQueryOptions, type FsActionResponse, type FsDeleteQuery, type FsEntriesQuery, type FsEntry, type FsMoveRequest, type FsMoveResponse, type FsPathQuery, type FsStat, type FsUploadBatchQuery, type FsUploadBatchResponse, type FsWriteResponse, type HealthResponse, InMemorySessionPersistDriver, type InMemorySessionPersistDriverOptions, type InspectorUrlOptions, type ListEventsRequest, type ListPage, type ListPageRequest, LiveAcpConnection, type McpConfigQuery, type McpServerConfig, type PermissionReply, type PermissionRequestListener, type ProblemDetails, type ProcessConfig, type ProcessCreateRequest, type ProcessInfo, type ProcessInputRequest, type ProcessInputResponse, type ProcessListResponse, type ProcessLogEntry, type ProcessLogFollowQuery, type ProcessLogListener, type ProcessLogSubscription, type ProcessLogsQuery, type ProcessLogsResponse, type ProcessLogsStream, type ProcessRunRequest, type ProcessRunResponse, type ProcessSignalQuery, type ProcessState, type ProcessTerminalClientFrame, type ProcessTerminalConnectOptions, type ProcessTerminalErrorFrame, type ProcessTerminalExitFrame, type ProcessTerminalReadyFrame, type ProcessTerminalResizeRequest, type ProcessTerminalResizeResponse, type ProcessTerminalServerFrame, ProcessTerminalSession, type ProcessTerminalSessionOptions, type ProcessTerminalWebSocketUrlOptions, SandboxAgent, type SandboxAgentConnectOptions, SandboxAgentError, type SandboxAgentHealthWaitOptions, type SandboxAgentStartOptions, SandboxProvider, Session, type SessionCreateRequest, type SessionEvent, type SessionEventListener, type SessionPermissionRequest, type SessionPermissionRequestOption, type SessionPersistDriver, type SessionRecord, type SessionResumeOrCreateRequest, type SessionSendOptions, type SkillsConfig, type SkillsConfigQuery, type TerminalErrorStatus, type TerminalExitStatus, type TerminalReadyStatus, type TerminalResizePayload, type TerminalStatusMessage, UnsupportedPermissionReplyError, UnsupportedSessionCategoryError, UnsupportedSessionConfigOptionError, UnsupportedSessionValueError, buildInspectorUrl };
|
package/dist/index.js
CHANGED
|
@@ -20,7 +20,7 @@ var InMemorySessionPersistDriver = class {
|
|
|
20
20
|
}
|
|
21
21
|
async getSession(id) {
|
|
22
22
|
const session = this.sessions.get(id);
|
|
23
|
-
return session ? cloneSessionRecord(session) :
|
|
23
|
+
return session ? cloneSessionRecord(session) : void 0;
|
|
24
24
|
}
|
|
25
25
|
async listSessions(request = {}) {
|
|
26
26
|
const sorted = [...this.sessions.values()].sort((a, b) => {
|
|
@@ -68,13 +68,13 @@ var InMemorySessionPersistDriver = class {
|
|
|
68
68
|
nextCursor: page.nextCursor
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
|
-
async insertEvent(event) {
|
|
72
|
-
const events = this.eventsBySession.get(
|
|
71
|
+
async insertEvent(sessionId, event) {
|
|
72
|
+
const events = this.eventsBySession.get(sessionId) ?? [];
|
|
73
73
|
events.push(cloneSessionEvent(event));
|
|
74
74
|
if (events.length > this.maxEventsPerSession) {
|
|
75
75
|
events.splice(0, events.length - this.maxEventsPerSession);
|
|
76
76
|
}
|
|
77
|
-
this.eventsBySession.set(
|
|
77
|
+
this.eventsBySession.set(sessionId, events);
|
|
78
78
|
}
|
|
79
79
|
};
|
|
80
80
|
function cloneSessionRecord(session) {
|
|
@@ -125,12 +125,14 @@ var DEFAULT_BASE_URL = "http://sandbox-agent";
|
|
|
125
125
|
var DEFAULT_REPLAY_MAX_EVENTS = 50;
|
|
126
126
|
var DEFAULT_REPLAY_MAX_CHARS = 12e3;
|
|
127
127
|
var EVENT_INDEX_SCAN_EVENTS_LIMIT = 500;
|
|
128
|
+
var MAX_EVENT_INDEX_INSERT_RETRIES = 3;
|
|
128
129
|
var SESSION_CANCEL_METHOD = "session/cancel";
|
|
129
130
|
var MANUAL_CANCEL_ERROR = "Manual session/cancel calls are not allowed. Use destroySession(sessionId) instead.";
|
|
130
131
|
var HEALTH_WAIT_MIN_DELAY_MS = 500;
|
|
131
132
|
var HEALTH_WAIT_MAX_DELAY_MS = 15e3;
|
|
132
133
|
var HEALTH_WAIT_LOG_AFTER_MS = 5e3;
|
|
133
134
|
var HEALTH_WAIT_LOG_EVERY_MS = 1e4;
|
|
135
|
+
var HEALTH_WAIT_ENSURE_SERVER_AFTER_FAILURES = 3;
|
|
134
136
|
var SandboxAgentError = class extends Error {
|
|
135
137
|
status;
|
|
136
138
|
problem;
|
|
@@ -638,10 +640,12 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
638
640
|
defaultHeaders;
|
|
639
641
|
healthWait;
|
|
640
642
|
healthWaitAbortController = new AbortController();
|
|
643
|
+
sandboxProvider;
|
|
644
|
+
sandboxProviderId;
|
|
645
|
+
sandboxProviderRawId;
|
|
641
646
|
persist;
|
|
642
647
|
replayMaxEvents;
|
|
643
648
|
replayMaxChars;
|
|
644
|
-
spawnHandle;
|
|
645
649
|
healthPromise;
|
|
646
650
|
healthError;
|
|
647
651
|
disposed = false;
|
|
@@ -653,6 +657,7 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
653
657
|
pendingPermissionRequests = /* @__PURE__ */ new Map();
|
|
654
658
|
nextSessionEventIndexBySession = /* @__PURE__ */ new Map();
|
|
655
659
|
seedSessionEventIndexBySession = /* @__PURE__ */ new Map();
|
|
660
|
+
pendingObservedEnvelopePersistenceBySession = /* @__PURE__ */ new Map();
|
|
656
661
|
constructor(options) {
|
|
657
662
|
const baseUrl = options.baseUrl?.trim();
|
|
658
663
|
if (!baseUrl && !options.fetch) {
|
|
@@ -666,7 +671,7 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
666
671
|
}
|
|
667
672
|
this.fetcher = resolvedFetch;
|
|
668
673
|
this.defaultHeaders = options.headers;
|
|
669
|
-
this.healthWait = normalizeHealthWaitOptions(options.waitForHealth, options.signal);
|
|
674
|
+
this.healthWait = normalizeHealthWaitOptions(options.skipHealthCheck, options.waitForHealth, options.signal);
|
|
670
675
|
this.persist = options.persist ?? new InMemorySessionPersistDriver();
|
|
671
676
|
this.replayMaxEvents = normalizePositiveInt(options.replayMaxEvents, DEFAULT_REPLAY_MAX_EVENTS);
|
|
672
677
|
this.replayMaxChars = normalizePositiveInt(options.replayMaxChars, DEFAULT_REPLAY_MAX_CHARS);
|
|
@@ -675,26 +680,66 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
675
680
|
static async connect(options) {
|
|
676
681
|
return new _SandboxAgent(options);
|
|
677
682
|
}
|
|
678
|
-
static async start(options
|
|
679
|
-
const
|
|
680
|
-
if (!
|
|
681
|
-
throw new Error(
|
|
683
|
+
static async start(options) {
|
|
684
|
+
const provider = options.sandbox;
|
|
685
|
+
if (!provider.getUrl && !provider.getFetch) {
|
|
686
|
+
throw new Error(`Sandbox provider '${provider.name}' must implement getUrl() or getFetch().`);
|
|
682
687
|
}
|
|
683
|
-
const
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
688
|
+
const existingSandbox = options.sandboxId ? parseSandboxProviderId(options.sandboxId) : null;
|
|
689
|
+
if (existingSandbox && existingSandbox.provider !== provider.name) {
|
|
690
|
+
throw new Error(
|
|
691
|
+
`SandboxAgent.start received sandboxId '${options.sandboxId}' for provider '${existingSandbox.provider}', but the configured provider is '${provider.name}'.`
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
const rawSandboxId = existingSandbox?.rawId ?? await provider.create();
|
|
695
|
+
const prefixedSandboxId = `${provider.name}/${rawSandboxId}`;
|
|
696
|
+
const createdSandbox = !existingSandbox;
|
|
697
|
+
if (existingSandbox) {
|
|
698
|
+
await provider.ensureServer?.(rawSandboxId);
|
|
699
|
+
}
|
|
700
|
+
try {
|
|
701
|
+
const fetcher = await resolveProviderFetch(provider, rawSandboxId);
|
|
702
|
+
const baseUrl = provider.getUrl ? await provider.getUrl(rawSandboxId) : void 0;
|
|
703
|
+
const providerFetch = options.fetch ?? fetcher;
|
|
704
|
+
const commonConnectOptions = {
|
|
705
|
+
headers: options.headers,
|
|
706
|
+
persist: options.persist,
|
|
707
|
+
replayMaxEvents: options.replayMaxEvents,
|
|
708
|
+
replayMaxChars: options.replayMaxChars,
|
|
709
|
+
signal: options.signal,
|
|
710
|
+
skipHealthCheck: options.skipHealthCheck,
|
|
711
|
+
token: options.token ?? await resolveProviderToken(provider, rawSandboxId)
|
|
712
|
+
};
|
|
713
|
+
const client = providerFetch ? new _SandboxAgent({
|
|
714
|
+
...commonConnectOptions,
|
|
715
|
+
baseUrl,
|
|
716
|
+
fetch: providerFetch
|
|
717
|
+
}) : new _SandboxAgent({
|
|
718
|
+
...commonConnectOptions,
|
|
719
|
+
baseUrl: requireSandboxBaseUrl(baseUrl, provider.name)
|
|
720
|
+
});
|
|
721
|
+
client.sandboxProvider = provider;
|
|
722
|
+
client.sandboxProviderId = prefixedSandboxId;
|
|
723
|
+
client.sandboxProviderRawId = rawSandboxId;
|
|
724
|
+
return client;
|
|
725
|
+
} catch (error) {
|
|
726
|
+
if (createdSandbox) {
|
|
727
|
+
try {
|
|
728
|
+
await provider.destroy(rawSandboxId);
|
|
729
|
+
} catch {
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
throw error;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
get sandboxId() {
|
|
736
|
+
return this.sandboxProviderId;
|
|
737
|
+
}
|
|
738
|
+
get sandbox() {
|
|
739
|
+
return this.sandboxProvider;
|
|
740
|
+
}
|
|
741
|
+
get inspectorUrl() {
|
|
742
|
+
return `${this.baseUrl.replace(/\/+$/, "")}/ui/`;
|
|
698
743
|
}
|
|
699
744
|
async dispose() {
|
|
700
745
|
this.disposed = true;
|
|
@@ -707,6 +752,7 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
707
752
|
this.liveConnections.clear();
|
|
708
753
|
const pending = [...this.pendingLiveConnections.values()];
|
|
709
754
|
this.pendingLiveConnections.clear();
|
|
755
|
+
this.pendingObservedEnvelopePersistenceBySession.clear();
|
|
710
756
|
const pendingSettled = await Promise.allSettled(pending);
|
|
711
757
|
for (const item of pendingSettled) {
|
|
712
758
|
if (item.status === "fulfilled") {
|
|
@@ -718,9 +764,21 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
718
764
|
await connection.close();
|
|
719
765
|
})
|
|
720
766
|
);
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
767
|
+
}
|
|
768
|
+
async destroySandbox() {
|
|
769
|
+
const provider = this.sandboxProvider;
|
|
770
|
+
const rawSandboxId = this.sandboxProviderRawId;
|
|
771
|
+
try {
|
|
772
|
+
if (provider && rawSandboxId) {
|
|
773
|
+
await provider.destroy(rawSandboxId);
|
|
774
|
+
} else if (!provider || !rawSandboxId) {
|
|
775
|
+
throw new Error("SandboxAgent is not attached to a provisioned sandbox.");
|
|
776
|
+
}
|
|
777
|
+
} finally {
|
|
778
|
+
await this.dispose();
|
|
779
|
+
this.sandboxProvider = void 0;
|
|
780
|
+
this.sandboxProviderId = void 0;
|
|
781
|
+
this.sandboxProviderRawId = void 0;
|
|
724
782
|
}
|
|
725
783
|
}
|
|
726
784
|
async listSessions(request = {}) {
|
|
@@ -746,7 +804,7 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
746
804
|
}
|
|
747
805
|
const localSessionId = request.id?.trim() || randomId();
|
|
748
806
|
const live = await this.getLiveConnection(request.agent.trim());
|
|
749
|
-
const sessionInit = normalizeSessionInit(request.sessionInit);
|
|
807
|
+
const sessionInit = normalizeSessionInit(request.sessionInit, request.cwd);
|
|
750
808
|
const response = await live.createRemoteSession(localSessionId, sessionInit);
|
|
751
809
|
const record = {
|
|
752
810
|
id: localSessionId,
|
|
@@ -754,12 +812,12 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
754
812
|
agentSessionId: response.sessionId,
|
|
755
813
|
lastConnectionId: live.connectionId,
|
|
756
814
|
createdAt: nowMs(),
|
|
815
|
+
sandboxId: this.sandboxProviderId,
|
|
757
816
|
sessionInit,
|
|
758
817
|
configOptions: cloneConfigOptions(response.configOptions),
|
|
759
818
|
modes: cloneModes(response.modes)
|
|
760
819
|
};
|
|
761
820
|
await this.persist.updateSession(record);
|
|
762
|
-
this.nextSessionEventIndexBySession.set(record.id, 1);
|
|
763
821
|
live.bindSession(record.id, record.agentSessionId);
|
|
764
822
|
let session = this.upsertSessionHandle(record);
|
|
765
823
|
try {
|
|
@@ -1299,7 +1357,9 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
1299
1357
|
agent,
|
|
1300
1358
|
serverId,
|
|
1301
1359
|
onObservedEnvelope: (connection, envelope, direction, localSessionId) => {
|
|
1302
|
-
void this.
|
|
1360
|
+
void this.enqueueObservedEnvelopePersistence(connection, envelope, direction, localSessionId).catch((error) => {
|
|
1361
|
+
console.error("Failed to persist observed sandbox-agent envelope", error);
|
|
1362
|
+
});
|
|
1303
1363
|
},
|
|
1304
1364
|
onPermissionRequest: async (connection, localSessionId, agentSessionId, request) => this.enqueuePermissionRequest(connection, localSessionId, agentSessionId, request)
|
|
1305
1365
|
});
|
|
@@ -1324,16 +1384,29 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
1324
1384
|
if (!localSessionId) {
|
|
1325
1385
|
return;
|
|
1326
1386
|
}
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1387
|
+
let event = null;
|
|
1388
|
+
for (let attempt = 0; attempt < MAX_EVENT_INDEX_INSERT_RETRIES; attempt += 1) {
|
|
1389
|
+
event = {
|
|
1390
|
+
id: randomId(),
|
|
1391
|
+
eventIndex: await this.allocateSessionEventIndex(localSessionId),
|
|
1392
|
+
sessionId: localSessionId,
|
|
1393
|
+
createdAt: nowMs(),
|
|
1394
|
+
connectionId: connection.connectionId,
|
|
1395
|
+
sender: direction === "outbound" ? "client" : "agent",
|
|
1396
|
+
payload: cloneEnvelope(envelope)
|
|
1397
|
+
};
|
|
1398
|
+
try {
|
|
1399
|
+
await this.persist.insertEvent(localSessionId, event);
|
|
1400
|
+
break;
|
|
1401
|
+
} catch (error) {
|
|
1402
|
+
if (!isSessionEventIndexConflict(error) || attempt === MAX_EVENT_INDEX_INSERT_RETRIES - 1) {
|
|
1403
|
+
throw error;
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
if (!event) {
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1337
1410
|
await this.persistSessionStateFromEvent(localSessionId, envelope, direction);
|
|
1338
1411
|
const listeners = this.eventListeners.get(localSessionId);
|
|
1339
1412
|
if (!listeners || listeners.size === 0) {
|
|
@@ -1343,6 +1416,22 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
1343
1416
|
listener(event);
|
|
1344
1417
|
}
|
|
1345
1418
|
}
|
|
1419
|
+
async enqueueObservedEnvelopePersistence(connection, envelope, direction, localSessionId) {
|
|
1420
|
+
if (!localSessionId) {
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
const previous = this.pendingObservedEnvelopePersistenceBySession.get(localSessionId) ?? Promise.resolve();
|
|
1424
|
+
const current = previous.catch(() => {
|
|
1425
|
+
}).then(() => this.persistObservedEnvelope(connection, envelope, direction, localSessionId));
|
|
1426
|
+
this.pendingObservedEnvelopePersistenceBySession.set(localSessionId, current);
|
|
1427
|
+
try {
|
|
1428
|
+
await current;
|
|
1429
|
+
} finally {
|
|
1430
|
+
if (this.pendingObservedEnvelopePersistenceBySession.get(localSessionId) === current) {
|
|
1431
|
+
this.pendingObservedEnvelopePersistenceBySession.delete(localSessionId);
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1346
1435
|
async persistSessionStateFromEvent(sessionId, envelope, direction) {
|
|
1347
1436
|
if (direction !== "inbound") {
|
|
1348
1437
|
return;
|
|
@@ -1586,6 +1675,7 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
1586
1675
|
let delayMs = HEALTH_WAIT_MIN_DELAY_MS;
|
|
1587
1676
|
let nextLogAt = startedAt + HEALTH_WAIT_LOG_AFTER_MS;
|
|
1588
1677
|
let lastError;
|
|
1678
|
+
let consecutiveFailures = 0;
|
|
1589
1679
|
while (!this.disposed && (deadline === void 0 || Date.now() < deadline)) {
|
|
1590
1680
|
throwIfAborted(signal);
|
|
1591
1681
|
try {
|
|
@@ -1594,11 +1684,20 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
1594
1684
|
return;
|
|
1595
1685
|
}
|
|
1596
1686
|
lastError = new Error(`Unexpected health response: ${JSON.stringify(health)}`);
|
|
1687
|
+
consecutiveFailures++;
|
|
1597
1688
|
} catch (error) {
|
|
1598
1689
|
if (isAbortError(error)) {
|
|
1599
1690
|
throw error;
|
|
1600
1691
|
}
|
|
1601
1692
|
lastError = error;
|
|
1693
|
+
consecutiveFailures++;
|
|
1694
|
+
}
|
|
1695
|
+
if (consecutiveFailures >= HEALTH_WAIT_ENSURE_SERVER_AFTER_FAILURES && this.sandboxProvider?.ensureServer && this.sandboxProviderRawId) {
|
|
1696
|
+
try {
|
|
1697
|
+
await this.sandboxProvider.ensureServer(this.sandboxProviderRawId);
|
|
1698
|
+
} catch {
|
|
1699
|
+
}
|
|
1700
|
+
consecutiveFailures = 0;
|
|
1602
1701
|
}
|
|
1603
1702
|
const now = Date.now();
|
|
1604
1703
|
if (now >= nextLogAt) {
|
|
@@ -1644,6 +1743,12 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
1644
1743
|
});
|
|
1645
1744
|
}
|
|
1646
1745
|
};
|
|
1746
|
+
function isSessionEventIndexConflict(error) {
|
|
1747
|
+
if (!(error instanceof Error)) {
|
|
1748
|
+
return false;
|
|
1749
|
+
}
|
|
1750
|
+
return /UNIQUE constraint failed: .*session_id, .*event_index/.test(error.message);
|
|
1751
|
+
}
|
|
1647
1752
|
function parseProcessTerminalServerFrame(payload) {
|
|
1648
1753
|
try {
|
|
1649
1754
|
const parsed = JSON.parse(payload);
|
|
@@ -1725,16 +1830,16 @@ function toAgentQuery(options) {
|
|
|
1725
1830
|
no_cache: options.noCache
|
|
1726
1831
|
};
|
|
1727
1832
|
}
|
|
1728
|
-
function normalizeSessionInit(value) {
|
|
1833
|
+
function normalizeSessionInit(value, cwdShorthand) {
|
|
1729
1834
|
if (!value) {
|
|
1730
1835
|
return {
|
|
1731
|
-
cwd: defaultCwd(),
|
|
1836
|
+
cwd: cwdShorthand ?? defaultCwd(),
|
|
1732
1837
|
mcpServers: []
|
|
1733
1838
|
};
|
|
1734
1839
|
}
|
|
1735
1840
|
return {
|
|
1736
1841
|
...value,
|
|
1737
|
-
cwd: value.cwd ?? defaultCwd(),
|
|
1842
|
+
cwd: value.cwd ?? cwdShorthand ?? defaultCwd(),
|
|
1738
1843
|
mcpServers: value.mcpServers ?? []
|
|
1739
1844
|
};
|
|
1740
1845
|
}
|
|
@@ -1848,32 +1953,50 @@ function normalizePositiveInt(value, fallback) {
|
|
|
1848
1953
|
}
|
|
1849
1954
|
return Math.floor(value);
|
|
1850
1955
|
}
|
|
1851
|
-
function normalizeHealthWaitOptions(
|
|
1852
|
-
if (
|
|
1956
|
+
function normalizeHealthWaitOptions(skipHealthCheck, waitForHealth, signal) {
|
|
1957
|
+
if (skipHealthCheck === true || waitForHealth === false) {
|
|
1853
1958
|
return { enabled: false };
|
|
1854
1959
|
}
|
|
1855
|
-
if (
|
|
1960
|
+
if (waitForHealth === true || waitForHealth === void 0) {
|
|
1856
1961
|
return { enabled: true, signal };
|
|
1857
1962
|
}
|
|
1858
|
-
const timeoutMs = typeof
|
|
1963
|
+
const timeoutMs = typeof waitForHealth.timeoutMs === "number" && Number.isFinite(waitForHealth.timeoutMs) && waitForHealth.timeoutMs > 0 ? Math.floor(waitForHealth.timeoutMs) : void 0;
|
|
1859
1964
|
return {
|
|
1860
1965
|
enabled: true,
|
|
1861
1966
|
signal,
|
|
1862
1967
|
timeoutMs
|
|
1863
1968
|
};
|
|
1864
1969
|
}
|
|
1865
|
-
function
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
if (spawn === true || spawn === void 0) {
|
|
1870
|
-
return { enabled: defaultEnabled };
|
|
1970
|
+
function parseSandboxProviderId(sandboxId) {
|
|
1971
|
+
const slashIndex = sandboxId.indexOf("/");
|
|
1972
|
+
if (slashIndex < 1 || slashIndex === sandboxId.length - 1) {
|
|
1973
|
+
throw new Error(`Sandbox IDs must be prefixed as "{provider}/{id}". Received '${sandboxId}'.`);
|
|
1871
1974
|
}
|
|
1872
1975
|
return {
|
|
1873
|
-
|
|
1874
|
-
|
|
1976
|
+
provider: sandboxId.slice(0, slashIndex),
|
|
1977
|
+
rawId: sandboxId.slice(slashIndex + 1)
|
|
1875
1978
|
};
|
|
1876
1979
|
}
|
|
1980
|
+
function requireSandboxBaseUrl(baseUrl, providerName) {
|
|
1981
|
+
if (!baseUrl) {
|
|
1982
|
+
throw new Error(`Sandbox provider '${providerName}' did not return a base URL.`);
|
|
1983
|
+
}
|
|
1984
|
+
return baseUrl;
|
|
1985
|
+
}
|
|
1986
|
+
async function resolveProviderFetch(provider, rawSandboxId) {
|
|
1987
|
+
if (provider.getFetch) {
|
|
1988
|
+
return await provider.getFetch(rawSandboxId);
|
|
1989
|
+
}
|
|
1990
|
+
return void 0;
|
|
1991
|
+
}
|
|
1992
|
+
async function resolveProviderToken(provider, rawSandboxId) {
|
|
1993
|
+
const maybeGetToken = provider.getToken;
|
|
1994
|
+
if (typeof maybeGetToken !== "function") {
|
|
1995
|
+
return void 0;
|
|
1996
|
+
}
|
|
1997
|
+
const token = await maybeGetToken.call(provider, rawSandboxId);
|
|
1998
|
+
return typeof token === "string" && token ? token : void 0;
|
|
1999
|
+
}
|
|
1877
2000
|
async function readProblem(response) {
|
|
1878
2001
|
try {
|
|
1879
2002
|
const text = await response.clone().text();
|
|
@@ -1938,7 +2061,7 @@ function deriveModesFromConfigOptions(configOptions) {
|
|
|
1938
2061
|
return null;
|
|
1939
2062
|
}
|
|
1940
2063
|
const modeOption = findConfigOptionByCategory(configOptions, "mode");
|
|
1941
|
-
if (!modeOption || !Array.isArray(modeOption.options)) {
|
|
2064
|
+
if (!modeOption || modeOption.type !== "select" || !Array.isArray(modeOption.options)) {
|
|
1942
2065
|
return null;
|
|
1943
2066
|
}
|
|
1944
2067
|
const availableModes = modeOption.options.flatMap((entry) => flattenConfigOptions(entry)).map((entry) => ({
|