@qwen-code/arena-agent 0.0.10-dev.15 → 0.0.10-dev.16
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/.env.example +25 -3
- package/CHANGELOG.md +23 -1
- package/README.md +80 -26
- package/dist/agent.d.ts +5 -1
- package/dist/agent.js +99 -28
- package/dist/agent.js.map +1 -1
- package/dist/arena-history.d.ts +12 -0
- package/dist/arena-history.js +119 -0
- package/dist/arena-history.js.map +1 -0
- package/dist/brave-search.js +7 -4
- package/dist/brave-search.js.map +1 -1
- package/dist/canonical-data.d.ts +7 -0
- package/dist/canonical-data.js +10 -3
- package/dist/canonical-data.js.map +1 -1
- package/dist/cli.js +4 -2
- package/dist/cli.js.map +1 -1
- package/dist/code-cli.js +0 -0
- package/dist/code-tools.js +2 -4
- package/dist/code-tools.js.map +1 -1
- package/dist/compaction.d.ts +2 -0
- package/dist/compaction.js +6 -2
- package/dist/compaction.js.map +1 -1
- package/dist/config.d.ts +3 -1
- package/dist/config.js +4 -2
- package/dist/config.js.map +1 -1
- package/dist/connector-errors.d.ts +16 -0
- package/dist/connector-errors.js +69 -0
- package/dist/connector-errors.js.map +1 -0
- package/dist/connector-files.d.ts +31 -0
- package/dist/connector-files.js +249 -0
- package/dist/connector-files.js.map +1 -0
- package/dist/connector-input.d.ts +10 -0
- package/dist/connector-input.js +79 -0
- package/dist/connector-input.js.map +1 -0
- package/dist/connector-mcp.d.ts +15 -2
- package/dist/connector-mcp.js +201 -41
- package/dist/connector-mcp.js.map +1 -1
- package/dist/connectors.d.ts +7 -2
- package/dist/connectors.js +42 -30
- package/dist/connectors.js.map +1 -1
- package/dist/fetch-page.js +2 -2
- package/dist/fetch-page.js.map +1 -1
- package/dist/history.d.ts +4 -2
- package/dist/history.js +4 -1
- package/dist/history.js.map +1 -1
- package/dist/image-choice.d.ts +23 -0
- package/dist/image-choice.js +190 -0
- package/dist/image-choice.js.map +1 -0
- package/dist/image-model.d.ts +6 -0
- package/dist/image-model.js +32 -0
- package/dist/image-model.js.map +1 -0
- package/dist/model-router-media.js +5 -3
- package/dist/model-router-media.js.map +1 -1
- package/dist/process-manager.js +9 -9
- package/dist/process-manager.js.map +1 -1
- package/dist/prompts.d.ts +0 -1
- package/dist/prompts.js +3 -27
- package/dist/prompts.js.map +1 -1
- package/dist/repo.d.ts +1 -1
- package/dist/repo.js +26 -4
- package/dist/repo.js.map +1 -1
- package/dist/routify-web-fetch.js +2 -2
- package/dist/routify-web-fetch.js.map +1 -1
- package/dist/server.js +24 -12
- package/dist/server.js.map +1 -1
- package/dist/session-memory-prompts.d.ts +2 -1
- package/dist/session-memory-prompts.js +2 -1
- package/dist/session-memory-prompts.js.map +1 -1
- package/dist/spec.d.ts +4 -4
- package/dist/spec.js +25 -12
- package/dist/spec.js.map +1 -1
- package/dist/startup-main-config.d.ts +1 -0
- package/dist/startup-main-config.js +1 -0
- package/dist/startup-main-config.js.map +1 -1
- package/dist/startup-tool-config.js +12 -2
- package/dist/startup-tool-config.js.map +1 -1
- package/dist/tools.d.ts +2 -0
- package/dist/tools.js +133 -49
- package/dist/tools.js.map +1 -1
- package/dist/tui.js +15 -2
- package/dist/tui.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/web/app.js +8 -0
- package/dist/web/app.js.map +1 -1
- package/package.json +1 -1
- package/web/styles.css +22 -0
- package/dist/code-arena-output.d.ts +0 -15
- package/dist/code-arena-output.js +0 -41
- package/dist/code-arena-output.js.map +0 -1
- package/dist/legacy-compaction.d.ts +0 -20
- package/dist/legacy-compaction.js +0 -209
- package/dist/legacy-compaction.js.map +0 -1
- package/dist/web/app.d.ts +0 -1
package/dist/tools.js
CHANGED
|
@@ -2,6 +2,7 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { copyFileSync, existsSync, mkdirSync, realpathSync } from "node:fs";
|
|
3
3
|
import { open, readFile, realpath, stat, writeFile } from "node:fs/promises";
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
5
6
|
import { Ajv } from "ajv";
|
|
6
7
|
import addFormats from "ajv-formats";
|
|
7
8
|
import { detect as detectEncoding } from "chardet";
|
|
@@ -10,6 +11,7 @@ import mime from "mime/lite";
|
|
|
10
11
|
import { AskUserHandlerError } from "./ask-user.js";
|
|
11
12
|
import { executeBraveImageSearch, executeBraveWebSearch, } from "./brave-search.js";
|
|
12
13
|
import { CodeToolExecutor } from "./code-tools.js";
|
|
14
|
+
import { schemaOrderedValue } from "./arena-history.js";
|
|
13
15
|
import { ConnectorTools, connectorFailureText } from "./connector-mcp.js";
|
|
14
16
|
import { supportsImageInput } from "./config.js";
|
|
15
17
|
import { extractStructuredDocument, isStructuredDocument, } from "./document-reader.js";
|
|
@@ -17,6 +19,8 @@ import { FetchPageReader } from "./fetch-page.js";
|
|
|
17
19
|
import { replaceFirstText } from "./file-edit.js";
|
|
18
20
|
import { generatedImageBytesForExtension, prepareImageForModel, } from "./image.js";
|
|
19
21
|
import { imageTextFromFallbackModel } from "./image-input-fallback.js";
|
|
22
|
+
import { offerGeneratedImages } from "./image-choice.js";
|
|
23
|
+
import { arenaImageModel, imageOptionModels } from "./image-model.js";
|
|
20
24
|
import { isCodeArenaMode } from "./mode.js";
|
|
21
25
|
import { generateImageWithModelRouter, generateSpeechWithModelRouter, } from "./model-router-media.js";
|
|
22
26
|
import { processManagerForWorkspace, } from "./process-manager.js";
|
|
@@ -596,6 +600,8 @@ async function responseBytes(response, limit = 4 * 1024 * 1024) {
|
|
|
596
600
|
}
|
|
597
601
|
class ArenaToolExecutor {
|
|
598
602
|
config;
|
|
603
|
+
interactionBoundaries = new Set();
|
|
604
|
+
interactionQueue = Promise.resolve();
|
|
599
605
|
workspace;
|
|
600
606
|
workspaceReal;
|
|
601
607
|
tmpReal;
|
|
@@ -619,6 +625,7 @@ class ArenaToolExecutor {
|
|
|
619
625
|
// The captured runtime uses Zod object parsing: unknown object keys are
|
|
620
626
|
// stripped before dispatch instead of being emitted as validation issues.
|
|
621
627
|
removeAdditional: "all",
|
|
628
|
+
useDefaults: true,
|
|
622
629
|
});
|
|
623
630
|
// ask_user needs a handler: TUI/WebUI collect real user input, while
|
|
624
631
|
// headless `-p` may delegate to a configured remote User Agent. add_voice
|
|
@@ -638,7 +645,18 @@ class ArenaToolExecutor {
|
|
|
638
645
|
this.varTmpReal = realpathSync("/var/tmp");
|
|
639
646
|
this.processManager = processManagerForWorkspace(this.workspaceReal);
|
|
640
647
|
this.voiceRegistry = voiceRegistryForWorkspace(this.workspaceReal);
|
|
641
|
-
|
|
648
|
+
const askUserHandler = options.askUserHandler;
|
|
649
|
+
this.askUserHandler = askUserHandler ? (callId, questions, context) => {
|
|
650
|
+
// TUI/WebUI own one active question panel. Parallel tools can prepare
|
|
651
|
+
// auditions concurrently, but must not overwrite another pending answer.
|
|
652
|
+
const response = this.interactionQueue.then(() => {
|
|
653
|
+
(this.signal || context?.signal)?.throwIfAborted();
|
|
654
|
+
this.interactionBoundaries.add(callId);
|
|
655
|
+
return askUserHandler(callId, questions, context);
|
|
656
|
+
});
|
|
657
|
+
this.interactionQueue = response.then(() => { }, () => { });
|
|
658
|
+
return response;
|
|
659
|
+
} : undefined;
|
|
642
660
|
this.askUserEnabled = Boolean(options.askUserHandler);
|
|
643
661
|
if (config.imageInputFallbackModel?.trim()) {
|
|
644
662
|
const connection = imageInputFallbackConnection(config);
|
|
@@ -650,8 +668,8 @@ class ArenaToolExecutor {
|
|
|
650
668
|
}
|
|
651
669
|
this.signal = options.signal;
|
|
652
670
|
this.workspaceContext = options.workspaceContext;
|
|
653
|
-
if (config.connectors?.length && !config.disallowedTools?.includes("list_connector_tools")) {
|
|
654
|
-
this.connectors = new ConnectorTools(config.connectors, config.mergeConnectors, config.disallowedTools, options.signal);
|
|
671
|
+
if ((config.connectors?.length || config.connectedConnectors !== undefined) && !config.disallowedTools?.includes("list_connector_tools")) {
|
|
672
|
+
this.connectors = new ConnectorTools(config.connectors || [], config.mergeConnectors, config.disallowedTools, options.signal, { workspace: this.workspace, connected: config.connectedConnectors, resolvePath: raw => this.confinedPath(raw) });
|
|
655
673
|
}
|
|
656
674
|
this.pageReader = new FetchPageReader({ signal: options.signal });
|
|
657
675
|
this.routifyPageReader = new RoutifyFetchPageReader({
|
|
@@ -665,8 +683,14 @@ class ArenaToolExecutor {
|
|
|
665
683
|
type: "string",
|
|
666
684
|
validate: (value) => URL.canParse(value),
|
|
667
685
|
});
|
|
668
|
-
for (const definition of toolDefinitions(this.workspaceContext, "arena", config.connectors)) {
|
|
669
|
-
|
|
686
|
+
for (const definition of toolDefinitions(this.workspaceContext, "arena", config.connectors, config.connectedConnectors)) {
|
|
687
|
+
const parameters = structuredClone(definition.function.parameters);
|
|
688
|
+
// The latest wire schema moves this limit into the description. Keep the
|
|
689
|
+
// documented command bound enforced internally without changing that schema.
|
|
690
|
+
if (definition.function.name === "bash" || definition.function.name === "start_process") {
|
|
691
|
+
record(record(parameters.properties).command).maxLength = 64_000;
|
|
692
|
+
}
|
|
693
|
+
this.validators.set(definition.function.name, this.ajv.compile(parameters));
|
|
670
694
|
}
|
|
671
695
|
}
|
|
672
696
|
// The tool schemas offered to the model for this session. Identical to the
|
|
@@ -681,13 +705,35 @@ class ArenaToolExecutor {
|
|
|
681
705
|
return definitions.filter((definition) => !disallowed.has(definition.function.name));
|
|
682
706
|
}
|
|
683
707
|
allDefinitions() {
|
|
684
|
-
return [...toolDefinitions(this.workspaceContext, "arena", this.config.connectors),
|
|
708
|
+
return [...toolDefinitions(this.workspaceContext, "arena", this.config.connectors, this.config.connectedConnectors),
|
|
685
709
|
...(this.connectors?.definitions() || [])];
|
|
686
710
|
}
|
|
687
711
|
async beginConnectorTurn(history) {
|
|
688
712
|
await this.connectors?.beginTurn(history);
|
|
689
713
|
}
|
|
690
714
|
async closeConnectors() { await this.connectors?.close(); }
|
|
715
|
+
historyCall(call) {
|
|
716
|
+
if (call.function.arguments === "")
|
|
717
|
+
return call;
|
|
718
|
+
const parsed = this.parseArguments(call);
|
|
719
|
+
if (parsed.error !== undefined)
|
|
720
|
+
return call;
|
|
721
|
+
const name = call.function.name;
|
|
722
|
+
let value = parsed.value;
|
|
723
|
+
const validate = this.validators.get(name) || this.connectors?.validator(name);
|
|
724
|
+
const schema = this.allDefinitions().find(definition => definition.function.name === name)?.function.parameters;
|
|
725
|
+
if (validate && schema) {
|
|
726
|
+
const checked = this.validateArguments(name, value, validate);
|
|
727
|
+
if (checked.validationMessage === undefined)
|
|
728
|
+
value = this.connectors?.has(name)
|
|
729
|
+
? this.connectors.historyValue(name, checked.value) : schemaOrderedValue(checked.value, schema);
|
|
730
|
+
}
|
|
731
|
+
const argumentsText = JSON.stringify(value);
|
|
732
|
+
return argumentsText === call.function.arguments ? call : { ...call, function: { ...call.function, arguments: argumentsText } };
|
|
733
|
+
}
|
|
734
|
+
takeInteractionBoundary(callId) {
|
|
735
|
+
return this.interactionBoundaries.delete(callId);
|
|
736
|
+
}
|
|
691
737
|
parseArguments(call) {
|
|
692
738
|
const rawArguments = typeof call.function?.arguments === "string"
|
|
693
739
|
? call.function.arguments
|
|
@@ -717,7 +763,13 @@ class ArenaToolExecutor {
|
|
|
717
763
|
return { rawArguments, value };
|
|
718
764
|
}
|
|
719
765
|
validateArguments(name, value, validate) {
|
|
720
|
-
|
|
766
|
+
let validatedArguments;
|
|
767
|
+
try {
|
|
768
|
+
validatedArguments = (this.connectors?.has(name) ? this.connectors.normalizeArguments(name, value) : structuredClone(value));
|
|
769
|
+
}
|
|
770
|
+
catch {
|
|
771
|
+
return { validationMessage: connectorFailureText };
|
|
772
|
+
}
|
|
721
773
|
if (!validate(validatedArguments)) {
|
|
722
774
|
return {
|
|
723
775
|
validationMessage: this.connectors?.has(name) ? connectorFailureText : arenaValidationError(name, value, validate.errors),
|
|
@@ -873,7 +925,7 @@ class ArenaToolExecutor {
|
|
|
873
925
|
return {
|
|
874
926
|
role: "tool",
|
|
875
927
|
tool_call_id: callId,
|
|
876
|
-
content: json(this.runtimeFailureResult(name, validatedArguments, error, startedAt)),
|
|
928
|
+
content: json(this.runtimeFailureResult(name, validatedArguments, error, startedAt, name === "generate_image" ? context?.signal : undefined)),
|
|
877
929
|
};
|
|
878
930
|
}
|
|
879
931
|
const content = typeof result === "string" ? result : json(result);
|
|
@@ -906,20 +958,20 @@ class ArenaToolExecutor {
|
|
|
906
958
|
};
|
|
907
959
|
}
|
|
908
960
|
}
|
|
909
|
-
runtimeFailureResult(name, values, error, startedAt) {
|
|
910
|
-
if (this.signal?.aborted)
|
|
961
|
+
runtimeFailureResult(name, values, error, startedAt, contextSignal) {
|
|
962
|
+
if ((this.signal || contextSignal)?.aborted)
|
|
911
963
|
return { status: "aborted" };
|
|
912
964
|
const message = errorMessage(error);
|
|
913
965
|
const durationMs = Date.now() - startedAt;
|
|
914
966
|
if (name === "bash") {
|
|
915
967
|
return {
|
|
916
|
-
status: "shell_error",
|
|
917
|
-
stderr: message,
|
|
918
968
|
stdout: "",
|
|
969
|
+
stdout_truncated: false,
|
|
970
|
+
stderr: message,
|
|
971
|
+
stderr_truncated: false,
|
|
919
972
|
exit_code: null,
|
|
973
|
+
status: "shell_error",
|
|
920
974
|
duration_ms: durationMs,
|
|
921
|
-
stderr_truncated: false,
|
|
922
|
-
stdout_truncated: false,
|
|
923
975
|
};
|
|
924
976
|
}
|
|
925
977
|
if (name === "start_process") {
|
|
@@ -942,8 +994,8 @@ class ArenaToolExecutor {
|
|
|
942
994
|
if (name === "get_process_output") {
|
|
943
995
|
return {
|
|
944
996
|
status: "shell_error",
|
|
945
|
-
log_tail: message,
|
|
946
997
|
exit_code: null,
|
|
998
|
+
log_tail: message,
|
|
947
999
|
listening_ports: [],
|
|
948
1000
|
};
|
|
949
1001
|
}
|
|
@@ -1020,7 +1072,7 @@ class ArenaToolExecutor {
|
|
|
1020
1072
|
case "edit_file":
|
|
1021
1073
|
return this.editFile(String(values.path), String(values.old_text), String(values.new_text));
|
|
1022
1074
|
case "read_file":
|
|
1023
|
-
return this.readWorkspaceFile(String(values.path), toolCallId);
|
|
1075
|
+
return this.readWorkspaceFile(String(values.path), toolCallId, values.offset, values.limit);
|
|
1024
1076
|
case "fetch_page":
|
|
1025
1077
|
return routifyWebFetchEnabled()
|
|
1026
1078
|
? this.routifyPageReader.read(String(values.url), Number(values.chunkIndex || 0))
|
|
@@ -1041,7 +1093,7 @@ class ArenaToolExecutor {
|
|
|
1041
1093
|
? path.resolve(this.workspaceContext.cwd)
|
|
1042
1094
|
: "/home/user")), Number(values.startup_wait || 5));
|
|
1043
1095
|
case "generate_image":
|
|
1044
|
-
return this.generateImage(String(values.file_path), String(values.prompt), values.images);
|
|
1096
|
+
return this.generateImage(String(values.file_path), String(values.prompt), values.images, values.offer_options === true, toolCallId, context);
|
|
1045
1097
|
case "generate_speech":
|
|
1046
1098
|
return this.generateSpeech(String(values.file_path), String(values.text), String(values.voice_id), values.language === undefined ? undefined : String(values.language));
|
|
1047
1099
|
case "get_process_output":
|
|
@@ -1198,9 +1250,8 @@ class ArenaToolExecutor {
|
|
|
1198
1250
|
}
|
|
1199
1251
|
return new Promise((resolve, reject) => {
|
|
1200
1252
|
const env = this.sandboxEnvironment();
|
|
1201
|
-
//
|
|
1202
|
-
//
|
|
1203
|
-
// 必须杀整组,并在宽限期后强制结算兜底。
|
|
1253
|
+
// A process group lets cancellation stop descendants too. A wait timeout
|
|
1254
|
+
// is different: the captured contract permits the command to continue.
|
|
1204
1255
|
const child = spawn("/bin/bash", ["-lc", command], {
|
|
1205
1256
|
cwd: workdir,
|
|
1206
1257
|
env,
|
|
@@ -1237,10 +1288,8 @@ class ArenaToolExecutor {
|
|
|
1237
1288
|
clearTimeout(timeout);
|
|
1238
1289
|
if (forceSettle)
|
|
1239
1290
|
clearTimeout(forceSettle);
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
// servers belong in start_process.
|
|
1243
|
-
killTree("SIGKILL");
|
|
1291
|
+
if (!timedOut || this.signal?.aborted)
|
|
1292
|
+
killTree("SIGKILL");
|
|
1244
1293
|
this.signal?.removeEventListener("abort", abort);
|
|
1245
1294
|
if (this.signal?.aborted) {
|
|
1246
1295
|
reject(this.signal.reason || new Error("operation cancelled"));
|
|
@@ -1249,13 +1298,13 @@ class ArenaToolExecutor {
|
|
|
1249
1298
|
const out = stdout.result();
|
|
1250
1299
|
const err = stderr.result();
|
|
1251
1300
|
resolve({
|
|
1252
|
-
status: timedOut ? "timeout" : signal ? "killed" : "completed",
|
|
1253
|
-
stderr: err.text,
|
|
1254
1301
|
stdout: out.text,
|
|
1302
|
+
stdout_truncated: out.truncated,
|
|
1303
|
+
stderr: err.text,
|
|
1304
|
+
stderr_truncated: err.truncated,
|
|
1255
1305
|
exit_code: timedOut ? null : code,
|
|
1306
|
+
status: timedOut ? "timeout" : signal ? "killed" : "completed",
|
|
1256
1307
|
duration_ms: Date.now() - startedAt,
|
|
1257
|
-
stderr_truncated: err.truncated,
|
|
1258
|
-
stdout_truncated: out.truncated,
|
|
1259
1308
|
});
|
|
1260
1309
|
};
|
|
1261
1310
|
// 宽限期结束时升级为组级 SIGKILL 再结算:abort 只发 SIGTERM,
|
|
@@ -1268,8 +1317,12 @@ class ArenaToolExecutor {
|
|
|
1268
1317
|
};
|
|
1269
1318
|
const timeout = setTimeout(() => {
|
|
1270
1319
|
timedOut = true;
|
|
1271
|
-
|
|
1272
|
-
|
|
1320
|
+
finish(null, null);
|
|
1321
|
+
// Keep draining the pipes so a continuing command cannot block or get
|
|
1322
|
+
// SIGPIPE. Do not keep the host CLI alive just to collect discarded output.
|
|
1323
|
+
child.unref();
|
|
1324
|
+
child.stdout.unref?.();
|
|
1325
|
+
child.stderr.unref?.();
|
|
1273
1326
|
}, timeoutSeconds * 1000);
|
|
1274
1327
|
const abort = () => {
|
|
1275
1328
|
killTree("SIGTERM");
|
|
@@ -1277,10 +1330,12 @@ class ArenaToolExecutor {
|
|
|
1277
1330
|
};
|
|
1278
1331
|
this.signal?.addEventListener("abort", abort, { once: true });
|
|
1279
1332
|
child.stdout.on("data", (chunk) => {
|
|
1280
|
-
|
|
1333
|
+
if (!settled)
|
|
1334
|
+
stdout.append(chunk);
|
|
1281
1335
|
});
|
|
1282
1336
|
child.stderr.on("data", (chunk) => {
|
|
1283
|
-
|
|
1337
|
+
if (!settled)
|
|
1338
|
+
stderr.append(chunk);
|
|
1284
1339
|
});
|
|
1285
1340
|
child.once("error", (error) => {
|
|
1286
1341
|
if (settled)
|
|
@@ -1424,7 +1479,7 @@ class ArenaToolExecutor {
|
|
|
1424
1479
|
}
|
|
1425
1480
|
return { status: "success", message: `Edited ${rawPath}.` };
|
|
1426
1481
|
}
|
|
1427
|
-
async readWorkspaceFile(rawPath, toolCallId) {
|
|
1482
|
+
async readWorkspaceFile(rawPath, toolCallId, offset, limit) {
|
|
1428
1483
|
let target;
|
|
1429
1484
|
let fileSize;
|
|
1430
1485
|
let sample;
|
|
@@ -1507,7 +1562,10 @@ class ArenaToolExecutor {
|
|
|
1507
1562
|
return result;
|
|
1508
1563
|
}
|
|
1509
1564
|
if (kind === "text") {
|
|
1510
|
-
const
|
|
1565
|
+
const start = (offset ?? 1) - 1;
|
|
1566
|
+
const selected = offset === undefined && limit === undefined ? data
|
|
1567
|
+
: Buffer.from(decodeText(data).split("\n").slice(start, limit === undefined ? undefined : start + limit).join("\n"), "utf8");
|
|
1568
|
+
const text = readTextResult(selected);
|
|
1511
1569
|
return {
|
|
1512
1570
|
kind: "text",
|
|
1513
1571
|
lines: data.reduce((count, byte) => count + (byte === 0x0a ? 1 : 0), 1),
|
|
@@ -1589,7 +1647,15 @@ class ArenaToolExecutor {
|
|
|
1589
1647
|
return { status: "success" };
|
|
1590
1648
|
}
|
|
1591
1649
|
async webSearch(query, depth) {
|
|
1592
|
-
|
|
1650
|
+
const result = await executeBraveWebSearch(query, depth, { signal: this.signal });
|
|
1651
|
+
// Keep the shared provider/Code Arena envelope unchanged. Agent Arena's
|
|
1652
|
+
// live history emits title before URL; restored history has its own order.
|
|
1653
|
+
if (!Array.isArray(result.results))
|
|
1654
|
+
return result;
|
|
1655
|
+
return { ...result, results: result.results.map(value => {
|
|
1656
|
+
const { id, title, url, ...rest } = record(value);
|
|
1657
|
+
return { id, title, url, ...rest };
|
|
1658
|
+
}) };
|
|
1593
1659
|
}
|
|
1594
1660
|
async imageSearch(query, count) {
|
|
1595
1661
|
const payload = await executeBraveImageSearch(query, count, {
|
|
@@ -1637,10 +1703,10 @@ class ArenaToolExecutor {
|
|
|
1637
1703
|
mkdirSync(path.dirname(target), { recursive: true });
|
|
1638
1704
|
await writeFile(target, await responseBytes(imageResponse));
|
|
1639
1705
|
saved.push({
|
|
1640
|
-
title: stringValue(item.title),
|
|
1641
1706
|
file_path: this.relativePath(target),
|
|
1642
|
-
source_url: sourceUrl,
|
|
1643
1707
|
thumbnail_url: thumbnailUrl,
|
|
1708
|
+
title: stringValue(item.title),
|
|
1709
|
+
source_url: sourceUrl,
|
|
1644
1710
|
});
|
|
1645
1711
|
break;
|
|
1646
1712
|
}
|
|
@@ -1665,13 +1731,13 @@ class ArenaToolExecutor {
|
|
|
1665
1731
|
const target = await this.confinedPath(rawPath);
|
|
1666
1732
|
if (!(await stat(target)).isFile())
|
|
1667
1733
|
throw new Error(rawPath);
|
|
1668
|
-
return {
|
|
1734
|
+
return { status: "success", path: rawPath };
|
|
1669
1735
|
}
|
|
1670
1736
|
catch {
|
|
1671
1737
|
return { status: "error", message: "Error calling present file tool" };
|
|
1672
1738
|
}
|
|
1673
1739
|
}
|
|
1674
|
-
async generateImage(rawPath, prompt, images) {
|
|
1740
|
+
async generateImage(rawPath, prompt, images, offerOptions = false, toolCallId = "generate_image", context) {
|
|
1675
1741
|
if (!rawPath) {
|
|
1676
1742
|
return { status: "error", message: "File path cannot be empty." };
|
|
1677
1743
|
}
|
|
@@ -1683,8 +1749,8 @@ class ArenaToolExecutor {
|
|
|
1683
1749
|
message: "File path must end in .jpg, .jpeg, or .png.",
|
|
1684
1750
|
};
|
|
1685
1751
|
}
|
|
1686
|
-
|
|
1687
|
-
|
|
1752
|
+
if (!offerOptions)
|
|
1753
|
+
mkdirSync(path.dirname(target), { recursive: true });
|
|
1688
1754
|
const references = [];
|
|
1689
1755
|
for (const imagePath of images || []) {
|
|
1690
1756
|
const source = await this.confinedPath(imagePath);
|
|
@@ -1709,14 +1775,26 @@ class ArenaToolExecutor {
|
|
|
1709
1775
|
mimeType,
|
|
1710
1776
|
});
|
|
1711
1777
|
}
|
|
1712
|
-
const
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
}
|
|
1719
|
-
|
|
1778
|
+
const size = process.env.ARENA_IMAGE_SIZE || "1024*1024";
|
|
1779
|
+
const generate = async (imageModel) => {
|
|
1780
|
+
const generated = await generateImageWithModelRouter({
|
|
1781
|
+
images: references, model: imageModel, prompt, signal: this.signal || context?.signal, size,
|
|
1782
|
+
});
|
|
1783
|
+
return generatedImageBytesForExtension(generated.bytes, generated.mimeType, extension);
|
|
1784
|
+
};
|
|
1785
|
+
if (offerOptions) {
|
|
1786
|
+
const models = imageOptionModels();
|
|
1787
|
+
return offerGeneratedImages({
|
|
1788
|
+
workspace: this.workspaceReal, stateRoot: this.config.stateRoot,
|
|
1789
|
+
target, rawPath, callId: toolCallId, context,
|
|
1790
|
+
signal: this.signal || context?.signal, models, generate,
|
|
1791
|
+
fingerprint: JSON.stringify({ target, rawPath, prompt, size, models,
|
|
1792
|
+
images: references.map(image => ({ mimeType: image.mimeType,
|
|
1793
|
+
sha256: createHash("sha256").update(image.bytes).digest("hex") })) }),
|
|
1794
|
+
askUser: this.askUserHandler, confinedPath: filePath => this.confinedPath(filePath),
|
|
1795
|
+
});
|
|
1796
|
+
}
|
|
1797
|
+
await writeFile(target, await generate(arenaImageModel()));
|
|
1720
1798
|
return {
|
|
1721
1799
|
status: "success",
|
|
1722
1800
|
file_path: rawPath,
|
|
@@ -1781,6 +1859,12 @@ export class ToolExecutor {
|
|
|
1781
1859
|
availableTools() {
|
|
1782
1860
|
return this.implementation.availableTools();
|
|
1783
1861
|
}
|
|
1862
|
+
historyCall(call) {
|
|
1863
|
+
return this.implementation instanceof ArenaToolExecutor ? this.implementation.historyCall(call) : call;
|
|
1864
|
+
}
|
|
1865
|
+
takeInteractionBoundary(callId) {
|
|
1866
|
+
return this.implementation instanceof ArenaToolExecutor && this.implementation.takeInteractionBoundary(callId);
|
|
1867
|
+
}
|
|
1784
1868
|
async beginConnectorTurn(history) {
|
|
1785
1869
|
if (this.implementation instanceof ArenaToolExecutor)
|
|
1786
1870
|
await this.implementation.beginConnectorTurn(history);
|