@wrongstack/acp 1.0.29 → 1.0.33
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/agent.js +52 -23
- package/dist/client/acp-session-messages.d.ts +24 -0
- package/dist/client/acp-session.d.ts +1 -0
- package/dist/client.js +258 -179
- package/dist/index.js +285 -182
- package/dist/wrongstack-acp-agent.js +22 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -239,6 +239,10 @@ async function handleSessionNewOp(ctx, id, params) {
|
|
|
239
239
|
}
|
|
240
240
|
const skipped = [];
|
|
241
241
|
const mcpServers = parseMcpServers(p.mcpServers, (reason) => skipped.push(reason));
|
|
242
|
+
if (ctx.sessions.size >= ctx.maxSessions) {
|
|
243
|
+
await ctx.sendError(id, -32e3, `active session limit reached (${ctx.maxSessions})`);
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
242
246
|
const sessionId = `sess_${ctx.allocId()}`;
|
|
243
247
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
244
248
|
const state = {
|
|
@@ -289,6 +293,9 @@ async function handleSessionLoadOp(ctx, id, params, replayHistory = true) {
|
|
|
289
293
|
if (!existing && sessionId && ctx.store) {
|
|
290
294
|
const persisted = await ctx.store.load(sessionId);
|
|
291
295
|
if (persisted) {
|
|
296
|
+
if (ctx.sessions.has(sessionId)) {
|
|
297
|
+
return handleSessionLoadOp(ctx, id, params, replayHistory);
|
|
298
|
+
}
|
|
292
299
|
if (ctx.sessions.size >= ctx.maxSessions) {
|
|
293
300
|
await ctx.sendError(id, -32e3, `active session limit reached (${ctx.maxSessions})`);
|
|
294
301
|
return false;
|
|
@@ -299,6 +306,13 @@ async function handleSessionLoadOp(ctx, id, params, replayHistory = true) {
|
|
|
299
306
|
}
|
|
300
307
|
const candidateCwd = loadCwd ?? persisted.cwd ?? ctx.defaultCwd;
|
|
301
308
|
const restoredCwd = await resolveSessionCwd(candidateCwd) ?? ctx.defaultCwd;
|
|
309
|
+
if (ctx.sessions.has(sessionId)) {
|
|
310
|
+
return handleSessionLoadOp(ctx, id, params, replayHistory);
|
|
311
|
+
}
|
|
312
|
+
if (ctx.sessions.size >= ctx.maxSessions) {
|
|
313
|
+
await ctx.sendError(id, -32e3, `active session limit reached (${ctx.maxSessions})`);
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
302
316
|
const restored = {
|
|
303
317
|
id: sessionId,
|
|
304
318
|
cwd: restoredCwd,
|
|
@@ -402,9 +416,17 @@ async function handleSessionForkOp(ctx, id, params) {
|
|
|
402
416
|
}
|
|
403
417
|
forkCwd = resolved;
|
|
404
418
|
}
|
|
419
|
+
if (ctx.sessions.get(sourceId) !== source) {
|
|
420
|
+
await ctx.sendError(id, -32e3, `session not found: ${sourceId}`);
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
405
423
|
const forkSkipped = [];
|
|
406
424
|
const forkRequested = parseMcpServers(p.mcpServers, (reason) => forkSkipped.push(reason));
|
|
407
425
|
const forkMcpServers = forkRequested.length > 0 ? forkRequested : source.mcpServers;
|
|
426
|
+
if (ctx.sessions.size >= ctx.maxSessions) {
|
|
427
|
+
await ctx.sendError(id, -32e3, `active session limit reached (${ctx.maxSessions})`);
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
408
430
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
409
431
|
const sessionId = `sess_${ctx.allocId()}`;
|
|
410
432
|
const forked = {
|
|
@@ -1517,7 +1539,7 @@ function toACPInputSchema(src) {
|
|
|
1517
1539
|
if (typeof s.maximum === "number") out.maximum = s.maximum;
|
|
1518
1540
|
if (s.items) out.items = toACPInputSchema(s.items);
|
|
1519
1541
|
if (s.properties && typeof s.properties === "object") {
|
|
1520
|
-
const props =
|
|
1542
|
+
const props = /* @__PURE__ */ Object.create(null);
|
|
1521
1543
|
for (const [k, v] of Object.entries(s.properties)) {
|
|
1522
1544
|
props[k] = toACPInputSchema(v);
|
|
1523
1545
|
}
|
|
@@ -1843,8 +1865,8 @@ function cancelLateAcpSession(transport, createPromise) {
|
|
|
1843
1865
|
const timer = setTimeout(() => {
|
|
1844
1866
|
reject(new Error("late session/cancel send timed out"));
|
|
1845
1867
|
}, LATE_CANCEL_SEND_TIMEOUT_MS);
|
|
1846
|
-
Promise.resolve(
|
|
1847
|
-
transport.send({
|
|
1868
|
+
Promise.resolve().then(
|
|
1869
|
+
() => transport.send({
|
|
1848
1870
|
jsonrpc: "2.0",
|
|
1849
1871
|
method: "session/cancel",
|
|
1850
1872
|
params: { sessionId: lateId }
|
|
@@ -1883,11 +1905,6 @@ function cancelLateAcpSession(transport, createPromise) {
|
|
|
1883
1905
|
});
|
|
1884
1906
|
}
|
|
1885
1907
|
|
|
1886
|
-
// src/client/acp-message-routing.ts
|
|
1887
|
-
function isBestEffortAckMethod(method) {
|
|
1888
|
-
return method === "mcp/connect" || method === "mcp/message" || method === "mcp/disconnect" || method === "elicitation/create" || method === "elicitation/complete";
|
|
1889
|
-
}
|
|
1890
|
-
|
|
1891
1908
|
// src/client/acp-session-errors.ts
|
|
1892
1909
|
var ACPSessionError = class extends Error {
|
|
1893
1910
|
kind;
|
|
@@ -1900,7 +1917,7 @@ var ACPSessionError = class extends Error {
|
|
|
1900
1917
|
}
|
|
1901
1918
|
};
|
|
1902
1919
|
function isJsonRpcError(v) {
|
|
1903
|
-
return typeof v === "object" && v !== null &&
|
|
1920
|
+
return typeof v === "object" && v !== null && Number.isSafeInteger(v.code) && typeof v.message === "string";
|
|
1904
1921
|
}
|
|
1905
1922
|
function isAuthRequiredError(err) {
|
|
1906
1923
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -1948,6 +1965,7 @@ async function executeLoadSession(ctx, sessionId, mcpServers, cwd) {
|
|
|
1948
1965
|
if (ctx.sessionId && ctx.sessionId !== sessionId) {
|
|
1949
1966
|
await ctx.closeSession();
|
|
1950
1967
|
}
|
|
1968
|
+
if (ctx.closed) throw new ACPSessionError("closed", "session is closed");
|
|
1951
1969
|
ctx.resetScratch();
|
|
1952
1970
|
const servers = filterMcpServers(ctx.agentCapabilities, mcpServers ?? ctx.opts.mcpServers);
|
|
1953
1971
|
const id = ctx.allocId();
|
|
@@ -1959,6 +1977,7 @@ async function executeLoadSession(ctx, sessionId, mcpServers, cwd) {
|
|
|
1959
1977
|
if (isJsonRpcError(result)) {
|
|
1960
1978
|
throw new ACPSessionError("prompt_failed", `session/load failed: ${result.message}`, result);
|
|
1961
1979
|
}
|
|
1980
|
+
if (ctx.closed) throw new ACPSessionError("closed", "session is closed");
|
|
1962
1981
|
ctx.setSessionId(sessionId);
|
|
1963
1982
|
}
|
|
1964
1983
|
async function executeResumeSession(ctx, sessionId, mcpServers, cwd) {
|
|
@@ -1972,6 +1991,7 @@ async function executeResumeSession(ctx, sessionId, mcpServers, cwd) {
|
|
|
1972
1991
|
if (ctx.sessionId && ctx.sessionId !== sessionId) {
|
|
1973
1992
|
await ctx.closeSession();
|
|
1974
1993
|
}
|
|
1994
|
+
if (ctx.closed) throw new ACPSessionError("closed", "session is closed");
|
|
1975
1995
|
const servers = filterMcpServers(ctx.agentCapabilities, mcpServers ?? ctx.opts.mcpServers);
|
|
1976
1996
|
const id = ctx.allocId();
|
|
1977
1997
|
const result = await ctx.sendRequest(id, "session/resume", {
|
|
@@ -1982,6 +2002,7 @@ async function executeResumeSession(ctx, sessionId, mcpServers, cwd) {
|
|
|
1982
2002
|
if (isJsonRpcError(result)) {
|
|
1983
2003
|
throw new ACPSessionError("prompt_failed", `session/resume failed: ${result.message}`, result);
|
|
1984
2004
|
}
|
|
2005
|
+
if (ctx.closed) throw new ACPSessionError("closed", "session is closed");
|
|
1985
2006
|
ctx.setSessionId(sessionId);
|
|
1986
2007
|
}
|
|
1987
2008
|
async function executeListSessions(ctx, cursor, cwd) {
|
|
@@ -2130,10 +2151,11 @@ async function executeCreateSession(ctx) {
|
|
|
2130
2151
|
}
|
|
2131
2152
|
|
|
2132
2153
|
// src/client/acp-session-auth.ts
|
|
2154
|
+
function ensureOpen(host) {
|
|
2155
|
+
if (host.state === "closed") throw new ACPSessionError("closed", "session is closed");
|
|
2156
|
+
}
|
|
2133
2157
|
async function authenticate(host, methodId) {
|
|
2134
|
-
|
|
2135
|
-
throw new ACPSessionError("closed", "session is closed");
|
|
2136
|
-
}
|
|
2158
|
+
ensureOpen(host);
|
|
2137
2159
|
if (host.state !== "ready" && host.state !== "authenticated") {
|
|
2138
2160
|
throw new ACPSessionError(
|
|
2139
2161
|
"protocol_error",
|
|
@@ -2158,12 +2180,11 @@ async function authenticate(host, methodId) {
|
|
|
2158
2180
|
if (isJsonRpcError(result)) {
|
|
2159
2181
|
throw new ACPSessionError("auth_failed", `authenticate failed: ${result.message}`, result);
|
|
2160
2182
|
}
|
|
2183
|
+
ensureOpen(host);
|
|
2161
2184
|
host.state = "authenticated";
|
|
2162
2185
|
}
|
|
2163
2186
|
async function logout(host) {
|
|
2164
|
-
|
|
2165
|
-
throw new ACPSessionError("closed", "session is closed");
|
|
2166
|
-
}
|
|
2187
|
+
ensureOpen(host);
|
|
2167
2188
|
if (!host.agentCapabilities.auth?.logout) {
|
|
2168
2189
|
throw new ACPSessionError(
|
|
2169
2190
|
"unsupported_capability",
|
|
@@ -2175,6 +2196,7 @@ async function logout(host) {
|
|
|
2175
2196
|
if (isJsonRpcError(result)) {
|
|
2176
2197
|
throw new ACPSessionError("logout_failed", `logout failed: ${result.message}`, result);
|
|
2177
2198
|
}
|
|
2199
|
+
ensureOpen(host);
|
|
2178
2200
|
host.state = "ready";
|
|
2179
2201
|
}
|
|
2180
2202
|
async function createSessionWithAuth(host) {
|
|
@@ -2216,6 +2238,44 @@ async function ensureAuthenticated(host) {
|
|
|
2216
2238
|
);
|
|
2217
2239
|
}
|
|
2218
2240
|
|
|
2241
|
+
// src/client/acp-session-content.ts
|
|
2242
|
+
function textContent(text) {
|
|
2243
|
+
return { type: "text", text };
|
|
2244
|
+
}
|
|
2245
|
+
function imageContent(mimeType, data) {
|
|
2246
|
+
return { type: "image", mimeType, data };
|
|
2247
|
+
}
|
|
2248
|
+
function audioContent(mimeType, data) {
|
|
2249
|
+
return { type: "audio", mimeType, data };
|
|
2250
|
+
}
|
|
2251
|
+
function extractText(block) {
|
|
2252
|
+
if (typeof block !== "object" || block === null) return "";
|
|
2253
|
+
const b = block;
|
|
2254
|
+
if (b.type === "text" && typeof b.text === "string") return b.text;
|
|
2255
|
+
if (b.type === "resource" && b.resource && typeof b.resource === "object" && typeof b.resource.text === "string") {
|
|
2256
|
+
return b.resource.text;
|
|
2257
|
+
}
|
|
2258
|
+
return "";
|
|
2259
|
+
}
|
|
2260
|
+
function isRecord(v) {
|
|
2261
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
2262
|
+
}
|
|
2263
|
+
function emptyRunResult(stopReason) {
|
|
2264
|
+
return {
|
|
2265
|
+
text: "",
|
|
2266
|
+
stopReason,
|
|
2267
|
+
hasText: false,
|
|
2268
|
+
toolCalls: [],
|
|
2269
|
+
diffs: [],
|
|
2270
|
+
thoughts: ""
|
|
2271
|
+
};
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
// src/client/acp-message-routing.ts
|
|
2275
|
+
function isBestEffortAckMethod(method) {
|
|
2276
|
+
return method === "mcp/connect" || method === "mcp/message" || method === "mcp/disconnect" || method === "elicitation/create" || method === "elicitation/complete";
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2219
2279
|
// src/client/file-server.ts
|
|
2220
2280
|
import { randomBytes } from "node:crypto";
|
|
2221
2281
|
import { realpathSync } from "node:fs";
|
|
@@ -2272,6 +2332,9 @@ var FileServer = class {
|
|
|
2272
2332
|
const stat3 = await this.operations.stat(safe).catch((err) => {
|
|
2273
2333
|
throw mapFsError(err, safe);
|
|
2274
2334
|
});
|
|
2335
|
+
if (controller.signal.aborted) {
|
|
2336
|
+
throw new FsError("TIMEOUT", safe, `readTextFile timed out after ${this.timeoutMs}ms`);
|
|
2337
|
+
}
|
|
2275
2338
|
if (stat3.size > this.maxReadBytes) {
|
|
2276
2339
|
throw new FsError(
|
|
2277
2340
|
"TOO_LARGE",
|
|
@@ -2283,6 +2346,17 @@ var FileServer = class {
|
|
|
2283
2346
|
encoding: "utf8",
|
|
2284
2347
|
signal: controller.signal
|
|
2285
2348
|
});
|
|
2349
|
+
if (controller.signal.aborted) {
|
|
2350
|
+
throw new FsError("TIMEOUT", safe, `readTextFile timed out after ${this.timeoutMs}ms`);
|
|
2351
|
+
}
|
|
2352
|
+
const actualSize = Buffer.byteLength(content, "utf8");
|
|
2353
|
+
if (actualSize > this.maxReadBytes) {
|
|
2354
|
+
throw new FsError(
|
|
2355
|
+
"TOO_LARGE",
|
|
2356
|
+
safe,
|
|
2357
|
+
`file is ${actualSize} bytes after read, max read is ${this.maxReadBytes} bytes`
|
|
2358
|
+
);
|
|
2359
|
+
}
|
|
2286
2360
|
if (params.line == null && params.limit == null) return { content };
|
|
2287
2361
|
const lines = content.match(/[^\n]*\n|[^\n]+$/g) ?? [];
|
|
2288
2362
|
const start = (params.line ?? 1) - 1;
|
|
@@ -2321,9 +2395,21 @@ var FileServer = class {
|
|
|
2321
2395
|
encoding: "utf8",
|
|
2322
2396
|
signal: controller.signal
|
|
2323
2397
|
});
|
|
2398
|
+
if (controller.signal.aborted) {
|
|
2399
|
+
throw new FsError("TIMEOUT", safe, `writeTextFile timed out after ${this.timeoutMs}ms`);
|
|
2400
|
+
}
|
|
2324
2401
|
await this.assertRealInside(tmp);
|
|
2402
|
+
if (controller.signal.aborted) {
|
|
2403
|
+
throw new FsError("TIMEOUT", safe, `writeTextFile timed out after ${this.timeoutMs}ms`);
|
|
2404
|
+
}
|
|
2325
2405
|
await this.assertRealInside(path2.dirname(safe));
|
|
2406
|
+
if (controller.signal.aborted) {
|
|
2407
|
+
throw new FsError("TIMEOUT", safe, `writeTextFile timed out after ${this.timeoutMs}ms`);
|
|
2408
|
+
}
|
|
2326
2409
|
await this.operations.rename(tmp, safe);
|
|
2410
|
+
if (controller.signal.aborted) {
|
|
2411
|
+
throw new FsError("TIMEOUT", safe, `writeTextFile timed out after ${this.timeoutMs}ms`);
|
|
2412
|
+
}
|
|
2327
2413
|
} catch (err) {
|
|
2328
2414
|
if (err instanceof FsError) {
|
|
2329
2415
|
await this.operations.unlink(tmp).catch(() => void 0);
|
|
@@ -2422,7 +2508,7 @@ function safeRealpathSync(p) {
|
|
|
2422
2508
|
|
|
2423
2509
|
// src/client/acp-session-callbacks.ts
|
|
2424
2510
|
var DEFAULT_PERMISSION_TIMEOUT_MS = 6e4;
|
|
2425
|
-
async function handleAcpPermissionRequest(msg, permissionPolicy, sender,
|
|
2511
|
+
async function handleAcpPermissionRequest(msg, permissionPolicy, sender, callbackOptions2 = {}) {
|
|
2426
2512
|
const id = msg.id;
|
|
2427
2513
|
if (id === void 0) return;
|
|
2428
2514
|
const params = msg.params;
|
|
@@ -2439,7 +2525,7 @@ async function handleAcpPermissionRequest(msg, permissionPolicy, sender, callbac
|
|
|
2439
2525
|
toolCall,
|
|
2440
2526
|
options: permissionOptions
|
|
2441
2527
|
},
|
|
2442
|
-
|
|
2528
|
+
callbackOptions2
|
|
2443
2529
|
);
|
|
2444
2530
|
await sender.sendResult(id, { outcome });
|
|
2445
2531
|
} catch (err) {
|
|
@@ -2451,7 +2537,7 @@ async function handleAcpPermissionRequest(msg, permissionPolicy, sender, callbac
|
|
|
2451
2537
|
await sender.sendErrorResponse(id, -32603, `permission policy failed: ${message}`);
|
|
2452
2538
|
}
|
|
2453
2539
|
}
|
|
2454
|
-
async function handleAcpFsRequest(msg, fileServer, permissionPolicy, sender,
|
|
2540
|
+
async function handleAcpFsRequest(msg, fileServer, permissionPolicy, sender, callbackOptions2 = {}) {
|
|
2455
2541
|
const id = msg.id;
|
|
2456
2542
|
if (id === void 0) return;
|
|
2457
2543
|
const params = msg.params;
|
|
@@ -2468,7 +2554,7 @@ async function handleAcpFsRequest(msg, fileServer, permissionPolicy, sender, cal
|
|
|
2468
2554
|
kind: "edit",
|
|
2469
2555
|
rawInput: { path: params.path, sessionId: params.sessionId }
|
|
2470
2556
|
},
|
|
2471
|
-
|
|
2557
|
+
callbackOptions2
|
|
2472
2558
|
);
|
|
2473
2559
|
if (authorization !== "allowed") {
|
|
2474
2560
|
const isCancelled = authorization === "cancelled";
|
|
@@ -2503,7 +2589,7 @@ async function handleAcpFsRequest(msg, fileServer, permissionPolicy, sender, cal
|
|
|
2503
2589
|
await sender.sendErrorResponse(id, code, message);
|
|
2504
2590
|
}
|
|
2505
2591
|
}
|
|
2506
|
-
async function handleAcpTerminalRequest(msg, terminalServer, permissionPolicy, sender,
|
|
2592
|
+
async function handleAcpTerminalRequest(msg, terminalServer, permissionPolicy, sender, callbackOptions2 = {}) {
|
|
2507
2593
|
const id = msg.id;
|
|
2508
2594
|
if (id === void 0) return;
|
|
2509
2595
|
const params = msg.params ?? {};
|
|
@@ -2523,7 +2609,7 @@ async function handleAcpTerminalRequest(msg, terminalServer, permissionPolicy, s
|
|
|
2523
2609
|
sessionId: params.sessionId
|
|
2524
2610
|
}
|
|
2525
2611
|
},
|
|
2526
|
-
|
|
2612
|
+
callbackOptions2
|
|
2527
2613
|
);
|
|
2528
2614
|
if (authorization !== "allowed") {
|
|
2529
2615
|
const isCancelled = authorization === "cancelled";
|
|
@@ -2584,7 +2670,7 @@ async function handleAcpTerminalRequest(msg, terminalServer, permissionPolicy, s
|
|
|
2584
2670
|
await sender.sendErrorResponse(id, -32603, message);
|
|
2585
2671
|
}
|
|
2586
2672
|
}
|
|
2587
|
-
async function authorizeAcpCallback(permissionPolicy, partial,
|
|
2673
|
+
async function authorizeAcpCallback(permissionPolicy, partial, callbackOptions2) {
|
|
2588
2674
|
try {
|
|
2589
2675
|
const outcome = await runPermissionWithDeadline(
|
|
2590
2676
|
permissionPolicy,
|
|
@@ -2602,25 +2688,25 @@ async function authorizeAcpCallback(permissionPolicy, partial, callbackOptions)
|
|
|
2602
2688
|
{ optionId: "reject", name: "Reject", kind: "reject_once" }
|
|
2603
2689
|
]
|
|
2604
2690
|
},
|
|
2605
|
-
|
|
2691
|
+
callbackOptions2
|
|
2606
2692
|
);
|
|
2607
2693
|
return outcome.outcome === "selected" && outcome.optionId !== "reject" && outcome.optionId !== "reject_once" && outcome.optionId !== "reject_always" ? "allowed" : "denied";
|
|
2608
2694
|
} catch (err) {
|
|
2609
2695
|
return isAbortLikeError(err) ? "cancelled" : "denied";
|
|
2610
2696
|
}
|
|
2611
2697
|
}
|
|
2612
|
-
async function runPermissionWithDeadline(permissionPolicy, call,
|
|
2613
|
-
const timeoutMs = resolvePermissionDeadline(
|
|
2698
|
+
async function runPermissionWithDeadline(permissionPolicy, call, callbackOptions2) {
|
|
2699
|
+
const timeoutMs = resolvePermissionDeadline(callbackOptions2.permissionTimeoutMs);
|
|
2614
2700
|
const controller = new AbortController();
|
|
2615
2701
|
const abort = () => controller.abort();
|
|
2616
2702
|
const timer = timeoutMs === null ? null : setTimeout(abort, timeoutMs);
|
|
2617
2703
|
let removeAbort;
|
|
2618
|
-
if (
|
|
2619
|
-
if (
|
|
2704
|
+
if (callbackOptions2.signal) {
|
|
2705
|
+
if (callbackOptions2.signal.aborted) {
|
|
2620
2706
|
abort();
|
|
2621
2707
|
} else {
|
|
2622
|
-
|
|
2623
|
-
removeAbort = () =>
|
|
2708
|
+
callbackOptions2.signal.addEventListener("abort", abort, { once: true });
|
|
2709
|
+
removeAbort = () => callbackOptions2.signal?.removeEventListener("abort", abort);
|
|
2624
2710
|
}
|
|
2625
2711
|
}
|
|
2626
2712
|
try {
|
|
@@ -2656,54 +2742,21 @@ function resolvePermissionDeadline(value) {
|
|
|
2656
2742
|
return DEFAULT_PERMISSION_TIMEOUT_MS;
|
|
2657
2743
|
}
|
|
2658
2744
|
|
|
2659
|
-
// src/client/acp-session-content.ts
|
|
2660
|
-
function textContent(text) {
|
|
2661
|
-
return { type: "text", text };
|
|
2662
|
-
}
|
|
2663
|
-
function imageContent(mimeType, data) {
|
|
2664
|
-
return { type: "image", mimeType, data };
|
|
2665
|
-
}
|
|
2666
|
-
function audioContent(mimeType, data) {
|
|
2667
|
-
return { type: "audio", mimeType, data };
|
|
2668
|
-
}
|
|
2669
|
-
function extractText(block) {
|
|
2670
|
-
if (typeof block !== "object" || block === null) return "";
|
|
2671
|
-
const b = block;
|
|
2672
|
-
if (b.type === "text" && typeof b.text === "string") return b.text;
|
|
2673
|
-
if (b.type === "resource" && b.resource && typeof b.resource === "object" && typeof b.resource.text === "string") {
|
|
2674
|
-
return b.resource.text;
|
|
2675
|
-
}
|
|
2676
|
-
return "";
|
|
2677
|
-
}
|
|
2678
|
-
function isRecord(v) {
|
|
2679
|
-
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
2680
|
-
}
|
|
2681
|
-
function emptyRunResult(stopReason) {
|
|
2682
|
-
return {
|
|
2683
|
-
text: "",
|
|
2684
|
-
stopReason,
|
|
2685
|
-
hasText: false,
|
|
2686
|
-
toolCalls: [],
|
|
2687
|
-
diffs: [],
|
|
2688
|
-
thoughts: ""
|
|
2689
|
-
};
|
|
2690
|
-
}
|
|
2691
|
-
|
|
2692
2745
|
// src/client/acp-session-updates.ts
|
|
2693
2746
|
function createSessionScratch() {
|
|
2694
2747
|
return { text: "", thoughts: "", toolCalls: /* @__PURE__ */ new Map(), diffs: [] };
|
|
2695
2748
|
}
|
|
2696
|
-
function handleAcpSessionUpdate(msg, scratch,
|
|
2749
|
+
function handleAcpSessionUpdate(msg, scratch, emitProgress2) {
|
|
2697
2750
|
const update = msg.params?.update;
|
|
2698
2751
|
if (typeof update !== "object" || update === null) return;
|
|
2699
2752
|
const u = update;
|
|
2700
|
-
|
|
2753
|
+
emitProgress2({ type: "raw", update: u });
|
|
2701
2754
|
switch (u.sessionUpdate) {
|
|
2702
2755
|
case "agent_message_chunk": {
|
|
2703
2756
|
const text = extractText(u.content);
|
|
2704
2757
|
if (text) {
|
|
2705
2758
|
scratch.text += text;
|
|
2706
|
-
|
|
2759
|
+
emitProgress2({ type: "message", text });
|
|
2707
2760
|
}
|
|
2708
2761
|
return;
|
|
2709
2762
|
}
|
|
@@ -2712,29 +2765,29 @@ function handleAcpSessionUpdate(msg, scratch, emitProgress) {
|
|
|
2712
2765
|
const text = extractText(u.content);
|
|
2713
2766
|
if (text) {
|
|
2714
2767
|
scratch.thoughts += text;
|
|
2715
|
-
|
|
2768
|
+
emitProgress2({ type: "thought", text });
|
|
2716
2769
|
}
|
|
2717
2770
|
return;
|
|
2718
2771
|
}
|
|
2719
2772
|
case "tool_call":
|
|
2720
2773
|
case "tool_call_update":
|
|
2721
|
-
captureToolCall(u, u.sessionUpdate === "tool_call", scratch,
|
|
2774
|
+
captureToolCall(u, u.sessionUpdate === "tool_call", scratch, emitProgress2);
|
|
2722
2775
|
return;
|
|
2723
2776
|
case "plan":
|
|
2724
2777
|
if (Array.isArray(u.entries)) {
|
|
2725
2778
|
scratch.plan = u.entries;
|
|
2726
|
-
|
|
2779
|
+
emitProgress2({ type: "plan", entries: u.entries });
|
|
2727
2780
|
}
|
|
2728
2781
|
return;
|
|
2729
2782
|
case "usage_update":
|
|
2730
|
-
if (typeof u.used === "number" && typeof u.size === "number") {
|
|
2783
|
+
if (typeof u.used === "number" && Number.isFinite(u.used) && u.used >= 0 && typeof u.size === "number" && Number.isFinite(u.size) && u.size >= 0) {
|
|
2731
2784
|
const usage = {
|
|
2732
2785
|
used: u.used,
|
|
2733
2786
|
size: u.size,
|
|
2734
2787
|
...typeof u.cost === "object" && u.cost !== null ? { cost: u.cost } : {}
|
|
2735
2788
|
};
|
|
2736
2789
|
scratch.usage = usage;
|
|
2737
|
-
|
|
2790
|
+
emitProgress2({ type: "usage", usage });
|
|
2738
2791
|
}
|
|
2739
2792
|
return;
|
|
2740
2793
|
case "available_commands_update":
|
|
@@ -2749,7 +2802,7 @@ function handleAcpSessionUpdate(msg, scratch, emitProgress) {
|
|
|
2749
2802
|
return;
|
|
2750
2803
|
}
|
|
2751
2804
|
}
|
|
2752
|
-
function captureToolCall(u, isNew, scratch,
|
|
2805
|
+
function captureToolCall(u, isNew, scratch, emitProgress2) {
|
|
2753
2806
|
const toolCallId = typeof u.toolCallId === "string" ? u.toolCallId : "";
|
|
2754
2807
|
if (!toolCallId) return;
|
|
2755
2808
|
const prev = scratch.toolCalls.get(toolCallId);
|
|
@@ -2771,16 +2824,119 @@ function captureToolCall(u, isNew, scratch, emitProgress) {
|
|
|
2771
2824
|
newText: c.newText
|
|
2772
2825
|
};
|
|
2773
2826
|
scratch.diffs.push(diff);
|
|
2774
|
-
|
|
2827
|
+
emitProgress2({ type: "diff", diff });
|
|
2775
2828
|
}
|
|
2776
2829
|
}
|
|
2777
2830
|
}
|
|
2778
|
-
|
|
2831
|
+
emitProgress2({
|
|
2779
2832
|
type: isNew ? "tool_call" : "tool_call_update",
|
|
2780
2833
|
toolCall: record
|
|
2781
2834
|
});
|
|
2782
2835
|
}
|
|
2783
2836
|
|
|
2837
|
+
// src/client/acp-session-messages.ts
|
|
2838
|
+
function responseSender() {
|
|
2839
|
+
return {
|
|
2840
|
+
sendResult: (id, result) => this.sendResult(id, result),
|
|
2841
|
+
sendErrorResponse: (id, code, message) => this.sendErrorResponse(id, code, message)
|
|
2842
|
+
};
|
|
2843
|
+
}
|
|
2844
|
+
function callbackOptions() {
|
|
2845
|
+
const promptSignal = this.promptCallbackAbort?.signal;
|
|
2846
|
+
const signal = promptSignal ? AbortSignal.any([this.callbackAbort.signal, promptSignal]) : this.callbackAbort.signal;
|
|
2847
|
+
return { signal, permissionTimeoutMs: Number.POSITIVE_INFINITY };
|
|
2848
|
+
}
|
|
2849
|
+
function handleMessage(msg) {
|
|
2850
|
+
if (msg.id !== void 0 && (msg.result !== void 0 || msg.error !== void 0)) {
|
|
2851
|
+
const pending = this.pending.get(msg.id);
|
|
2852
|
+
if (!pending) return;
|
|
2853
|
+
clearTimeout(pending.timeoutHandle);
|
|
2854
|
+
this.pending.delete(msg.id);
|
|
2855
|
+
if (msg.error !== void 0) {
|
|
2856
|
+
const method = pending.method;
|
|
2857
|
+
const kind = method === "session/new" ? "session_create_failed" : method === "authenticate" ? "auth_failed" : "protocol_error";
|
|
2858
|
+
pending.reject(
|
|
2859
|
+
new ACPSessionError(kind, msg.error.message ?? "unknown JSON-RPC error", msg.error)
|
|
2860
|
+
);
|
|
2861
|
+
} else {
|
|
2862
|
+
pending.resolve(msg.result);
|
|
2863
|
+
}
|
|
2864
|
+
return;
|
|
2865
|
+
}
|
|
2866
|
+
if (msg.method === "session/update") {
|
|
2867
|
+
const updateSessionId = msg.params?.sessionId;
|
|
2868
|
+
if (typeof updateSessionId === "string" && updateSessionId !== this.sessionId) return;
|
|
2869
|
+
handleAcpSessionUpdate(msg, this.scratch, (event) => this.emitProgress(event));
|
|
2870
|
+
return;
|
|
2871
|
+
}
|
|
2872
|
+
if (msg.method === "session/request_permission") {
|
|
2873
|
+
handleAcpPermissionRequest(
|
|
2874
|
+
msg,
|
|
2875
|
+
this.permissionPolicy,
|
|
2876
|
+
this.responseSender(),
|
|
2877
|
+
this.callbackOptions()
|
|
2878
|
+
).catch(() => {
|
|
2879
|
+
});
|
|
2880
|
+
return;
|
|
2881
|
+
}
|
|
2882
|
+
if (msg.method === "fs/read_text_file" || msg.method === "fs/write_text_file") {
|
|
2883
|
+
handleAcpFsRequest(
|
|
2884
|
+
msg,
|
|
2885
|
+
this.fileServer,
|
|
2886
|
+
this.permissionPolicy,
|
|
2887
|
+
this.responseSender(),
|
|
2888
|
+
this.callbackOptions()
|
|
2889
|
+
).catch(() => {
|
|
2890
|
+
});
|
|
2891
|
+
return;
|
|
2892
|
+
}
|
|
2893
|
+
if (msg.method?.startsWith("terminal/")) {
|
|
2894
|
+
handleAcpTerminalRequest(
|
|
2895
|
+
msg,
|
|
2896
|
+
this.terminalServer,
|
|
2897
|
+
this.permissionPolicy,
|
|
2898
|
+
this.responseSender(),
|
|
2899
|
+
this.callbackOptions()
|
|
2900
|
+
).catch(() => {
|
|
2901
|
+
});
|
|
2902
|
+
return;
|
|
2903
|
+
}
|
|
2904
|
+
if (isBestEffortAckMethod(msg.method)) {
|
|
2905
|
+
if (msg.id !== void 0) {
|
|
2906
|
+
this.sendErrorResponse(msg.id, -32601, `Unsupported method: ${msg.method}`).catch(() => {
|
|
2907
|
+
});
|
|
2908
|
+
}
|
|
2909
|
+
return;
|
|
2910
|
+
}
|
|
2911
|
+
if (msg.method === "$/cancel_request") {
|
|
2912
|
+
return;
|
|
2913
|
+
}
|
|
2914
|
+
if (msg.method) {
|
|
2915
|
+
if (msg.id !== void 0) {
|
|
2916
|
+
void this.sendErrorResponse(msg.id, -32601, `Unsupported method: ${msg.method}`).catch(
|
|
2917
|
+
() => {
|
|
2918
|
+
}
|
|
2919
|
+
);
|
|
2920
|
+
return;
|
|
2921
|
+
}
|
|
2922
|
+
console.warn(
|
|
2923
|
+
JSON.stringify({
|
|
2924
|
+
level: "warn",
|
|
2925
|
+
event: "acp_session.unhandled_method",
|
|
2926
|
+
method: msg.method,
|
|
2927
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
2928
|
+
})
|
|
2929
|
+
);
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
function emitProgress(event) {
|
|
2933
|
+
if (!this.progressHandler) return;
|
|
2934
|
+
try {
|
|
2935
|
+
this.progressHandler(event);
|
|
2936
|
+
} catch {
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2784
2940
|
// src/client/permission.ts
|
|
2785
2941
|
function pickAllow(options) {
|
|
2786
2942
|
const ranked = [...options].sort((a, b) => {
|
|
@@ -2819,6 +2975,7 @@ function makePermissionPolicy(decide) {
|
|
|
2819
2975
|
return async (req) => {
|
|
2820
2976
|
if (req.signal.aborted) return { outcome: "cancelled" };
|
|
2821
2977
|
const allow = await decide(req);
|
|
2978
|
+
if (req.signal.aborted) return { outcome: "cancelled" };
|
|
2822
2979
|
return allow ? pickAllow(req.options) : pickReject(req.options);
|
|
2823
2980
|
};
|
|
2824
2981
|
}
|
|
@@ -2871,8 +3028,8 @@ var TerminalServer = class {
|
|
|
2871
3028
|
constructor(opts) {
|
|
2872
3029
|
this.projectRoot = path3.resolve(opts.projectRoot);
|
|
2873
3030
|
this.commandTimeoutMs = opts.commandTimeoutMs ?? 5 * 6e4;
|
|
2874
|
-
this.outputByteLimit = opts.outputByteLimit
|
|
2875
|
-
this.maxOutputByteLimit = opts.maxOutputByteLimit
|
|
3031
|
+
this.outputByteLimit = this.clampFiniteInt(opts.outputByteLimit, 1024 * 1024);
|
|
3032
|
+
this.maxOutputByteLimit = this.clampFiniteInt(opts.maxOutputByteLimit, 16 * 1024 * 1024);
|
|
2876
3033
|
this.instanceId = `term_srv_${randomBytes2(4).toString("hex")}`;
|
|
2877
3034
|
this.maxTerminals = this.clampFiniteInt(opts.maxTerminals, 32);
|
|
2878
3035
|
this.abortSignal = opts.signal;
|
|
@@ -2933,7 +3090,7 @@ var TerminalServer = class {
|
|
|
2933
3090
|
clearTimeout(state.timeoutHandle);
|
|
2934
3091
|
state.timeoutHandle = null;
|
|
2935
3092
|
}
|
|
2936
|
-
const exitStatus = {
|
|
3093
|
+
const exitStatus = state.exitStatus ?? {
|
|
2937
3094
|
exitCode: typeof code === "number" ? code : null,
|
|
2938
3095
|
signal: typeof signalName === "string" ? signalName : null
|
|
2939
3096
|
};
|
|
@@ -3133,7 +3290,7 @@ var TerminalServer = class {
|
|
|
3133
3290
|
* unbounded memory growth.
|
|
3134
3291
|
*/
|
|
3135
3292
|
clampFiniteInt(value, defaultValue) {
|
|
3136
|
-
if (value === void 0 || !Number.isFinite(value) || value < 1) {
|
|
3293
|
+
if (value === void 0 || !Number.isFinite(value) || value < 1 || !Number.isSafeInteger(Math.trunc(value))) {
|
|
3137
3294
|
return defaultValue;
|
|
3138
3295
|
}
|
|
3139
3296
|
return Math.trunc(value);
|
|
@@ -3264,7 +3421,7 @@ var WebSocketClientTransport = class {
|
|
|
3264
3421
|
)
|
|
3265
3422
|
);
|
|
3266
3423
|
}
|
|
3267
|
-
const timeoutMs = this.opts.handshakeTimeoutMs
|
|
3424
|
+
const timeoutMs = finitePositiveLimit(this.opts.handshakeTimeoutMs, 3e4);
|
|
3268
3425
|
return new Promise((resolve5, reject) => {
|
|
3269
3426
|
const ws = new WS(this.opts.url, this.opts.protocols);
|
|
3270
3427
|
ws.binaryType = "arraybuffer";
|
|
@@ -3410,7 +3567,9 @@ var WebSocketClientTransport = class {
|
|
|
3410
3567
|
}
|
|
3411
3568
|
};
|
|
3412
3569
|
function finitePositiveLimit(value, fallback) {
|
|
3413
|
-
|
|
3570
|
+
if (value === void 0 || !Number.isFinite(value)) return fallback;
|
|
3571
|
+
const normalized = Math.floor(value);
|
|
3572
|
+
return Number.isSafeInteger(normalized) && normalized >= 1 ? normalized : fallback;
|
|
3414
3573
|
}
|
|
3415
3574
|
|
|
3416
3575
|
// src/client/acp-session.ts
|
|
@@ -3622,7 +3781,9 @@ var ACPSession = class _ACPSession {
|
|
|
3622
3781
|
opContext() {
|
|
3623
3782
|
const self = this;
|
|
3624
3783
|
return {
|
|
3625
|
-
closed
|
|
3784
|
+
get closed() {
|
|
3785
|
+
return self.closed;
|
|
3786
|
+
},
|
|
3626
3787
|
get sessionId() {
|
|
3627
3788
|
return self.sessionId;
|
|
3628
3789
|
},
|
|
@@ -3923,105 +4084,16 @@ var ACPSession = class _ACPSession {
|
|
|
3923
4084
|
});
|
|
3924
4085
|
}
|
|
3925
4086
|
responseSender() {
|
|
3926
|
-
return
|
|
3927
|
-
sendResult: (id, result) => this.sendResult(id, result),
|
|
3928
|
-
sendErrorResponse: (id, code, message) => this.sendErrorResponse(id, code, message)
|
|
3929
|
-
};
|
|
4087
|
+
return responseSender.call(this.acpSessionMessagesHost());
|
|
3930
4088
|
}
|
|
3931
4089
|
callbackOptions() {
|
|
3932
|
-
|
|
3933
|
-
const signal = promptSignal ? AbortSignal.any([this.callbackAbort.signal, promptSignal]) : this.callbackAbort.signal;
|
|
3934
|
-
return { signal, permissionTimeoutMs: Number.POSITIVE_INFINITY };
|
|
4090
|
+
return callbackOptions.call(this.acpSessionMessagesHost());
|
|
3935
4091
|
}
|
|
3936
4092
|
handleMessage(msg) {
|
|
3937
|
-
|
|
3938
|
-
const pending = this.pending.get(msg.id);
|
|
3939
|
-
if (!pending) return;
|
|
3940
|
-
clearTimeout(pending.timeoutHandle);
|
|
3941
|
-
this.pending.delete(msg.id);
|
|
3942
|
-
if (msg.error !== void 0) {
|
|
3943
|
-
const method = pending.method;
|
|
3944
|
-
const kind = method === "session/new" ? "session_create_failed" : method === "authenticate" ? "auth_failed" : "protocol_error";
|
|
3945
|
-
pending.reject(
|
|
3946
|
-
new ACPSessionError(kind, msg.error.message ?? "unknown JSON-RPC error", msg.error)
|
|
3947
|
-
);
|
|
3948
|
-
} else {
|
|
3949
|
-
pending.resolve(msg.result);
|
|
3950
|
-
}
|
|
3951
|
-
return;
|
|
3952
|
-
}
|
|
3953
|
-
if (msg.method === "session/update") {
|
|
3954
|
-
const updateSessionId = msg.params?.sessionId;
|
|
3955
|
-
if (typeof updateSessionId === "string" && updateSessionId !== this.sessionId) return;
|
|
3956
|
-
handleAcpSessionUpdate(msg, this.scratch, (event) => this.emitProgress(event));
|
|
3957
|
-
return;
|
|
3958
|
-
}
|
|
3959
|
-
if (msg.method === "session/request_permission") {
|
|
3960
|
-
handleAcpPermissionRequest(
|
|
3961
|
-
msg,
|
|
3962
|
-
this.permissionPolicy,
|
|
3963
|
-
this.responseSender(),
|
|
3964
|
-
this.callbackOptions()
|
|
3965
|
-
).catch(() => {
|
|
3966
|
-
});
|
|
3967
|
-
return;
|
|
3968
|
-
}
|
|
3969
|
-
if (msg.method === "fs/read_text_file" || msg.method === "fs/write_text_file") {
|
|
3970
|
-
handleAcpFsRequest(
|
|
3971
|
-
msg,
|
|
3972
|
-
this.fileServer,
|
|
3973
|
-
this.permissionPolicy,
|
|
3974
|
-
this.responseSender(),
|
|
3975
|
-
this.callbackOptions()
|
|
3976
|
-
).catch(() => {
|
|
3977
|
-
});
|
|
3978
|
-
return;
|
|
3979
|
-
}
|
|
3980
|
-
if (msg.method?.startsWith("terminal/")) {
|
|
3981
|
-
handleAcpTerminalRequest(
|
|
3982
|
-
msg,
|
|
3983
|
-
this.terminalServer,
|
|
3984
|
-
this.permissionPolicy,
|
|
3985
|
-
this.responseSender(),
|
|
3986
|
-
this.callbackOptions()
|
|
3987
|
-
).catch(() => {
|
|
3988
|
-
});
|
|
3989
|
-
return;
|
|
3990
|
-
}
|
|
3991
|
-
if (isBestEffortAckMethod(msg.method)) {
|
|
3992
|
-
if (msg.id !== void 0) {
|
|
3993
|
-
this.sendErrorResponse(msg.id, -32601, `Unsupported method: ${msg.method}`).catch(() => {
|
|
3994
|
-
});
|
|
3995
|
-
}
|
|
3996
|
-
return;
|
|
3997
|
-
}
|
|
3998
|
-
if (msg.method === "$/cancel_request") {
|
|
3999
|
-
return;
|
|
4000
|
-
}
|
|
4001
|
-
if (msg.method) {
|
|
4002
|
-
if (msg.id !== void 0) {
|
|
4003
|
-
void this.sendErrorResponse(msg.id, -32601, `Unsupported method: ${msg.method}`).catch(
|
|
4004
|
-
() => {
|
|
4005
|
-
}
|
|
4006
|
-
);
|
|
4007
|
-
return;
|
|
4008
|
-
}
|
|
4009
|
-
console.warn(
|
|
4010
|
-
JSON.stringify({
|
|
4011
|
-
level: "warn",
|
|
4012
|
-
event: "acp_session.unhandled_method",
|
|
4013
|
-
method: msg.method,
|
|
4014
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4015
|
-
})
|
|
4016
|
-
);
|
|
4017
|
-
}
|
|
4093
|
+
handleMessage.call(this.acpSessionMessagesHost(), msg);
|
|
4018
4094
|
}
|
|
4019
4095
|
emitProgress(event) {
|
|
4020
|
-
|
|
4021
|
-
try {
|
|
4022
|
-
this.progressHandler(event);
|
|
4023
|
-
} catch {
|
|
4024
|
-
}
|
|
4096
|
+
emitProgress.call(this.acpSessionMessagesHost(), event);
|
|
4025
4097
|
}
|
|
4026
4098
|
progressHandler = null;
|
|
4027
4099
|
scratch = createSessionScratch();
|
|
@@ -4051,6 +4123,23 @@ var ACPSession = class _ACPSession {
|
|
|
4051
4123
|
opts: this.opts
|
|
4052
4124
|
};
|
|
4053
4125
|
}
|
|
4126
|
+
acpSessionMessagesHost() {
|
|
4127
|
+
void this.sendResult;
|
|
4128
|
+
void this.sendErrorResponse;
|
|
4129
|
+
void this.promptCallbackAbort;
|
|
4130
|
+
void this.callbackAbort;
|
|
4131
|
+
void this.pending;
|
|
4132
|
+
void this.sessionId;
|
|
4133
|
+
void this.scratch;
|
|
4134
|
+
void this.emitProgress;
|
|
4135
|
+
void this.permissionPolicy;
|
|
4136
|
+
void this.responseSender;
|
|
4137
|
+
void this.callbackOptions;
|
|
4138
|
+
void this.fileServer;
|
|
4139
|
+
void this.terminalServer;
|
|
4140
|
+
void this.progressHandler;
|
|
4141
|
+
return this;
|
|
4142
|
+
}
|
|
4054
4143
|
};
|
|
4055
4144
|
|
|
4056
4145
|
// src/client/tool-translator.ts
|
|
@@ -4096,18 +4185,30 @@ var ToolTranslator = class {
|
|
|
4096
4185
|
* response arrives.
|
|
4097
4186
|
*/
|
|
4098
4187
|
async callTool(transport, name, args, callId = crypto.randomUUID()) {
|
|
4099
|
-
|
|
4100
|
-
jsonrpc: "2.0",
|
|
4101
|
-
method: "tools/call",
|
|
4102
|
-
id: callId,
|
|
4103
|
-
params: { name, arguments: args }
|
|
4104
|
-
});
|
|
4188
|
+
if (this.pending.has(callId)) throw new Error(`Tool call id ${callId} is already pending`);
|
|
4105
4189
|
return new Promise((resolve5, reject) => {
|
|
4106
4190
|
const timeout = setTimeout(() => {
|
|
4107
4191
|
this.pending.delete(callId);
|
|
4108
4192
|
reject(new Error(`Tool call ${name} timed out after ${this.opts.totalTimeoutMs}ms`));
|
|
4109
4193
|
}, this.opts.totalTimeoutMs);
|
|
4110
|
-
|
|
4194
|
+
const pending = { resolve: resolve5, reject, timeout };
|
|
4195
|
+
this.pending.set(callId, pending);
|
|
4196
|
+
const failSend = (err) => {
|
|
4197
|
+
if (this.pending.get(callId) !== pending) return;
|
|
4198
|
+
clearTimeout(timeout);
|
|
4199
|
+
this.pending.delete(callId);
|
|
4200
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
4201
|
+
};
|
|
4202
|
+
try {
|
|
4203
|
+
transport.send({
|
|
4204
|
+
jsonrpc: "2.0",
|
|
4205
|
+
method: "tools/call",
|
|
4206
|
+
id: callId,
|
|
4207
|
+
params: { name, arguments: args }
|
|
4208
|
+
}).catch(failSend);
|
|
4209
|
+
} catch (err) {
|
|
4210
|
+
failSend(err);
|
|
4211
|
+
}
|
|
4111
4212
|
});
|
|
4112
4213
|
}
|
|
4113
4214
|
cancelAll() {
|
|
@@ -4241,7 +4342,8 @@ async function runAcpBench(opts) {
|
|
|
4241
4342
|
const timeoutMs = opts.timeoutMs ?? 6e4;
|
|
4242
4343
|
const checkFs = opts.checkFs ?? false;
|
|
4243
4344
|
const marker = opts.marker ?? randomMarker();
|
|
4244
|
-
const
|
|
4345
|
+
const requestedConcurrency = opts.concurrency ?? 2;
|
|
4346
|
+
const concurrency = Number.isNaN(requestedConcurrency) ? 2 : Math.max(1, Math.floor(requestedConcurrency));
|
|
4245
4347
|
const seen = /* @__PURE__ */ new Set();
|
|
4246
4348
|
const ids = [];
|
|
4247
4349
|
for (const raw of opts.agentIds) {
|
|
@@ -5081,7 +5183,8 @@ var DEFAULT_MAX_CONCURRENCY = 4;
|
|
|
5081
5183
|
async function mapBound(items, worker, limit) {
|
|
5082
5184
|
const results = new Array(items.length);
|
|
5083
5185
|
if (items.length === 0) return results;
|
|
5084
|
-
const
|
|
5186
|
+
const normalizedLimit = Number.isNaN(limit) ? DEFAULT_MAX_CONCURRENCY : Math.floor(limit);
|
|
5187
|
+
const safeLimit = Math.max(1, Math.min(normalizedLimit, items.length));
|
|
5085
5188
|
let nextIndex = 0;
|
|
5086
5189
|
const workerCount = Math.min(safeLimit, items.length);
|
|
5087
5190
|
const runners = [];
|