sandbox-agent 0.3.0 → 0.3.1
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 +41 -4
- package/dist/index.js +274 -11
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NewSessionRequest, SessionConfigOption, SessionModeState, AnyMessage, AcpEnvelopeDirection, NewSessionResponse, PromptRequest, PromptResponse, SetSessionModeResponse, SetSessionConfigOptionResponse } from 'acp-http-client';
|
|
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
3
|
|
|
4
4
|
type SandboxAgentSpawnLogMode = "inherit" | "pipe" | "silent";
|
|
@@ -1488,8 +1488,25 @@ interface SessionSendOptions {
|
|
|
1488
1488
|
notification?: boolean;
|
|
1489
1489
|
}
|
|
1490
1490
|
type SessionEventListener = (event: SessionEvent) => void;
|
|
1491
|
+
type PermissionReply = "once" | "always" | "reject";
|
|
1492
|
+
type PermissionRequestListener = (request: SessionPermissionRequest) => void;
|
|
1491
1493
|
type ProcessLogListener = (entry: ProcessLogEntry) => void;
|
|
1492
1494
|
type ProcessLogFollowQuery = Omit<ProcessLogsQuery, "follow">;
|
|
1495
|
+
interface SessionPermissionRequestOption {
|
|
1496
|
+
optionId: string;
|
|
1497
|
+
name: string;
|
|
1498
|
+
kind: PermissionOptionKind;
|
|
1499
|
+
}
|
|
1500
|
+
interface SessionPermissionRequest {
|
|
1501
|
+
id: string;
|
|
1502
|
+
createdAt: number;
|
|
1503
|
+
sessionId: string;
|
|
1504
|
+
agentSessionId: string;
|
|
1505
|
+
availableReplies: PermissionReply[];
|
|
1506
|
+
options: SessionPermissionRequestOption[];
|
|
1507
|
+
toolCall: RequestPermissionRequest["toolCall"];
|
|
1508
|
+
rawRequest: RequestPermissionRequest;
|
|
1509
|
+
}
|
|
1493
1510
|
interface AgentQueryOptions {
|
|
1494
1511
|
config?: boolean;
|
|
1495
1512
|
noCache?: boolean;
|
|
@@ -1532,6 +1549,12 @@ declare class UnsupportedSessionConfigOptionError extends Error {
|
|
|
1532
1549
|
readonly availableConfigIds: string[];
|
|
1533
1550
|
constructor(sessionId: string, configId: string, availableConfigIds: string[]);
|
|
1534
1551
|
}
|
|
1552
|
+
declare class UnsupportedPermissionReplyError extends Error {
|
|
1553
|
+
readonly permissionId: string;
|
|
1554
|
+
readonly requestedReply: PermissionReply;
|
|
1555
|
+
readonly availableReplies: PermissionReply[];
|
|
1556
|
+
constructor(permissionId: string, requestedReply: PermissionReply, availableReplies: PermissionReply[]);
|
|
1557
|
+
}
|
|
1535
1558
|
declare class Session {
|
|
1536
1559
|
private record;
|
|
1537
1560
|
private readonly sandbox;
|
|
@@ -1543,7 +1566,7 @@ declare class Session {
|
|
|
1543
1566
|
get createdAt(): number;
|
|
1544
1567
|
get destroyedAt(): number | undefined;
|
|
1545
1568
|
refresh(): Promise<Session>;
|
|
1546
|
-
|
|
1569
|
+
rawSend(method: string, params?: Record<string, unknown>, options?: SessionSendOptions): Promise<unknown>;
|
|
1547
1570
|
prompt(prompt: PromptRequest["prompt"]): Promise<PromptResponse>;
|
|
1548
1571
|
setMode(modeId: string): Promise<SetSessionModeResponse | void>;
|
|
1549
1572
|
setConfigOption(configId: string, value: string): Promise<SetSessionConfigOptionResponse>;
|
|
@@ -1552,6 +1575,9 @@ declare class Session {
|
|
|
1552
1575
|
getConfigOptions(): Promise<SessionConfigOption[]>;
|
|
1553
1576
|
getModes(): Promise<SessionModeState | null>;
|
|
1554
1577
|
onEvent(listener: SessionEventListener): () => void;
|
|
1578
|
+
onPermissionRequest(listener: PermissionRequestListener): () => void;
|
|
1579
|
+
respondPermission(permissionId: string, reply: PermissionReply): Promise<void>;
|
|
1580
|
+
rawRespondPermission(permissionId: string, response: RequestPermissionResponse): Promise<void>;
|
|
1555
1581
|
toRecord(): SessionRecord;
|
|
1556
1582
|
apply(record: SessionRecord): void;
|
|
1557
1583
|
}
|
|
@@ -1567,6 +1593,7 @@ declare class LiveAcpConnection {
|
|
|
1567
1593
|
private lastAdapterExit;
|
|
1568
1594
|
private lastAdapterExitAt;
|
|
1569
1595
|
private readonly onObservedEnvelope;
|
|
1596
|
+
private readonly onPermissionRequest;
|
|
1570
1597
|
private constructor();
|
|
1571
1598
|
static create(options: {
|
|
1572
1599
|
baseUrl: string;
|
|
@@ -1576,6 +1603,7 @@ declare class LiveAcpConnection {
|
|
|
1576
1603
|
agent: string;
|
|
1577
1604
|
serverId: string;
|
|
1578
1605
|
onObservedEnvelope: (connection: LiveAcpConnection, envelope: AnyMessage, direction: AcpEnvelopeDirection, localSessionId: string | null) => void;
|
|
1606
|
+
onPermissionRequest: (connection: LiveAcpConnection, localSessionId: string, agentSessionId: string, request: RequestPermissionRequest) => Promise<RequestPermissionResponse>;
|
|
1579
1607
|
}): Promise<LiveAcpConnection>;
|
|
1580
1608
|
close(): Promise<void>;
|
|
1581
1609
|
hasBoundSession(localSessionId: string, agentSessionId?: string): boolean;
|
|
@@ -1585,6 +1613,7 @@ declare class LiveAcpConnection {
|
|
|
1585
1613
|
sendSessionMethod(localSessionId: string, method: string, params: Record<string, unknown>, options: SessionSendOptions): Promise<unknown>;
|
|
1586
1614
|
private handleEnvelope;
|
|
1587
1615
|
private handleAdapterNotification;
|
|
1616
|
+
private handlePermissionRequest;
|
|
1588
1617
|
private resolveSessionId;
|
|
1589
1618
|
private localFromEnvelopeParams;
|
|
1590
1619
|
}
|
|
@@ -1629,6 +1658,8 @@ declare class SandboxAgent {
|
|
|
1629
1658
|
private readonly pendingLiveConnections;
|
|
1630
1659
|
private readonly sessionHandles;
|
|
1631
1660
|
private readonly eventListeners;
|
|
1661
|
+
private readonly permissionListeners;
|
|
1662
|
+
private readonly pendingPermissionRequests;
|
|
1632
1663
|
private readonly nextSessionEventIndexBySession;
|
|
1633
1664
|
private readonly seedSessionEventIndexBySession;
|
|
1634
1665
|
constructor(options: SandboxAgentConnectOptions);
|
|
@@ -1662,13 +1693,16 @@ declare class SandboxAgent {
|
|
|
1662
1693
|
getSessionModes(sessionId: string): Promise<SessionModeState | null>;
|
|
1663
1694
|
private setSessionCategoryValue;
|
|
1664
1695
|
private hydrateSessionConfigOptions;
|
|
1665
|
-
|
|
1696
|
+
rawSendSessionMethod(sessionId: string, method: string, params: Record<string, unknown>, options?: SessionSendOptions): Promise<{
|
|
1666
1697
|
session: Session;
|
|
1667
1698
|
response: unknown;
|
|
1668
1699
|
}>;
|
|
1669
1700
|
private sendSessionMethodInternal;
|
|
1670
1701
|
private persistSessionStateFromMethod;
|
|
1671
1702
|
onSessionEvent(sessionId: string, listener: SessionEventListener): () => void;
|
|
1703
|
+
onPermissionRequest(sessionId: string, listener: PermissionRequestListener): () => void;
|
|
1704
|
+
respondPermission(permissionId: string, reply: PermissionReply): Promise<void>;
|
|
1705
|
+
rawRespondPermission(permissionId: string, response: RequestPermissionResponse): Promise<void>;
|
|
1672
1706
|
getHealth(): Promise<HealthResponse>;
|
|
1673
1707
|
listAgents(options?: AgentQueryOptions): Promise<AgentListResponse>;
|
|
1674
1708
|
getAgent(agent: string, options?: AgentQueryOptions): Promise<AgentInfo>;
|
|
@@ -1713,6 +1747,9 @@ declare class SandboxAgent {
|
|
|
1713
1747
|
private collectReplayEvents;
|
|
1714
1748
|
private upsertSessionHandle;
|
|
1715
1749
|
private requireSessionRecord;
|
|
1750
|
+
private enqueuePermissionRequest;
|
|
1751
|
+
private resolvePendingPermission;
|
|
1752
|
+
private cancelPendingPermissionsForSession;
|
|
1716
1753
|
private requestJson;
|
|
1717
1754
|
private requestRaw;
|
|
1718
1755
|
private startHealthWait;
|
|
@@ -1744,4 +1781,4 @@ interface InspectorUrlOptions {
|
|
|
1744
1781
|
*/
|
|
1745
1782
|
declare function buildInspectorUrl(options: InspectorUrlOptions): string;
|
|
1746
1783
|
|
|
1747
|
-
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 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 SandboxAgentSpawnLogMode, type SandboxAgentSpawnOptions, type SandboxAgentStartOptions, Session, type SessionCreateRequest, type SessionEvent, type SessionEventListener, type SessionPersistDriver, type SessionRecord, type SessionResumeOrCreateRequest, type SessionSendOptions, type SkillsConfig, type SkillsConfigQuery, type TerminalErrorStatus, type TerminalExitStatus, type TerminalReadyStatus, type TerminalResizePayload, type TerminalStatusMessage, UnsupportedSessionCategoryError, UnsupportedSessionConfigOptionError, UnsupportedSessionValueError, buildInspectorUrl };
|
|
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 SandboxAgentSpawnLogMode, type SandboxAgentSpawnOptions, type SandboxAgentStartOptions, 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
|
@@ -192,6 +192,20 @@ var UnsupportedSessionConfigOptionError = class extends Error {
|
|
|
192
192
|
this.availableConfigIds = availableConfigIds;
|
|
193
193
|
}
|
|
194
194
|
};
|
|
195
|
+
var UnsupportedPermissionReplyError = class extends Error {
|
|
196
|
+
permissionId;
|
|
197
|
+
requestedReply;
|
|
198
|
+
availableReplies;
|
|
199
|
+
constructor(permissionId, requestedReply, availableReplies) {
|
|
200
|
+
super(
|
|
201
|
+
`Permission '${permissionId}' does not support reply '${requestedReply}'. Available replies: ${availableReplies.join(", ") || "(none)"}`
|
|
202
|
+
);
|
|
203
|
+
this.name = "UnsupportedPermissionReplyError";
|
|
204
|
+
this.permissionId = permissionId;
|
|
205
|
+
this.requestedReply = requestedReply;
|
|
206
|
+
this.availableReplies = availableReplies;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
195
209
|
var Session = class {
|
|
196
210
|
record;
|
|
197
211
|
sandbox;
|
|
@@ -225,13 +239,13 @@ var Session = class {
|
|
|
225
239
|
this.apply(latest.toRecord());
|
|
226
240
|
return this;
|
|
227
241
|
}
|
|
228
|
-
async
|
|
229
|
-
const updated = await this.sandbox.
|
|
242
|
+
async rawSend(method, params = {}, options = {}) {
|
|
243
|
+
const updated = await this.sandbox.rawSendSessionMethod(this.id, method, params, options);
|
|
230
244
|
this.apply(updated.session.toRecord());
|
|
231
245
|
return updated.response;
|
|
232
246
|
}
|
|
233
247
|
async prompt(prompt) {
|
|
234
|
-
const response = await this.
|
|
248
|
+
const response = await this.rawSend("session/prompt", { prompt });
|
|
235
249
|
return response;
|
|
236
250
|
}
|
|
237
251
|
async setMode(modeId) {
|
|
@@ -263,6 +277,15 @@ var Session = class {
|
|
|
263
277
|
onEvent(listener) {
|
|
264
278
|
return this.sandbox.onSessionEvent(this.id, listener);
|
|
265
279
|
}
|
|
280
|
+
onPermissionRequest(listener) {
|
|
281
|
+
return this.sandbox.onPermissionRequest(this.id, listener);
|
|
282
|
+
}
|
|
283
|
+
async respondPermission(permissionId, reply) {
|
|
284
|
+
await this.sandbox.respondPermission(permissionId, reply);
|
|
285
|
+
}
|
|
286
|
+
async rawRespondPermission(permissionId, response) {
|
|
287
|
+
await this.sandbox.rawRespondPermission(permissionId, response);
|
|
288
|
+
}
|
|
266
289
|
toRecord() {
|
|
267
290
|
return { ...this.record };
|
|
268
291
|
}
|
|
@@ -282,11 +305,13 @@ var LiveAcpConnection = class _LiveAcpConnection {
|
|
|
282
305
|
lastAdapterExit = null;
|
|
283
306
|
lastAdapterExitAt = 0;
|
|
284
307
|
onObservedEnvelope;
|
|
285
|
-
|
|
308
|
+
onPermissionRequest;
|
|
309
|
+
constructor(agent, connectionId, acp, onObservedEnvelope, onPermissionRequest) {
|
|
286
310
|
this.agent = agent;
|
|
287
311
|
this.connectionId = connectionId;
|
|
288
312
|
this.acp = acp;
|
|
289
313
|
this.onObservedEnvelope = onObservedEnvelope;
|
|
314
|
+
this.onPermissionRequest = onPermissionRequest;
|
|
290
315
|
}
|
|
291
316
|
static async create(options) {
|
|
292
317
|
const connectionId = randomId();
|
|
@@ -301,6 +326,12 @@ var LiveAcpConnection = class _LiveAcpConnection {
|
|
|
301
326
|
bootstrapQuery: { agent: options.agent }
|
|
302
327
|
},
|
|
303
328
|
client: {
|
|
329
|
+
requestPermission: async (request) => {
|
|
330
|
+
if (!live) {
|
|
331
|
+
return cancelledPermissionResponse();
|
|
332
|
+
}
|
|
333
|
+
return live.handlePermissionRequest(request);
|
|
334
|
+
},
|
|
304
335
|
sessionUpdate: async (_notification) => {
|
|
305
336
|
},
|
|
306
337
|
extNotification: async (method, params) => {
|
|
@@ -315,7 +346,13 @@ var LiveAcpConnection = class _LiveAcpConnection {
|
|
|
315
346
|
live.handleEnvelope(envelope, direction);
|
|
316
347
|
}
|
|
317
348
|
});
|
|
318
|
-
live = new _LiveAcpConnection(
|
|
349
|
+
live = new _LiveAcpConnection(
|
|
350
|
+
options.agent,
|
|
351
|
+
connectionId,
|
|
352
|
+
acp,
|
|
353
|
+
options.onObservedEnvelope,
|
|
354
|
+
options.onPermissionRequest
|
|
355
|
+
);
|
|
319
356
|
const initResult = await acp.initialize({
|
|
320
357
|
protocolVersion: PROTOCOL_VERSION,
|
|
321
358
|
clientInfo: {
|
|
@@ -420,6 +457,19 @@ var LiveAcpConnection = class _LiveAcpConnection {
|
|
|
420
457
|
};
|
|
421
458
|
this.lastAdapterExitAt = Date.now();
|
|
422
459
|
}
|
|
460
|
+
async handlePermissionRequest(request) {
|
|
461
|
+
const agentSessionId = request.sessionId;
|
|
462
|
+
const localSessionId = this.localByAgentSessionId.get(agentSessionId);
|
|
463
|
+
if (!localSessionId) {
|
|
464
|
+
return cancelledPermissionResponse();
|
|
465
|
+
}
|
|
466
|
+
return this.onPermissionRequest(
|
|
467
|
+
this,
|
|
468
|
+
localSessionId,
|
|
469
|
+
agentSessionId,
|
|
470
|
+
clonePermissionRequest(request)
|
|
471
|
+
);
|
|
472
|
+
}
|
|
423
473
|
resolveSessionId(envelope, direction) {
|
|
424
474
|
const id = envelopeId(envelope);
|
|
425
475
|
const method = envelopeMethod(envelope);
|
|
@@ -619,6 +669,8 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
619
669
|
pendingLiveConnections = /* @__PURE__ */ new Map();
|
|
620
670
|
sessionHandles = /* @__PURE__ */ new Map();
|
|
621
671
|
eventListeners = /* @__PURE__ */ new Map();
|
|
672
|
+
permissionListeners = /* @__PURE__ */ new Map();
|
|
673
|
+
pendingPermissionRequests = /* @__PURE__ */ new Map();
|
|
622
674
|
nextSessionEventIndexBySession = /* @__PURE__ */ new Map();
|
|
623
675
|
seedSessionEventIndexBySession = /* @__PURE__ */ new Map();
|
|
624
676
|
constructor(options) {
|
|
@@ -667,6 +719,10 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
667
719
|
async dispose() {
|
|
668
720
|
this.disposed = true;
|
|
669
721
|
this.healthWaitAbortController.abort(createAbortError("SandboxAgent was disposed."));
|
|
722
|
+
for (const [permissionId, pending2] of this.pendingPermissionRequests) {
|
|
723
|
+
this.pendingPermissionRequests.delete(permissionId);
|
|
724
|
+
pending2.resolve(cancelledPermissionResponse());
|
|
725
|
+
}
|
|
670
726
|
const connections = [...this.liveConnections.values()];
|
|
671
727
|
this.liveConnections.clear();
|
|
672
728
|
const pending = [...this.pendingLiveConnections.values()];
|
|
@@ -788,6 +844,7 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
788
844
|
return this.createSession(request);
|
|
789
845
|
}
|
|
790
846
|
async destroySession(id) {
|
|
847
|
+
this.cancelPendingPermissionsForSession(id);
|
|
791
848
|
try {
|
|
792
849
|
await this.sendSessionMethodInternal(id, SESSION_CANCEL_METHOD, {}, {}, true);
|
|
793
850
|
} catch {
|
|
@@ -877,7 +934,23 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
877
934
|
}
|
|
878
935
|
async getSessionModes(sessionId) {
|
|
879
936
|
const record = await this.requireSessionRecord(sessionId);
|
|
880
|
-
|
|
937
|
+
if (record.modes && record.modes.availableModes.length > 0) {
|
|
938
|
+
return cloneModes(record.modes);
|
|
939
|
+
}
|
|
940
|
+
const hydrated = await this.hydrateSessionConfigOptions(record.id, record);
|
|
941
|
+
if (hydrated.modes && hydrated.modes.availableModes.length > 0) {
|
|
942
|
+
return cloneModes(hydrated.modes);
|
|
943
|
+
}
|
|
944
|
+
const derived = deriveModesFromConfigOptions(hydrated.configOptions);
|
|
945
|
+
if (!derived) {
|
|
946
|
+
return cloneModes(hydrated.modes);
|
|
947
|
+
}
|
|
948
|
+
const updated = {
|
|
949
|
+
...hydrated,
|
|
950
|
+
modes: derived
|
|
951
|
+
};
|
|
952
|
+
await this.persist.updateSession(updated);
|
|
953
|
+
return cloneModes(derived);
|
|
881
954
|
}
|
|
882
955
|
async setSessionCategoryValue(sessionId, category, value) {
|
|
883
956
|
const resolvedValue = value.trim();
|
|
@@ -919,7 +992,7 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
919
992
|
await this.persist.updateSession(updated);
|
|
920
993
|
return updated;
|
|
921
994
|
}
|
|
922
|
-
async
|
|
995
|
+
async rawSendSessionMethod(sessionId, method, params, options = {}) {
|
|
923
996
|
return this.sendSessionMethodInternal(sessionId, method, params, options, false);
|
|
924
997
|
}
|
|
925
998
|
async sendSessionMethodInternal(sessionId, method, params, options, allowManagedCancel) {
|
|
@@ -1015,6 +1088,42 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
1015
1088
|
}
|
|
1016
1089
|
};
|
|
1017
1090
|
}
|
|
1091
|
+
onPermissionRequest(sessionId, listener) {
|
|
1092
|
+
const listeners = this.permissionListeners.get(sessionId) ?? /* @__PURE__ */ new Set();
|
|
1093
|
+
listeners.add(listener);
|
|
1094
|
+
this.permissionListeners.set(sessionId, listeners);
|
|
1095
|
+
return () => {
|
|
1096
|
+
const set = this.permissionListeners.get(sessionId);
|
|
1097
|
+
if (!set) {
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
set.delete(listener);
|
|
1101
|
+
if (set.size === 0) {
|
|
1102
|
+
this.permissionListeners.delete(sessionId);
|
|
1103
|
+
}
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
async respondPermission(permissionId, reply) {
|
|
1107
|
+
const pending = this.pendingPermissionRequests.get(permissionId);
|
|
1108
|
+
if (!pending) {
|
|
1109
|
+
throw new Error(`permission '${permissionId}' not found`);
|
|
1110
|
+
}
|
|
1111
|
+
let response;
|
|
1112
|
+
try {
|
|
1113
|
+
response = permissionReplyToResponse(permissionId, pending.request, reply);
|
|
1114
|
+
} catch (error) {
|
|
1115
|
+
pending.reject(error instanceof Error ? error : new Error(String(error)));
|
|
1116
|
+
this.pendingPermissionRequests.delete(permissionId);
|
|
1117
|
+
throw error;
|
|
1118
|
+
}
|
|
1119
|
+
this.resolvePendingPermission(permissionId, response);
|
|
1120
|
+
}
|
|
1121
|
+
async rawRespondPermission(permissionId, response) {
|
|
1122
|
+
if (!this.pendingPermissionRequests.has(permissionId)) {
|
|
1123
|
+
throw new Error(`permission '${permissionId}' not found`);
|
|
1124
|
+
}
|
|
1125
|
+
this.resolvePendingPermission(permissionId, clonePermissionResponse(response));
|
|
1126
|
+
}
|
|
1018
1127
|
async getHealth() {
|
|
1019
1128
|
return this.requestHealth();
|
|
1020
1129
|
}
|
|
@@ -1024,9 +1133,21 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
1024
1133
|
});
|
|
1025
1134
|
}
|
|
1026
1135
|
async getAgent(agent, options) {
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1136
|
+
try {
|
|
1137
|
+
return await this.requestJson("GET", `${API_PREFIX}/agents/${encodeURIComponent(agent)}`, {
|
|
1138
|
+
query: toAgentQuery(options)
|
|
1139
|
+
});
|
|
1140
|
+
} catch (error) {
|
|
1141
|
+
if (!(error instanceof SandboxAgentError) || error.status !== 404) {
|
|
1142
|
+
throw error;
|
|
1143
|
+
}
|
|
1144
|
+
const listed = await this.listAgents(options);
|
|
1145
|
+
const match = listed.agents.find((entry) => entry.id === agent);
|
|
1146
|
+
if (match) {
|
|
1147
|
+
return match;
|
|
1148
|
+
}
|
|
1149
|
+
throw error;
|
|
1150
|
+
}
|
|
1030
1151
|
}
|
|
1031
1152
|
async installAgent(agent, request = {}) {
|
|
1032
1153
|
return this.requestJson("POST", `${API_PREFIX}/agents/${encodeURIComponent(agent)}/install`, {
|
|
@@ -1217,7 +1338,8 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
1217
1338
|
serverId,
|
|
1218
1339
|
onObservedEnvelope: (connection, envelope, direction, localSessionId) => {
|
|
1219
1340
|
void this.persistObservedEnvelope(connection, envelope, direction, localSessionId);
|
|
1220
|
-
}
|
|
1341
|
+
},
|
|
1342
|
+
onPermissionRequest: async (connection, localSessionId, agentSessionId, request) => this.enqueuePermissionRequest(connection, localSessionId, agentSessionId, request)
|
|
1221
1343
|
});
|
|
1222
1344
|
const raced = this.liveConnections.get(agent);
|
|
1223
1345
|
if (raced) {
|
|
@@ -1378,6 +1500,57 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
1378
1500
|
}
|
|
1379
1501
|
return record;
|
|
1380
1502
|
}
|
|
1503
|
+
async enqueuePermissionRequest(_connection, localSessionId, agentSessionId, request) {
|
|
1504
|
+
const listeners = this.permissionListeners.get(localSessionId);
|
|
1505
|
+
if (!listeners || listeners.size === 0) {
|
|
1506
|
+
return cancelledPermissionResponse();
|
|
1507
|
+
}
|
|
1508
|
+
const pendingId = randomId();
|
|
1509
|
+
const permissionRequest = {
|
|
1510
|
+
id: pendingId,
|
|
1511
|
+
createdAt: nowMs(),
|
|
1512
|
+
sessionId: localSessionId,
|
|
1513
|
+
agentSessionId,
|
|
1514
|
+
availableReplies: availablePermissionReplies(request.options),
|
|
1515
|
+
options: request.options.map(clonePermissionOption),
|
|
1516
|
+
toolCall: clonePermissionToolCall(request.toolCall),
|
|
1517
|
+
rawRequest: clonePermissionRequest(request)
|
|
1518
|
+
};
|
|
1519
|
+
return await new Promise((resolve, reject) => {
|
|
1520
|
+
this.pendingPermissionRequests.set(pendingId, {
|
|
1521
|
+
id: pendingId,
|
|
1522
|
+
sessionId: localSessionId,
|
|
1523
|
+
request: clonePermissionRequest(request),
|
|
1524
|
+
resolve,
|
|
1525
|
+
reject
|
|
1526
|
+
});
|
|
1527
|
+
try {
|
|
1528
|
+
for (const listener of listeners) {
|
|
1529
|
+
listener(permissionRequest);
|
|
1530
|
+
}
|
|
1531
|
+
} catch (error) {
|
|
1532
|
+
this.pendingPermissionRequests.delete(pendingId);
|
|
1533
|
+
reject(error);
|
|
1534
|
+
}
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
resolvePendingPermission(permissionId, response) {
|
|
1538
|
+
const pending = this.pendingPermissionRequests.get(permissionId);
|
|
1539
|
+
if (!pending) {
|
|
1540
|
+
throw new Error(`permission '${permissionId}' not found`);
|
|
1541
|
+
}
|
|
1542
|
+
this.pendingPermissionRequests.delete(permissionId);
|
|
1543
|
+
pending.resolve(response);
|
|
1544
|
+
}
|
|
1545
|
+
cancelPendingPermissionsForSession(sessionId) {
|
|
1546
|
+
for (const [permissionId, pending] of this.pendingPermissionRequests) {
|
|
1547
|
+
if (pending.sessionId !== sessionId) {
|
|
1548
|
+
continue;
|
|
1549
|
+
}
|
|
1550
|
+
this.pendingPermissionRequests.delete(permissionId);
|
|
1551
|
+
pending.resolve(cancelledPermissionResponse());
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1381
1554
|
async requestJson(method, path, options = {}) {
|
|
1382
1555
|
const response = await this.requestRaw(method, path, {
|
|
1383
1556
|
query: options.query,
|
|
@@ -1679,6 +1852,22 @@ function envelopeSessionIdFromResult(message) {
|
|
|
1679
1852
|
function cloneEnvelope(envelope) {
|
|
1680
1853
|
return JSON.parse(JSON.stringify(envelope));
|
|
1681
1854
|
}
|
|
1855
|
+
function clonePermissionRequest(request) {
|
|
1856
|
+
return JSON.parse(JSON.stringify(request));
|
|
1857
|
+
}
|
|
1858
|
+
function clonePermissionResponse(response) {
|
|
1859
|
+
return JSON.parse(JSON.stringify(response));
|
|
1860
|
+
}
|
|
1861
|
+
function clonePermissionOption(option) {
|
|
1862
|
+
return {
|
|
1863
|
+
optionId: option.optionId,
|
|
1864
|
+
name: option.name,
|
|
1865
|
+
kind: option.kind
|
|
1866
|
+
};
|
|
1867
|
+
}
|
|
1868
|
+
function clonePermissionToolCall(toolCall) {
|
|
1869
|
+
return JSON.parse(JSON.stringify(toolCall));
|
|
1870
|
+
}
|
|
1682
1871
|
function isRecord(value) {
|
|
1683
1872
|
return typeof value === "object" && value !== null;
|
|
1684
1873
|
}
|
|
@@ -1788,6 +1977,24 @@ function extractKnownModeIds(modes) {
|
|
|
1788
1977
|
}
|
|
1789
1978
|
return modes.availableModes.map((mode) => typeof mode.id === "string" ? mode.id : null).filter((value) => !!value);
|
|
1790
1979
|
}
|
|
1980
|
+
function deriveModesFromConfigOptions(configOptions) {
|
|
1981
|
+
if (!configOptions || configOptions.length === 0) {
|
|
1982
|
+
return null;
|
|
1983
|
+
}
|
|
1984
|
+
const modeOption = findConfigOptionByCategory(configOptions, "mode");
|
|
1985
|
+
if (!modeOption || !Array.isArray(modeOption.options)) {
|
|
1986
|
+
return null;
|
|
1987
|
+
}
|
|
1988
|
+
const availableModes = modeOption.options.flatMap((entry) => flattenConfigOptions(entry)).map((entry) => ({
|
|
1989
|
+
id: entry.value,
|
|
1990
|
+
name: entry.name,
|
|
1991
|
+
description: entry.description ?? null
|
|
1992
|
+
}));
|
|
1993
|
+
return {
|
|
1994
|
+
currentModeId: typeof modeOption.currentValue === "string" && modeOption.currentValue.length > 0 ? modeOption.currentValue : availableModes[0]?.id ?? "",
|
|
1995
|
+
availableModes
|
|
1996
|
+
};
|
|
1997
|
+
}
|
|
1791
1998
|
function applyCurrentMode(modes, currentModeId) {
|
|
1792
1999
|
if (modes && Array.isArray(modes.availableModes)) {
|
|
1793
2000
|
return {
|
|
@@ -1809,6 +2016,24 @@ function applyConfigOptionValue(configOptions, configId, value) {
|
|
|
1809
2016
|
updated[idx] = { ...updated[idx], currentValue: value };
|
|
1810
2017
|
return updated;
|
|
1811
2018
|
}
|
|
2019
|
+
function flattenConfigOptions(entry) {
|
|
2020
|
+
if (!isRecord(entry)) {
|
|
2021
|
+
return [];
|
|
2022
|
+
}
|
|
2023
|
+
if (typeof entry.value === "string" && typeof entry.name === "string") {
|
|
2024
|
+
return [
|
|
2025
|
+
{
|
|
2026
|
+
value: entry.value,
|
|
2027
|
+
name: entry.name,
|
|
2028
|
+
description: typeof entry.description === "string" ? entry.description : void 0
|
|
2029
|
+
}
|
|
2030
|
+
];
|
|
2031
|
+
}
|
|
2032
|
+
if (!Array.isArray(entry.options)) {
|
|
2033
|
+
return [];
|
|
2034
|
+
}
|
|
2035
|
+
return entry.options.flatMap((nested) => flattenConfigOptions(nested));
|
|
2036
|
+
}
|
|
1812
2037
|
function envelopeSessionUpdate(message) {
|
|
1813
2038
|
if (!isRecord(message) || !("params" in message) || !isRecord(message.params)) {
|
|
1814
2039
|
return null;
|
|
@@ -1830,6 +2055,43 @@ function cloneModes(value) {
|
|
|
1830
2055
|
}
|
|
1831
2056
|
return JSON.parse(JSON.stringify(value));
|
|
1832
2057
|
}
|
|
2058
|
+
function availablePermissionReplies(options) {
|
|
2059
|
+
const replies = /* @__PURE__ */ new Set();
|
|
2060
|
+
for (const option of options) {
|
|
2061
|
+
if (option.kind === "allow_once") {
|
|
2062
|
+
replies.add("once");
|
|
2063
|
+
} else if (option.kind === "allow_always") {
|
|
2064
|
+
replies.add("always");
|
|
2065
|
+
} else if (option.kind === "reject_once" || option.kind === "reject_always") {
|
|
2066
|
+
replies.add("reject");
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
return [...replies];
|
|
2070
|
+
}
|
|
2071
|
+
function permissionReplyToResponse(permissionId, request, reply) {
|
|
2072
|
+
const preferredKinds = reply === "once" ? ["allow_once"] : reply === "always" ? ["allow_always", "allow_once"] : ["reject_once", "reject_always"];
|
|
2073
|
+
const selected = preferredKinds.map((kind) => request.options.find((option) => option.kind === kind)).find((option) => Boolean(option));
|
|
2074
|
+
if (!selected) {
|
|
2075
|
+
throw new UnsupportedPermissionReplyError(
|
|
2076
|
+
permissionId,
|
|
2077
|
+
reply,
|
|
2078
|
+
availablePermissionReplies(request.options)
|
|
2079
|
+
);
|
|
2080
|
+
}
|
|
2081
|
+
return {
|
|
2082
|
+
outcome: {
|
|
2083
|
+
outcome: "selected",
|
|
2084
|
+
optionId: selected.optionId
|
|
2085
|
+
}
|
|
2086
|
+
};
|
|
2087
|
+
}
|
|
2088
|
+
function cancelledPermissionResponse() {
|
|
2089
|
+
return {
|
|
2090
|
+
outcome: {
|
|
2091
|
+
outcome: "cancelled"
|
|
2092
|
+
}
|
|
2093
|
+
};
|
|
2094
|
+
}
|
|
1833
2095
|
function isSessionConfigOption(value) {
|
|
1834
2096
|
return isRecord(value) && typeof value.id === "string" && typeof value.name === "string" && typeof value.type === "string";
|
|
1835
2097
|
}
|
|
@@ -2038,6 +2300,7 @@ export {
|
|
|
2038
2300
|
SandboxAgent,
|
|
2039
2301
|
SandboxAgentError,
|
|
2040
2302
|
Session,
|
|
2303
|
+
UnsupportedPermissionReplyError,
|
|
2041
2304
|
UnsupportedSessionCategoryError,
|
|
2042
2305
|
UnsupportedSessionConfigOptionError,
|
|
2043
2306
|
UnsupportedSessionValueError,
|