- Harness: providers, model catalog synced hourly, automatic free picks, cooldowns, tool loop with context trimming, MCP computer tools - Bots queue runs replies and tasks through the harness; jobs wait (attempt refunded) while every model is resting - Envs: Settings > Environment, scoped general/workspace/computer, written to computers and redacted from tool output - Models: Settings > Models, per-harness provider select defaulting to OpenRouter, and a models modal on each bot's chat (the coordinator's edits the defaults) - Chat: thinking row while a reply runs, file attachments (upload, paste, drop) that replies read as images, audio and document text - Fix media uploads failing after a user's first file, and private files being served from the public media path Co-Authored-By: Claude Opus 5.5 <[email protected]>
67 lines
2.1 KiB
TypeScript
67 lines
2.1 KiB
TypeScript
import type { HarnessTool } from "../../types";
|
|
|
|
type Params = {
|
|
tool: HarnessTool;
|
|
/**
|
|
* Absolute paths of the env files on the computer.
|
|
*/
|
|
shell_path: string;
|
|
json_path: string;
|
|
};
|
|
|
|
/**
|
|
* Makes a computer tool run with the computer's envs. The sandbox's shells
|
|
* don't read a profile, so bash commands source the env file first and
|
|
* Python or JavaScript code loads the JSON one. The model never sees the
|
|
* addition; other tools pass through.
|
|
*/
|
|
export default function withComputerEnv({
|
|
tool,
|
|
shell_path,
|
|
json_path,
|
|
}: Params): HarnessTool {
|
|
const shell_prefix = `[ -f ${shell_path} ] && . ${shell_path}; `;
|
|
|
|
if (tool.name == "sandbox_execute_bash") {
|
|
return {
|
|
...tool,
|
|
run: ({ args, signal }) =>
|
|
tool.run({
|
|
args: typeof args?.cmd == "string" ? { ...args, cmd: `${shell_prefix}${args.cmd}` } : args,
|
|
signal,
|
|
}),
|
|
};
|
|
}
|
|
|
|
if (tool.name == "sandbox_execute_code") {
|
|
return {
|
|
...tool,
|
|
run: ({ args, signal }) =>
|
|
tool.run({
|
|
args:
|
|
typeof args?.code == "string"
|
|
? { ...args, code: `${codePrefix({ language: args.language, json_path })}${args.code}` }
|
|
: args,
|
|
signal,
|
|
}),
|
|
};
|
|
}
|
|
|
|
return tool;
|
|
}
|
|
|
|
function codePrefix({ language, json_path }: { language?: unknown; json_path: string }) {
|
|
const path = JSON.stringify(json_path);
|
|
const lang = typeof language == "string" ? language.toLowerCase() : "python";
|
|
|
|
if (lang.startsWith("py")) {
|
|
return `import json as _cobble_json, os as _cobble_os; _cobble_os.environ.update(_cobble_json.load(open(${path})) if _cobble_os.path.exists(${path}) else {})\n`;
|
|
}
|
|
|
|
if (lang.startsWith("js") || lang.startsWith("javascript") || lang.startsWith("node")) {
|
|
return `try { Object.assign(process.env, JSON.parse(require("fs").readFileSync(${path}, "utf8"))); } catch {}\n`;
|
|
}
|
|
|
|
return "";
|
|
}
|