@wrongstack/acp 0.295.0 → 0.295.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/agent/protocol-handler.d.ts +9 -0
- package/dist/agent/protocol-handler.d.ts.map +1 -1
- package/dist/agent/server-agent-turn.d.ts +83 -1
- package/dist/agent/server-agent-turn.d.ts.map +1 -1
- package/dist/agent/session-store.d.ts.map +1 -1
- package/dist/agent/stdio-transport.d.ts +18 -0
- package/dist/agent/stdio-transport.d.ts.map +1 -1
- package/dist/agent/wrongstack-acp-agent.d.ts +35 -0
- package/dist/agent/wrongstack-acp-agent.d.ts.map +1 -1
- package/dist/agent.js +30 -23
- package/dist/agent.js.map +3 -3
- package/dist/client/file-server.d.ts +19 -0
- package/dist/client/file-server.d.ts.map +1 -1
- package/dist/client/terminal-server.d.ts.map +1 -1
- package/dist/client/trust-boundary-permission.d.ts +16 -1
- package/dist/client/trust-boundary-permission.d.ts.map +1 -1
- package/dist/client/websocket-transport.d.ts.map +1 -1
- package/dist/client.js +40 -35
- package/dist/client.js.map +2 -2
- package/dist/index.js +91 -82
- package/dist/index.js.map +4 -4
- package/dist/integration/acp-bench.d.ts +32 -0
- package/dist/integration/acp-bench.d.ts.map +1 -1
- package/dist/integration/acp-subagent-runner.d.ts.map +1 -1
- package/dist/integration/ensemble-runner.d.ts +22 -1
- package/dist/integration/ensemble-runner.d.ts.map +1 -1
- package/dist/registry/acp-registry-fetch.d.ts +1 -1
- package/dist/registry/acp-registry-fetch.d.ts.map +1 -1
- package/dist/registry/ensemble-registry.d.ts +22 -0
- package/dist/registry/ensemble-registry.d.ts.map +1 -1
- package/dist/version.d.ts +8 -0
- package/dist/version.d.ts.map +1 -1
- package/dist/wrongstack-acp-agent.js +26 -20
- package/dist/wrongstack-acp-agent.js.map +2 -2
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -9,9 +9,9 @@ function assertNeverSessionUpdate(x) {
|
|
|
9
9
|
// src/version.ts
|
|
10
10
|
import { createRequire } from "node:module";
|
|
11
11
|
var require2 = createRequire(import.meta.url);
|
|
12
|
-
function readPackageVersion() {
|
|
12
|
+
function readPackageVersion(load = () => require2("../package.json")) {
|
|
13
13
|
try {
|
|
14
|
-
const packageJson =
|
|
14
|
+
const packageJson = load();
|
|
15
15
|
if (typeof packageJson.version === "string" && packageJson.version.length > 0) {
|
|
16
16
|
return packageJson.version;
|
|
17
17
|
}
|
|
@@ -79,7 +79,7 @@ var ACPProtocolHandler = class {
|
|
|
79
79
|
this.replayFor = opts.replayFor;
|
|
80
80
|
this.seedFor = opts.seedFor;
|
|
81
81
|
this.disposeFor = opts.disposeFor;
|
|
82
|
-
this.maxSessions = Number.isFinite(opts.maxSessions) &&
|
|
82
|
+
this.maxSessions = Number.isFinite(opts.maxSessions) && opts.maxSessions > 0 ? Math.floor(opts.maxSessions) : DEFAULT_MAX_SESSIONS;
|
|
83
83
|
this.store = opts.store;
|
|
84
84
|
if (typeof this.transport.onMessage === "function") {
|
|
85
85
|
this.transport.onMessage((m) => this.maybeResolvePending(m));
|
|
@@ -361,7 +361,7 @@ var ACPProtocolHandler = class {
|
|
|
361
361
|
}
|
|
362
362
|
if (existing) {
|
|
363
363
|
existing.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
364
|
-
const replay =
|
|
364
|
+
const replay = this.replayFor?.(sessionId);
|
|
365
365
|
if (replay) {
|
|
366
366
|
for (const update of replay) {
|
|
367
367
|
await this.sendNotification({ sessionId, update });
|
|
@@ -426,10 +426,8 @@ var ACPProtocolHandler = class {
|
|
|
426
426
|
return false;
|
|
427
427
|
}
|
|
428
428
|
session.abort.abort();
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
this.disposeSession(sessionId);
|
|
432
|
-
}
|
|
429
|
+
this.sessions.delete(sessionId);
|
|
430
|
+
this.disposeSession(sessionId);
|
|
433
431
|
await this.transport.send(toWire({
|
|
434
432
|
jsonrpc: "2.0",
|
|
435
433
|
id,
|
|
@@ -738,6 +736,7 @@ function errorToJsonRpc(err) {
|
|
|
738
736
|
|
|
739
737
|
// src/agent/stdio-transport.ts
|
|
740
738
|
import { expectDefined, writeErr } from "@wrongstack/core/utils";
|
|
739
|
+
import { treeKill } from "@wrongstack/core/utils/tree-kill";
|
|
741
740
|
|
|
742
741
|
// src/win32-cmd.ts
|
|
743
742
|
var WIN32_CMD_META = /[&|<>"\r\n\0]/;
|
|
@@ -767,7 +766,7 @@ function quoteWin32CmdArg(arg) {
|
|
|
767
766
|
var DEFAULT_MAX_FRAME_CHARS = 20 * 1024 * 1024;
|
|
768
767
|
var DEFAULT_MAX_QUEUED_MESSAGES = 1e3;
|
|
769
768
|
function positiveLimit(value, fallback) {
|
|
770
|
-
return Number.isFinite(value) &&
|
|
769
|
+
return value !== void 0 && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
|
|
771
770
|
}
|
|
772
771
|
var StdioTransport = class {
|
|
773
772
|
stdin = process.stdin;
|
|
@@ -782,10 +781,7 @@ var StdioTransport = class {
|
|
|
782
781
|
maxQueuedMessages;
|
|
783
782
|
constructor(opts = {}) {
|
|
784
783
|
this.maxFrameChars = positiveLimit(opts.maxFrameChars, DEFAULT_MAX_FRAME_CHARS);
|
|
785
|
-
this.maxQueuedMessages = positiveLimit(
|
|
786
|
-
opts.maxQueuedMessages,
|
|
787
|
-
DEFAULT_MAX_QUEUED_MESSAGES
|
|
788
|
-
);
|
|
784
|
+
this.maxQueuedMessages = positiveLimit(opts.maxQueuedMessages, DEFAULT_MAX_QUEUED_MESSAGES);
|
|
789
785
|
this.stdin.resume();
|
|
790
786
|
this.stdin.setEncoding("utf8");
|
|
791
787
|
this.stdin.on("data", (chunk) => this.onData(chunk));
|
|
@@ -806,7 +802,8 @@ var StdioTransport = class {
|
|
|
806
802
|
this.stdout.write(chunk, "utf8");
|
|
807
803
|
}
|
|
808
804
|
read() {
|
|
809
|
-
if (this.messageQueue.length > 0)
|
|
805
|
+
if (this.messageQueue.length > 0)
|
|
806
|
+
return Promise.resolve(expectDefined(this.messageQueue.shift()));
|
|
810
807
|
if (this.closed) return Promise.resolve(null);
|
|
811
808
|
return new Promise((resolve3) => {
|
|
812
809
|
this.resolveRead = resolve3;
|
|
@@ -908,10 +905,7 @@ var ClientTransport = class {
|
|
|
908
905
|
...options
|
|
909
906
|
};
|
|
910
907
|
this.maxFrameChars = positiveLimit(options.maxFrameChars, DEFAULT_MAX_FRAME_CHARS);
|
|
911
|
-
this.maxQueuedMessages = positiveLimit(
|
|
912
|
-
options.maxQueuedMessages,
|
|
913
|
-
DEFAULT_MAX_QUEUED_MESSAGES
|
|
914
|
-
);
|
|
908
|
+
this.maxQueuedMessages = positiveLimit(options.maxQueuedMessages, DEFAULT_MAX_QUEUED_MESSAGES);
|
|
915
909
|
}
|
|
916
910
|
async start() {
|
|
917
911
|
if (this.child) return;
|
|
@@ -930,13 +924,13 @@ var ClientTransport = class {
|
|
|
930
924
|
const spawnCwd = isPkgLauncher ? os.homedir() : this.opts.cwd;
|
|
931
925
|
try {
|
|
932
926
|
const childArgs = this.opts.args ?? [];
|
|
933
|
-
const
|
|
934
|
-
this.child = spawn3(
|
|
927
|
+
const invocation = spawnInvocation(this.opts.command, childArgs, process.platform);
|
|
928
|
+
this.child = spawn3(invocation.command, invocation.args, {
|
|
935
929
|
env: { ...buildChildEnv2(), ...this.opts.env },
|
|
936
930
|
cwd: spawnCwd,
|
|
937
931
|
stdio: ["pipe", "pipe", "pipe"],
|
|
938
932
|
windowsHide: true,
|
|
939
|
-
...
|
|
933
|
+
...verbatimOptions(invocation)
|
|
940
934
|
});
|
|
941
935
|
} catch (err) {
|
|
942
936
|
clearTimeout(timeout);
|
|
@@ -1001,7 +995,8 @@ var ClientTransport = class {
|
|
|
1001
995
|
});
|
|
1002
996
|
}
|
|
1003
997
|
read() {
|
|
1004
|
-
if (this.messageQueue.length > 0)
|
|
998
|
+
if (this.messageQueue.length > 0)
|
|
999
|
+
return Promise.resolve(expectDefined(this.messageQueue.shift()));
|
|
1005
1000
|
if (this.closed) return Promise.resolve(null);
|
|
1006
1001
|
return new Promise((resolve3) => {
|
|
1007
1002
|
this.resolveRead = resolve3;
|
|
@@ -1020,10 +1015,7 @@ var ClientTransport = class {
|
|
|
1020
1015
|
this.handlers.clear();
|
|
1021
1016
|
const child = this.child;
|
|
1022
1017
|
if (!child) return;
|
|
1023
|
-
|
|
1024
|
-
child.kill();
|
|
1025
|
-
} catch {
|
|
1026
|
-
}
|
|
1018
|
+
treeKill(child);
|
|
1027
1019
|
this.child = null;
|
|
1028
1020
|
}
|
|
1029
1021
|
onChildData(chunk) {
|
|
@@ -1087,6 +1079,13 @@ var ClientTransport = class {
|
|
|
1087
1079
|
}
|
|
1088
1080
|
}
|
|
1089
1081
|
};
|
|
1082
|
+
function spawnInvocation(command, args, platform) {
|
|
1083
|
+
if (platform !== "win32") return { command, args };
|
|
1084
|
+
return buildWin32CmdShimInvocation(command, args);
|
|
1085
|
+
}
|
|
1086
|
+
function verbatimOptions(invocation) {
|
|
1087
|
+
return invocation.windowsVerbatimArguments ? { windowsVerbatimArguments: invocation.windowsVerbatimArguments } : {};
|
|
1088
|
+
}
|
|
1090
1089
|
|
|
1091
1090
|
// src/agent/tools-registry.ts
|
|
1092
1091
|
var ACPToolsRegistry = class {
|
|
@@ -1203,8 +1202,8 @@ function toolToPriority(tool) {
|
|
|
1203
1202
|
}
|
|
1204
1203
|
|
|
1205
1204
|
// src/agent/wrongstack-acp-agent.ts
|
|
1206
|
-
import { fileURLToPath } from "node:url";
|
|
1207
1205
|
import { createServer } from "node:http";
|
|
1206
|
+
import { fileURLToPath } from "node:url";
|
|
1208
1207
|
import { writeErr as writeErr2 } from "@wrongstack/core/utils";
|
|
1209
1208
|
var WrongStackACPServer = class {
|
|
1210
1209
|
transport;
|
|
@@ -1265,10 +1264,9 @@ var WrongStackACPServer = class {
|
|
|
1265
1264
|
let httpChain = Promise.resolve();
|
|
1266
1265
|
this.httpServer = createServer(async (req, res) => {
|
|
1267
1266
|
if (authToken) {
|
|
1268
|
-
const url = new URL(req.url
|
|
1267
|
+
const url = new URL(requestPath(req.url), `http://${host}:${port}`);
|
|
1269
1268
|
const queryToken = url.searchParams.get("token");
|
|
1270
|
-
const
|
|
1271
|
-
const bearerToken = Array.isArray(authHeader) ? authHeader[0]?.replace(/^Bearer\s+/i, "") : authHeader?.replace(/^Bearer\s+/i, "");
|
|
1269
|
+
const bearerToken = headerValue(req.headers.authorization)?.replace(/^Bearer\s+/i, "");
|
|
1272
1270
|
const supplied = queryToken ?? bearerToken ?? "";
|
|
1273
1271
|
if (supplied !== authToken) {
|
|
1274
1272
|
res.writeHead(401, { "Content-Type": "application/json" });
|
|
@@ -1277,7 +1275,7 @@ var WrongStackACPServer = class {
|
|
|
1277
1275
|
}
|
|
1278
1276
|
}
|
|
1279
1277
|
const selfOrigin = `http://${host}:${port}`;
|
|
1280
|
-
const reqOrigin =
|
|
1278
|
+
const reqOrigin = headerValue(req.headers.origin);
|
|
1281
1279
|
if (reqOrigin && reqOrigin !== selfOrigin) {
|
|
1282
1280
|
res.writeHead(403);
|
|
1283
1281
|
res.end(JSON.stringify({ error: "cross-origin request forbidden" }));
|
|
@@ -1357,6 +1355,8 @@ var WrongStackACPServer = class {
|
|
|
1357
1355
|
try {
|
|
1358
1356
|
await requestPromise;
|
|
1359
1357
|
} catch {
|
|
1358
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1359
|
+
res.end(JSON.stringify({ error: { code: -32603, message: "Internal error" } }));
|
|
1360
1360
|
}
|
|
1361
1361
|
});
|
|
1362
1362
|
return new Promise((resolve3) => {
|
|
@@ -1382,6 +1382,12 @@ var WrongStackACPServer = class {
|
|
|
1382
1382
|
var defaultEchoRunTurn = async (_input, _emit) => {
|
|
1383
1383
|
return { stopReason: "end_turn" };
|
|
1384
1384
|
};
|
|
1385
|
+
function headerValue(value) {
|
|
1386
|
+
return Array.isArray(value) ? value[0] : value;
|
|
1387
|
+
}
|
|
1388
|
+
function requestPath(value) {
|
|
1389
|
+
return value ?? "/";
|
|
1390
|
+
}
|
|
1385
1391
|
async function main() {
|
|
1386
1392
|
const server = new WrongStackACPServer();
|
|
1387
1393
|
await server.start();
|
|
@@ -1400,6 +1406,14 @@ import { randomBytes } from "node:crypto";
|
|
|
1400
1406
|
import { realpathSync } from "node:fs";
|
|
1401
1407
|
import * as fsp from "node:fs/promises";
|
|
1402
1408
|
import * as path from "node:path";
|
|
1409
|
+
var DEFAULT_FILE_OPERATIONS = {
|
|
1410
|
+
stat: fsp.stat,
|
|
1411
|
+
readFile: fsp.readFile,
|
|
1412
|
+
writeFile: fsp.writeFile,
|
|
1413
|
+
realpath: fsp.realpath,
|
|
1414
|
+
rename: fsp.rename,
|
|
1415
|
+
unlink: fsp.unlink
|
|
1416
|
+
};
|
|
1403
1417
|
var DEFAULT_MAX_READ_BYTES = 5 * 1024 * 1024;
|
|
1404
1418
|
var DEFAULT_MAX_WRITE_BYTES = 5 * 1024 * 1024;
|
|
1405
1419
|
var FsError = class extends Error {
|
|
@@ -1418,12 +1432,14 @@ var FileServer = class {
|
|
|
1418
1432
|
timeoutMs;
|
|
1419
1433
|
maxReadBytes;
|
|
1420
1434
|
maxWriteBytes;
|
|
1435
|
+
operations;
|
|
1421
1436
|
constructor(opts) {
|
|
1422
1437
|
this.root = path.resolve(opts.projectRoot);
|
|
1423
1438
|
this.realRoot = safeRealpathSync(this.root);
|
|
1424
1439
|
this.timeoutMs = opts.timeoutMs ?? 3e4;
|
|
1425
1440
|
this.maxReadBytes = opts.maxReadBytes ?? DEFAULT_MAX_READ_BYTES;
|
|
1426
1441
|
this.maxWriteBytes = opts.maxWriteBytes ?? DEFAULT_MAX_WRITE_BYTES;
|
|
1442
|
+
this.operations = opts.operations ?? DEFAULT_FILE_OPERATIONS;
|
|
1427
1443
|
}
|
|
1428
1444
|
/** Read a text file. Returns the content as a string. */
|
|
1429
1445
|
async readTextFile(params) {
|
|
@@ -1431,7 +1447,7 @@ var FileServer = class {
|
|
|
1431
1447
|
const controller = new AbortController();
|
|
1432
1448
|
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
1433
1449
|
try {
|
|
1434
|
-
const stat2 = await
|
|
1450
|
+
const stat2 = await this.operations.stat(safe).catch((err) => {
|
|
1435
1451
|
throw mapFsError(err, safe);
|
|
1436
1452
|
});
|
|
1437
1453
|
if (stat2.size > this.maxReadBytes) {
|
|
@@ -1441,7 +1457,7 @@ var FileServer = class {
|
|
|
1441
1457
|
`file is ${stat2.size} bytes, max read is ${this.maxReadBytes} bytes`
|
|
1442
1458
|
);
|
|
1443
1459
|
}
|
|
1444
|
-
const content = await
|
|
1460
|
+
const content = await this.operations.readFile(safe, {
|
|
1445
1461
|
encoding: "utf8",
|
|
1446
1462
|
signal: controller.signal
|
|
1447
1463
|
});
|
|
@@ -1471,20 +1487,20 @@ var FileServer = class {
|
|
|
1471
1487
|
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
1472
1488
|
const tmp = `${safe}.${randomBytes(6).toString("hex")}.tmp`;
|
|
1473
1489
|
try {
|
|
1474
|
-
await
|
|
1490
|
+
await this.operations.writeFile(tmp, params.content, {
|
|
1475
1491
|
encoding: "utf8",
|
|
1476
1492
|
signal: controller.signal
|
|
1477
1493
|
});
|
|
1478
1494
|
await this.assertRealInside(tmp);
|
|
1479
1495
|
await this.assertRealInside(path.dirname(safe));
|
|
1480
|
-
await
|
|
1496
|
+
await this.operations.rename(tmp, safe);
|
|
1481
1497
|
} catch (err) {
|
|
1482
1498
|
if (err instanceof FsError) {
|
|
1483
|
-
await
|
|
1499
|
+
await this.operations.unlink(tmp).catch(() => void 0);
|
|
1484
1500
|
throw err;
|
|
1485
1501
|
}
|
|
1486
1502
|
try {
|
|
1487
|
-
await
|
|
1503
|
+
await this.operations.unlink(tmp);
|
|
1488
1504
|
} catch {
|
|
1489
1505
|
}
|
|
1490
1506
|
if (controller.signal.aborted) {
|
|
@@ -1528,12 +1544,14 @@ var FileServer = class {
|
|
|
1528
1544
|
for (; ; ) {
|
|
1529
1545
|
let real;
|
|
1530
1546
|
try {
|
|
1531
|
-
real = await
|
|
1547
|
+
real = await this.operations.realpath(probe);
|
|
1532
1548
|
} catch (err) {
|
|
1533
1549
|
const code = err.code;
|
|
1534
1550
|
if (code === "ENOENT") {
|
|
1535
1551
|
const parent = path.dirname(probe);
|
|
1536
|
-
if (parent === probe)
|
|
1552
|
+
if (parent === probe) {
|
|
1553
|
+
throw new FsError("ENOENT", resolvedPath, `no existing ancestor: ${resolvedPath}`);
|
|
1554
|
+
}
|
|
1537
1555
|
probe = parent;
|
|
1538
1556
|
continue;
|
|
1539
1557
|
}
|
|
@@ -1631,7 +1649,6 @@ var TerminalServer = class {
|
|
|
1631
1649
|
this.outputByteLimit = opts.outputByteLimit ?? 1024 * 1024;
|
|
1632
1650
|
this.maxOutputByteLimit = opts.maxOutputByteLimit ?? 16 * 1024 * 1024;
|
|
1633
1651
|
this.maxTerminals = this.clampFiniteInt(opts.maxTerminals, 32);
|
|
1634
|
-
if (this.maxTerminals < 1) throw new RangeError("maxTerminals must be at least 1");
|
|
1635
1652
|
this.abortSignal = opts.signal;
|
|
1636
1653
|
if (opts.signal) {
|
|
1637
1654
|
opts.signal.addEventListener("abort", this.abortHandler, { once: true });
|
|
@@ -1962,7 +1979,6 @@ var WebSocketClientTransport = class {
|
|
|
1962
1979
|
const ws = new WS(this.opts.url, this.opts.protocols);
|
|
1963
1980
|
this.ws = ws;
|
|
1964
1981
|
const timer = setTimeout(() => {
|
|
1965
|
-
if (settled) return;
|
|
1966
1982
|
settled = true;
|
|
1967
1983
|
try {
|
|
1968
1984
|
ws.close();
|
|
@@ -2000,7 +2016,7 @@ var WebSocketClientTransport = class {
|
|
|
2000
2016
|
}
|
|
2001
2017
|
try {
|
|
2002
2018
|
const serialized = JSON.stringify(msg);
|
|
2003
|
-
const buffered = Number.isFinite(this.ws.bufferedAmount) ? this.ws.bufferedAmount
|
|
2019
|
+
const buffered = Number.isFinite(this.ws.bufferedAmount) ? this.ws.bufferedAmount : 0;
|
|
2004
2020
|
if (buffered + Buffer.byteLength(serialized, "utf8") > this.maxBufferedBytes) {
|
|
2005
2021
|
this.stop();
|
|
2006
2022
|
return Promise.reject(new Error("WebSocket transport send buffer limit exceeded"));
|
|
@@ -2057,7 +2073,7 @@ var WebSocketClientTransport = class {
|
|
|
2057
2073
|
}
|
|
2058
2074
|
};
|
|
2059
2075
|
function finitePositiveLimit(value, fallback) {
|
|
2060
|
-
return Number.isFinite(value) &&
|
|
2076
|
+
return value !== void 0 && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
|
|
2061
2077
|
}
|
|
2062
2078
|
|
|
2063
2079
|
// src/client/acp-session.ts
|
|
@@ -3286,12 +3302,12 @@ async function benchOne(agentId, cmd, opts) {
|
|
|
3286
3302
|
signal
|
|
3287
3303
|
);
|
|
3288
3304
|
fsOk = fsRes.text.includes(fileToken);
|
|
3289
|
-
if (!fsOk)
|
|
3305
|
+
if (!fsOk)
|
|
3306
|
+
fsDetail = "agent did not return the file contents (may not have used a read tool)";
|
|
3290
3307
|
} catch (err) {
|
|
3291
3308
|
fsDetail = err instanceof Error ? err.message : String(err);
|
|
3292
3309
|
} finally {
|
|
3293
|
-
await
|
|
3294
|
-
});
|
|
3310
|
+
await removeBenchFile(filePath);
|
|
3295
3311
|
}
|
|
3296
3312
|
checks.push({ name: "fs", ok: fsOk, detail: fsDetail });
|
|
3297
3313
|
}
|
|
@@ -3306,8 +3322,7 @@ async function benchOne(agentId, cmd, opts) {
|
|
|
3306
3322
|
}
|
|
3307
3323
|
const required = checks.filter((c) => c.name !== "fs" || opts.checkFs);
|
|
3308
3324
|
const allReq = required.every((c) => c.ok);
|
|
3309
|
-
const
|
|
3310
|
-
const status = allReq ? "pass" : handshakeOk ? "partial" : "fail";
|
|
3325
|
+
const status = allReq ? "pass" : "partial";
|
|
3311
3326
|
return {
|
|
3312
3327
|
agentId,
|
|
3313
3328
|
status,
|
|
@@ -3399,7 +3414,9 @@ function renderAcpBenchText(result) {
|
|
|
3399
3414
|
for (const r of result.results) {
|
|
3400
3415
|
const checks = r.checks.map((c) => `${c.ok ? "\u2713" : "\u2717"}${c.name}`).join(" ");
|
|
3401
3416
|
const timing = r.handshakeMs !== void 0 ? ` hs=${r.handshakeMs}ms${r.promptMs !== void 0 ? ` prompt=${r.promptMs}ms` : ""}` : "";
|
|
3402
|
-
lines.push(
|
|
3417
|
+
lines.push(
|
|
3418
|
+
` ${icon(r.status)} ${r.agentId.padEnd(16)} ${r.status.toUpperCase().padEnd(7)} ${checks}${timing}`
|
|
3419
|
+
);
|
|
3403
3420
|
if (r.agentInfo) lines.push(` agent: ${r.agentInfo.name} ${r.agentInfo.version}`);
|
|
3404
3421
|
if (r.sample) lines.push(` reply: ${r.sample}`);
|
|
3405
3422
|
if (r.reason) lines.push(` reason: ${r.reason}`);
|
|
@@ -3411,6 +3428,9 @@ function renderAcpBenchText(result) {
|
|
|
3411
3428
|
);
|
|
3412
3429
|
return lines.join("\n");
|
|
3413
3430
|
}
|
|
3431
|
+
async function removeBenchFile(filePath, remove = fsp2.rm) {
|
|
3432
|
+
await remove(filePath, { force: true }).catch(() => void 0);
|
|
3433
|
+
}
|
|
3414
3434
|
|
|
3415
3435
|
// src/registry/agents.catalog.ts
|
|
3416
3436
|
var AGENTS_CATALOG = [
|
|
@@ -3799,16 +3819,8 @@ function mapACPKind(acpKind) {
|
|
|
3799
3819
|
}
|
|
3800
3820
|
}
|
|
3801
3821
|
function isRetryable(kind) {
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
case "provider_rate_limit":
|
|
3805
|
-
case "provider_timeout":
|
|
3806
|
-
case "tool_threw":
|
|
3807
|
-
case "budget_timeout":
|
|
3808
|
-
return true;
|
|
3809
|
-
default:
|
|
3810
|
-
return false;
|
|
3811
|
-
}
|
|
3822
|
+
void kind;
|
|
3823
|
+
return false;
|
|
3812
3824
|
}
|
|
3813
3825
|
var REGISTRY_ID_ALIASES = {
|
|
3814
3826
|
"claude-code": "claude-acp",
|
|
@@ -3948,11 +3960,15 @@ async function probeAcpAgents(opts) {
|
|
|
3948
3960
|
}
|
|
3949
3961
|
await runPhase(local, opts.concurrency ?? 4, localTimeout);
|
|
3950
3962
|
await runPhase(pkg, 2, pkgTimeout);
|
|
3951
|
-
return ids.map((id) => byId.get(id)
|
|
3963
|
+
return ids.map((id) => byId.get(id));
|
|
3952
3964
|
}
|
|
3953
3965
|
|
|
3966
|
+
// src/integration/ensemble-runner.ts
|
|
3967
|
+
import { SubagentBudget } from "@wrongstack/core/coordination";
|
|
3968
|
+
|
|
3954
3969
|
// src/registry/ensemble-registry.ts
|
|
3955
3970
|
import { spawn as spawn2 } from "node:child_process";
|
|
3971
|
+
import { treeKill as treeKill2 } from "@wrongstack/core/utils/tree-kill";
|
|
3956
3972
|
var PROBE_TIMEOUT_MS = 5e3;
|
|
3957
3973
|
var PROBE_CACHE_MS = 5e3;
|
|
3958
3974
|
var MAX_PARALLEL_PROBES = 4;
|
|
@@ -3976,7 +3992,7 @@ async function probeWithBound(items, worker, limit) {
|
|
|
3976
3992
|
await Promise.all(runners);
|
|
3977
3993
|
return results;
|
|
3978
3994
|
}
|
|
3979
|
-
async function defaultProbe(desc, timeoutMs) {
|
|
3995
|
+
async function defaultProbe(desc, timeoutMs, spawnProcess = spawn2, platform = process.platform) {
|
|
3980
3996
|
const start = Date.now();
|
|
3981
3997
|
return new Promise((resolve3) => {
|
|
3982
3998
|
let settled = false;
|
|
@@ -3986,7 +4002,9 @@ async function defaultProbe(desc, timeoutMs) {
|
|
|
3986
4002
|
if (settled) return;
|
|
3987
4003
|
settled = true;
|
|
3988
4004
|
try {
|
|
3989
|
-
child.
|
|
4005
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
4006
|
+
treeKill2(child);
|
|
4007
|
+
}
|
|
3990
4008
|
} catch {
|
|
3991
4009
|
}
|
|
3992
4010
|
resolve3(result);
|
|
@@ -3994,8 +4012,8 @@ async function defaultProbe(desc, timeoutMs) {
|
|
|
3994
4012
|
let child;
|
|
3995
4013
|
try {
|
|
3996
4014
|
const probeArgs = [...desc.probe.args ?? []];
|
|
3997
|
-
const shim =
|
|
3998
|
-
child =
|
|
4015
|
+
const shim = platform === "win32" ? buildWin32CmdShimInvocation(desc.probe.command, probeArgs) : null;
|
|
4016
|
+
child = spawnProcess(shim?.command ?? desc.probe.command, shim?.args ?? probeArgs, {
|
|
3999
4017
|
stdio: ["ignore", "pipe", "pipe"],
|
|
4000
4018
|
windowsHide: true,
|
|
4001
4019
|
...shim ? { windowsVerbatimArguments: shim.windowsVerbatimArguments } : {}
|
|
@@ -4028,7 +4046,7 @@ async function defaultProbe(desc, timeoutMs) {
|
|
|
4028
4046
|
clearTimeout(timer);
|
|
4029
4047
|
const durationMs = Date.now() - start;
|
|
4030
4048
|
const out = (stdout + stderr).trim();
|
|
4031
|
-
const isWindowsShellMiss =
|
|
4049
|
+
const isWindowsShellMiss = platform === "win32" && out.toLowerCase().includes("is not recognized");
|
|
4032
4050
|
if (isWindowsShellMiss) {
|
|
4033
4051
|
finish({
|
|
4034
4052
|
ok: false,
|
|
@@ -4040,7 +4058,7 @@ async function defaultProbe(desc, timeoutMs) {
|
|
|
4040
4058
|
if (out.length > 0) {
|
|
4041
4059
|
finish({
|
|
4042
4060
|
ok: true,
|
|
4043
|
-
version: out.split("\n")[0]
|
|
4061
|
+
version: out.split("\n")[0].trim(),
|
|
4044
4062
|
path: desc.probe.command,
|
|
4045
4063
|
durationMs
|
|
4046
4064
|
});
|
|
@@ -4116,7 +4134,6 @@ var EnsembleRegistry = class {
|
|
|
4116
4134
|
};
|
|
4117
4135
|
|
|
4118
4136
|
// src/integration/ensemble-runner.ts
|
|
4119
|
-
import { SubagentBudget } from "@wrongstack/core/coordination";
|
|
4120
4137
|
var DEFAULT_MAX_CONCURRENCY = 4;
|
|
4121
4138
|
async function mapBound(items, worker, limit) {
|
|
4122
4139
|
const results = new Array(items.length);
|
|
@@ -4259,10 +4276,7 @@ async function runEnsemble(opts) {
|
|
|
4259
4276
|
}
|
|
4260
4277
|
runnable.push({ id, cmd });
|
|
4261
4278
|
}
|
|
4262
|
-
const concurrency = Math.max(
|
|
4263
|
-
1,
|
|
4264
|
-
opts.maxConcurrency ?? DEFAULT_MAX_CONCURRENCY
|
|
4265
|
-
);
|
|
4279
|
+
const concurrency = Math.max(1, opts.maxConcurrency ?? DEFAULT_MAX_CONCURRENCY);
|
|
4266
4280
|
await mapBound(
|
|
4267
4281
|
runnable,
|
|
4268
4282
|
async ({ id, cmd }) => {
|
|
@@ -4311,15 +4325,11 @@ function renderEnsembleText(result) {
|
|
|
4311
4325
|
);
|
|
4312
4326
|
break;
|
|
4313
4327
|
case "failed":
|
|
4314
|
-
lines.push(
|
|
4315
|
-
`[${r.error?.kind ?? "unknown"}] ${r.error?.message ?? "failed"}`
|
|
4316
|
-
);
|
|
4328
|
+
lines.push(`[${r.error?.kind ?? "unknown"}] ${r.error?.message ?? "failed"}`);
|
|
4317
4329
|
lines.push(`[${r.agentId}] failed ${r.durationMs}ms`);
|
|
4318
4330
|
break;
|
|
4319
4331
|
case "cancelled":
|
|
4320
|
-
lines.push(
|
|
4321
|
-
`[${r.error?.kind ?? "aborted"}] ${r.error?.message ?? "cancelled"}`
|
|
4322
|
-
);
|
|
4332
|
+
lines.push(`[${r.error?.kind ?? "aborted"}] ${r.error?.message ?? "cancelled"}`);
|
|
4323
4333
|
lines.push(`[${r.agentId}] cancelled ${r.durationMs}ms`);
|
|
4324
4334
|
break;
|
|
4325
4335
|
case "skipped":
|
|
@@ -4381,15 +4391,14 @@ async function runOneAcpTask(opts) {
|
|
|
4381
4391
|
|
|
4382
4392
|
// src/registry/acp-registry-fetch.ts
|
|
4383
4393
|
var ACP_REGISTRY_URL = "https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json";
|
|
4384
|
-
function currentPlatformKey() {
|
|
4385
|
-
const os =
|
|
4386
|
-
const arch =
|
|
4394
|
+
function currentPlatformKey(platform = process.platform, architecture = process.arch) {
|
|
4395
|
+
const os = platform === "win32" ? "windows" : platform === "darwin" ? "darwin" : "linux";
|
|
4396
|
+
const arch = architecture === "arm64" ? "aarch64" : architecture === "x64" ? "x86_64" : architecture;
|
|
4387
4397
|
return `${os}-${arch}`;
|
|
4388
4398
|
}
|
|
4389
4399
|
function basename(cmd) {
|
|
4390
4400
|
const cleaned = cmd.replace(/^\.\//, "").replace(/\\/g, "/");
|
|
4391
|
-
|
|
4392
|
-
return parts[parts.length - 1] || cleaned;
|
|
4401
|
+
return cleaned.slice(cleaned.lastIndexOf("/") + 1);
|
|
4393
4402
|
}
|
|
4394
4403
|
function mapRegistryEntry(entry, platformKey = currentPlatformKey()) {
|
|
4395
4404
|
if (!entry || typeof entry.id !== "string" || entry.id.length === 0) return null;
|