@gleanwork/mcp-server-tester 2.0.0-beta.2 → 2.0.0-beta.4
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/cli/index.js +542 -87
- package/dist/fixtures/mcp.js +65 -8
- package/dist/fixtures/mcp.js.map +1 -1
- package/dist/index.cjs +7905 -6991
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +71 -1
- package/dist/index.d.ts +71 -1
- package/dist/index.js +7975 -7062
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
- package/scripts/cowork_linux.py +210 -0
package/dist/cli/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import { createRequire } from 'module';
|
|
|
13
13
|
import os, { homedir, tmpdir, userInfo } from 'os';
|
|
14
14
|
import { promisify } from 'util';
|
|
15
15
|
import crypto, { randomUUID, createHash } from 'crypto';
|
|
16
|
+
import { setTimeout as setTimeout$1 } from 'timers/promises';
|
|
16
17
|
import { Command } from 'commander';
|
|
17
18
|
import { render, useApp, useInput, Box, Text } from 'ink';
|
|
18
19
|
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
|
@@ -942,11 +943,11 @@ function isNodeDefaultFetch(fetch2) {
|
|
|
942
943
|
return source.includes("internal/deps/undici") || source.includes("lazy loading of undici");
|
|
943
944
|
}
|
|
944
945
|
async function createSafeNodeFetch() {
|
|
945
|
-
const [{ createRequire:
|
|
946
|
+
const [{ createRequire: createRequire4 }, { lookup }] = await Promise.all([
|
|
946
947
|
loadNodeModule("node:module"),
|
|
947
948
|
loadNodeModule("node:dns")
|
|
948
949
|
]);
|
|
949
|
-
const { Agent, fetch: fetch2 } =
|
|
950
|
+
const { Agent, fetch: fetch2 } = createRequire4(getCurrentModulePath())(
|
|
950
951
|
"undici"
|
|
951
952
|
);
|
|
952
953
|
const dispatcher = new Agent({
|
|
@@ -2454,7 +2455,7 @@ function createProviderToolFactory({
|
|
|
2454
2455
|
inputSchema
|
|
2455
2456
|
}) {
|
|
2456
2457
|
return ({
|
|
2457
|
-
execute,
|
|
2458
|
+
execute: execute2,
|
|
2458
2459
|
outputSchema,
|
|
2459
2460
|
needsApproval,
|
|
2460
2461
|
toModelOutput,
|
|
@@ -2468,7 +2469,7 @@ function createProviderToolFactory({
|
|
|
2468
2469
|
args,
|
|
2469
2470
|
inputSchema,
|
|
2470
2471
|
outputSchema,
|
|
2471
|
-
execute,
|
|
2472
|
+
execute: execute2,
|
|
2472
2473
|
needsApproval,
|
|
2473
2474
|
toModelOutput,
|
|
2474
2475
|
onInputStart,
|
|
@@ -2483,7 +2484,7 @@ function createProviderToolFactoryWithOutputSchema({
|
|
|
2483
2484
|
supportsDeferredResults
|
|
2484
2485
|
}) {
|
|
2485
2486
|
return ({
|
|
2486
|
-
execute,
|
|
2487
|
+
execute: execute2,
|
|
2487
2488
|
needsApproval,
|
|
2488
2489
|
toModelOutput,
|
|
2489
2490
|
onInputStart,
|
|
@@ -2496,7 +2497,7 @@ function createProviderToolFactoryWithOutputSchema({
|
|
|
2496
2497
|
args,
|
|
2497
2498
|
inputSchema,
|
|
2498
2499
|
outputSchema,
|
|
2499
|
-
execute,
|
|
2500
|
+
execute: execute2,
|
|
2500
2501
|
needsApproval,
|
|
2501
2502
|
toModelOutput,
|
|
2502
2503
|
onInputStart,
|
|
@@ -2654,11 +2655,11 @@ function isAsyncIterable(obj) {
|
|
|
2654
2655
|
return obj != null && typeof obj[Symbol.asyncIterator] === "function";
|
|
2655
2656
|
}
|
|
2656
2657
|
async function* executeTool({
|
|
2657
|
-
execute,
|
|
2658
|
+
execute: execute2,
|
|
2658
2659
|
input,
|
|
2659
2660
|
options
|
|
2660
2661
|
}) {
|
|
2661
|
-
const result =
|
|
2662
|
+
const result = execute2(input, options);
|
|
2662
2663
|
if (isAsyncIterable(result)) {
|
|
2663
2664
|
let lastOutput;
|
|
2664
2665
|
for await (const output of result) {
|
|
@@ -3563,6 +3564,30 @@ var init_pythonRuntime = __esm({
|
|
|
3563
3564
|
exec = promisify(execFile);
|
|
3564
3565
|
}
|
|
3565
3566
|
});
|
|
3567
|
+
|
|
3568
|
+
// src/evals/cowork/driver.ts
|
|
3569
|
+
var COMPUTER_USE_TOKEN_FIELDS, CoworkDriverError, CoworkHitlBudgetError;
|
|
3570
|
+
var init_driver = __esm({
|
|
3571
|
+
"src/evals/cowork/driver.ts"() {
|
|
3572
|
+
init_esm_shims();
|
|
3573
|
+
COMPUTER_USE_TOKEN_FIELDS = [
|
|
3574
|
+
"input_tokens",
|
|
3575
|
+
"output_tokens",
|
|
3576
|
+
"cache_creation_input_tokens",
|
|
3577
|
+
"cache_read_input_tokens"
|
|
3578
|
+
];
|
|
3579
|
+
CoworkDriverError = class extends Error {
|
|
3580
|
+
constructor(message, telemetry2) {
|
|
3581
|
+
super(message);
|
|
3582
|
+
this.telemetry = telemetry2;
|
|
3583
|
+
}
|
|
3584
|
+
kind = "failed";
|
|
3585
|
+
};
|
|
3586
|
+
CoworkHitlBudgetError = class extends CoworkDriverError {
|
|
3587
|
+
kind = "hitl-budget-exhausted";
|
|
3588
|
+
};
|
|
3589
|
+
}
|
|
3590
|
+
});
|
|
3566
3591
|
function resolveDriverPath(env) {
|
|
3567
3592
|
const configuredRoot = env.MST_COWORK_DRIVER_ROOT;
|
|
3568
3593
|
if (configuredRoot) {
|
|
@@ -3583,22 +3608,22 @@ async function runAnthropicComputerUseHitl(options) {
|
|
|
3583
3608
|
"HITL check"
|
|
3584
3609
|
);
|
|
3585
3610
|
}
|
|
3586
|
-
async function runComputerUseDriver(query2, options, mode,
|
|
3611
|
+
async function runComputerUseDriver(query2, options, mode, label2) {
|
|
3587
3612
|
const env = {
|
|
3588
3613
|
...process.env,
|
|
3589
3614
|
...options.env,
|
|
3590
3615
|
...options.model ? { MST_COWORK_CUA_MODEL: options.model } : {}
|
|
3591
3616
|
};
|
|
3592
3617
|
if (options.deadlineAt <= Date.now())
|
|
3593
|
-
throw new Error(`Computer Use ${
|
|
3618
|
+
throw new Error(`Computer Use ${label2} deadline exceeded; not retrying.`);
|
|
3594
3619
|
const python = await ensureCoworkPython(env);
|
|
3595
3620
|
const DRIVER_PATH = resolveDriverPath(env);
|
|
3596
3621
|
const timeoutMs = options.deadlineAt - Date.now();
|
|
3597
3622
|
if (timeoutMs <= 0)
|
|
3598
|
-
throw new Error(`Computer Use ${
|
|
3623
|
+
throw new Error(`Computer Use ${label2} deadline exceeded; not retrying.`);
|
|
3599
3624
|
const maxActions = options.maxActions ?? (mode === "hitl" ? 12 : 24);
|
|
3600
3625
|
diagnostic(
|
|
3601
|
-
`starting Computer Use ${
|
|
3626
|
+
`starting Computer Use ${label2} (script=${DRIVER_PATH}, maxActions=${maxActions}, timeoutMs=${timeoutMs})`
|
|
3602
3627
|
);
|
|
3603
3628
|
let stdout = "";
|
|
3604
3629
|
try {
|
|
@@ -3616,7 +3641,7 @@ async function runComputerUseDriver(query2, options, mode, label) {
|
|
|
3616
3641
|
const childError = error;
|
|
3617
3642
|
stdout = String(childError.stdout ?? "");
|
|
3618
3643
|
const reported = parseLastJsonLine(stdout);
|
|
3619
|
-
const
|
|
3644
|
+
const telemetry3 = parseTelemetry(reported?.telemetry, env, "partial");
|
|
3620
3645
|
const details = error.killed === true ? "driver terminated before completion; not retrying" : "driver exited unsuccessfully; not retrying";
|
|
3621
3646
|
if (mode === "hitl" && reported?.status === "failed" && typeof reported.error === "string" && /^Computer Use HITL check exceeded \d+ actions after attempting a visible prompt$/.test(
|
|
3622
3647
|
reported.error
|
|
@@ -3626,47 +3651,47 @@ async function runComputerUseDriver(query2, options, mode, label) {
|
|
|
3626
3651
|
);
|
|
3627
3652
|
throw new ComputerUseHitlBudgetError(
|
|
3628
3653
|
`HITL inspection reached its ${maxActions}-action budget.`,
|
|
3629
|
-
|
|
3654
|
+
telemetry3
|
|
3630
3655
|
);
|
|
3631
3656
|
}
|
|
3632
|
-
diagnostic(`Computer Use ${
|
|
3657
|
+
diagnostic(`Computer Use ${label2} failed: ${details}`);
|
|
3633
3658
|
throw new ComputerUseDriverError(
|
|
3634
|
-
`Computer Use ${
|
|
3635
|
-
|
|
3659
|
+
`Computer Use ${label2} failed: ${details}`,
|
|
3660
|
+
telemetry3
|
|
3636
3661
|
);
|
|
3637
3662
|
}
|
|
3638
3663
|
diagnostic(
|
|
3639
|
-
`Computer Use ${
|
|
3664
|
+
`Computer Use ${label2} exited successfully (stdoutBytes=${stdout.length})`
|
|
3640
3665
|
);
|
|
3641
3666
|
const record = parseLastJsonLine(stdout);
|
|
3642
3667
|
const expectedStatus = mode === "submit" ? "submitted" : "hitl_checked";
|
|
3643
3668
|
if (record?.status !== expectedStatus) {
|
|
3644
3669
|
throw new ComputerUseDriverError(
|
|
3645
|
-
`Computer Use ${
|
|
3670
|
+
`Computer Use ${label2} did not reach ${expectedStatus}.`,
|
|
3646
3671
|
parseTelemetry(record?.telemetry, env, "partial")
|
|
3647
3672
|
);
|
|
3648
3673
|
}
|
|
3649
3674
|
if (!isCount(record.action_count) || !isSafeModel(record.model, env)) {
|
|
3650
3675
|
throw new ComputerUseDriverError(
|
|
3651
|
-
`Computer Use ${
|
|
3676
|
+
`Computer Use ${label2} returned invalid result fields.`,
|
|
3652
3677
|
parseTelemetry(record.telemetry, env, "partial")
|
|
3653
3678
|
);
|
|
3654
3679
|
}
|
|
3655
|
-
const
|
|
3680
|
+
const telemetry2 = parseTelemetry(record.telemetry, env, "complete");
|
|
3656
3681
|
const common = {
|
|
3657
3682
|
action_count: record.action_count,
|
|
3658
3683
|
model: record.model,
|
|
3659
|
-
...
|
|
3684
|
+
...telemetry2 ? { telemetry: telemetry2 } : {}
|
|
3660
3685
|
};
|
|
3661
3686
|
diagnostic(
|
|
3662
|
-
`Computer Use ${
|
|
3687
|
+
`Computer Use ${label2} completed (actions=${common.action_count})`
|
|
3663
3688
|
);
|
|
3664
3689
|
if (mode === "hitl") return { status: "hitl_checked", ...common };
|
|
3665
3690
|
const submission = asRecord(record.submission_action);
|
|
3666
3691
|
if (submission?.action !== "key" || submission.text !== "enter") {
|
|
3667
3692
|
throw new ComputerUseDriverError(
|
|
3668
3693
|
"Computer Use submission returned an invalid submission boundary.",
|
|
3669
|
-
|
|
3694
|
+
telemetry2 ? { ...telemetry2, accounting: "partial" } : void 0
|
|
3670
3695
|
);
|
|
3671
3696
|
}
|
|
3672
3697
|
return {
|
|
@@ -3726,7 +3751,7 @@ function parseTelemetry(value, env, accounting) {
|
|
|
3726
3751
|
if (!usage || !coverage) return void 0;
|
|
3727
3752
|
const safeUsage = {};
|
|
3728
3753
|
const safeCoverage = {};
|
|
3729
|
-
for (const field of
|
|
3754
|
+
for (const field of COMPUTER_USE_TOKEN_FIELDS) {
|
|
3730
3755
|
const count = coverage[field];
|
|
3731
3756
|
if (!isCount(count) || count > record.planner_response_count)
|
|
3732
3757
|
return void 0;
|
|
@@ -3756,26 +3781,22 @@ function parseTelemetry(value, env, accounting) {
|
|
|
3756
3781
|
cost: { status: "unavailable" }
|
|
3757
3782
|
};
|
|
3758
3783
|
}
|
|
3759
|
-
var execFileAsync,
|
|
3784
|
+
var execFileAsync, ComputerUseDriverError, ComputerUseHitlBudgetError;
|
|
3760
3785
|
var init_anthropicComputerUse = __esm({
|
|
3761
3786
|
"src/evals/cowork/anthropicComputerUse.ts"() {
|
|
3762
3787
|
init_esm_shims();
|
|
3763
3788
|
init_pythonRuntime();
|
|
3789
|
+
init_driver();
|
|
3764
3790
|
execFileAsync = promisify(execFile);
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
3769
|
-
"cache_read_input_tokens"
|
|
3770
|
-
];
|
|
3771
|
-
ComputerUseDriverError = class extends Error {
|
|
3772
|
-
constructor(message, telemetry) {
|
|
3773
|
-
super(message);
|
|
3774
|
-
this.telemetry = telemetry;
|
|
3791
|
+
ComputerUseDriverError = class extends CoworkDriverError {
|
|
3792
|
+
constructor(message, telemetry2) {
|
|
3793
|
+
super(message, telemetry2);
|
|
3794
|
+
this.telemetry = telemetry2;
|
|
3775
3795
|
}
|
|
3776
3796
|
};
|
|
3777
3797
|
ComputerUseHitlBudgetError = class extends ComputerUseDriverError {
|
|
3778
3798
|
name = "ComputerUseHitlBudgetError";
|
|
3799
|
+
kind = "hitl-budget-exhausted";
|
|
3779
3800
|
};
|
|
3780
3801
|
}
|
|
3781
3802
|
});
|
|
@@ -5011,7 +5032,7 @@ async function prepareMacCoworkSession(options) {
|
|
|
5011
5032
|
const { running: wasRunning } = await controller.state();
|
|
5012
5033
|
await mkdir(lease, { mode: 448 });
|
|
5013
5034
|
const receiptPath = join(lease, "session.json");
|
|
5014
|
-
const
|
|
5035
|
+
const receipt2 = Buffer.from(
|
|
5015
5036
|
JSON.stringify({
|
|
5016
5037
|
version: 1,
|
|
5017
5038
|
nonce: randomUUID(),
|
|
@@ -5024,7 +5045,7 @@ async function prepareMacCoworkSession(options) {
|
|
|
5024
5045
|
let leaseInfo;
|
|
5025
5046
|
try {
|
|
5026
5047
|
leaseInfo = await lstat(lease);
|
|
5027
|
-
await writeFile(receiptPath,
|
|
5048
|
+
await writeFile(receiptPath, receipt2, { mode: 384, flag: "wx" });
|
|
5028
5049
|
} catch {
|
|
5029
5050
|
try {
|
|
5030
5051
|
await rmdir(lease);
|
|
@@ -5036,7 +5057,7 @@ async function prepareMacCoworkSession(options) {
|
|
|
5036
5057
|
let installAttempted = false;
|
|
5037
5058
|
const ownsLease = async () => {
|
|
5038
5059
|
const current = await lstat(lease);
|
|
5039
|
-
if (!current.isDirectory() || current.isSymbolicLink() || current.dev !== leaseInfo.dev || current.ino !== leaseInfo.ino || current.uid !== process.getuid?.() || (current.mode & 63) !== 0 || !(await privateBytes(receiptPath)).equals(
|
|
5060
|
+
if (!current.isDirectory() || current.isSymbolicLink() || current.dev !== leaseInfo.dev || current.ino !== leaseInfo.ino || current.uid !== process.getuid?.() || (current.mode & 63) !== 0 || !(await privateBytes(receiptPath)).equals(receipt2))
|
|
5040
5061
|
throw new Error(ERROR3);
|
|
5041
5062
|
const names = await readdir(lease);
|
|
5042
5063
|
if (names.length !== 1 || names[0] !== "session.json")
|
|
@@ -5088,7 +5109,7 @@ async function prepareMacCoworkSession(options) {
|
|
|
5088
5109
|
} catch {
|
|
5089
5110
|
const after = await lstat(lease);
|
|
5090
5111
|
if (after.isDirectory() && after.dev === leaseInfo.dev && after.ino === leaseInfo.ino)
|
|
5091
|
-
await writeFile(receiptPath,
|
|
5112
|
+
await writeFile(receiptPath, receipt2, { mode: 384, flag: "wx" });
|
|
5092
5113
|
throw new Error(ERROR3);
|
|
5093
5114
|
}
|
|
5094
5115
|
} catch {
|
|
@@ -5193,16 +5214,16 @@ async function recoverMacCoworkSession() {
|
|
|
5193
5214
|
if (!leaseInfo.isDirectory() || leaseInfo.isSymbolicLink() || leaseInfo.uid !== process.getuid?.() || leaseInfo.mode & 63)
|
|
5194
5215
|
throw new Error("Recovery lease is not a private owned directory.");
|
|
5195
5216
|
const bytes = await privateFile(receiptPath);
|
|
5196
|
-
const
|
|
5197
|
-
if (
|
|
5217
|
+
const receipt2 = Receipt.parse(JSON.parse(bytes.toString("utf8")));
|
|
5218
|
+
if (receipt2.profileDirectory !== profile)
|
|
5198
5219
|
throw new Error("Recovery profile mismatch.");
|
|
5199
|
-
assertStoppedOwner(
|
|
5220
|
+
assertStoppedOwner(receipt2.pid);
|
|
5200
5221
|
await mkdir(gate, { mode: 448 });
|
|
5201
5222
|
const verifyLease = async () => {
|
|
5202
5223
|
const current = await lstat(lease);
|
|
5203
5224
|
if (current.dev !== leaseInfo.dev || current.ino !== leaseInfo.ino || !(await privateFile(receiptPath)).equals(bytes) || (await readdir(lease)).join() !== "session.json")
|
|
5204
5225
|
throw new Error("Recovery lease changed; refusing cleanup.");
|
|
5205
|
-
assertStoppedOwner(
|
|
5226
|
+
assertStoppedOwner(receipt2.pid);
|
|
5206
5227
|
};
|
|
5207
5228
|
try {
|
|
5208
5229
|
await verifyLease();
|
|
@@ -5211,9 +5232,9 @@ async function recoverMacCoworkSession() {
|
|
|
5211
5232
|
const journal = JSON.parse(
|
|
5212
5233
|
(await privateFile(join(transaction, "journal.json"))).toString("utf8")
|
|
5213
5234
|
);
|
|
5214
|
-
if (journal.directory !==
|
|
5235
|
+
if (journal.directory !== receipt2.stagingDirectory)
|
|
5215
5236
|
throw new Error("Recovery staging mismatch.");
|
|
5216
|
-
} else if (await exists3(
|
|
5237
|
+
} else if (await exists3(receipt2.stagingDirectory))
|
|
5217
5238
|
throw new Error(
|
|
5218
5239
|
"Staging exists without its transaction; recovery refused."
|
|
5219
5240
|
);
|
|
@@ -5223,10 +5244,10 @@ async function recoverMacCoworkSession() {
|
|
|
5223
5244
|
throw new Error("Claude did not stop; recovery refused.");
|
|
5224
5245
|
await verifyLease();
|
|
5225
5246
|
if (hasTransaction) await restoreMacCoworkSettings(profile);
|
|
5226
|
-
if (await exists3(transaction) || await exists3(
|
|
5247
|
+
if (await exists3(transaction) || await exists3(receipt2.stagingDirectory))
|
|
5227
5248
|
throw new Error("Recovery state remains; lease retained.");
|
|
5228
|
-
if (
|
|
5229
|
-
if ((await controller.state()).running !==
|
|
5249
|
+
if (receipt2.wasRunning) await controller.start();
|
|
5250
|
+
if ((await controller.state()).running !== receipt2.wasRunning)
|
|
5230
5251
|
throw new Error(
|
|
5231
5252
|
"Could not restore prior Claude running state; lease retained."
|
|
5232
5253
|
);
|
|
@@ -5294,6 +5315,240 @@ var init_macos = __esm({
|
|
|
5294
5315
|
}
|
|
5295
5316
|
});
|
|
5296
5317
|
|
|
5318
|
+
// src/evals/cowork/linux.ts
|
|
5319
|
+
var linux_exports = {};
|
|
5320
|
+
__export(linux_exports, {
|
|
5321
|
+
linuxCoworkPlatform: () => linuxCoworkPlatform
|
|
5322
|
+
});
|
|
5323
|
+
async function readSettings(file) {
|
|
5324
|
+
const handle = await open$1(
|
|
5325
|
+
file,
|
|
5326
|
+
constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK
|
|
5327
|
+
);
|
|
5328
|
+
try {
|
|
5329
|
+
const metadata2 = await handle.stat();
|
|
5330
|
+
if (!metadata2.isFile() || metadata2.size > 1024 * 1024)
|
|
5331
|
+
throw new Error("Invalid prepared settings file.");
|
|
5332
|
+
return JSON.parse(await handle.readFile("utf8"));
|
|
5333
|
+
} finally {
|
|
5334
|
+
await handle.close();
|
|
5335
|
+
}
|
|
5336
|
+
}
|
|
5337
|
+
function telemetry(started, actions, accounting) {
|
|
5338
|
+
return {
|
|
5339
|
+
driver: "linux-desktop",
|
|
5340
|
+
accounting,
|
|
5341
|
+
duration_ms: Math.max(0, Date.now() - started),
|
|
5342
|
+
action_count: actions,
|
|
5343
|
+
planner: { status: "not-applicable" },
|
|
5344
|
+
cost: { status: "not-applicable" }
|
|
5345
|
+
};
|
|
5346
|
+
}
|
|
5347
|
+
function receipt(stdout) {
|
|
5348
|
+
try {
|
|
5349
|
+
const parsed = JSON.parse(stdout);
|
|
5350
|
+
if (!parsed || typeof parsed !== "object") return void 0;
|
|
5351
|
+
const r = parsed;
|
|
5352
|
+
if (!["ready", "submitted", "hitl_checked", "failed"].includes(
|
|
5353
|
+
String(r.status)
|
|
5354
|
+
) || typeof r.action_count !== "number" || !Number.isSafeInteger(r.action_count) || r.action_count < 0 || typeof r.duration_ms !== "number" || !Number.isFinite(r.duration_ms) || r.duration_ms < 0)
|
|
5355
|
+
return void 0;
|
|
5356
|
+
return {
|
|
5357
|
+
status: r.status,
|
|
5358
|
+
action_count: r.action_count,
|
|
5359
|
+
duration_ms: r.duration_ms,
|
|
5360
|
+
errorCode: typeof r.error === "string" && /^[a-z_]{1,64}$/.test(r.error) ? r.error : void 0
|
|
5361
|
+
};
|
|
5362
|
+
} catch {
|
|
5363
|
+
return void 0;
|
|
5364
|
+
}
|
|
5365
|
+
}
|
|
5366
|
+
async function execute(mode, payload, options) {
|
|
5367
|
+
const started = Date.now();
|
|
5368
|
+
const remaining = options.deadlineAt - started;
|
|
5369
|
+
if (remaining <= 0)
|
|
5370
|
+
throw new CoworkDriverError(
|
|
5371
|
+
"Linux desktop deadline exceeded; no action attempted."
|
|
5372
|
+
);
|
|
5373
|
+
const env = { ...process.env, ...options.env };
|
|
5374
|
+
if (!env.DISPLAY || !env.DBUS_SESSION_BUS_ADDRESS)
|
|
5375
|
+
throw new CoworkDriverError(
|
|
5376
|
+
"Linux Cowork requires a prepared DISPLAY and D-Bus desktop session."
|
|
5377
|
+
);
|
|
5378
|
+
if (env.MST_COWORK_URL_OPENER !== void 0 && !isAbsolute(env.MST_COWORK_URL_OPENER))
|
|
5379
|
+
throw new CoworkDriverError(
|
|
5380
|
+
"MST_COWORK_URL_OPENER must be an absolute executable path."
|
|
5381
|
+
);
|
|
5382
|
+
const script = createRequire(
|
|
5383
|
+
typeof __filename$1 === "string" ? __filename$1 : import.meta.url
|
|
5384
|
+
).resolve("@gleanwork/mcp-server-tester/cowork-linux-runtime");
|
|
5385
|
+
const timeout = Math.min(remaining, mode === "submit" ? 6e4 : 1e4);
|
|
5386
|
+
const driverTimeout = Math.max(
|
|
5387
|
+
1,
|
|
5388
|
+
Math.floor(timeout - Math.min(1e3, timeout / 10))
|
|
5389
|
+
);
|
|
5390
|
+
const result = await new Promise(
|
|
5391
|
+
(resolve10) => {
|
|
5392
|
+
const child = execFile(
|
|
5393
|
+
env.MST_COWORK_PYTHON ?? "python3",
|
|
5394
|
+
[
|
|
5395
|
+
script,
|
|
5396
|
+
"--mode",
|
|
5397
|
+
mode,
|
|
5398
|
+
"--timeout-ms",
|
|
5399
|
+
String(driverTimeout),
|
|
5400
|
+
"--max-actions",
|
|
5401
|
+
String(options.maxActions ?? 24)
|
|
5402
|
+
],
|
|
5403
|
+
{
|
|
5404
|
+
timeout,
|
|
5405
|
+
killSignal: "SIGKILL",
|
|
5406
|
+
maxBuffer: 64 * 1024,
|
|
5407
|
+
env: {
|
|
5408
|
+
...Object.fromEntries(
|
|
5409
|
+
SESSION_ENV.flatMap((k) => env[k] ? [[k, env[k]]] : [])
|
|
5410
|
+
),
|
|
5411
|
+
NO_AT_BRIDGE: "0"
|
|
5412
|
+
}
|
|
5413
|
+
},
|
|
5414
|
+
(error, stdout) => resolve10({ failed: error !== null, stdout: String(stdout) })
|
|
5415
|
+
);
|
|
5416
|
+
child.stdin?.on("error", () => {
|
|
5417
|
+
});
|
|
5418
|
+
child.stdin?.end(JSON.stringify(payload));
|
|
5419
|
+
}
|
|
5420
|
+
);
|
|
5421
|
+
const record = receipt(result.stdout);
|
|
5422
|
+
const expected = mode === "probe" ? "ready" : mode === "submit" ? "submitted" : "hitl_checked";
|
|
5423
|
+
if (result.failed || record?.status !== expected || record.action_count > (options.maxActions ?? 24))
|
|
5424
|
+
throw new CoworkDriverError(
|
|
5425
|
+
`Linux desktop ${mode} failed or its receipt was uncertain (${record?.errorCode ?? "missing_or_invalid_receipt"}); no retry attempted.`,
|
|
5426
|
+
record ? telemetry(started, record.action_count, "partial") : void 0
|
|
5427
|
+
);
|
|
5428
|
+
return record;
|
|
5429
|
+
}
|
|
5430
|
+
var SESSION_ENV, linuxCoworkPlatform;
|
|
5431
|
+
var init_linux = __esm({
|
|
5432
|
+
"src/evals/cowork/linux.ts"() {
|
|
5433
|
+
init_esm_shims();
|
|
5434
|
+
init_driver();
|
|
5435
|
+
SESSION_ENV = [
|
|
5436
|
+
"PATH",
|
|
5437
|
+
"HOME",
|
|
5438
|
+
"DISPLAY",
|
|
5439
|
+
"XAUTHORITY",
|
|
5440
|
+
"DBUS_SESSION_BUS_ADDRESS",
|
|
5441
|
+
"AT_SPI_BUS_ADDRESS",
|
|
5442
|
+
"XDG_RUNTIME_DIR",
|
|
5443
|
+
"XDG_CONFIG_HOME",
|
|
5444
|
+
"MST_COWORK_URL_OPENER",
|
|
5445
|
+
"LANG",
|
|
5446
|
+
"LC_ALL"
|
|
5447
|
+
];
|
|
5448
|
+
linuxCoworkPlatform = {
|
|
5449
|
+
dataDirectory: (options) => options.dataDir ?? join(
|
|
5450
|
+
process.env.XDG_CONFIG_HOME ?? join(homedir(), ".config"),
|
|
5451
|
+
"Claude-3p",
|
|
5452
|
+
"local-agent-mode-sessions"
|
|
5453
|
+
),
|
|
5454
|
+
async prepare({ manifest, env, model }) {
|
|
5455
|
+
const settingsFile = env.MST_COWORK_SETTINGS_FILE ?? "/etc/claude-desktop/managed-settings.json";
|
|
5456
|
+
if (!isAbsolute(settingsFile))
|
|
5457
|
+
throw new Error("Linux Cowork settings path must be absolute.");
|
|
5458
|
+
try {
|
|
5459
|
+
const settings = await readSettings(settingsFile);
|
|
5460
|
+
const models = settings.inferenceModels;
|
|
5461
|
+
if (model && !models?.some((m) => m.name === model))
|
|
5462
|
+
throw new Error("model");
|
|
5463
|
+
const expected = manifest.servers ?? [];
|
|
5464
|
+
const actual = settings.managedMcpServers;
|
|
5465
|
+
if (!Array.isArray(actual) || actual.length !== expected.length || settings.allowManagedMcpServersOnly !== true)
|
|
5466
|
+
throw new Error("servers");
|
|
5467
|
+
for (const [index, server] of expected.entries()) {
|
|
5468
|
+
if (server.transport !== "http") throw new Error("transport");
|
|
5469
|
+
const observed = actual.find(
|
|
5470
|
+
(s) => s.name === (server.label ?? `server-${index + 1}`)
|
|
5471
|
+
);
|
|
5472
|
+
if (!observed || observed.transport !== "http" || observed.url !== server.serverUrl)
|
|
5473
|
+
throw new Error("server");
|
|
5474
|
+
if (observed.toolPolicy?.["*"] === "allow" && manifest.coworkSetup?.approveWriteTools !== true)
|
|
5475
|
+
throw new Error("policy");
|
|
5476
|
+
}
|
|
5477
|
+
} catch {
|
|
5478
|
+
throw new Error(
|
|
5479
|
+
"Prepared Linux desktop settings do not match the eval model, MCP servers, or approval policy."
|
|
5480
|
+
);
|
|
5481
|
+
}
|
|
5482
|
+
await execute(
|
|
5483
|
+
"probe",
|
|
5484
|
+
{},
|
|
5485
|
+
{ deadlineAt: Date.now() + 15e3, maxActions: 1, env }
|
|
5486
|
+
);
|
|
5487
|
+
return {
|
|
5488
|
+
async dispose() {
|
|
5489
|
+
}
|
|
5490
|
+
};
|
|
5491
|
+
},
|
|
5492
|
+
async recover() {
|
|
5493
|
+
throw new Error(
|
|
5494
|
+
"Linux desktop recovery belongs to the runtime owner, not the MST driver."
|
|
5495
|
+
);
|
|
5496
|
+
},
|
|
5497
|
+
async submit(query2, options) {
|
|
5498
|
+
const started = Date.now();
|
|
5499
|
+
const result = await execute("submit", { prompt: query2 }, options);
|
|
5500
|
+
return {
|
|
5501
|
+
status: "submitted",
|
|
5502
|
+
action_count: result.action_count,
|
|
5503
|
+
telemetry: telemetry(started, result.action_count, "complete")
|
|
5504
|
+
};
|
|
5505
|
+
},
|
|
5506
|
+
async handleHitl(options) {
|
|
5507
|
+
if (!options.isComplete)
|
|
5508
|
+
throw new Error("Linux HITL requires a bound native completion check.");
|
|
5509
|
+
const started = Date.now();
|
|
5510
|
+
let actions = 0;
|
|
5511
|
+
const budget = options.maxActions ?? 12;
|
|
5512
|
+
try {
|
|
5513
|
+
while (Date.now() < options.deadlineAt) {
|
|
5514
|
+
if (await options.isComplete())
|
|
5515
|
+
return {
|
|
5516
|
+
status: "hitl_checked",
|
|
5517
|
+
action_count: actions,
|
|
5518
|
+
telemetry: telemetry(started, actions, "complete")
|
|
5519
|
+
};
|
|
5520
|
+
if (actions >= budget)
|
|
5521
|
+
throw new CoworkHitlBudgetError(
|
|
5522
|
+
"Linux HITL action budget exhausted.",
|
|
5523
|
+
telemetry(started, actions, "partial")
|
|
5524
|
+
);
|
|
5525
|
+
const result = await execute(
|
|
5526
|
+
"hitl",
|
|
5527
|
+
{ approveWriteTools: options.approveWriteTools === true },
|
|
5528
|
+
{ ...options, maxActions: budget - actions }
|
|
5529
|
+
);
|
|
5530
|
+
actions += result.action_count;
|
|
5531
|
+
await setTimeout$1(
|
|
5532
|
+
Math.min(500, Math.max(0, options.deadlineAt - Date.now()))
|
|
5533
|
+
);
|
|
5534
|
+
}
|
|
5535
|
+
} catch (error) {
|
|
5536
|
+
if (error instanceof CoworkHitlBudgetError) throw error;
|
|
5537
|
+
const partialActions = error instanceof CoworkDriverError ? error.telemetry?.action_count ?? 0 : 0;
|
|
5538
|
+
throw new CoworkDriverError(
|
|
5539
|
+
"Linux HITL failed for the bound native session.",
|
|
5540
|
+
telemetry(started, actions + partialActions, "partial")
|
|
5541
|
+
);
|
|
5542
|
+
}
|
|
5543
|
+
throw new CoworkDriverError(
|
|
5544
|
+
"Linux HITL deadline exceeded.",
|
|
5545
|
+
telemetry(started, actions, "partial")
|
|
5546
|
+
);
|
|
5547
|
+
}
|
|
5548
|
+
};
|
|
5549
|
+
}
|
|
5550
|
+
});
|
|
5551
|
+
|
|
5297
5552
|
// src/cli/index.ts
|
|
5298
5553
|
init_esm_shims();
|
|
5299
5554
|
|
|
@@ -5308,12 +5563,12 @@ init_esm_shims();
|
|
|
5308
5563
|
|
|
5309
5564
|
// src/cli/components/Spinner.tsx
|
|
5310
5565
|
init_esm_shims();
|
|
5311
|
-
function Spinner({ label }) {
|
|
5566
|
+
function Spinner({ label: label2 }) {
|
|
5312
5567
|
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
5313
5568
|
/* @__PURE__ */ jsx(Text, { color: "cyan", children: /* @__PURE__ */ jsx(InkSpinner, { type: "dots" }) }),
|
|
5314
5569
|
/* @__PURE__ */ jsxs(Text, { children: [
|
|
5315
5570
|
" ",
|
|
5316
|
-
|
|
5571
|
+
label2
|
|
5317
5572
|
] })
|
|
5318
5573
|
] });
|
|
5319
5574
|
}
|
|
@@ -5375,7 +5630,7 @@ init_esm_shims();
|
|
|
5375
5630
|
|
|
5376
5631
|
// package.json
|
|
5377
5632
|
var package_default = {
|
|
5378
|
-
version: "2.0.0-beta.
|
|
5633
|
+
version: "2.0.0-beta.4"};
|
|
5379
5634
|
|
|
5380
5635
|
// src/cli/templates/index.ts
|
|
5381
5636
|
function getPlaywrightConfigTemplate(answers) {
|
|
@@ -6107,8 +6362,56 @@ async function performClientCredentialsFlow(config) {
|
|
|
6107
6362
|
};
|
|
6108
6363
|
}
|
|
6109
6364
|
|
|
6365
|
+
// src/mcp/connectionDiagnostics.ts
|
|
6366
|
+
init_esm_shims();
|
|
6367
|
+
function classifyMCPConnectionFailure(error) {
|
|
6368
|
+
if (typeof error !== "object" || error === null) return "connection_failed";
|
|
6369
|
+
const details = error;
|
|
6370
|
+
const status = details.response?.status ?? details.status ?? details.code;
|
|
6371
|
+
if (typeof status === "number" && Number.isInteger(status) && status >= 400 && status <= 599) {
|
|
6372
|
+
return `http_${status}`;
|
|
6373
|
+
}
|
|
6374
|
+
const message = error instanceof Error ? error.message : "";
|
|
6375
|
+
const httpStatus = /\b(?:HTTP|status(?: code)?|code:)\s*[:=(]?\s*([45]\d{2})\b/i.exec(
|
|
6376
|
+
message
|
|
6377
|
+
)?.[1];
|
|
6378
|
+
if (httpStatus) return `http_${httpStatus}`;
|
|
6379
|
+
for (const code of ["ECONNRESET", "ECONNREFUSED", "ETIMEDOUT", "ENOTFOUND"]) {
|
|
6380
|
+
if (details.code === code || message.toUpperCase().includes(code)) {
|
|
6381
|
+
return code.toLowerCase();
|
|
6382
|
+
}
|
|
6383
|
+
}
|
|
6384
|
+
if (/timed out|timeout/i.test(message)) return "timeout";
|
|
6385
|
+
if (/network|socket hang up|fetch failed/i.test(message))
|
|
6386
|
+
return "network_error";
|
|
6387
|
+
return "connection_failed";
|
|
6388
|
+
}
|
|
6389
|
+
var MCPHttpConnectionError = class extends Error {
|
|
6390
|
+
constructor(streamableError, sseError, retryable, retryAfterMs) {
|
|
6391
|
+
const streamableHttpFailure = classifyMCPConnectionFailure(streamableError);
|
|
6392
|
+
const sseFailure = classifyMCPConnectionFailure(sseError);
|
|
6393
|
+
super(
|
|
6394
|
+
`MCP connection failed: streamableHttp=${streamableHttpFailure}; sse=${sseFailure}`
|
|
6395
|
+
);
|
|
6396
|
+
this.retryable = retryable;
|
|
6397
|
+
this.retryAfterMs = retryAfterMs;
|
|
6398
|
+
this.name = "MCPHttpConnectionError";
|
|
6399
|
+
this.streamableHttpFailure = streamableHttpFailure;
|
|
6400
|
+
this.sseFailure = sseFailure;
|
|
6401
|
+
}
|
|
6402
|
+
streamableHttpFailure;
|
|
6403
|
+
sseFailure;
|
|
6404
|
+
};
|
|
6405
|
+
function formatMCPConnectionFailure(error) {
|
|
6406
|
+
if (error instanceof MCPHttpConnectionError) {
|
|
6407
|
+
return `MCP connection failed: streamableHttp=${error.streamableHttpFailure}; sse=${error.sseFailure}`;
|
|
6408
|
+
}
|
|
6409
|
+
return classifyMCPConnectionFailure(error);
|
|
6410
|
+
}
|
|
6411
|
+
|
|
6110
6412
|
// src/mcp/clientFactory.ts
|
|
6111
6413
|
function getRetryAfterDelayMs(err) {
|
|
6414
|
+
if (err instanceof MCPHttpConnectionError) return err.retryAfterMs;
|
|
6112
6415
|
const response = err?.response;
|
|
6113
6416
|
const retryAfter = response?.headers?.get?.("Retry-After");
|
|
6114
6417
|
if (retryAfter) {
|
|
@@ -6127,6 +6430,7 @@ function isTransientNetworkError(err) {
|
|
|
6127
6430
|
return msg.includes("econnreset") || msg.includes("econnrefused") || msg.includes("etimedout") || msg.includes("enotfound") || msg.includes("network") || msg.includes("socket hang up") || msg.includes("fetch failed");
|
|
6128
6431
|
}
|
|
6129
6432
|
function isRetryableError(err) {
|
|
6433
|
+
if (err instanceof MCPHttpConnectionError) return err.retryable;
|
|
6130
6434
|
return isTransientNetworkError(err) || isRateLimitError(err);
|
|
6131
6435
|
}
|
|
6132
6436
|
async function retryWithBackoff(fn, maxAttempts) {
|
|
@@ -6144,7 +6448,7 @@ async function retryWithBackoff(fn, maxAttempts) {
|
|
|
6144
6448
|
attempt + 1,
|
|
6145
6449
|
maxAttempts + 1,
|
|
6146
6450
|
delayMs,
|
|
6147
|
-
err
|
|
6451
|
+
formatMCPConnectionFailure(err)
|
|
6148
6452
|
);
|
|
6149
6453
|
await new Promise((resolve10) => setTimeout(resolve10, delayMs));
|
|
6150
6454
|
} else {
|
|
@@ -6301,15 +6605,24 @@ async function createMCPClientForConfig(config, options) {
|
|
|
6301
6605
|
} catch (err) {
|
|
6302
6606
|
debugHttp(
|
|
6303
6607
|
"streamableHttp failed (%s), falling back to SSE",
|
|
6304
|
-
err
|
|
6608
|
+
formatMCPConnectionFailure(err)
|
|
6305
6609
|
);
|
|
6306
6610
|
debugClient("Streamable HTTP failed, falling back to SSE transport");
|
|
6307
6611
|
debugHttp("Attempting transport: sse");
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6612
|
+
try {
|
|
6613
|
+
const sseTransport = new SSEClientTransport(url, {
|
|
6614
|
+
requestInit,
|
|
6615
|
+
...options?.authProvider ? { authProvider: options.authProvider } : {}
|
|
6616
|
+
});
|
|
6617
|
+
await client.connect(sseTransport, connectOptions);
|
|
6618
|
+
} catch (sseError) {
|
|
6619
|
+
throw new MCPHttpConnectionError(
|
|
6620
|
+
err,
|
|
6621
|
+
sseError,
|
|
6622
|
+
isRetryableError(err) || isRetryableError(sseError),
|
|
6623
|
+
getRetryAfterDelayMs(err) ?? getRetryAfterDelayMs(sseError)
|
|
6624
|
+
);
|
|
6625
|
+
}
|
|
6313
6626
|
debugClient("Connected via SSE");
|
|
6314
6627
|
debugHttp("Connection established via sse");
|
|
6315
6628
|
}
|
|
@@ -7116,8 +7429,8 @@ ${errorText}`
|
|
|
7116
7429
|
return;
|
|
7117
7430
|
}
|
|
7118
7431
|
try {
|
|
7119
|
-
const
|
|
7120
|
-
await
|
|
7432
|
+
const open6 = await import('open');
|
|
7433
|
+
await open6.default(url.toString());
|
|
7121
7434
|
debug2("Opened browser for authentication");
|
|
7122
7435
|
} catch (error) {
|
|
7123
7436
|
debug2("Failed to open browser:", error);
|
|
@@ -10565,14 +10878,97 @@ init_esm_shims();
|
|
|
10565
10878
|
|
|
10566
10879
|
// src/evals/cowork/platform.ts
|
|
10567
10880
|
init_esm_shims();
|
|
10568
|
-
async function getCoworkPlatform() {
|
|
10569
|
-
if (process.platform === "darwin")
|
|
10881
|
+
async function getCoworkPlatform(provider) {
|
|
10882
|
+
if (process.platform === "darwin" && provider === "anthropic-computer-use")
|
|
10570
10883
|
return (await Promise.resolve().then(() => (init_macos(), macos_exports))).macCoworkPlatform;
|
|
10884
|
+
if (process.platform === "linux" && provider === "linux-desktop")
|
|
10885
|
+
return (await Promise.resolve().then(() => (init_linux(), linux_exports))).linuxCoworkPlatform;
|
|
10571
10886
|
throw new Error(
|
|
10572
|
-
`Cowork
|
|
10887
|
+
`Cowork driver ${provider} is not supported on ${process.platform}.`
|
|
10573
10888
|
);
|
|
10574
10889
|
}
|
|
10575
10890
|
|
|
10891
|
+
// src/evals/cowork/mcpReadiness.ts
|
|
10892
|
+
init_esm_shims();
|
|
10893
|
+
init_config();
|
|
10894
|
+
var PREFLIGHT_TIMEOUT_MS = 3e4;
|
|
10895
|
+
var CoworkMcpReadinessError = class extends Error {
|
|
10896
|
+
servers;
|
|
10897
|
+
constructor(servers) {
|
|
10898
|
+
super(
|
|
10899
|
+
`Cowork MCP preflight failed; no task was submitted. ${servers.map(
|
|
10900
|
+
(server) => `${server.label}=${server.status}${server.error ? `(${server.error})` : ""}(${server.elapsedMs}ms)`
|
|
10901
|
+
).join(", ")}`
|
|
10902
|
+
);
|
|
10903
|
+
this.name = "CoworkMcpReadinessError";
|
|
10904
|
+
this.servers = servers;
|
|
10905
|
+
}
|
|
10906
|
+
};
|
|
10907
|
+
function label(server, index) {
|
|
10908
|
+
return server.label ?? `server-${index + 1}`;
|
|
10909
|
+
}
|
|
10910
|
+
async function withTimeout(promise, timeoutMs) {
|
|
10911
|
+
let timer;
|
|
10912
|
+
try {
|
|
10913
|
+
return await Promise.race([
|
|
10914
|
+
promise,
|
|
10915
|
+
new Promise((_, reject) => {
|
|
10916
|
+
timer = setTimeout(
|
|
10917
|
+
() => reject(new Error("MCP preflight timed out")),
|
|
10918
|
+
timeoutMs
|
|
10919
|
+
);
|
|
10920
|
+
})
|
|
10921
|
+
]);
|
|
10922
|
+
} finally {
|
|
10923
|
+
if (timer) clearTimeout(timer);
|
|
10924
|
+
}
|
|
10925
|
+
}
|
|
10926
|
+
function resolveServer(server, env) {
|
|
10927
|
+
if (!isHttpConfig(server)) return server;
|
|
10928
|
+
const [coworkServer] = toCoworkServers([server]);
|
|
10929
|
+
if (!coworkServer) throw new Error("Invalid Cowork MCP configuration.");
|
|
10930
|
+
const headers = resolveCoworkMcpHeaders([coworkServer], env);
|
|
10931
|
+
return {
|
|
10932
|
+
...server,
|
|
10933
|
+
// Use the same validated runtime headers as Cowork setup.
|
|
10934
|
+
auth: void 0,
|
|
10935
|
+
headers: headers[coworkServer.label]
|
|
10936
|
+
};
|
|
10937
|
+
}
|
|
10938
|
+
async function verifyCoworkMcpServers(servers, env) {
|
|
10939
|
+
const results = await Promise.all(
|
|
10940
|
+
servers.map(async (server, index) => {
|
|
10941
|
+
const started = Date.now();
|
|
10942
|
+
let client;
|
|
10943
|
+
try {
|
|
10944
|
+
client = await createMCPClientForConfig(resolveServer(server, env));
|
|
10945
|
+
const tools = await withTimeout(
|
|
10946
|
+
client.listTools(),
|
|
10947
|
+
PREFLIGHT_TIMEOUT_MS
|
|
10948
|
+
);
|
|
10949
|
+
return {
|
|
10950
|
+
label: label(server, index),
|
|
10951
|
+
status: "connected",
|
|
10952
|
+
toolCount: tools.tools.length,
|
|
10953
|
+
elapsedMs: Date.now() - started
|
|
10954
|
+
};
|
|
10955
|
+
} catch (error) {
|
|
10956
|
+
return {
|
|
10957
|
+
label: label(server, index),
|
|
10958
|
+
status: "failed",
|
|
10959
|
+
elapsedMs: Date.now() - started,
|
|
10960
|
+
error: formatMCPConnectionFailure(error)
|
|
10961
|
+
};
|
|
10962
|
+
} finally {
|
|
10963
|
+
if (client) await closeMCPClient(client).catch(() => void 0);
|
|
10964
|
+
}
|
|
10965
|
+
})
|
|
10966
|
+
);
|
|
10967
|
+
if (results.some((result) => result.status !== "connected"))
|
|
10968
|
+
throw new CoworkMcpReadinessError(results);
|
|
10969
|
+
return results;
|
|
10970
|
+
}
|
|
10971
|
+
|
|
10576
10972
|
// src/evals/externalHost/builtins/anthropicClaude.ts
|
|
10577
10973
|
init_esm_shims();
|
|
10578
10974
|
|
|
@@ -11155,7 +11551,7 @@ async function waitForClaudeMatch(options, requireCompletion) {
|
|
|
11155
11551
|
}
|
|
11156
11552
|
lastPending = trace;
|
|
11157
11553
|
}
|
|
11158
|
-
await
|
|
11554
|
+
await delay3(POLL_INTERVAL_MS);
|
|
11159
11555
|
}
|
|
11160
11556
|
if (lastPending) {
|
|
11161
11557
|
throw new Error(
|
|
@@ -11318,7 +11714,7 @@ async function waitForAccessibilityTrace(options) {
|
|
|
11318
11714
|
if (fallback) {
|
|
11319
11715
|
return fallback;
|
|
11320
11716
|
}
|
|
11321
|
-
await
|
|
11717
|
+
await delay3(POLL_INTERVAL_MS);
|
|
11322
11718
|
}
|
|
11323
11719
|
throw new Error(
|
|
11324
11720
|
`Timed out waiting for Claude Chat Desktop visible response for marker ${options.context.marker}`
|
|
@@ -11407,6 +11803,7 @@ async function parseClaudeTrace(candidate, marker16) {
|
|
|
11407
11803
|
auditParsed,
|
|
11408
11804
|
transcriptParsed,
|
|
11409
11805
|
usageAvailable: usage !== void 0,
|
|
11806
|
+
knownUsageFields: knownUsageFields(resultEvents),
|
|
11410
11807
|
costAvailable: resultEvents.some(
|
|
11411
11808
|
(event) => typeof event.total_cost_usd === "number"
|
|
11412
11809
|
),
|
|
@@ -11918,6 +12315,35 @@ function extractAggregatedUsage(events) {
|
|
|
11918
12315
|
cacheCreationInputTokens: (total.cacheCreationInputTokens ?? 0) + (value.cacheCreationInputTokens ?? 0)
|
|
11919
12316
|
}));
|
|
11920
12317
|
}
|
|
12318
|
+
function knownUsageFields(events) {
|
|
12319
|
+
const fields = [
|
|
12320
|
+
[
|
|
12321
|
+
"inputTokens",
|
|
12322
|
+
(event) => event.usage?.input_tokens ?? event.usage?.inputTokens
|
|
12323
|
+
],
|
|
12324
|
+
[
|
|
12325
|
+
"outputTokens",
|
|
12326
|
+
(event) => event.usage?.output_tokens ?? event.usage?.outputTokens
|
|
12327
|
+
],
|
|
12328
|
+
[
|
|
12329
|
+
"cacheReadInputTokens",
|
|
12330
|
+
(event) => event.usage?.cache_read_input_tokens ?? event.usage?.cacheReadInputTokens
|
|
12331
|
+
],
|
|
12332
|
+
[
|
|
12333
|
+
"cacheCreationInputTokens",
|
|
12334
|
+
(event) => event.usage?.cache_creation_input_tokens ?? event.usage?.cacheCreationInputTokens
|
|
12335
|
+
],
|
|
12336
|
+
["totalCostUsd", (event) => event.total_cost_usd],
|
|
12337
|
+
["durationMs", (event) => event.duration_ms],
|
|
12338
|
+
["durationApiMs", (event) => event.duration_api_ms]
|
|
12339
|
+
];
|
|
12340
|
+
return fields.filter(
|
|
12341
|
+
([key, value]) => events.length > 0 && events.every((event) => {
|
|
12342
|
+
const number = value(event);
|
|
12343
|
+
return typeof number === "number" && Number.isFinite(number) && number >= 0 && (key === "totalCostUsd" || Number.isSafeInteger(number));
|
|
12344
|
+
})
|
|
12345
|
+
).map(([key]) => key);
|
|
12346
|
+
}
|
|
11921
12347
|
function extractUsage(event) {
|
|
11922
12348
|
const usage = event.usage;
|
|
11923
12349
|
const inputTokens = getNumber(usage, "input_tokens") ?? getNumber(usage, "inputTokens");
|
|
@@ -12014,19 +12440,28 @@ function classifyTraceFailure(message) {
|
|
|
12014
12440
|
function formatError2(err) {
|
|
12015
12441
|
return err instanceof Error ? err.message : String(err);
|
|
12016
12442
|
}
|
|
12017
|
-
function
|
|
12443
|
+
function delay3(ms) {
|
|
12018
12444
|
return new Promise((resolve10) => setTimeout(resolve10, ms));
|
|
12019
12445
|
}
|
|
12020
12446
|
|
|
12021
12447
|
// src/evals/coworkHost.ts
|
|
12022
|
-
|
|
12448
|
+
init_driver();
|
|
12023
12449
|
var OptionsSchema = z.object({
|
|
12024
|
-
computerUseProvider: z.
|
|
12450
|
+
computerUseProvider: z.enum(["anthropic-computer-use", "linux-desktop"]).default(
|
|
12451
|
+
() => process.platform === "linux" ? "linux-desktop" : "anthropic-computer-use"
|
|
12452
|
+
),
|
|
12025
12453
|
computerUseMaxActions: z.number().int().min(1).max(64).default(24),
|
|
12026
12454
|
hitlMaxActions: z.number().int().min(1).max(24).default(12),
|
|
12027
12455
|
computerUseModel: z.string().regex(/^[A-Za-z0-9._:-]+$/).optional(),
|
|
12028
12456
|
dataDir: z.string().min(1).optional()
|
|
12029
|
-
}).strict()
|
|
12457
|
+
}).strict().superRefine((options, context) => {
|
|
12458
|
+
if (options.computerUseProvider === "linux-desktop" && options.computerUseModel !== void 0)
|
|
12459
|
+
context.addIssue({
|
|
12460
|
+
code: "custom",
|
|
12461
|
+
path: ["computerUseModel"],
|
|
12462
|
+
message: "linux-desktop does not use a planner model."
|
|
12463
|
+
});
|
|
12464
|
+
});
|
|
12030
12465
|
var CoworkSchema = z.object({
|
|
12031
12466
|
type: z.string(),
|
|
12032
12467
|
options: OptionsSchema.default(() => OptionsSchema.parse({})),
|
|
@@ -12052,9 +12487,9 @@ async function runBatch(requests, context, selectedPlatform) {
|
|
|
12052
12487
|
);
|
|
12053
12488
|
const config = configs[0];
|
|
12054
12489
|
const env = { ...process.env, ...context.env, ...config.env };
|
|
12055
|
-
if (!env.ANTHROPIC_API_KEY)
|
|
12490
|
+
if (config.options.computerUseProvider === "anthropic-computer-use" && !env.ANTHROPIC_API_KEY)
|
|
12056
12491
|
throw new Error("ANTHROPIC_API_KEY is required for Cowork Computer Use.");
|
|
12057
|
-
const platform = await getCoworkPlatform();
|
|
12492
|
+
const platform = await getCoworkPlatform(config.options.computerUseProvider);
|
|
12058
12493
|
const dataDir = platform.dataDirectory(config.options);
|
|
12059
12494
|
const servers = requests[0].input.servers;
|
|
12060
12495
|
const { arms: _arms, ...manifest } = context.manifest;
|
|
@@ -12085,12 +12520,20 @@ async function runBatch(requests, context, selectedPlatform) {
|
|
|
12085
12520
|
active = true;
|
|
12086
12521
|
try {
|
|
12087
12522
|
if (env.MST_COWORK_RECOVER === "1") await platform.recover();
|
|
12088
|
-
if (servers.length || config.model)
|
|
12523
|
+
if (config.options.computerUseProvider === "linux-desktop" || servers.length || config.model)
|
|
12089
12524
|
session = await platform.prepare({
|
|
12090
12525
|
manifest: managedManifest,
|
|
12091
12526
|
env,
|
|
12092
12527
|
model: config.model
|
|
12093
12528
|
});
|
|
12529
|
+
if (servers.length) {
|
|
12530
|
+
const readiness = await verifyCoworkMcpServers(servers, env);
|
|
12531
|
+
process.stderr.write(
|
|
12532
|
+
`[mst:cowork] MCP preflight ready: ${readiness.map(
|
|
12533
|
+
(server) => `${server.label}(${server.toolCount ?? 0} tools, ${server.elapsedMs}ms)`
|
|
12534
|
+
).join(", ")}\\n`
|
|
12535
|
+
);
|
|
12536
|
+
}
|
|
12094
12537
|
for (const [index, request] of requests.entries()) {
|
|
12095
12538
|
const caseStartedAt = Date.now();
|
|
12096
12539
|
const computerUse = {
|
|
@@ -12145,7 +12588,7 @@ async function runBatch(requests, context, selectedPlatform) {
|
|
|
12145
12588
|
if (computerUse.submission.status !== "completed") {
|
|
12146
12589
|
computerUse.submission = {
|
|
12147
12590
|
status: "failed",
|
|
12148
|
-
...error instanceof
|
|
12591
|
+
...error instanceof CoworkDriverError && error.telemetry ? { telemetry: error.telemetry } : {}
|
|
12149
12592
|
};
|
|
12150
12593
|
}
|
|
12151
12594
|
results[index] = failure(safeError(error));
|
|
@@ -12184,6 +12627,18 @@ async function runBatch(requests, context, selectedPlatform) {
|
|
|
12184
12627
|
maxActions: config.options.hitlMaxActions,
|
|
12185
12628
|
model: config.options.computerUseModel,
|
|
12186
12629
|
env,
|
|
12630
|
+
approveWriteTools: context.manifest.coworkSetup?.approveWriteTools === true,
|
|
12631
|
+
isComplete: async () => {
|
|
12632
|
+
const current = await findMatchingClaudeSessions({
|
|
12633
|
+
...match,
|
|
12634
|
+
sessionPath
|
|
12635
|
+
});
|
|
12636
|
+
if (current.length !== 1)
|
|
12637
|
+
throw new Error(
|
|
12638
|
+
"Bound native session is missing or ambiguous."
|
|
12639
|
+
);
|
|
12640
|
+
return current[0].isComplete;
|
|
12641
|
+
},
|
|
12187
12642
|
task: `Handle only the currently open Cowork task just submitted with this exact query: ${request.input.scenario}. Do not switch tasks. Never create, type, or resubmit a task. If the current task cannot be identified uniquely, stop without an action.`
|
|
12188
12643
|
});
|
|
12189
12644
|
computerUse.hitl = {
|
|
@@ -12193,11 +12648,11 @@ async function runBatch(requests, context, selectedPlatform) {
|
|
|
12193
12648
|
}
|
|
12194
12649
|
} catch (error) {
|
|
12195
12650
|
computerUse.hitl = {
|
|
12196
|
-
status: error instanceof
|
|
12197
|
-
...error instanceof
|
|
12651
|
+
status: error instanceof CoworkDriverError && error.kind === "hitl-budget-exhausted" ? "budget-exhausted" : "failed",
|
|
12652
|
+
...error instanceof CoworkDriverError && error.telemetry ? { telemetry: error.telemetry } : {}
|
|
12198
12653
|
};
|
|
12199
12654
|
const message = safeError(error);
|
|
12200
|
-
if (error instanceof
|
|
12655
|
+
if (error instanceof CoworkDriverError && error.kind === "hitl-budget-exhausted") {
|
|
12201
12656
|
hitlWarning = message;
|
|
12202
12657
|
} else {
|
|
12203
12658
|
hitlError = message;
|
|
@@ -12350,7 +12805,7 @@ async function runBuiltinHost(input, host, context, factory) {
|
|
|
12350
12805
|
}
|
|
12351
12806
|
controller.signal.throwIfAborted();
|
|
12352
12807
|
}
|
|
12353
|
-
async function
|
|
12808
|
+
async function execute2() {
|
|
12354
12809
|
try {
|
|
12355
12810
|
checkDeadline();
|
|
12356
12811
|
if (config.hostType !== "cli") {
|
|
@@ -12428,9 +12883,9 @@ async function runBuiltinHost(input, host, context, factory) {
|
|
|
12428
12883
|
if (config.hostType === "cli" && config.cli?.claudeMcpServers !== void 0) {
|
|
12429
12884
|
void expired.catch(() => {
|
|
12430
12885
|
});
|
|
12431
|
-
return await
|
|
12886
|
+
return await execute2();
|
|
12432
12887
|
}
|
|
12433
|
-
const result = await Promise.race([
|
|
12888
|
+
const result = await Promise.race([execute2(), expired]);
|
|
12434
12889
|
checkDeadline();
|
|
12435
12890
|
return result;
|
|
12436
12891
|
} catch (error) {
|
|
@@ -12580,8 +13035,8 @@ function claudeCliHost(options) {
|
|
|
12580
13035
|
if (options.servers) {
|
|
12581
13036
|
for (const key of Object.keys(mcpServers)) delete mcpServers[key];
|
|
12582
13037
|
for (const entry of options.servers) {
|
|
12583
|
-
const
|
|
12584
|
-
mcpServers[
|
|
13038
|
+
const label2 = entry.label ?? "mcp-server";
|
|
13039
|
+
mcpServers[label2] = entry.transport === "http" ? {
|
|
12585
13040
|
type: "http",
|
|
12586
13041
|
url: entry.serverUrl,
|
|
12587
13042
|
headers: {
|
|
@@ -16012,9 +16467,9 @@ function buildArmDeltas(arms) {
|
|
|
16012
16467
|
);
|
|
16013
16468
|
}
|
|
16014
16469
|
function redactServerForReport(server) {
|
|
16015
|
-
const
|
|
16470
|
+
const label2 = server.label ? { label: server.label } : {};
|
|
16016
16471
|
if (server.transport === "stdio") {
|
|
16017
|
-
return { transport: "stdio", command: server.command, ...
|
|
16472
|
+
return { transport: "stdio", command: server.command, ...label2 };
|
|
16018
16473
|
}
|
|
16019
16474
|
const url = new URL(server.serverUrl);
|
|
16020
16475
|
url.username = "";
|
|
@@ -16024,7 +16479,7 @@ function redactServerForReport(server) {
|
|
|
16024
16479
|
return {
|
|
16025
16480
|
transport: "http",
|
|
16026
16481
|
serverUrl: url.toString(),
|
|
16027
|
-
...
|
|
16482
|
+
...label2,
|
|
16028
16483
|
...server.auth?.accessTokenEnv ? { auth: { accessTokenEnv: server.auth.accessTokenEnv } } : {}
|
|
16029
16484
|
};
|
|
16030
16485
|
}
|
|
@@ -16325,7 +16780,7 @@ async function runEvalSuite(options) {
|
|
|
16325
16780
|
for (const arm of armResults) {
|
|
16326
16781
|
totalHostUsage = sumUsage(totalHostUsage, arm.result?.totalHostUsage);
|
|
16327
16782
|
}
|
|
16328
|
-
const
|
|
16783
|
+
const telemetry2 = {
|
|
16329
16784
|
cases: allResults.length,
|
|
16330
16785
|
toolCalls: countToolCalls(allResults),
|
|
16331
16786
|
failedCases: allResults.filter((result) => !result.pass).length,
|
|
@@ -16345,7 +16800,7 @@ async function runEvalSuite(options) {
|
|
|
16345
16800
|
passRate: allResults.length > 0 ? allResults.filter((result) => result.pass).length / allResults.length : 0,
|
|
16346
16801
|
...computedMetrics
|
|
16347
16802
|
},
|
|
16348
|
-
telemetry,
|
|
16803
|
+
telemetry: telemetry2,
|
|
16349
16804
|
armDeltas: buildArmDeltas(armResults),
|
|
16350
16805
|
results: allResults
|
|
16351
16806
|
};
|