openmausbot 0.1.94 → 0.1.95
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/assets/{DesktopViewer-bxM7NV3N.js → DesktopViewer-BwWpc4RK.js} +1 -1
- package/dist/assets/{abnfDiagram-O67JEVCF-BhMdJhRJ.js → abnfDiagram-O67JEVCF-jsQkIqTn.js} +1 -1
- package/dist/assets/{arc-B5itKDcx.js → arc-7kei4Hsu.js} +1 -1
- package/dist/assets/{architectureDiagram-NJMV4G6O-C5d7UY6U.js → architectureDiagram-NJMV4G6O-CRWDFDMq.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-BrZIhMmu.js → blockDiagram-BEXU5L5S-CTKW_IKs.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-7I0ArfbD.js → c4Diagram-YGBWAQC7-BBTW7-6M.js} +1 -1
- package/dist/assets/channel-DS2NbT5x.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-BnEsyN36.js → chunk-2Q5K7J3B-M_Ox-MkD.js} +1 -1
- package/dist/assets/{chunk-3FUC2YCW-BXATQ52s.js → chunk-3FUC2YCW-D2YiN-Ud.js} +2 -2
- package/dist/assets/{chunk-5VM5RSS4-B0BpTkEx.js → chunk-5VM5RSS4-BmVYJ0f8.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-BtLTz6FR.js → chunk-GTNCS2PH-BtiLVVnZ.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-D6gcZOYt.js → chunk-GWA4HPMP-DFYSb6J6.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-DCLrosvf.js → chunk-JWPE2WC7-mknKBzgV.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-B9JsknV9.js → chunk-NETBCI7D-DgSYGiOu.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-CsoTxw7z.js → chunk-WEXAMYUT-C7ZvuguH.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-0F_frB0c.js → chunk-XXDRQBXY-CPqxINpA.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-DkrpnflX.js → classDiagram-v2-NBCMYWYE-Cd6xfJaC.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-BNDwYBt9.js → cose-bilkent-JH36ORCC-BSXY5qRh.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-JUkNr1Mk.js → cynefin-OW5HDTMX-D_lhEMj1.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-DKeh9N4M.js → cynefinDiagram-VND7K2PF-Da9FrFwk.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-ve-6T_wX.js → dagre-6A5THRUB-T77jMOXe.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-BuJ2d8rT.js → diagram-22UHCM2B-Dkj96X5E.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-BgOs_Ni-.js → diagram-3UASUU5V-CMi9zlD_.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-DMK6VmcP.js → diagram-ATOU4E4O-CXK6YUfS.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-DksASlcY.js → diagram-CDSNMT55-BE7B1u9Q.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-Bgn8I5k6.js → diagram-MLGK6HIB-CCMQphKr.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-zK0jqO8N.js → diagram-MPIPVDR6-iHJ_-tGH.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-vKrbzTwr.js → ebnfDiagram-ZINNZB2B-DONsCWQ7.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-Bzv2mPNf.js → elk-276RUBZZ-CU_WOaXk.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-CvOyYuxu.js → erDiagram-OPXOYQCR-BeYwM04G.js} +1 -1
- package/dist/assets/{flowDiagram-KWPJA3E3-CtbnDbc0.js → flowDiagram-KWPJA3E3-Bdw9DqEl.js} +1 -1
- package/dist/assets/{ganttDiagram-FUAMR5RP-c4dMRYyG.js → ganttDiagram-FUAMR5RP-C9iwq3qm.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-B34uaQho.js → gitGraphDiagram-X574FWY7-Dx-VWzUg.js} +1 -1
- package/dist/assets/{index-CPiqc_DN.js → index-KygHRE3E.js} +1 -1
- package/dist/assets/index-NF7XCHAy.css +1 -0
- package/dist/assets/index-X_5ydXIE.js +610 -0
- package/dist/assets/{infoDiagram-VRGFBTTK-o64ma4CI.js → infoDiagram-VRGFBTTK-BG7uaEd4.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-DV4C9BAm.js → ishikawaDiagram-OU5B5YK6-D72WAXnM.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-Bf0AHk1H.js → journeyDiagram-ZHPQQLJL-DKH1gjYT.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-B1UMSKmE.js → kanban-definition-PNTS6WVX-3upm9JUT.js} +1 -1
- package/dist/assets/{linear-BpKhuyLm.js → linear-CcR4pc0-.js} +1 -1
- package/dist/assets/{mermaid.core-B_BKNCw5.js → mermaid.core-C6yU5FSS.js} +5 -5
- package/dist/assets/{mindmap-definition-NLK3R4M7-DeARruT1.js → mindmap-definition-NLK3R4M7-DOI2xEuv.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-BOQEk_1D.js → pegDiagram-GJSIUBJH-BVWJSgLd.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-EIfoiTZo.js → pieDiagram-5QR66LMP-jtbYezhq.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-GgZwT-fi.js → quadrantDiagram-O4NWA36T-g0u8mtPX.js} +1 -1
- package/dist/assets/{railroadDiagram-XR7U4H2S-D9rDQomj.js → railroadDiagram-XR7U4H2S-k8689Wny.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-DAh1MIeo.js → requirementDiagram-PLB6GJNP-DsJ22PDt.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-CPr6Uz64.js → sankeyDiagram-IPEJSGJF-CneWBRgG.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-BU6VIdcm.js → sequenceDiagram-PO4LG4MO-DWsFCWap.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-qF2HO41r.js → sizeCapture-INFHLROL-Bvf0YE3S.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-DlJ8G4MD.js → stateDiagram-v2-GCMORJYK-Bxb3Iei8.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-DzYE8I54.js → swimlanes-2SLR337P-DLkk7W4A.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-C6bZtBXk.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-B7wzEQ0A.js → timeline-definition-EJHVYXUP-KR9bIHYC.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-BvGgrxIP.js → usecaseDiagram-POWQR4AR-DfsOrj8t.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-CEECdgQg.js → vennDiagram-UO4OBE2U-DRSTzG3h.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-XQHA0P_P.js → wardleyDiagram-VNRHLVJA-DtEdhPgT.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-B5mLcZI2.js → xychartDiagram-PMCCYNJV-B-XhTxjG.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/cloud-home-start.js +2 -1
- package/dist-server/companion/src/routes.js +39 -0
- package/dist-server/container-mcp.js +2 -2
- package/dist-server/drivers/agents-proxy.js +58 -38
- package/dist-server/enterprise/server/index.js +2 -1
- package/dist-server/{browser-proxy.js → harness-mcp-proxy.js} +35 -19
- package/dist-server/index.js +3159 -2313
- package/dist-server/local-computer.js +2 -1
- package/dist-server/openmausbot.js +314 -1336
- package/dist-server/pair-cli.js +314 -1336
- package/dist-server/proxy-paths.js +2 -1
- package/dist-server/server/account-signin.js +0 -2
- package/dist-server/server/activity.js +201 -0
- package/dist-server/server/auto-approve.js +6 -0
- package/dist-server/server/automatic-recovery.js +1 -1
- package/dist-server/server/boat-create-idempotency.js +0 -7
- package/dist-server/server/boat-delete-journal.js +0 -4
- package/dist-server/server/bot-package.js +1 -19
- package/dist-server/server/browser-runtime.js +1 -2
- package/dist-server/server/chief-of-staff.js +2 -3
- package/dist-server/server/cloud-backend.js +0 -4
- package/dist-server/server/cloud-computer-tools.js +110 -0
- package/dist-server/server/cloud-home.js +0 -1
- package/dist-server/server/cloud-lending.js +0 -3
- package/dist-server/server/composio.js +31 -2
- package/dist-server/server/computer-control.js +0 -15
- package/dist-server/server/computer-observation.js +0 -74
- package/dist-server/server/config.js +29 -1
- package/dist-server/server/container-computer.js +7 -19
- package/dist-server/server/contracts.js +9 -0
- package/dist-server/server/control-client.js +4 -10
- package/dist-server/server/drivers/acp/core.js +7 -5
- package/dist-server/server/drivers/agents-call.js +42 -29
- package/dist-server/server/drivers/agents-catalog.js +24 -11
- package/dist-server/server/drivers/agents-proxy.js +8 -30
- package/dist-server/server/drivers/antigravity.js +1 -4
- package/dist-server/server/drivers/boatagent.js +23 -4
- package/dist-server/server/drivers/cerebras.js +3 -3
- package/dist-server/server/drivers/chat-mcp-tools.js +4 -7
- package/dist-server/server/drivers/claude.js +16 -14
- package/dist-server/server/drivers/openai-chat.js +3 -6
- package/dist-server/server/drivers/retry.js +8 -4
- package/dist-server/server/engine-install.js +1 -5
- package/dist-server/server/harness/registry.js +0 -1
- package/dist-server/server/{browser-proxy.js → harness-mcp-proxy.js} +40 -20
- package/dist-server/server/index.js +789 -256
- package/dist-server/server/lending-memory.js +12 -8
- package/dist-server/server/memory-entries.js +38 -8
- package/dist-server/server/memory-organize.js +1 -8
- package/dist-server/server/memory-topics.js +4 -4
- package/dist-server/server/memory-upkeep.js +13 -15
- package/dist-server/server/message-file.js +5 -9
- package/dist-server/server/outbound-counts.js +79 -0
- package/dist-server/server/outbound-requests.js +60 -0
- package/dist-server/server/package-import.js +1 -1
- package/dist-server/server/peer-delivery.js +1 -0
- package/dist-server/server/proxy-paths.js +2 -1
- package/dist-server/server/room-handoffs.js +5 -13
- package/dist-server/server/routine-requests.js +1 -1
- package/dist-server/server/routines.js +5 -1
- package/dist-server/server/setup-mode.js +0 -2
- package/dist-server/server/skill-learn.js +0 -1
- package/dist-server/server/skill-library.js +0 -15
- package/dist-server/server/store.js +9 -1
- package/dist-server/server/surface.js +65 -20
- package/dist-server/server/system-prompt.js +4 -4
- package/dist-server/server/team-memory.js +209 -0
- package/dist-server/server/thread-events.js +2 -2
- package/dist-server/server/vps-computer.js +4 -14
- package/dist-server/server/workspace.js +85 -47
- package/dist-server/shared/activity.js +3 -0
- package/dist-server/shared/bot-avatar.js +8 -0
- package/dist-server/shared/connector-scopes.js +78 -0
- package/dist-server/shared/outbound.js +128 -0
- package/dist-server/shared/tool-surface.js +3 -2
- package/dist-server/vps-container-mcp.js +2 -2
- package/enterprise/server/index.js +2 -1
- package/package.json +1 -1
- package/dist/assets/channel-CdJX6i8k.js +0 -1
- package/dist/assets/index-CPYrGsv1.js +0 -610
- package/dist/assets/index-CzT42GR8.css +0 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-D-U4GtLQ.js +0 -8
- package/dist-server/server/drivers/chat-boat-tools.js +0 -90
- package/dist-server/server/drivers/grokagent.js +0 -4
- package/dist-server/server/openmaus-status-capsule.js +0 -329
|
@@ -7,7 +7,6 @@ import { stripControlPlaneEnv } from "../config.js";
|
|
|
7
7
|
import { augmentedPath } from "../env-path.js";
|
|
8
8
|
import { killCliTree, spawnCli } from "../procs.js";
|
|
9
9
|
import { chatImage } from "./chat-images.js";
|
|
10
|
-
import { ChatBoatClient } from "./chat-boat-tools.js";
|
|
11
10
|
import { mcpStdioServer } from "../mcp-gate-config.js";
|
|
12
11
|
import { allowsTool, canUseMcpServer, parseToolScope } from "../../shared/tool-scope.js";
|
|
13
12
|
/** The transport cannot safely continue this turn. A dispatched operation may
|
|
@@ -238,7 +237,7 @@ const validatorOptions = {
|
|
|
238
237
|
// or describe an open tuple. Ajv still enforces every constraint.
|
|
239
238
|
strictRequired: false, strictTypes: false, strictTuples: false,
|
|
240
239
|
};
|
|
241
|
-
function
|
|
240
|
+
export function compileToolSchema(schema) {
|
|
242
241
|
const dialect = schema.$schema;
|
|
243
242
|
if (dialect !== undefined && dialect !== "http://json-schema.org/draft-07/schema#" && dialect !== "https://json-schema.org/draft/2020-12/schema") {
|
|
244
243
|
throw new Error("MCP tool schema uses an unsupported dialect; use JSON Schema draft-07 or 2020-12");
|
|
@@ -291,8 +290,6 @@ export async function mountChatTools(integrations, signal, computerUse = false,
|
|
|
291
290
|
const scope = parsed.scope;
|
|
292
291
|
const servers = [];
|
|
293
292
|
const eligible = (server) => scope === undefined || canUseMcpServer(scope, server);
|
|
294
|
-
if (computerUse && integrations?.computer && eligible("computer"))
|
|
295
|
-
servers.push(["computer", integrations.computer]);
|
|
296
293
|
if (computerUse && integrations?.localComputer && eligible("computer"))
|
|
297
294
|
servers.push(["computer", integrations.localComputer]);
|
|
298
295
|
if (computerUse && integrations?.browser && eligible("browser"))
|
|
@@ -339,10 +336,10 @@ export async function mountChatTools(integrations, signal, computerUse = false,
|
|
|
339
336
|
throw aborted();
|
|
340
337
|
// Every mounted MCP server can return images when the caller enables
|
|
341
338
|
// image delivery, including custom servers. Text stays bounded below.
|
|
342
|
-
const client =
|
|
339
|
+
const client = new ChatMcpClient(descriptor, computerUse);
|
|
343
340
|
clients.push(client);
|
|
344
341
|
const include = (tool) => scope === undefined || (object(tool) && typeof tool.name === "string" && allowsTool(scope, { kind: "mcp", server: name, name: tool.name }));
|
|
345
|
-
const tools =
|
|
342
|
+
const tools = await client.tools(signal, include);
|
|
346
343
|
return { name, client, builtInBrowser: descriptor === integrations?.browser, tools };
|
|
347
344
|
}));
|
|
348
345
|
for (const mount of mounts) {
|
|
@@ -360,7 +357,7 @@ export async function mountChatTools(integrations, signal, computerUse = false,
|
|
|
360
357
|
throw new Error("MCP tools require an object input schema");
|
|
361
358
|
if (Buffer.byteLength(JSON.stringify(tool.inputSchema)) > SCHEMA_BYTES)
|
|
362
359
|
throw new Error("MCP tool schema exceeds the 64KB limit");
|
|
363
|
-
const schema =
|
|
360
|
+
const schema = compileToolSchema(tool.inputSchema);
|
|
364
361
|
const parameters = { ...tool.inputSchema };
|
|
365
362
|
const constraints = {};
|
|
366
363
|
if (computerUse) {
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
//
|
|
7
7
|
// Integrations become MCP servers on the CLI:
|
|
8
8
|
// - Composio Sessions (connected apps → tools) over streamable HTTP
|
|
9
|
-
// -
|
|
10
|
-
//
|
|
9
|
+
// - every computer (this Mac, a Local VM, a VPS, or a Boat cloud
|
|
10
|
+
// computer through server/harness-mcp-proxy.ts computer) as the one
|
|
11
|
+
// stdio `computer` server in turn.integrations.localComputer
|
|
11
12
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
12
13
|
import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
13
14
|
import { createServer as createNetServer } from "node:net";
|
|
@@ -339,7 +340,7 @@ export function claudeCliUpdate(version, cli) {
|
|
|
339
340
|
const effects = [
|
|
340
341
|
...(missing.includes("--autocompact") ? ["no compaction window picked by OpenMausBot"] : []),
|
|
341
342
|
...(missing.includes("--setting-sources") ? ["bots still see this machine's own Claude Code setup"] : []),
|
|
342
|
-
...(missing.includes("--system-prompt-snapshot") ? ["
|
|
343
|
+
...(missing.includes("--system-prompt-snapshot") ? ["resumed turns cannot refresh stale system prompts"] : []),
|
|
343
344
|
];
|
|
344
345
|
return {
|
|
345
346
|
title: "Update Claude Code for context controls",
|
|
@@ -1034,10 +1035,11 @@ export const ClaudeDriver = {
|
|
|
1034
1035
|
// harness snapshots every instance whenever it describes them — app
|
|
1035
1036
|
// load, the Engines page, and right after `claude update`, which is
|
|
1036
1037
|
// exactly when the answer changes — so a turn normally finds it filled.
|
|
1037
|
-
//
|
|
1038
|
-
//
|
|
1039
|
-
//
|
|
1040
|
-
//
|
|
1038
|
+
// A turn that finds it empty reads the version itself first: the
|
|
1039
|
+
// snapshot-refresh flag every turn passes is newer than the other context
|
|
1040
|
+
// controls, and an unknown flag would reject the turn. If that read
|
|
1041
|
+
// fails, most flags assume a current CLI; the snapshot flag needs a
|
|
1042
|
+
// confirmed version and autocompact a confirmed help listing.
|
|
1041
1043
|
let cliVersion = null;
|
|
1042
1044
|
let cliVersionChecked = false;
|
|
1043
1045
|
// Whether `claude --help` lists --autocompact, read once per CLI version
|
|
@@ -1203,7 +1205,7 @@ export const ClaudeDriver = {
|
|
|
1203
1205
|
args.push("--disallowedTools", config.disallowedTools.join(","));
|
|
1204
1206
|
}
|
|
1205
1207
|
const turnEnvironment = environment();
|
|
1206
|
-
if (
|
|
1208
|
+
if (!cliVersionChecked) {
|
|
1207
1209
|
const version = await readCliVersion(turnEnvironment);
|
|
1208
1210
|
if (version) {
|
|
1209
1211
|
cliVersion = parseClaudeCliVersion(version);
|
|
@@ -1246,12 +1248,12 @@ export const ClaudeDriver = {
|
|
|
1246
1248
|
if (compactWindow && cliHasAutocompact === true) {
|
|
1247
1249
|
args.push("--autocompact", compactWindow);
|
|
1248
1250
|
}
|
|
1249
|
-
//
|
|
1250
|
-
// Claude's recorded system prompt
|
|
1251
|
-
//
|
|
1252
|
-
//
|
|
1253
|
-
//
|
|
1254
|
-
if (
|
|
1251
|
+
// A resumed conversation can still carry an earlier assignment, place
|
|
1252
|
+
// or teammate list in Claude's recorded system prompt, so every turn
|
|
1253
|
+
// refreshes the recorded prompt, on --resume too. Gated by the version
|
|
1254
|
+
// floor like every other flag the CLI may predate: an unknown flag is a
|
|
1255
|
+
// hard argument error, not a graceful degrade.
|
|
1256
|
+
if (cliVersionChecked && claudeCliSupports(cliVersion, "--system-prompt-snapshot")) {
|
|
1255
1257
|
args.push("--system-prompt-snapshot", "off");
|
|
1256
1258
|
}
|
|
1257
1259
|
const turnModel = config.managed ? turn.model : await resolveClaudeTurnModel(turn.model, turnEnvironment);
|
|
@@ -325,7 +325,7 @@ export function createOpenAIChatRuntime(options) {
|
|
|
325
325
|
throw new Error(options.missingKeyError);
|
|
326
326
|
if (active.has(turn.threadId))
|
|
327
327
|
throw new Error("a turn is already running on this thread");
|
|
328
|
-
if (options.computerUse && (turn.images?.length || turn.integrations?.
|
|
328
|
+
if (options.computerUse && (turn.images?.length || turn.integrations?.localComputer || turn.integrations?.browser))
|
|
329
329
|
assertImageTransport(options.apiUrl);
|
|
330
330
|
const turnId = newId();
|
|
331
331
|
const abort = new AbortController();
|
|
@@ -334,7 +334,7 @@ export function createOpenAIChatRuntime(options) {
|
|
|
334
334
|
for (const message of messages)
|
|
335
335
|
retainImages(message.content);
|
|
336
336
|
const model = turn.model || options.models().default;
|
|
337
|
-
const secrets = [options.apiKey
|
|
337
|
+
const secrets = [options.apiKey].filter((value) => Boolean(value));
|
|
338
338
|
for (const integration of Object.values(turn.integrations ?? {})) {
|
|
339
339
|
const entries = object(integration);
|
|
340
340
|
const specs = entries && "command" in entries ? [entries] : Object.values(entries ?? {}).map(object);
|
|
@@ -695,10 +695,7 @@ export function createOpenAIChatRuntime(options) {
|
|
|
695
695
|
adapter: {
|
|
696
696
|
provider: options.driverKind,
|
|
697
697
|
capabilities: { ...(options.computerUse ? { computerMcp: options.tools !== false,
|
|
698
|
-
|
|
699
|
-
// mount the leased Boat descriptor either. The fleet invariant test
|
|
700
|
-
// pins usesCloudComputer === (remoteAgent || cloudComputerMcp).
|
|
701
|
-
usesCloudComputer: options.tools !== false, cloudComputerMcp: options.tools !== false, localComputerMcp: options.tools !== false,
|
|
698
|
+
localComputerMcp: options.tools !== false,
|
|
702
699
|
browserMcp: options.tools !== false, nativeImageInput: true, images: true } : {}),
|
|
703
700
|
sessionModelSwitch: "in-session", customMcp: options.tools !== false, agentsMcp: options.tools !== false, composioMcp: options.tools !== false,
|
|
704
701
|
// The runtime owns the whole tool loop, so it can always take a
|
|
@@ -17,6 +17,10 @@ const TERMINAL_PATTERNS = [
|
|
|
17
17
|
pattern: /\b(?:40[13]|unauthorized|forbidden|invalid api key|missing bearer|authentication required|not logged in|logged out)\b/i,
|
|
18
18
|
reason: "auth",
|
|
19
19
|
},
|
|
20
|
+
// A subscription's usage window is hours away, so its limit is terminal
|
|
21
|
+
// for this engine even when the provider phrases it as a rate limit;
|
|
22
|
+
// checked before the transient 429 pattern for that reason.
|
|
23
|
+
{ pattern: /\busage limit\b|\bhit your (?:usage )?limit\b|\b(?:weekly|daily|monthly|subscription|usage) limit reached\b|\bout of credits\b/i, reason: "quota" },
|
|
20
24
|
{ pattern: /\b402\b|\bquota\b|\bbilling\b|\bsubscription\b/i, reason: "quota" },
|
|
21
25
|
{ pattern: /\bmodel not found\b|\bunknown model\b|\bdoes not exist for model\b|\bunsupported model\b/i, reason: "unknown_model" },
|
|
22
26
|
{ pattern: /\b400\b|\b422\b|\binvalid request\b|\bmalformed\b|\bunexpected status\b/i, reason: "invalid_request" },
|
|
@@ -50,14 +54,14 @@ export function classifyError(err) {
|
|
|
50
54
|
const { exitCode: code } = err;
|
|
51
55
|
if (code !== null && code < 0)
|
|
52
56
|
return { transient: false, reason: "interrupted" };
|
|
53
|
-
for (const { pattern, reason } of TRANSIENT_PATTERNS) {
|
|
54
|
-
if (pattern.test(text))
|
|
55
|
-
return { transient: true, reason };
|
|
56
|
-
}
|
|
57
57
|
for (const { pattern, reason } of TERMINAL_PATTERNS) {
|
|
58
58
|
if (pattern.test(text))
|
|
59
59
|
return { transient: false, reason };
|
|
60
60
|
}
|
|
61
|
+
for (const { pattern, reason } of TRANSIENT_PATTERNS) {
|
|
62
|
+
if (pattern.test(text))
|
|
63
|
+
return { transient: true, reason };
|
|
64
|
+
}
|
|
61
65
|
return { transient: false, reason: "terminal_exit" };
|
|
62
66
|
}
|
|
63
67
|
for (const { pattern, reason } of TERMINAL_PATTERNS) {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// driver's own install descriptor and never from a request, npm runs with
|
|
5
5
|
// a fixed argument list, and the binary is found on the engines' PATH
|
|
6
6
|
// afterwards because that directory is registered ahead of everything else.
|
|
7
|
-
import {
|
|
7
|
+
import { mkdirSync } from "node:fs";
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
import { stripVTControlCharacters } from "node:util";
|
|
10
10
|
import { DATA_DIR, stripWorkspaceCredentialEnv } from "./config.js";
|
|
@@ -169,7 +169,3 @@ function runNpm(args, env, cwd, timeoutMs) {
|
|
|
169
169
|
});
|
|
170
170
|
});
|
|
171
171
|
}
|
|
172
|
-
/** True once something has been installed here, for status pages. */
|
|
173
|
-
export function enginesInstalled(baseDir = DATA_DIR) {
|
|
174
|
-
return existsSync(enginesBinDir(baseDir));
|
|
175
|
-
}
|
|
@@ -228,7 +228,6 @@ export class ProviderRegistry {
|
|
|
228
228
|
modelVariants: inst.adapter.capabilities.modelVariants === true,
|
|
229
229
|
queueing: inst.adapter.capabilities.queueing === true,
|
|
230
230
|
localComputerMcp: inst.adapter.capabilities.localComputerMcp === true,
|
|
231
|
-
cloudComputerMcp: inst.adapter.capabilities.cloudComputerMcp === true,
|
|
232
231
|
approvalReview: inst.reviewPermission !== undefined,
|
|
233
232
|
},
|
|
234
233
|
access: driver?.metadata.access ?? "subscription",
|
|
@@ -1,17 +1,29 @@
|
|
|
1
|
-
// Per-turn MCP entry point
|
|
2
|
-
//
|
|
1
|
+
// Per-turn MCP entry point for the tools the harness serves itself: the
|
|
2
|
+
// built-in browser and the cloud computer. argv[2] names which one. Only a
|
|
3
|
+
// turn-scoped capability crosses into the agent process; engine commands,
|
|
4
|
+
// session names, and provider credentials (the Boat token) stay in OMB.
|
|
3
5
|
import { pathToFileURL } from "node:url";
|
|
4
6
|
const MAX_INPUT_BYTES = 1_048_576;
|
|
5
7
|
const MAX_OUTPUT_BYTES = 16_777_216;
|
|
6
8
|
const MAX_PENDING = 16;
|
|
9
|
+
const NAMES = { browser: "Browser", computer: "Cloud computer" };
|
|
10
|
+
/** What a model needs after a failed call, per tool family: the failure is
|
|
11
|
+
* about this one connection, and an uncertain action must be checked first. */
|
|
12
|
+
const RECOVERY = {
|
|
13
|
+
browser: " This failure does not establish that all browsers are unavailable. Inspect available connections with select_computer if that tool is present. Keep the requested computer and account; do not bypass a permission refusal or human takeover, and do not repeat an uncertain action without checking its result.",
|
|
14
|
+
computer: " Keep working on the assigned cloud computer only; do not bypass a person's control of it, and take a fresh screenshot before repeating an action whose result is uncertain.",
|
|
15
|
+
};
|
|
16
|
+
export function parseHarnessMcpKind(value) {
|
|
17
|
+
return value === "browser" || value === "computer" ? value : null;
|
|
18
|
+
}
|
|
7
19
|
function failure(id, method, message, code = -32603) {
|
|
8
20
|
return method === "tools/call"
|
|
9
21
|
? { jsonrpc: "2.0", id, result: { content: [{ type: "text", text: message }], isError: true } }
|
|
10
22
|
: { jsonrpc: "2.0", id, error: { code, message } };
|
|
11
23
|
}
|
|
12
|
-
async function boundedJson(response) {
|
|
24
|
+
async function boundedJson(response, name) {
|
|
13
25
|
if (!response.body)
|
|
14
|
-
throw new Error(
|
|
26
|
+
throw new Error(`${name} server returned an empty response.`);
|
|
15
27
|
const reader = response.body.getReader();
|
|
16
28
|
const chunks = [];
|
|
17
29
|
let size = 0;
|
|
@@ -22,7 +34,7 @@ async function boundedJson(response) {
|
|
|
22
34
|
break;
|
|
23
35
|
size += value.byteLength;
|
|
24
36
|
if (size > MAX_OUTPUT_BYTES)
|
|
25
|
-
throw new Error(
|
|
37
|
+
throw new Error(`${name} response exceeded the size limit.`);
|
|
26
38
|
chunks.push(Buffer.from(value));
|
|
27
39
|
}
|
|
28
40
|
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
@@ -31,7 +43,7 @@ async function boundedJson(response) {
|
|
|
31
43
|
await reader.cancel().catch(() => { });
|
|
32
44
|
}
|
|
33
45
|
}
|
|
34
|
-
export async function
|
|
46
|
+
export async function harnessMcpRequest(frame, connection) {
|
|
35
47
|
if (!frame || typeof frame !== "object" || Array.isArray(frame))
|
|
36
48
|
return failure(null, null, "Invalid request.", -32600);
|
|
37
49
|
const message = frame;
|
|
@@ -40,10 +52,11 @@ export async function browserProxyRequest(frame, connection) {
|
|
|
40
52
|
return failure(null, null, "Invalid request.", -32600);
|
|
41
53
|
if (!Object.hasOwn(message, "id"))
|
|
42
54
|
return undefined;
|
|
55
|
+
const name = NAMES[connection.kind];
|
|
43
56
|
if (message.method === "initialize")
|
|
44
57
|
return {
|
|
45
58
|
jsonrpc: "2.0", id,
|
|
46
|
-
result: { protocolVersion: "2024-11-05", capabilities: { tools: {} }, serverInfo: { name:
|
|
59
|
+
result: { protocolVersion: "2024-11-05", capabilities: { tools: {} }, serverInfo: { name: `openmausbot-${connection.kind}`, version: "1" } },
|
|
47
60
|
};
|
|
48
61
|
if (message.method === "ping")
|
|
49
62
|
return { jsonrpc: "2.0", id, result: {} };
|
|
@@ -52,30 +65,30 @@ export async function browserProxyRequest(frame, connection) {
|
|
|
52
65
|
try {
|
|
53
66
|
const url = new URL(connection.url);
|
|
54
67
|
if (!connection.token || url.protocol !== "http:" || !["127.0.0.1", "localhost", "[::1]"].includes(url.hostname) || url.username || url.password || url.pathname !== "/" || url.search || url.hash) {
|
|
55
|
-
throw new Error(
|
|
68
|
+
throw new Error(`${name} connection is not configured. Start a new bot turn from OpenMausBot.`);
|
|
56
69
|
}
|
|
57
70
|
const body = JSON.stringify({ method: message.method, params: message.params ?? {} });
|
|
58
71
|
if (Buffer.byteLength(body) > MAX_INPUT_BYTES)
|
|
59
|
-
throw new Error(
|
|
60
|
-
const response = await fetch(new URL(
|
|
72
|
+
throw new Error(`${name} request exceeded the size limit.`);
|
|
73
|
+
const response = await fetch(new URL(`/api/internal/${connection.kind}/mcp`, url), {
|
|
61
74
|
method: "POST", redirect: "error",
|
|
62
75
|
headers: { "content-type": "application/json", authorization: `Bearer ${connection.token}` },
|
|
63
76
|
body, signal: AbortSignal.timeout(130_000),
|
|
64
77
|
});
|
|
65
|
-
const payload = await boundedJson(response);
|
|
78
|
+
const payload = await boundedJson(response, name);
|
|
66
79
|
if (!response.ok || !payload || typeof payload !== "object" || !Object.hasOwn(payload, "result")) {
|
|
67
|
-
throw new Error(typeof payload?.error === "string" ? payload.error.slice(0, 2_000) :
|
|
80
|
+
throw new Error(typeof payload?.error === "string" ? payload.error.slice(0, 2_000) : `${name} server is unavailable. Start a new bot turn or try again.`);
|
|
68
81
|
}
|
|
69
82
|
return { jsonrpc: "2.0", id, result: payload.result };
|
|
70
83
|
}
|
|
71
84
|
catch (error) {
|
|
72
85
|
const detail = error instanceof Error && !/fetch failed|abort|timeout/i.test(error.message)
|
|
73
|
-
? error.message :
|
|
74
|
-
return failure(id, message.method, detail +
|
|
86
|
+
? error.message : `${name} connection was interrupted. Do not repeat the action until its state is checked.`;
|
|
87
|
+
return failure(id, message.method, detail + RECOVERY[connection.kind]);
|
|
75
88
|
}
|
|
76
89
|
}
|
|
77
|
-
function run() {
|
|
78
|
-
const connection = { url: process.env.OMB_HARNESS_URL ?? "", token: process.env.
|
|
90
|
+
function run(kind) {
|
|
91
|
+
const connection = { url: process.env.OMB_HARNESS_URL ?? "", token: process.env.OMB_MCP_TOKEN ?? "", kind };
|
|
79
92
|
let input = Buffer.alloc(0);
|
|
80
93
|
let pending = 0;
|
|
81
94
|
const output = (message) => {
|
|
@@ -114,11 +127,11 @@ function run() {
|
|
|
114
127
|
if (pending >= MAX_PENDING) {
|
|
115
128
|
const request = frame;
|
|
116
129
|
if (request && Object.hasOwn(request, "id"))
|
|
117
|
-
output(failure(request.id ?? null, request.method,
|
|
130
|
+
output(failure(request.id ?? null, request.method, `Too many pending ${kind} requests.`));
|
|
118
131
|
continue;
|
|
119
132
|
}
|
|
120
133
|
pending++;
|
|
121
|
-
void
|
|
134
|
+
void harnessMcpRequest(frame, connection).then(output).finally(() => { pending--; });
|
|
122
135
|
}
|
|
123
136
|
if (input.length > MAX_INPUT_BYTES) {
|
|
124
137
|
process.stdin.destroy();
|
|
@@ -126,5 +139,12 @@ function run() {
|
|
|
126
139
|
}
|
|
127
140
|
});
|
|
128
141
|
}
|
|
129
|
-
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href)
|
|
130
|
-
|
|
142
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
143
|
+
const kind = parseHarnessMcpKind(process.argv[2]);
|
|
144
|
+
if (!kind) {
|
|
145
|
+
process.stderr.write("usage: harness-mcp-proxy browser|computer\n");
|
|
146
|
+
process.exitCode = 2;
|
|
147
|
+
}
|
|
148
|
+
else
|
|
149
|
+
run(kind);
|
|
150
|
+
}
|