sandbox-agent 0.1.11 → 0.2.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/dist/index.d.ts +508 -892
- package/dist/index.js +807 -147
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
package/dist/index.js
CHANGED
|
@@ -1,5 +1,129 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
import {
|
|
3
|
+
AcpHttpClient,
|
|
4
|
+
PROTOCOL_VERSION
|
|
5
|
+
} from "acp-http-client";
|
|
6
|
+
|
|
7
|
+
// src/types.ts
|
|
8
|
+
var DEFAULT_MAX_SESSIONS = 1024;
|
|
9
|
+
var DEFAULT_MAX_EVENTS_PER_SESSION = 500;
|
|
10
|
+
var DEFAULT_LIST_LIMIT = 100;
|
|
11
|
+
var InMemorySessionPersistDriver = class {
|
|
12
|
+
maxSessions;
|
|
13
|
+
maxEventsPerSession;
|
|
14
|
+
sessions = /* @__PURE__ */ new Map();
|
|
15
|
+
eventsBySession = /* @__PURE__ */ new Map();
|
|
16
|
+
constructor(options = {}) {
|
|
17
|
+
this.maxSessions = normalizeCap(options.maxSessions, DEFAULT_MAX_SESSIONS);
|
|
18
|
+
this.maxEventsPerSession = normalizeCap(
|
|
19
|
+
options.maxEventsPerSession,
|
|
20
|
+
DEFAULT_MAX_EVENTS_PER_SESSION
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
async getSession(id) {
|
|
24
|
+
const session = this.sessions.get(id);
|
|
25
|
+
return session ? cloneSessionRecord(session) : null;
|
|
26
|
+
}
|
|
27
|
+
async listSessions(request = {}) {
|
|
28
|
+
const sorted = [...this.sessions.values()].sort((a, b) => {
|
|
29
|
+
if (a.createdAt !== b.createdAt) {
|
|
30
|
+
return a.createdAt - b.createdAt;
|
|
31
|
+
}
|
|
32
|
+
return a.id.localeCompare(b.id);
|
|
33
|
+
});
|
|
34
|
+
const page = paginate(sorted, request);
|
|
35
|
+
return {
|
|
36
|
+
items: page.items.map(cloneSessionRecord),
|
|
37
|
+
nextCursor: page.nextCursor
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
async updateSession(session) {
|
|
41
|
+
this.sessions.set(session.id, { ...session });
|
|
42
|
+
if (!this.eventsBySession.has(session.id)) {
|
|
43
|
+
this.eventsBySession.set(session.id, []);
|
|
44
|
+
}
|
|
45
|
+
if (this.sessions.size <= this.maxSessions) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const overflow = this.sessions.size - this.maxSessions;
|
|
49
|
+
const removable = [...this.sessions.values()].sort((a, b) => {
|
|
50
|
+
if (a.createdAt !== b.createdAt) {
|
|
51
|
+
return a.createdAt - b.createdAt;
|
|
52
|
+
}
|
|
53
|
+
return a.id.localeCompare(b.id);
|
|
54
|
+
}).slice(0, overflow).map((sessionToRemove) => sessionToRemove.id);
|
|
55
|
+
for (const sessionId of removable) {
|
|
56
|
+
this.sessions.delete(sessionId);
|
|
57
|
+
this.eventsBySession.delete(sessionId);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async listEvents(request) {
|
|
61
|
+
const all = [...this.eventsBySession.get(request.sessionId) ?? []].sort((a, b) => {
|
|
62
|
+
if (a.eventIndex !== b.eventIndex) {
|
|
63
|
+
return a.eventIndex - b.eventIndex;
|
|
64
|
+
}
|
|
65
|
+
return a.id.localeCompare(b.id);
|
|
66
|
+
});
|
|
67
|
+
const page = paginate(all, request);
|
|
68
|
+
return {
|
|
69
|
+
items: page.items.map(cloneSessionEvent),
|
|
70
|
+
nextCursor: page.nextCursor
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
async insertEvent(event) {
|
|
74
|
+
const events = this.eventsBySession.get(event.sessionId) ?? [];
|
|
75
|
+
events.push(cloneSessionEvent(event));
|
|
76
|
+
if (events.length > this.maxEventsPerSession) {
|
|
77
|
+
events.splice(0, events.length - this.maxEventsPerSession);
|
|
78
|
+
}
|
|
79
|
+
this.eventsBySession.set(event.sessionId, events);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
function cloneSessionRecord(session) {
|
|
83
|
+
return {
|
|
84
|
+
...session,
|
|
85
|
+
sessionInit: session.sessionInit ? JSON.parse(JSON.stringify(session.sessionInit)) : void 0
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function cloneSessionEvent(event) {
|
|
89
|
+
return {
|
|
90
|
+
...event,
|
|
91
|
+
payload: JSON.parse(JSON.stringify(event.payload))
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function normalizeCap(value, fallback) {
|
|
95
|
+
if (!Number.isFinite(value) || (value ?? 0) < 1) {
|
|
96
|
+
return fallback;
|
|
97
|
+
}
|
|
98
|
+
return Math.floor(value);
|
|
99
|
+
}
|
|
100
|
+
function paginate(items, request) {
|
|
101
|
+
const offset = parseCursor(request.cursor);
|
|
102
|
+
const limit = normalizeCap(request.limit, DEFAULT_LIST_LIMIT);
|
|
103
|
+
const slice = items.slice(offset, offset + limit);
|
|
104
|
+
const nextOffset = offset + slice.length;
|
|
105
|
+
return {
|
|
106
|
+
items: slice,
|
|
107
|
+
nextCursor: nextOffset < items.length ? String(nextOffset) : void 0
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function parseCursor(cursor) {
|
|
111
|
+
if (!cursor) {
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
const parsed = Number.parseInt(cursor, 10);
|
|
115
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
return parsed;
|
|
119
|
+
}
|
|
120
|
+
|
|
1
121
|
// src/client.ts
|
|
2
122
|
var API_PREFIX = "/v1";
|
|
123
|
+
var FS_PATH = `${API_PREFIX}/fs`;
|
|
124
|
+
var DEFAULT_REPLAY_MAX_EVENTS = 50;
|
|
125
|
+
var DEFAULT_REPLAY_MAX_CHARS = 12e3;
|
|
126
|
+
var EVENT_INDEX_SCAN_EVENTS_LIMIT = 500;
|
|
3
127
|
var SandboxAgentError = class extends Error {
|
|
4
128
|
status;
|
|
5
129
|
problem;
|
|
@@ -12,17 +136,246 @@ var SandboxAgentError = class extends Error {
|
|
|
12
136
|
this.response = response;
|
|
13
137
|
}
|
|
14
138
|
};
|
|
139
|
+
var Session = class {
|
|
140
|
+
record;
|
|
141
|
+
sandbox;
|
|
142
|
+
constructor(sandbox, record) {
|
|
143
|
+
this.sandbox = sandbox;
|
|
144
|
+
this.record = { ...record };
|
|
145
|
+
}
|
|
146
|
+
get id() {
|
|
147
|
+
return this.record.id;
|
|
148
|
+
}
|
|
149
|
+
get agent() {
|
|
150
|
+
return this.record.agent;
|
|
151
|
+
}
|
|
152
|
+
get agentSessionId() {
|
|
153
|
+
return this.record.agentSessionId;
|
|
154
|
+
}
|
|
155
|
+
get lastConnectionId() {
|
|
156
|
+
return this.record.lastConnectionId;
|
|
157
|
+
}
|
|
158
|
+
get createdAt() {
|
|
159
|
+
return this.record.createdAt;
|
|
160
|
+
}
|
|
161
|
+
get destroyedAt() {
|
|
162
|
+
return this.record.destroyedAt;
|
|
163
|
+
}
|
|
164
|
+
async refresh() {
|
|
165
|
+
const latest = await this.sandbox.getSession(this.id);
|
|
166
|
+
if (!latest) {
|
|
167
|
+
throw new Error(`session '${this.id}' no longer exists`);
|
|
168
|
+
}
|
|
169
|
+
this.apply(latest.toRecord());
|
|
170
|
+
return this;
|
|
171
|
+
}
|
|
172
|
+
async send(method, params = {}, options = {}) {
|
|
173
|
+
const updated = await this.sandbox.sendSessionMethod(this.id, method, params, options);
|
|
174
|
+
this.apply(updated.session.toRecord());
|
|
175
|
+
return updated.response;
|
|
176
|
+
}
|
|
177
|
+
async prompt(prompt) {
|
|
178
|
+
const response = await this.send("session/prompt", { prompt });
|
|
179
|
+
return response;
|
|
180
|
+
}
|
|
181
|
+
onEvent(listener) {
|
|
182
|
+
return this.sandbox.onSessionEvent(this.id, listener);
|
|
183
|
+
}
|
|
184
|
+
toRecord() {
|
|
185
|
+
return { ...this.record };
|
|
186
|
+
}
|
|
187
|
+
apply(record) {
|
|
188
|
+
this.record = { ...record };
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
var LiveAcpConnection = class _LiveAcpConnection {
|
|
192
|
+
connectionId;
|
|
193
|
+
agent;
|
|
194
|
+
acp;
|
|
195
|
+
sessionByLocalId = /* @__PURE__ */ new Map();
|
|
196
|
+
localByAgentSessionId = /* @__PURE__ */ new Map();
|
|
197
|
+
pendingNewSessionLocals = [];
|
|
198
|
+
pendingRequestSessionById = /* @__PURE__ */ new Map();
|
|
199
|
+
pendingReplayByLocalSessionId = /* @__PURE__ */ new Map();
|
|
200
|
+
onObservedEnvelope;
|
|
201
|
+
constructor(agent, connectionId, acp, onObservedEnvelope) {
|
|
202
|
+
this.agent = agent;
|
|
203
|
+
this.connectionId = connectionId;
|
|
204
|
+
this.acp = acp;
|
|
205
|
+
this.onObservedEnvelope = onObservedEnvelope;
|
|
206
|
+
}
|
|
207
|
+
static async create(options) {
|
|
208
|
+
const connectionId = randomId();
|
|
209
|
+
let live = null;
|
|
210
|
+
const acp = new AcpHttpClient({
|
|
211
|
+
baseUrl: options.baseUrl,
|
|
212
|
+
token: options.token,
|
|
213
|
+
fetch: options.fetcher,
|
|
214
|
+
headers: options.headers,
|
|
215
|
+
transport: {
|
|
216
|
+
path: `${API_PREFIX}/acp/${encodeURIComponent(options.serverId)}`,
|
|
217
|
+
bootstrapQuery: { agent: options.agent }
|
|
218
|
+
},
|
|
219
|
+
client: {
|
|
220
|
+
sessionUpdate: async (_notification) => {
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
onEnvelope: (envelope, direction) => {
|
|
224
|
+
if (!live) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
live.handleEnvelope(envelope, direction);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
live = new _LiveAcpConnection(options.agent, connectionId, acp, options.onObservedEnvelope);
|
|
231
|
+
const initResult = await acp.initialize({
|
|
232
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
233
|
+
clientInfo: {
|
|
234
|
+
name: "sandbox-agent-sdk",
|
|
235
|
+
version: "v1"
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
if (initResult.authMethods && initResult.authMethods.length > 0) {
|
|
239
|
+
await autoAuthenticate(acp, initResult.authMethods);
|
|
240
|
+
}
|
|
241
|
+
return live;
|
|
242
|
+
}
|
|
243
|
+
async close() {
|
|
244
|
+
await this.acp.disconnect();
|
|
245
|
+
}
|
|
246
|
+
hasBoundSession(localSessionId, agentSessionId) {
|
|
247
|
+
const bound = this.sessionByLocalId.get(localSessionId);
|
|
248
|
+
if (!bound) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
if (agentSessionId && bound !== agentSessionId) {
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
bindSession(localSessionId, agentSessionId) {
|
|
257
|
+
this.sessionByLocalId.set(localSessionId, agentSessionId);
|
|
258
|
+
this.localByAgentSessionId.set(agentSessionId, localSessionId);
|
|
259
|
+
}
|
|
260
|
+
queueReplay(localSessionId, replayText) {
|
|
261
|
+
if (!replayText) {
|
|
262
|
+
this.pendingReplayByLocalSessionId.delete(localSessionId);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
this.pendingReplayByLocalSessionId.set(localSessionId, replayText);
|
|
266
|
+
}
|
|
267
|
+
async createRemoteSession(localSessionId, sessionInit) {
|
|
268
|
+
this.pendingNewSessionLocals.push(localSessionId);
|
|
269
|
+
try {
|
|
270
|
+
const response = await this.acp.newSession(sessionInit);
|
|
271
|
+
this.bindSession(localSessionId, response.sessionId);
|
|
272
|
+
return response;
|
|
273
|
+
} catch (error) {
|
|
274
|
+
const index = this.pendingNewSessionLocals.indexOf(localSessionId);
|
|
275
|
+
if (index !== -1) {
|
|
276
|
+
this.pendingNewSessionLocals.splice(index, 1);
|
|
277
|
+
}
|
|
278
|
+
throw error;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
async sendSessionMethod(localSessionId, method, params, options) {
|
|
282
|
+
const agentSessionId = this.sessionByLocalId.get(localSessionId);
|
|
283
|
+
if (!agentSessionId) {
|
|
284
|
+
throw new Error(`session '${localSessionId}' is not bound to live ACP connection '${this.connectionId}'`);
|
|
285
|
+
}
|
|
286
|
+
const mappedParams = mapSessionParams(params, agentSessionId);
|
|
287
|
+
if (method === "session/prompt") {
|
|
288
|
+
const replayText = this.pendingReplayByLocalSessionId.get(localSessionId);
|
|
289
|
+
if (replayText) {
|
|
290
|
+
this.pendingReplayByLocalSessionId.delete(localSessionId);
|
|
291
|
+
injectReplayPrompt(mappedParams, replayText);
|
|
292
|
+
}
|
|
293
|
+
if (options.notification) {
|
|
294
|
+
await this.acp.extNotification(method, mappedParams);
|
|
295
|
+
return void 0;
|
|
296
|
+
}
|
|
297
|
+
return this.acp.prompt(mappedParams);
|
|
298
|
+
}
|
|
299
|
+
if (method === "session/cancel") {
|
|
300
|
+
await this.acp.cancel(mappedParams);
|
|
301
|
+
return void 0;
|
|
302
|
+
}
|
|
303
|
+
if (method === "session/set_mode") {
|
|
304
|
+
return this.acp.setSessionMode(mappedParams);
|
|
305
|
+
}
|
|
306
|
+
if (method === "session/set_config_option") {
|
|
307
|
+
return this.acp.setSessionConfigOption(mappedParams);
|
|
308
|
+
}
|
|
309
|
+
if (options.notification) {
|
|
310
|
+
await this.acp.extNotification(method, mappedParams);
|
|
311
|
+
return void 0;
|
|
312
|
+
}
|
|
313
|
+
return this.acp.extMethod(method, mappedParams);
|
|
314
|
+
}
|
|
315
|
+
handleEnvelope(envelope, direction) {
|
|
316
|
+
const localSessionId = this.resolveSessionId(envelope, direction);
|
|
317
|
+
this.onObservedEnvelope(this, envelope, direction, localSessionId);
|
|
318
|
+
}
|
|
319
|
+
resolveSessionId(envelope, direction) {
|
|
320
|
+
const id = envelopeId(envelope);
|
|
321
|
+
const method = envelopeMethod(envelope);
|
|
322
|
+
if (direction === "outbound") {
|
|
323
|
+
if (id && method === "session/new") {
|
|
324
|
+
const localSessionId = this.pendingNewSessionLocals.shift() ?? null;
|
|
325
|
+
if (localSessionId) {
|
|
326
|
+
this.pendingRequestSessionById.set(id, localSessionId);
|
|
327
|
+
}
|
|
328
|
+
return localSessionId;
|
|
329
|
+
}
|
|
330
|
+
const localFromParams = this.localFromEnvelopeParams(envelope);
|
|
331
|
+
if (id && localFromParams) {
|
|
332
|
+
this.pendingRequestSessionById.set(id, localFromParams);
|
|
333
|
+
}
|
|
334
|
+
return localFromParams;
|
|
335
|
+
}
|
|
336
|
+
if (id) {
|
|
337
|
+
const pending = this.pendingRequestSessionById.get(id) ?? null;
|
|
338
|
+
if (pending) {
|
|
339
|
+
this.pendingRequestSessionById.delete(id);
|
|
340
|
+
const sessionIdFromResult = envelopeSessionIdFromResult(envelope);
|
|
341
|
+
if (sessionIdFromResult) {
|
|
342
|
+
this.bindSession(pending, sessionIdFromResult);
|
|
343
|
+
}
|
|
344
|
+
return pending;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return this.localFromEnvelopeParams(envelope);
|
|
348
|
+
}
|
|
349
|
+
localFromEnvelopeParams(envelope) {
|
|
350
|
+
const agentSessionId = envelopeSessionIdFromParams(envelope);
|
|
351
|
+
if (!agentSessionId) {
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
return this.localByAgentSessionId.get(agentSessionId) ?? null;
|
|
355
|
+
}
|
|
356
|
+
};
|
|
15
357
|
var SandboxAgent = class _SandboxAgent {
|
|
16
358
|
baseUrl;
|
|
17
359
|
token;
|
|
18
360
|
fetcher;
|
|
19
361
|
defaultHeaders;
|
|
362
|
+
persist;
|
|
363
|
+
replayMaxEvents;
|
|
364
|
+
replayMaxChars;
|
|
20
365
|
spawnHandle;
|
|
366
|
+
liveConnections = /* @__PURE__ */ new Map();
|
|
367
|
+
sessionHandles = /* @__PURE__ */ new Map();
|
|
368
|
+
eventListeners = /* @__PURE__ */ new Map();
|
|
369
|
+
nextSessionEventIndexBySession = /* @__PURE__ */ new Map();
|
|
370
|
+
seedSessionEventIndexBySession = /* @__PURE__ */ new Map();
|
|
21
371
|
constructor(options) {
|
|
22
372
|
this.baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
23
373
|
this.token = options.token;
|
|
24
374
|
this.fetcher = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
25
375
|
this.defaultHeaders = options.headers;
|
|
376
|
+
this.persist = options.persist ?? new InMemorySessionPersistDriver();
|
|
377
|
+
this.replayMaxEvents = normalizePositiveInt(options.replayMaxEvents, DEFAULT_REPLAY_MAX_EVENTS);
|
|
378
|
+
this.replayMaxChars = normalizePositiveInt(options.replayMaxChars, DEFAULT_REPLAY_MAX_CHARS);
|
|
26
379
|
if (!this.fetcher) {
|
|
27
380
|
throw new Error("Fetch API is not available; provide a fetch implementation.");
|
|
28
381
|
}
|
|
@@ -41,97 +394,167 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
41
394
|
baseUrl: handle.baseUrl,
|
|
42
395
|
token: handle.token,
|
|
43
396
|
fetch: options.fetch,
|
|
44
|
-
headers: options.headers
|
|
397
|
+
headers: options.headers,
|
|
398
|
+
persist: options.persist,
|
|
399
|
+
replayMaxEvents: options.replayMaxEvents,
|
|
400
|
+
replayMaxChars: options.replayMaxChars
|
|
45
401
|
});
|
|
46
402
|
client.spawnHandle = handle;
|
|
47
403
|
return client;
|
|
48
404
|
}
|
|
49
|
-
async
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
405
|
+
async dispose() {
|
|
406
|
+
const connections = [...this.liveConnections.values()];
|
|
407
|
+
this.liveConnections.clear();
|
|
408
|
+
await Promise.all(
|
|
409
|
+
connections.map(async (connection) => {
|
|
410
|
+
await connection.close();
|
|
411
|
+
})
|
|
412
|
+
);
|
|
413
|
+
if (this.spawnHandle) {
|
|
414
|
+
await this.spawnHandle.dispose();
|
|
415
|
+
this.spawnHandle = void 0;
|
|
416
|
+
}
|
|
59
417
|
}
|
|
60
|
-
async
|
|
61
|
-
|
|
418
|
+
async listSessions(request = {}) {
|
|
419
|
+
const page = await this.persist.listSessions(request);
|
|
420
|
+
return {
|
|
421
|
+
items: page.items.map((record) => this.upsertSessionHandle(record)),
|
|
422
|
+
nextCursor: page.nextCursor
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
async getSession(id) {
|
|
426
|
+
const record = await this.persist.getSession(id);
|
|
427
|
+
if (!record) {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
return this.upsertSessionHandle(record);
|
|
62
431
|
}
|
|
63
|
-
async
|
|
64
|
-
return this.
|
|
432
|
+
async getEvents(request) {
|
|
433
|
+
return this.persist.listEvents(request);
|
|
65
434
|
}
|
|
66
|
-
async createSession(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
435
|
+
async createSession(request) {
|
|
436
|
+
if (!request.agent.trim()) {
|
|
437
|
+
throw new Error("createSession requires a non-empty agent");
|
|
438
|
+
}
|
|
439
|
+
const localSessionId = request.id?.trim() || randomId();
|
|
440
|
+
const live = await this.getLiveConnection(request.agent.trim());
|
|
441
|
+
const sessionInit = normalizeSessionInit(request.sessionInit);
|
|
442
|
+
const response = await live.createRemoteSession(localSessionId, sessionInit);
|
|
443
|
+
const record = {
|
|
444
|
+
id: localSessionId,
|
|
445
|
+
agent: request.agent.trim(),
|
|
446
|
+
agentSessionId: response.sessionId,
|
|
447
|
+
lastConnectionId: live.connectionId,
|
|
448
|
+
createdAt: nowMs(),
|
|
449
|
+
sessionInit
|
|
450
|
+
};
|
|
451
|
+
await this.persist.updateSession(record);
|
|
452
|
+
this.nextSessionEventIndexBySession.set(record.id, 1);
|
|
453
|
+
live.bindSession(record.id, record.agentSessionId);
|
|
454
|
+
return this.upsertSessionHandle(record);
|
|
455
|
+
}
|
|
456
|
+
async resumeSession(id) {
|
|
457
|
+
const existing = await this.persist.getSession(id);
|
|
458
|
+
if (!existing) {
|
|
459
|
+
throw new Error(`session '${id}' not found`);
|
|
460
|
+
}
|
|
461
|
+
const live = await this.getLiveConnection(existing.agent);
|
|
462
|
+
if (existing.lastConnectionId === live.connectionId && live.hasBoundSession(id, existing.agentSessionId)) {
|
|
463
|
+
return this.upsertSessionHandle(existing);
|
|
464
|
+
}
|
|
465
|
+
const replaySource = await this.collectReplayEvents(existing.id, this.replayMaxEvents);
|
|
466
|
+
const replayText = buildReplayText(replaySource, this.replayMaxChars);
|
|
467
|
+
const recreated = await live.createRemoteSession(existing.id, normalizeSessionInit(existing.sessionInit));
|
|
468
|
+
const updated = {
|
|
469
|
+
...existing,
|
|
470
|
+
agentSessionId: recreated.sessionId,
|
|
471
|
+
lastConnectionId: live.connectionId,
|
|
472
|
+
destroyedAt: void 0
|
|
473
|
+
};
|
|
474
|
+
await this.persist.updateSession(updated);
|
|
475
|
+
live.bindSession(updated.id, updated.agentSessionId);
|
|
476
|
+
live.queueReplay(updated.id, replayText);
|
|
477
|
+
return this.upsertSessionHandle(updated);
|
|
478
|
+
}
|
|
479
|
+
async resumeOrCreateSession(request) {
|
|
480
|
+
const existing = await this.persist.getSession(request.id);
|
|
481
|
+
if (existing) {
|
|
482
|
+
return this.resumeSession(existing.id);
|
|
483
|
+
}
|
|
484
|
+
return this.createSession(request);
|
|
70
485
|
}
|
|
71
|
-
async
|
|
72
|
-
|
|
486
|
+
async destroySession(id) {
|
|
487
|
+
const existing = await this.persist.getSession(id);
|
|
488
|
+
if (!existing) {
|
|
489
|
+
throw new Error(`session '${id}' not found`);
|
|
490
|
+
}
|
|
491
|
+
const updated = {
|
|
492
|
+
...existing,
|
|
493
|
+
destroyedAt: nowMs()
|
|
494
|
+
};
|
|
495
|
+
await this.persist.updateSession(updated);
|
|
496
|
+
return this.upsertSessionHandle(updated);
|
|
497
|
+
}
|
|
498
|
+
async sendSessionMethod(sessionId, method, params, options = {}) {
|
|
499
|
+
const record = await this.persist.getSession(sessionId);
|
|
500
|
+
if (!record) {
|
|
501
|
+
throw new Error(`session '${sessionId}' not found`);
|
|
502
|
+
}
|
|
503
|
+
const live = await this.getLiveConnection(record.agent);
|
|
504
|
+
if (!live.hasBoundSession(record.id, record.agentSessionId)) {
|
|
505
|
+
const restored = await this.resumeSession(record.id);
|
|
506
|
+
return this.sendSessionMethod(restored.id, method, params, options);
|
|
507
|
+
}
|
|
508
|
+
const response = await live.sendSessionMethod(record.id, method, params, options);
|
|
509
|
+
const refreshed = await this.requireSessionRecord(record.id);
|
|
510
|
+
return {
|
|
511
|
+
session: this.upsertSessionHandle(refreshed),
|
|
512
|
+
response
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
onSessionEvent(sessionId, listener) {
|
|
516
|
+
const listeners = this.eventListeners.get(sessionId) ?? /* @__PURE__ */ new Set();
|
|
517
|
+
listeners.add(listener);
|
|
518
|
+
this.eventListeners.set(sessionId, listeners);
|
|
519
|
+
return () => {
|
|
520
|
+
const set = this.eventListeners.get(sessionId);
|
|
521
|
+
if (!set) {
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
set.delete(listener);
|
|
525
|
+
if (set.size === 0) {
|
|
526
|
+
this.eventListeners.delete(sessionId);
|
|
527
|
+
}
|
|
528
|
+
};
|
|
73
529
|
}
|
|
74
|
-
async
|
|
75
|
-
|
|
76
|
-
body: request
|
|
77
|
-
});
|
|
530
|
+
async getHealth() {
|
|
531
|
+
return this.requestJson("GET", `${API_PREFIX}/health`);
|
|
78
532
|
}
|
|
79
|
-
async
|
|
80
|
-
return this.requestJson("GET", `${API_PREFIX}/
|
|
81
|
-
query
|
|
533
|
+
async listAgents(options) {
|
|
534
|
+
return this.requestJson("GET", `${API_PREFIX}/agents`, {
|
|
535
|
+
query: options?.config ? { config: "true" } : void 0
|
|
82
536
|
});
|
|
83
537
|
}
|
|
84
|
-
async
|
|
85
|
-
return this.
|
|
86
|
-
query
|
|
87
|
-
accept: "text/event-stream",
|
|
88
|
-
signal
|
|
538
|
+
async getAgent(agent, options) {
|
|
539
|
+
return this.requestJson("GET", `${API_PREFIX}/agents/${encodeURIComponent(agent)}`, {
|
|
540
|
+
query: options?.config ? { config: "true" } : void 0
|
|
89
541
|
});
|
|
90
542
|
}
|
|
91
|
-
async
|
|
92
|
-
return this.
|
|
93
|
-
|
|
94
|
-
body: request,
|
|
95
|
-
accept: "text/event-stream",
|
|
96
|
-
signal
|
|
543
|
+
async installAgent(agent, request = {}) {
|
|
544
|
+
return this.requestJson("POST", `${API_PREFIX}/agents/${encodeURIComponent(agent)}/install`, {
|
|
545
|
+
body: request
|
|
97
546
|
});
|
|
98
547
|
}
|
|
99
|
-
async
|
|
100
|
-
|
|
101
|
-
yield* this.parseSseStream(response);
|
|
548
|
+
async listAcpServers() {
|
|
549
|
+
return this.requestJson("GET", `${API_PREFIX}/acp`);
|
|
102
550
|
}
|
|
103
|
-
async
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
async replyQuestion(sessionId, questionId, request) {
|
|
108
|
-
await this.requestJson(
|
|
109
|
-
"POST",
|
|
110
|
-
`${API_PREFIX}/sessions/${encodeURIComponent(sessionId)}/questions/${encodeURIComponent(questionId)}/reply`,
|
|
111
|
-
{ body: request }
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
async rejectQuestion(sessionId, questionId) {
|
|
115
|
-
await this.requestJson(
|
|
116
|
-
"POST",
|
|
117
|
-
`${API_PREFIX}/sessions/${encodeURIComponent(sessionId)}/questions/${encodeURIComponent(questionId)}/reject`
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
async replyPermission(sessionId, permissionId, request) {
|
|
121
|
-
await this.requestJson(
|
|
122
|
-
"POST",
|
|
123
|
-
`${API_PREFIX}/sessions/${encodeURIComponent(sessionId)}/permissions/${encodeURIComponent(permissionId)}/reply`,
|
|
124
|
-
{ body: request }
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
async terminateSession(sessionId) {
|
|
128
|
-
await this.requestJson("POST", `${API_PREFIX}/sessions/${encodeURIComponent(sessionId)}/terminate`);
|
|
129
|
-
}
|
|
130
|
-
async listFsEntries(query) {
|
|
131
|
-
return this.requestJson("GET", `${API_PREFIX}/fs/entries`, { query });
|
|
551
|
+
async listFsEntries(query = {}) {
|
|
552
|
+
return this.requestJson("GET", `${FS_PATH}/entries`, {
|
|
553
|
+
query
|
|
554
|
+
});
|
|
132
555
|
}
|
|
133
556
|
async readFsFile(query) {
|
|
134
|
-
const response = await this.requestRaw("GET", `${
|
|
557
|
+
const response = await this.requestRaw("GET", `${FS_PATH}/file`, {
|
|
135
558
|
query,
|
|
136
559
|
accept: "application/octet-stream"
|
|
137
560
|
});
|
|
@@ -139,69 +562,198 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
139
562
|
return new Uint8Array(buffer);
|
|
140
563
|
}
|
|
141
564
|
async writeFsFile(query, body) {
|
|
142
|
-
const response = await this.requestRaw("PUT", `${
|
|
565
|
+
const response = await this.requestRaw("PUT", `${FS_PATH}/file`, {
|
|
143
566
|
query,
|
|
144
567
|
rawBody: body,
|
|
145
568
|
contentType: "application/octet-stream",
|
|
146
569
|
accept: "application/json"
|
|
147
570
|
});
|
|
148
|
-
|
|
149
|
-
return text ? JSON.parse(text) : { path: "", bytesWritten: 0 };
|
|
571
|
+
return await response.json();
|
|
150
572
|
}
|
|
151
573
|
async deleteFsEntry(query) {
|
|
152
|
-
return this.requestJson("DELETE", `${
|
|
574
|
+
return this.requestJson("DELETE", `${FS_PATH}/entry`, { query });
|
|
153
575
|
}
|
|
154
576
|
async mkdirFs(query) {
|
|
155
|
-
return this.requestJson("POST", `${
|
|
577
|
+
return this.requestJson("POST", `${FS_PATH}/mkdir`, { query });
|
|
156
578
|
}
|
|
157
|
-
async moveFs(request
|
|
158
|
-
return this.requestJson("POST", `${
|
|
579
|
+
async moveFs(request) {
|
|
580
|
+
return this.requestJson("POST", `${FS_PATH}/move`, { body: request });
|
|
159
581
|
}
|
|
160
582
|
async statFs(query) {
|
|
161
|
-
return this.requestJson("GET", `${
|
|
583
|
+
return this.requestJson("GET", `${FS_PATH}/stat`, { query });
|
|
162
584
|
}
|
|
163
585
|
async uploadFsBatch(body, query) {
|
|
164
|
-
const response = await this.requestRaw("POST", `${
|
|
586
|
+
const response = await this.requestRaw("POST", `${FS_PATH}/upload-batch`, {
|
|
165
587
|
query,
|
|
166
588
|
rawBody: body,
|
|
167
589
|
contentType: "application/x-tar",
|
|
168
590
|
accept: "application/json"
|
|
169
591
|
});
|
|
170
|
-
|
|
171
|
-
return text ? JSON.parse(text) : { paths: [], truncated: false };
|
|
592
|
+
return await response.json();
|
|
172
593
|
}
|
|
173
|
-
async
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
594
|
+
async getMcpConfig(query) {
|
|
595
|
+
return this.requestJson("GET", `${API_PREFIX}/config/mcp`, { query });
|
|
596
|
+
}
|
|
597
|
+
async setMcpConfig(query, config) {
|
|
598
|
+
await this.requestRaw("PUT", `${API_PREFIX}/config/mcp`, { query, body: config });
|
|
599
|
+
}
|
|
600
|
+
async deleteMcpConfig(query) {
|
|
601
|
+
await this.requestRaw("DELETE", `${API_PREFIX}/config/mcp`, { query });
|
|
602
|
+
}
|
|
603
|
+
async getSkillsConfig(query) {
|
|
604
|
+
return this.requestJson("GET", `${API_PREFIX}/config/skills`, { query });
|
|
605
|
+
}
|
|
606
|
+
async setSkillsConfig(query, config) {
|
|
607
|
+
await this.requestRaw("PUT", `${API_PREFIX}/config/skills`, { query, body: config });
|
|
608
|
+
}
|
|
609
|
+
async deleteSkillsConfig(query) {
|
|
610
|
+
await this.requestRaw("DELETE", `${API_PREFIX}/config/skills`, { query });
|
|
611
|
+
}
|
|
612
|
+
async getLiveConnection(agent) {
|
|
613
|
+
const existing = this.liveConnections.get(agent);
|
|
614
|
+
if (existing) {
|
|
615
|
+
return existing;
|
|
616
|
+
}
|
|
617
|
+
const serverId = `sdk-${agent}-${randomId()}`;
|
|
618
|
+
const created = await LiveAcpConnection.create({
|
|
619
|
+
baseUrl: this.baseUrl,
|
|
620
|
+
token: this.token,
|
|
621
|
+
fetcher: this.fetcher,
|
|
622
|
+
headers: this.defaultHeaders,
|
|
623
|
+
agent,
|
|
624
|
+
serverId,
|
|
625
|
+
onObservedEnvelope: (connection, envelope, direction, localSessionId) => {
|
|
626
|
+
void this.persistObservedEnvelope(connection, envelope, direction, localSessionId);
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
this.liveConnections.set(agent, created);
|
|
630
|
+
return created;
|
|
631
|
+
}
|
|
632
|
+
async persistObservedEnvelope(connection, envelope, direction, localSessionId) {
|
|
633
|
+
if (!localSessionId) {
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
const event = {
|
|
637
|
+
id: randomId(),
|
|
638
|
+
eventIndex: await this.allocateSessionEventIndex(localSessionId),
|
|
639
|
+
sessionId: localSessionId,
|
|
640
|
+
createdAt: nowMs(),
|
|
641
|
+
connectionId: connection.connectionId,
|
|
642
|
+
sender: direction === "outbound" ? "client" : "agent",
|
|
643
|
+
payload: cloneEnvelope(envelope)
|
|
644
|
+
};
|
|
645
|
+
await this.persist.insertEvent(event);
|
|
646
|
+
const listeners = this.eventListeners.get(localSessionId);
|
|
647
|
+
if (!listeners || listeners.size === 0) {
|
|
648
|
+
return;
|
|
177
649
|
}
|
|
650
|
+
for (const listener of listeners) {
|
|
651
|
+
listener(event);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
async allocateSessionEventIndex(sessionId) {
|
|
655
|
+
await this.ensureSessionEventIndexSeeded(sessionId);
|
|
656
|
+
const nextIndex = this.nextSessionEventIndexBySession.get(sessionId) ?? 1;
|
|
657
|
+
this.nextSessionEventIndexBySession.set(sessionId, nextIndex + 1);
|
|
658
|
+
return nextIndex;
|
|
659
|
+
}
|
|
660
|
+
async ensureSessionEventIndexSeeded(sessionId) {
|
|
661
|
+
if (this.nextSessionEventIndexBySession.has(sessionId)) {
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
if (!this.seedSessionEventIndexBySession.has(sessionId)) {
|
|
665
|
+
const pending2 = (async () => {
|
|
666
|
+
const maxPersistedIndex = await this.findMaxPersistedSessionEventIndex(sessionId);
|
|
667
|
+
this.nextSessionEventIndexBySession.set(sessionId, Math.max(1, maxPersistedIndex + 1));
|
|
668
|
+
})().finally(() => {
|
|
669
|
+
this.seedSessionEventIndexBySession.delete(sessionId);
|
|
670
|
+
});
|
|
671
|
+
this.seedSessionEventIndexBySession.set(sessionId, pending2);
|
|
672
|
+
}
|
|
673
|
+
const pending = this.seedSessionEventIndexBySession.get(sessionId);
|
|
674
|
+
if (pending) {
|
|
675
|
+
await pending;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
async findMaxPersistedSessionEventIndex(sessionId) {
|
|
679
|
+
let maxIndex = 0;
|
|
680
|
+
let eventCursor;
|
|
681
|
+
while (true) {
|
|
682
|
+
const eventsPage = await this.persist.listEvents({
|
|
683
|
+
sessionId,
|
|
684
|
+
cursor: eventCursor,
|
|
685
|
+
limit: EVENT_INDEX_SCAN_EVENTS_LIMIT
|
|
686
|
+
});
|
|
687
|
+
for (const event of eventsPage.items) {
|
|
688
|
+
if (Number.isFinite(event.eventIndex) && event.eventIndex > maxIndex) {
|
|
689
|
+
maxIndex = Math.floor(event.eventIndex);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
if (!eventsPage.nextCursor) {
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
eventCursor = eventsPage.nextCursor;
|
|
696
|
+
}
|
|
697
|
+
return maxIndex;
|
|
698
|
+
}
|
|
699
|
+
async collectReplayEvents(sessionId, maxEvents) {
|
|
700
|
+
const all = [];
|
|
701
|
+
let cursor;
|
|
702
|
+
while (true) {
|
|
703
|
+
const page = await this.persist.listEvents({
|
|
704
|
+
sessionId,
|
|
705
|
+
cursor,
|
|
706
|
+
limit: Math.max(100, maxEvents)
|
|
707
|
+
});
|
|
708
|
+
all.push(...page.items);
|
|
709
|
+
if (!page.nextCursor) {
|
|
710
|
+
break;
|
|
711
|
+
}
|
|
712
|
+
cursor = page.nextCursor;
|
|
713
|
+
}
|
|
714
|
+
return all.slice(-maxEvents);
|
|
715
|
+
}
|
|
716
|
+
upsertSessionHandle(record) {
|
|
717
|
+
const existing = this.sessionHandles.get(record.id);
|
|
718
|
+
if (existing) {
|
|
719
|
+
existing.apply(record);
|
|
720
|
+
return existing;
|
|
721
|
+
}
|
|
722
|
+
const created = new Session(this, record);
|
|
723
|
+
this.sessionHandles.set(record.id, created);
|
|
724
|
+
return created;
|
|
725
|
+
}
|
|
726
|
+
async requireSessionRecord(id) {
|
|
727
|
+
const record = await this.persist.getSession(id);
|
|
728
|
+
if (!record) {
|
|
729
|
+
throw new Error(`session '${id}' not found`);
|
|
730
|
+
}
|
|
731
|
+
return record;
|
|
178
732
|
}
|
|
179
733
|
async requestJson(method, path, options = {}) {
|
|
180
734
|
const response = await this.requestRaw(method, path, {
|
|
181
735
|
query: options.query,
|
|
182
736
|
body: options.body,
|
|
183
737
|
headers: options.headers,
|
|
184
|
-
accept: options.accept ?? "application/json"
|
|
738
|
+
accept: options.accept ?? "application/json",
|
|
739
|
+
signal: options.signal
|
|
185
740
|
});
|
|
186
741
|
if (response.status === 204) {
|
|
187
742
|
return void 0;
|
|
188
743
|
}
|
|
189
|
-
|
|
190
|
-
if (!text) {
|
|
191
|
-
return void 0;
|
|
192
|
-
}
|
|
193
|
-
return JSON.parse(text);
|
|
744
|
+
return await response.json();
|
|
194
745
|
}
|
|
195
746
|
async requestRaw(method, path, options = {}) {
|
|
196
747
|
const url = this.buildUrl(path, options.query);
|
|
197
|
-
const headers =
|
|
198
|
-
if (this.token) {
|
|
199
|
-
headers.set("Authorization", `Bearer ${this.token}`);
|
|
200
|
-
}
|
|
748
|
+
const headers = this.buildHeaders(options.headers);
|
|
201
749
|
if (options.accept) {
|
|
202
750
|
headers.set("Accept", options.accept);
|
|
203
751
|
}
|
|
204
|
-
const init = {
|
|
752
|
+
const init = {
|
|
753
|
+
method,
|
|
754
|
+
headers,
|
|
755
|
+
signal: options.signal
|
|
756
|
+
};
|
|
205
757
|
if (options.rawBody !== void 0 && options.body !== void 0) {
|
|
206
758
|
throw new Error("requestRaw received both rawBody and body");
|
|
207
759
|
}
|
|
@@ -214,17 +766,24 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
214
766
|
headers.set("Content-Type", "application/json");
|
|
215
767
|
init.body = JSON.stringify(options.body);
|
|
216
768
|
}
|
|
217
|
-
if (options.headers) {
|
|
218
|
-
const extra = new Headers(options.headers);
|
|
219
|
-
extra.forEach((value, key) => headers.set(key, value));
|
|
220
|
-
}
|
|
221
769
|
const response = await this.fetcher(url, init);
|
|
222
770
|
if (!response.ok) {
|
|
223
|
-
const problem = await
|
|
771
|
+
const problem = await readProblem(response);
|
|
224
772
|
throw new SandboxAgentError(response.status, problem, response);
|
|
225
773
|
}
|
|
226
774
|
return response;
|
|
227
775
|
}
|
|
776
|
+
buildHeaders(extra) {
|
|
777
|
+
const headers = new Headers(this.defaultHeaders ?? void 0);
|
|
778
|
+
if (this.token) {
|
|
779
|
+
headers.set("Authorization", `Bearer ${this.token}`);
|
|
780
|
+
}
|
|
781
|
+
if (extra) {
|
|
782
|
+
const merged = new Headers(extra);
|
|
783
|
+
merged.forEach((value, key) => headers.set(key, value));
|
|
784
|
+
}
|
|
785
|
+
return headers;
|
|
786
|
+
}
|
|
228
787
|
buildUrl(path, query) {
|
|
229
788
|
const url = new URL(`${this.baseUrl}${path}`);
|
|
230
789
|
if (query) {
|
|
@@ -237,55 +796,152 @@ var SandboxAgent = class _SandboxAgent {
|
|
|
237
796
|
}
|
|
238
797
|
return url.toString();
|
|
239
798
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
} catch {
|
|
248
|
-
return void 0;
|
|
249
|
-
}
|
|
799
|
+
};
|
|
800
|
+
async function autoAuthenticate(acp, methods) {
|
|
801
|
+
const envBased = methods.find(
|
|
802
|
+
(m) => m.id === "codex-api-key" || m.id === "openai-api-key" || m.id === "anthropic-api-key"
|
|
803
|
+
);
|
|
804
|
+
if (!envBased) {
|
|
805
|
+
return;
|
|
250
806
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
807
|
+
try {
|
|
808
|
+
await acp.authenticate({ methodId: envBased.id });
|
|
809
|
+
} catch {
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
function normalizeSessionInit(value) {
|
|
813
|
+
if (!value) {
|
|
814
|
+
return {
|
|
815
|
+
cwd: defaultCwd(),
|
|
816
|
+
mcpServers: []
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
return {
|
|
820
|
+
...value,
|
|
821
|
+
cwd: value.cwd ?? defaultCwd(),
|
|
822
|
+
mcpServers: value.mcpServers ?? []
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
function mapSessionParams(params, agentSessionId) {
|
|
826
|
+
return {
|
|
827
|
+
...params,
|
|
828
|
+
sessionId: agentSessionId
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
function injectReplayPrompt(params, replayText) {
|
|
832
|
+
const prompt = Array.isArray(params.prompt) ? [...params.prompt] : [];
|
|
833
|
+
prompt.unshift({
|
|
834
|
+
type: "text",
|
|
835
|
+
text: replayText
|
|
836
|
+
});
|
|
837
|
+
params.prompt = prompt;
|
|
838
|
+
}
|
|
839
|
+
function buildReplayText(events, maxChars) {
|
|
840
|
+
if (events.length === 0) {
|
|
841
|
+
return null;
|
|
842
|
+
}
|
|
843
|
+
const prefix = "Previous session history is replayed below as JSON-RPC envelopes. Use it as context before responding to the latest user prompt.\n";
|
|
844
|
+
let text = prefix;
|
|
845
|
+
for (const event of events) {
|
|
846
|
+
const line = JSON.stringify({
|
|
847
|
+
createdAt: event.createdAt,
|
|
848
|
+
sender: event.sender,
|
|
849
|
+
payload: event.payload
|
|
850
|
+
});
|
|
851
|
+
if (text.length + line.length + 1 > maxChars) {
|
|
852
|
+
text += "\n[history truncated]";
|
|
853
|
+
break;
|
|
277
854
|
}
|
|
855
|
+
text += `${line}
|
|
856
|
+
`;
|
|
278
857
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
858
|
+
return text;
|
|
859
|
+
}
|
|
860
|
+
function envelopeMethod(message) {
|
|
861
|
+
if (!isRecord(message) || !("method" in message) || typeof message["method"] !== "string") {
|
|
862
|
+
return null;
|
|
283
863
|
}
|
|
284
|
-
|
|
285
|
-
|
|
864
|
+
return message["method"];
|
|
865
|
+
}
|
|
866
|
+
function envelopeId(message) {
|
|
867
|
+
if (!isRecord(message) || !("id" in message) || message["id"] === void 0 || message["id"] === null) {
|
|
868
|
+
return null;
|
|
286
869
|
}
|
|
287
|
-
return
|
|
288
|
-
}
|
|
870
|
+
return String(message["id"]);
|
|
871
|
+
}
|
|
872
|
+
function envelopeSessionIdFromParams(message) {
|
|
873
|
+
if (!isRecord(message) || !("params" in message) || !isRecord(message["params"])) {
|
|
874
|
+
return null;
|
|
875
|
+
}
|
|
876
|
+
const params = message["params"];
|
|
877
|
+
if (typeof params.sessionId === "string" && params.sessionId.length > 0) {
|
|
878
|
+
return params.sessionId;
|
|
879
|
+
}
|
|
880
|
+
return null;
|
|
881
|
+
}
|
|
882
|
+
function envelopeSessionIdFromResult(message) {
|
|
883
|
+
if (!isRecord(message) || !("result" in message) || !isRecord(message["result"])) {
|
|
884
|
+
return null;
|
|
885
|
+
}
|
|
886
|
+
const result = message["result"];
|
|
887
|
+
if (typeof result.sessionId === "string" && result.sessionId.length > 0) {
|
|
888
|
+
return result.sessionId;
|
|
889
|
+
}
|
|
890
|
+
return null;
|
|
891
|
+
}
|
|
892
|
+
function cloneEnvelope(envelope) {
|
|
893
|
+
return JSON.parse(JSON.stringify(envelope));
|
|
894
|
+
}
|
|
895
|
+
function isRecord(value) {
|
|
896
|
+
return typeof value === "object" && value !== null;
|
|
897
|
+
}
|
|
898
|
+
function randomId() {
|
|
899
|
+
if (typeof globalThis.crypto?.randomUUID === "function") {
|
|
900
|
+
return globalThis.crypto.randomUUID();
|
|
901
|
+
}
|
|
902
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
903
|
+
}
|
|
904
|
+
function nowMs() {
|
|
905
|
+
return Date.now();
|
|
906
|
+
}
|
|
907
|
+
function defaultCwd() {
|
|
908
|
+
if (typeof process !== "undefined" && typeof process.cwd === "function") {
|
|
909
|
+
return process.cwd();
|
|
910
|
+
}
|
|
911
|
+
return "/";
|
|
912
|
+
}
|
|
913
|
+
function normalizePositiveInt(value, fallback) {
|
|
914
|
+
if (!Number.isFinite(value) || (value ?? 0) < 1) {
|
|
915
|
+
return fallback;
|
|
916
|
+
}
|
|
917
|
+
return Math.floor(value);
|
|
918
|
+
}
|
|
919
|
+
function normalizeSpawnOptions(spawn, defaultEnabled) {
|
|
920
|
+
if (spawn === false) {
|
|
921
|
+
return { enabled: false };
|
|
922
|
+
}
|
|
923
|
+
if (spawn === true || spawn === void 0) {
|
|
924
|
+
return { enabled: defaultEnabled };
|
|
925
|
+
}
|
|
926
|
+
return {
|
|
927
|
+
...spawn,
|
|
928
|
+
enabled: spawn.enabled ?? defaultEnabled
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
async function readProblem(response) {
|
|
932
|
+
try {
|
|
933
|
+
const text = await response.clone().text();
|
|
934
|
+
if (!text) {
|
|
935
|
+
return void 0;
|
|
936
|
+
}
|
|
937
|
+
return JSON.parse(text);
|
|
938
|
+
} catch {
|
|
939
|
+
return void 0;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
// src/index.ts
|
|
944
|
+
import { AcpRpcError } from "acp-http-client";
|
|
289
945
|
|
|
290
946
|
// src/inspector.ts
|
|
291
947
|
function buildInspectorUrl(options) {
|
|
@@ -301,8 +957,12 @@ function buildInspectorUrl(options) {
|
|
|
301
957
|
return `${normalized}/ui/${queryString ? `?${queryString}` : ""}`;
|
|
302
958
|
}
|
|
303
959
|
export {
|
|
960
|
+
AcpRpcError,
|
|
961
|
+
InMemorySessionPersistDriver,
|
|
962
|
+
LiveAcpConnection,
|
|
304
963
|
SandboxAgent,
|
|
305
964
|
SandboxAgentError,
|
|
965
|
+
Session,
|
|
306
966
|
buildInspectorUrl
|
|
307
967
|
};
|
|
308
968
|
//# sourceMappingURL=index.js.map
|