farai 0.1.4 → 0.1.6
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 +612 -672
- package/dist/cli/index.js.map +31 -29
- package/docker/kali/farai-proxy-init.sh +0 -2
- package/docker/kali/farai-proxy-teardown.sh +0 -1
- package/package.json +2 -1
package/dist/cli/index.js
CHANGED
|
@@ -3635,7 +3635,7 @@ function proxyConfig(value) {
|
|
|
3635
3635
|
}
|
|
3636
3636
|
function resolveProxyConfig(config) {
|
|
3637
3637
|
return {
|
|
3638
|
-
transparent: config.proxy?.transparent
|
|
3638
|
+
transparent: config.proxy?.transparent === true,
|
|
3639
3639
|
ports: config.proxy?.ports?.length ? config.proxy.ports : DEFAULT_TRANSPARENT_PROXY_PORTS
|
|
3640
3640
|
};
|
|
3641
3641
|
}
|
|
@@ -3958,10 +3958,9 @@ function atomicWriteFile(path, content, mode) {
|
|
|
3958
3958
|
}
|
|
3959
3959
|
}
|
|
3960
3960
|
var DEFAULT_TRANSPARENT_PROXY_PORTS, LSP_SERVER_IDS, DEFAULT_CONFIG_TEMPLATE = `model = "big-pickle"
|
|
3961
|
-
max_turn_seconds = 900
|
|
3962
3961
|
|
|
3963
3962
|
[proxy]
|
|
3964
|
-
transparent =
|
|
3963
|
+
transparent = false
|
|
3965
3964
|
|
|
3966
3965
|
[mcp_servers.mitmproxy-mcp]
|
|
3967
3966
|
command = "mitmproxy-mcp"
|
|
@@ -5018,14 +5017,45 @@ function sanitizeToolOutput(value) {
|
|
|
5018
5017
|
if (http && isBinaryLike(http.body)) {
|
|
5019
5018
|
return `${sanitizeText(http.head).trimEnd()}
|
|
5020
5019
|
|
|
5021
|
-
|
|
5020
|
+
${binaryPreview(http.body, "binary body")}`;
|
|
5022
5021
|
}
|
|
5023
5022
|
if (isBinaryLike(value)) {
|
|
5024
|
-
return
|
|
5025
|
-
Use a file-oriented command such as file, unzip -l, strings, or hexdump -C to inspect it.`;
|
|
5023
|
+
return binaryPreview(value, "binary-like output");
|
|
5026
5024
|
}
|
|
5027
5025
|
return sanitizeText(value);
|
|
5028
5026
|
}
|
|
5027
|
+
function binaryPreview(value, label) {
|
|
5028
|
+
const bytes = Buffer.from(value, "utf8");
|
|
5029
|
+
const strings = printableStrings(bytes).slice(0, 24);
|
|
5030
|
+
const hex = [...bytes.subarray(0, 192)].map((byte) => byte.toString(16).padStart(2, "0")).reduce((lines, byte, index) => {
|
|
5031
|
+
const line = Math.floor(index / 16);
|
|
5032
|
+
lines[line] = `${lines[line] ?? ""}${lines[line] ? " " : ""}${byte}`;
|
|
5033
|
+
return lines;
|
|
5034
|
+
}, []);
|
|
5035
|
+
return [`[${label}: ${bytes.byteLength} bytes; showing readable strings and a hex preview]`, ...strings.length ? [strings.join(`
|
|
5036
|
+
`)] : [], ...hex.length ? [hex.join(`
|
|
5037
|
+
`)] : []].join(`
|
|
5038
|
+
`);
|
|
5039
|
+
}
|
|
5040
|
+
function printableStrings(bytes) {
|
|
5041
|
+
const result = [];
|
|
5042
|
+
let current = "";
|
|
5043
|
+
const flush = () => {
|
|
5044
|
+
if (current.length >= 4)
|
|
5045
|
+
result.push(current);
|
|
5046
|
+
current = "";
|
|
5047
|
+
};
|
|
5048
|
+
for (const byte of bytes) {
|
|
5049
|
+
if (byte === 9 || byte === 32 || byte >= 33 && byte <= 126)
|
|
5050
|
+
current += String.fromCharCode(byte);
|
|
5051
|
+
else if (byte === 10 || byte === 13)
|
|
5052
|
+
flush();
|
|
5053
|
+
else
|
|
5054
|
+
flush();
|
|
5055
|
+
}
|
|
5056
|
+
flush();
|
|
5057
|
+
return result;
|
|
5058
|
+
}
|
|
5029
5059
|
function isBinaryLike(value) {
|
|
5030
5060
|
if (!value)
|
|
5031
5061
|
return false;
|
|
@@ -5093,9 +5123,6 @@ function consumeStringEscape(value, start, bellTerminates) {
|
|
|
5093
5123
|
}
|
|
5094
5124
|
return index;
|
|
5095
5125
|
}
|
|
5096
|
-
function byteLength(value) {
|
|
5097
|
-
return Buffer.byteLength(value, "utf8");
|
|
5098
|
-
}
|
|
5099
5126
|
function splitHttpResponse(value) {
|
|
5100
5127
|
if (!value.startsWith("HTTP/"))
|
|
5101
5128
|
return;
|
|
@@ -6195,14 +6222,10 @@ function containerRelativePath(path) {
|
|
|
6195
6222
|
return ".";
|
|
6196
6223
|
return resolved.startsWith(prefix) ? resolved.slice(prefix.length) : resolved;
|
|
6197
6224
|
}
|
|
6198
|
-
function assertNotProtectedPath(path,
|
|
6225
|
+
function assertNotProtectedPath(path, _intent) {
|
|
6199
6226
|
const rel = containerRelativePath(path);
|
|
6200
|
-
if (rel === ".git" || rel.startsWith(".git/"))
|
|
6201
|
-
throw new Error("path is protected: .git");
|
|
6202
6227
|
if (rel === ".farai" || rel.startsWith(".farai/"))
|
|
6203
6228
|
throw new Error("path is protected: .farai");
|
|
6204
|
-
if (intent === "write" && rel === ".gitignore")
|
|
6205
|
-
throw new Error("path is protected: .gitignore");
|
|
6206
6229
|
}
|
|
6207
6230
|
function shQuote2(value) {
|
|
6208
6231
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
@@ -6274,7 +6297,7 @@ async function containerListFilesRecursive(context, path, limit) {
|
|
|
6274
6297
|
import os
|
|
6275
6298
|
root = ${JSON.stringify(root)}
|
|
6276
6299
|
limit = ${Math.max(1, Math.floor(limit))}
|
|
6277
|
-
exclude = {".
|
|
6300
|
+
exclude = {".farai", "node_modules"}
|
|
6278
6301
|
out = []
|
|
6279
6302
|
for dirpath, dirnames, filenames in os.walk(root):
|
|
6280
6303
|
dirnames[:] = sorted(d for d in dirnames if d not in exclude)
|
|
@@ -6300,7 +6323,7 @@ root = ${JSON.stringify(root)}
|
|
|
6300
6323
|
pattern = re.compile(base64.b64decode(${JSON.stringify(Buffer.from(pattern, "utf8").toString("base64"))}).decode())
|
|
6301
6324
|
include = ${include === undefined ? "None" : JSON.stringify(include)}
|
|
6302
6325
|
limit = ${Math.max(1, Math.floor(limit))}
|
|
6303
|
-
exclude = {".
|
|
6326
|
+
exclude = {".farai", "node_modules"}
|
|
6304
6327
|
matches = []
|
|
6305
6328
|
for dirpath, dirnames, filenames in os.walk(root):
|
|
6306
6329
|
dirnames[:] = sorted(d for d in dirnames if d not in exclude)
|
|
@@ -6432,12 +6455,8 @@ function safeWorkspacePath(workspace, path, intent) {
|
|
|
6432
6455
|
return resolved;
|
|
6433
6456
|
}
|
|
6434
6457
|
const normalized = rel.split(/[\\/]+/).join("/");
|
|
6435
|
-
if (normalized === ".git" || normalized.startsWith(".git/"))
|
|
6436
|
-
throw new Error("path is protected: .git");
|
|
6437
6458
|
if (normalized === ".farai" || normalized.startsWith(".farai/"))
|
|
6438
6459
|
throw new Error("path is protected: .farai");
|
|
6439
|
-
if (intent === "write" && normalized === ".gitignore")
|
|
6440
|
-
throw new Error("path is protected: .gitignore");
|
|
6441
6460
|
return resolved;
|
|
6442
6461
|
}
|
|
6443
6462
|
function safeExistingWorkspacePath(workspace, path, intent) {
|
|
@@ -6448,12 +6467,8 @@ function safeExistingWorkspacePath(workspace, path, intent) {
|
|
|
6448
6467
|
if (rel.startsWith("..") || rel === "")
|
|
6449
6468
|
throw new Error(`path escapes workspace${path.startsWith("/") ? ESCAPE_HINT : ""}`);
|
|
6450
6469
|
const normalized = rel.split(/[\\/]+/).join("/");
|
|
6451
|
-
if (normalized === ".git" || normalized.startsWith(".git/"))
|
|
6452
|
-
throw new Error("path is protected: .git");
|
|
6453
6470
|
if (normalized === ".farai" || normalized.startsWith(".farai/"))
|
|
6454
6471
|
throw new Error("path is protected: .farai");
|
|
6455
|
-
if (intent === "write" && normalized === ".gitignore")
|
|
6456
|
-
throw new Error("path is protected: .gitignore");
|
|
6457
6472
|
return resolved;
|
|
6458
6473
|
}
|
|
6459
6474
|
function page(items, offset, limit) {
|
|
@@ -10901,7 +10916,7 @@ function scopedToolName(name) {
|
|
|
10901
10916
|
return TOOL_SCOPE_ALIASES.get(canonical) ?? canonical;
|
|
10902
10917
|
}
|
|
10903
10918
|
function resolveSubagentToolScope(input) {
|
|
10904
|
-
const available = new Set(input.availableTools.map((tool) => canonicalToolName(tool.name))
|
|
10919
|
+
const available = new Set(input.availableTools.map((tool) => canonicalToolName(tool.name)));
|
|
10905
10920
|
const requested = input.requestedTools?.map(scopedToolName);
|
|
10906
10921
|
if (requested) {
|
|
10907
10922
|
const unique = [...new Set(requested)];
|
|
@@ -10925,14 +10940,13 @@ function hasSharedWorkspaceEdits(tools) {
|
|
|
10925
10940
|
return tools.map(canonicalToolName).some((tool) => SHARED_WORKSPACE_EDIT_TOOLS.has(tool));
|
|
10926
10941
|
}
|
|
10927
10942
|
function buildSubagentTaskPrompt(input) {
|
|
10928
|
-
return ["you are a
|
|
10943
|
+
return ["you are a subagent working for a parent farai session.", `parent session: ${input.parentSessionId}`, `task: ${input.title}`, ...input.lane ? [`lane: ${input.lane}`] : [], ...input.tools?.length ? [`tool scope: ${input.tools.join(", ")}`] : [], "work autonomously on the delegated task. you may delegate concrete independent subtasks when useful. avoid repeating parent work or broadening the task without evidence.", "preserve exact evidence and return one concise result with status, summary, claims, artifacts, changes, coverage, uncertainty, next actions, and metrics. distinguish proven, candidate, disproven, and inconclusive claims. the parent owns synthesis and the final answer.", ...input.lanePrompt ? [input.lanePrompt] : [], input.task].join(`
|
|
10929
10944
|
|
|
10930
10945
|
`);
|
|
10931
10946
|
}
|
|
10932
|
-
var
|
|
10947
|
+
var SHARED_WORKSPACE_EDIT_TOOLS, TOOL_SCOPE_ALIASES;
|
|
10933
10948
|
var init_scope = __esm(() => {
|
|
10934
10949
|
init_tool_names();
|
|
10935
|
-
NON_DELEGABLE_TOOLS = new Set(["tool_search", "tool_invoke", "request_user_input", "agent_spawn", "agent_list", "agent_wait", "agent_message", "agent_followup", "agent_interrupt", "agent_close", "campaign_dispatch"]);
|
|
10936
10950
|
SHARED_WORKSPACE_EDIT_TOOLS = new Set(["fs_write", "fs_edit", "patch_apply", "code_write_script"]);
|
|
10937
10951
|
TOOL_SCOPE_ALIASES = new Map([["shell", "shell_exec"]]);
|
|
10938
10952
|
});
|
|
@@ -10948,7 +10962,7 @@ var init_dispatch = __esm(() => {
|
|
|
10948
10962
|
init_renderers();
|
|
10949
10963
|
campaignDispatchTool = {
|
|
10950
10964
|
name: "campaign_dispatch",
|
|
10951
|
-
description: "dispatch
|
|
10965
|
+
description: "dispatch child workers with non-overlapping ownership claims. workers return evidence and candidate hypotheses, never confirmed findings. set background=true only when the parent can continue independently.",
|
|
10952
10966
|
inputSchema: {
|
|
10953
10967
|
type: "object",
|
|
10954
10968
|
required: ["tasks"],
|
|
@@ -10962,7 +10976,6 @@ var init_dispatch = __esm(() => {
|
|
|
10962
10976
|
tasks: {
|
|
10963
10977
|
type: "array",
|
|
10964
10978
|
minItems: 1,
|
|
10965
|
-
maxItems: 3,
|
|
10966
10979
|
items: {
|
|
10967
10980
|
type: "object",
|
|
10968
10981
|
required: ["title", "prompt"],
|
|
@@ -10992,7 +11005,7 @@ var init_dispatch = __esm(() => {
|
|
|
10992
11005
|
}
|
|
10993
11006
|
},
|
|
10994
11007
|
mutates: true,
|
|
10995
|
-
timeoutMs:
|
|
11008
|
+
timeoutMs: Number.POSITIVE_INFINITY,
|
|
10996
11009
|
parallel: false,
|
|
10997
11010
|
concurrencyScope: "session",
|
|
10998
11011
|
renderHuman: defaultHumanRenderer,
|
|
@@ -11005,8 +11018,6 @@ var init_dispatch = __esm(() => {
|
|
|
11005
11018
|
throw new Error("delegation is unavailable in this runtime");
|
|
11006
11019
|
if (!Array.isArray(args.tasks) || args.tasks.length === 0)
|
|
11007
11020
|
throw new Error("tasks must contain at least one worker task");
|
|
11008
|
-
if (args.tasks.length > 3)
|
|
11009
|
-
throw new Error("tasks cannot contain more than three worker tasks");
|
|
11010
11021
|
const background = args.background === true;
|
|
11011
11022
|
const tasks = args.tasks.map((task) => {
|
|
11012
11023
|
if (!task || typeof task !== "object")
|
|
@@ -11030,8 +11041,6 @@ var init_dispatch = __esm(() => {
|
|
|
11030
11041
|
throw new Error("task title and prompt must be non-empty");
|
|
11031
11042
|
if (tasks.length > 1 && tasks.some((task) => !task.claim))
|
|
11032
11043
|
throw new Error("each parallel campaign worker requires an exclusive claim");
|
|
11033
|
-
if (background && tasks.some((task) => !task.lane))
|
|
11034
|
-
throw new Error("background campaign workers require an explicit lane");
|
|
11035
11044
|
const claims = tasks.flatMap((task) => task.claim ? [normalizeClaim(task.claim)] : []);
|
|
11036
11045
|
if (new Set(claims).size !== claims.length)
|
|
11037
11046
|
throw new Error("parallel campaign worker claims must be unique");
|
|
@@ -11041,15 +11050,13 @@ var init_dispatch = __esm(() => {
|
|
|
11041
11050
|
const lane = task.lane ? resolveLane(context.rootWorkspace ?? context.workspace, task.lane) : undefined;
|
|
11042
11051
|
if (task.lane && !lane)
|
|
11043
11052
|
throw new Error(`unknown subagent lane: ${task.lane}`);
|
|
11044
|
-
|
|
11053
|
+
resolveSubagentToolScope({
|
|
11045
11054
|
parent: context.session,
|
|
11046
11055
|
availableTools,
|
|
11047
11056
|
...lane?.tools ? {
|
|
11048
11057
|
requestedTools: lane.tools
|
|
11049
11058
|
} : {}
|
|
11050
11059
|
});
|
|
11051
|
-
if (background && hasSharedWorkspaceEdits(scope))
|
|
11052
|
-
throw new Error(`background campaign worker ${task.title} requires a non-editing lane`);
|
|
11053
11060
|
}
|
|
11054
11061
|
}
|
|
11055
11062
|
const results = await Promise.all(tasks.map(async (task) => {
|
|
@@ -13034,7 +13041,13 @@ function refreshSignature(input, configs) {
|
|
|
13034
13041
|
});
|
|
13035
13042
|
}
|
|
13036
13043
|
function resolveMcpPort(configs, portOffset = 0) {
|
|
13037
|
-
|
|
13044
|
+
const configured = configs.find((config) => config.mitmproxy)?.mitmproxy?.port;
|
|
13045
|
+
const base = Number.isInteger(configured) ? configured : DEFAULT_MITMPROXY_PORT;
|
|
13046
|
+
const offset = Number.isFinite(portOffset) ? Math.trunc(portOffset) : 0;
|
|
13047
|
+
const port = base + offset;
|
|
13048
|
+
if (port < 1 || port > 65535)
|
|
13049
|
+
throw new Error(`resolved MCP proxy port is outside 1-65535: ${port}`);
|
|
13050
|
+
return port;
|
|
13038
13051
|
}
|
|
13039
13052
|
function applyMcpPortTemplate(config, port) {
|
|
13040
13053
|
const replacePort = (value) => value.replaceAll("${PORT}", String(port)).replaceAll("${PROXY_PORT}", String(port)).replaceAll("{PORT}", String(port)).replaceAll("{PROXY_PORT}", String(port));
|
|
@@ -14626,20 +14639,18 @@ function parseDelegation(args, context, resumeSessionId) {
|
|
|
14626
14639
|
throw new Error("prompt must be a non-empty string");
|
|
14627
14640
|
const mode = args.mode === "detached" ? "detached" : "attached";
|
|
14628
14641
|
const lane = maybeString(args.lane);
|
|
14642
|
+
const model = maybeString(args.model);
|
|
14629
14643
|
const tools = Array.isArray(args.tools) ? [...new Set(args.tools.map((item) => asString(item, "tools[]").trim()).filter(Boolean))] : undefined;
|
|
14630
14644
|
if (Array.isArray(args.tools) && !tools?.length)
|
|
14631
14645
|
throw new Error("tools must contain at least one non-empty tool name");
|
|
14632
|
-
if (resumeSessionId && (lane || tools))
|
|
14633
|
-
throw new Error("resumed subagents preserve their original lane and tool scope");
|
|
14634
|
-
if (mode === "detached" && !resumeSessionId && !lane && !tools?.length)
|
|
14635
|
-
throw new Error("detached subagents require an explicit lane or tool scope");
|
|
14636
14646
|
const title = normalizeSessionTitle(maybeString(args.title) ?? (resumeSessionId ? childTitle(context, resumeSessionId) : titleFromPrompt(prompt, lane ? `${lane} task` : "subagent task")));
|
|
14637
14647
|
return {
|
|
14638
14648
|
title,
|
|
14639
14649
|
prompt,
|
|
14640
14650
|
mode,
|
|
14641
14651
|
lane,
|
|
14642
|
-
tools
|
|
14652
|
+
tools,
|
|
14653
|
+
model
|
|
14643
14654
|
};
|
|
14644
14655
|
}
|
|
14645
14656
|
async function delegate(args, context, resumeSessionId) {
|
|
@@ -14656,6 +14667,9 @@ async function delegate(args, context, resumeSessionId) {
|
|
|
14656
14667
|
} : {},
|
|
14657
14668
|
...input.tools?.length ? {
|
|
14658
14669
|
tools: input.tools
|
|
14670
|
+
} : {},
|
|
14671
|
+
...input.model ? {
|
|
14672
|
+
model: input.model
|
|
14659
14673
|
} : {}
|
|
14660
14674
|
});
|
|
14661
14675
|
return {
|
|
@@ -14704,7 +14718,7 @@ function followupTool() {
|
|
|
14704
14718
|
additionalProperties: false
|
|
14705
14719
|
},
|
|
14706
14720
|
mutates: true,
|
|
14707
|
-
timeoutMs:
|
|
14721
|
+
timeoutMs: Number.POSITIVE_INFINITY,
|
|
14708
14722
|
parallel: true,
|
|
14709
14723
|
concurrencyScope: "session",
|
|
14710
14724
|
renderHuman: agentResultRenderer,
|
|
@@ -14737,6 +14751,10 @@ var init_lifecycle = __esm(() => {
|
|
|
14737
14751
|
},
|
|
14738
14752
|
description: "optional restriction that cannot exceed the parent scope"
|
|
14739
14753
|
},
|
|
14754
|
+
model: {
|
|
14755
|
+
type: "string",
|
|
14756
|
+
description: "optional model override"
|
|
14757
|
+
},
|
|
14740
14758
|
mode: {
|
|
14741
14759
|
type: "string",
|
|
14742
14760
|
enum: ["attached", "detached"]
|
|
@@ -14744,7 +14762,7 @@ var init_lifecycle = __esm(() => {
|
|
|
14744
14762
|
};
|
|
14745
14763
|
agentSpawnTool = {
|
|
14746
14764
|
name: "agent_spawn",
|
|
14747
|
-
description: "Start a
|
|
14765
|
+
description: "Start a subagent. Attached waits for its result; detached lets it continue independently in the background.",
|
|
14748
14766
|
inputSchema: {
|
|
14749
14767
|
type: "object",
|
|
14750
14768
|
required: ["prompt"],
|
|
@@ -14752,7 +14770,7 @@ var init_lifecycle = __esm(() => {
|
|
|
14752
14770
|
additionalProperties: false
|
|
14753
14771
|
},
|
|
14754
14772
|
mutates: true,
|
|
14755
|
-
timeoutMs:
|
|
14773
|
+
timeoutMs: Number.POSITIVE_INFINITY,
|
|
14756
14774
|
parallel: true,
|
|
14757
14775
|
concurrencyScope: "session",
|
|
14758
14776
|
renderHuman: agentResultRenderer,
|
|
@@ -15643,7 +15661,7 @@ var init_request_user_input = __esm(() => {
|
|
|
15643
15661
|
},
|
|
15644
15662
|
mutates: false,
|
|
15645
15663
|
timeoutMs: 86400000,
|
|
15646
|
-
parallel:
|
|
15664
|
+
parallel: true,
|
|
15647
15665
|
concurrencyScope: "session",
|
|
15648
15666
|
renderHuman: (result) => result.output ?? result.summary,
|
|
15649
15667
|
renderModel: (result) => result.output ?? result.summary,
|
|
@@ -17366,9 +17384,10 @@ function renderCtfNotes(input) {
|
|
|
17366
17384
|
}
|
|
17367
17385
|
|
|
17368
17386
|
// src/agent-core/default-model.ts
|
|
17369
|
-
var DEFAULT_MODEL_PROVIDER_ID = "opencode", DEFAULT_MODEL_BASE_URL = "https://opencode.ai/zen/v1", DEFAULT_MODEL_ID = "big-pickle", DEFAULT_MODEL_PUBLIC_API_KEY = "public", DEFAULT_CONTEXT_WINDOW = 32000, DEFAULT_MAX_OUTPUT_TOKENS = 4096, DEFAULT_MAX_STEPS
|
|
17387
|
+
var DEFAULT_MODEL_PROVIDER_ID = "opencode", DEFAULT_MODEL_BASE_URL = "https://opencode.ai/zen/v1", DEFAULT_MODEL_ID = "big-pickle", DEFAULT_MODEL_PUBLIC_API_KEY = "public", DEFAULT_CONTEXT_WINDOW = 32000, DEFAULT_MAX_OUTPUT_TOKENS = 4096, DEFAULT_MAX_STEPS, DEFAULT_MAX_TURN_SECONDS;
|
|
17370
17388
|
var init_default_model = __esm(() => {
|
|
17371
|
-
|
|
17389
|
+
DEFAULT_MAX_STEPS = Number.POSITIVE_INFINITY;
|
|
17390
|
+
DEFAULT_MAX_TURN_SECONDS = Number.POSITIVE_INFINITY;
|
|
17372
17391
|
});
|
|
17373
17392
|
|
|
17374
17393
|
// src/agent-core/model-registry.ts
|
|
@@ -19931,11 +19950,6 @@ function activeBackgroundJobs(calls) {
|
|
|
19931
19950
|
}
|
|
19932
19951
|
return jobs;
|
|
19933
19952
|
}
|
|
19934
|
-
function findEquivalentBackgroundJob(jobs, tool, args) {
|
|
19935
|
-
const fingerprint = stableValue(args);
|
|
19936
|
-
const canonical = canonicalToolName(tool);
|
|
19937
|
-
return jobs.find((job) => job.tool === canonical && stableValue(job.args) === fingerprint);
|
|
19938
|
-
}
|
|
19939
19953
|
function processIdFromArgs(args) {
|
|
19940
19954
|
if (!args || typeof args !== "object" || Array.isArray(args))
|
|
19941
19955
|
return;
|
|
@@ -21476,188 +21490,21 @@ var init_history_projection = __esm(() => {
|
|
|
21476
21490
|
});
|
|
21477
21491
|
|
|
21478
21492
|
// src/agent-core/capability-admission.ts
|
|
21479
|
-
function matches(text, pattern) {
|
|
21480
|
-
return pattern.test(text.toLowerCase());
|
|
21481
|
-
}
|
|
21482
|
-
function exactToolMention(text, name) {
|
|
21483
|
-
return canonicalToolName(text.toLowerCase()).includes(name.toLowerCase());
|
|
21484
|
-
}
|
|
21485
|
-
function containsNetworkTarget(text) {
|
|
21486
|
-
return /https?:\/\/|\b(?:www\.)?(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z]{2,63}|xn--[a-z0-9-]{2,59})(?::\d{1,5})?\b|\b\d{1,3}(?:\.\d{1,3}){3}(?::\d{1,5})?\b/i.test(text);
|
|
21487
|
-
}
|
|
21488
|
-
function containsHostnameTarget(text) {
|
|
21489
|
-
return /\b(?:www\.)?(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z]{2,63}|xn--[a-z0-9-]{2,59})(?::\d{1,5})?\b/i.test(text);
|
|
21490
|
-
}
|
|
21491
|
-
function hasAssessmentIntent(text) {
|
|
21492
|
-
return matches(text, /\b(audit|assess(?:ment)?|security[ -]?(?:audit|test(?:ing)?)|pentest|scan|target|host|ctf|vulnerability|vuln|exploit|enumerat(?:e|ion)|recon|uji keamanan|cek keamanan|periksa keamanan)\b/);
|
|
21493
|
-
}
|
|
21494
|
-
function isInteractiveWebTask(session, userText = "") {
|
|
21495
|
-
const text = userText.toLowerCase();
|
|
21496
|
-
const assessmentPhase = ["recon", "enumeration", "hypothesis", "verification", "exploit_lab", "post_exploit_lab"].includes(session.phase);
|
|
21497
|
-
const hostnameTarget = containsHostnameTarget(text);
|
|
21498
|
-
const networkTarget = containsNetworkTarget(text);
|
|
21499
|
-
const explicitWebTarget = /https?:\/\//i.test(text);
|
|
21500
|
-
const explicitHttpService = networkTarget && matches(text, /\bhttps?\b/);
|
|
21501
|
-
const browserOperation = matches(text, /\b(web(?:site|app)?|site|browser|playwright|camoufox|page|halaman|situs|login|sign[ -]?in|form|dashboard|cookie|redirect|javascript|dom|frontend|endpoint|api)\b/);
|
|
21502
|
-
const interactiveOperation = explicitWebTarget || explicitHttpService || browserOperation;
|
|
21503
|
-
const interactiveWebIntent = explicitWebTarget || interactiveOperation;
|
|
21504
|
-
const passiveInfrastructure = isPassiveInfrastructureTask(text) && !browserOperation;
|
|
21505
|
-
const assessmentIntent = hasAssessmentIntent(text) && (hostnameTarget || networkTarget && interactiveWebIntent) && !passiveInfrastructure;
|
|
21506
|
-
const codingOnly = matches(text, /\b(code|coding|implement|refactor|bug|fix|unit test|typecheck|repository|repo|parser)\b|\.(ts|tsx|js|jsx|py|go|rs)\b/) && !matches(text, /https?:\/\/|\b(browser|playwright|camoufox|page|login|form|dashboard|cookie|redirect|javascript|dom|frontend)\b/);
|
|
21507
|
-
return !passiveInfrastructure && !codingOnly && (assessmentIntent || interactiveWebIntent || assessmentPhase && interactiveWebIntent);
|
|
21508
|
-
}
|
|
21509
|
-
function isPassiveInfrastructureTask(userText = "") {
|
|
21510
|
-
return matches(userText, /\b(subdomains?|passive[ -]?dns|certificate transparency|\bct logs?\b|crt\.sh|asset[ -]?(?:discovery|enumeration)|dns[ -]?(?:recon|enumeration)|enumerat(?:e|ion)\s+(?:dns|subdomains?))\b/);
|
|
21511
|
-
}
|
|
21512
|
-
function isExplicitRawHttpTask(userText = "") {
|
|
21513
|
-
const text = userText.toLowerCase();
|
|
21514
|
-
const negatedClient = /\b(?:do not|don't|never|avoid|jangan|tanpa)\b[^\n]{0,48}\b(?:http_request|curl|wget|httpie|xh)\b/.test(text);
|
|
21515
|
-
if (!negatedClient && /\b(?:http_request|curl|wget|httpie|xh)\b/.test(text))
|
|
21516
|
-
return true;
|
|
21517
|
-
if (/\b(?:raw http|wire format|request smuggling|response splitting|http\/1\.[01]|http\/2|http\/3|exact protocol|protocol verification)\b/.test(text))
|
|
21518
|
-
return true;
|
|
21519
|
-
if (/\b(?:ffuf|fuzz(?:er|ing)?|wordlist|brute[ -]?force|load test|benchmark)\b/.test(text))
|
|
21520
|
-
return true;
|
|
21521
|
-
return /\b(?:script|scripting|automate|repeatable|regression|integration test|api test|testing)\b/.test(text) && /\b(?:http|https|api|request|response|endpoint)\b/.test(text);
|
|
21522
|
-
}
|
|
21523
|
-
function browserKernelOperation(name) {
|
|
21524
|
-
return BROWSER_KERNEL.find((operation) => name === operation || name.endsWith(`_${operation}`));
|
|
21525
|
-
}
|
|
21526
|
-
function selectBrowserKernel(tools) {
|
|
21527
|
-
const selected = [];
|
|
21528
|
-
for (const operation of BROWSER_KERNEL) {
|
|
21529
|
-
const candidates = tools.filter((tool) => browserKernelOperation(tool.name) === operation).sort((left, right) => Number(right.name === operation) - Number(left.name === operation) || left.name.localeCompare(right.name));
|
|
21530
|
-
if (candidates[0])
|
|
21531
|
-
selected.push(candidates[0]);
|
|
21532
|
-
}
|
|
21533
|
-
return selected;
|
|
21534
|
-
}
|
|
21535
21493
|
function selectCapabilities(input) {
|
|
21536
21494
|
if (input.session.toolScope?.length) {
|
|
21537
21495
|
const scope = new Set(input.session.toolScope.map(canonicalToolName));
|
|
21538
|
-
const direct2 = input.tools.filter((tool) => scope.has(tool.name)).sort((a, b) => a.name.localeCompare(b.name));
|
|
21496
|
+
const direct2 = input.tools.filter((tool) => scope.has(canonicalToolName(tool.name))).sort((a, b) => a.name.localeCompare(b.name));
|
|
21539
21497
|
return {
|
|
21540
21498
|
direct: direct2,
|
|
21541
21499
|
deferred: [],
|
|
21542
21500
|
reasons: Object.fromEntries(direct2.map((tool) => [tool.name, "explicit subagent scope"]))
|
|
21543
21501
|
};
|
|
21544
21502
|
}
|
|
21545
|
-
const
|
|
21546
|
-
const coding = input.session.phase === "code_assist" || matches(text, /\b(code|coding|implement|refactor|bug|fix|test|typescript|javascript|python|golang|rust|file|repository|repo|build|typecheck)\b|\.(ts|tsx|js|jsx|py|go|rs)\b/);
|
|
21547
|
-
const recon = ["recon", "enumeration", "hypothesis", "verification", "exploit_lab", "post_exploit_lab"].includes(input.session.phase) || hasAssessmentIntent(text) || containsNetworkTarget(text) || matches(text, /\b(port|http|https|url|domain|endpoint|directory|nmap)\b/);
|
|
21548
|
-
const callback = matches(text, /\b(reverse shell|callback|listener|lhost|oast|out.of.band|ssrf|xxe)\b/);
|
|
21549
|
-
const campaign = Boolean(input.session.campaignId);
|
|
21550
|
-
const interactiveWeb = isInteractiveWebTask(input.session, text);
|
|
21551
|
-
const rawHttp = isExplicitRawHttpTask(text);
|
|
21552
|
-
const selected = new Set(ALWAYS);
|
|
21553
|
-
const reasons = {};
|
|
21554
|
-
for (const name of ALWAYS)
|
|
21555
|
-
reasons[name] = "kernel capability";
|
|
21556
|
-
if (!input.session.parentId) {
|
|
21557
|
-
for (const name of ["request_user_input", "agent_spawn"]) {
|
|
21558
|
-
selected.add(name);
|
|
21559
|
-
reasons[name] = "root session delegation";
|
|
21560
|
-
}
|
|
21561
|
-
}
|
|
21562
|
-
if (coding)
|
|
21563
|
-
for (const name of CODING) {
|
|
21564
|
-
selected.add(name);
|
|
21565
|
-
reasons[name] = "coding task";
|
|
21566
|
-
}
|
|
21567
|
-
if (recon)
|
|
21568
|
-
for (const name of RECON) {
|
|
21569
|
-
selected.add(name);
|
|
21570
|
-
reasons[name] = "recon task";
|
|
21571
|
-
}
|
|
21572
|
-
if (interactiveWeb) {
|
|
21573
|
-
for (const tool of selectBrowserKernel(input.tools)) {
|
|
21574
|
-
selected.add(tool.name);
|
|
21575
|
-
reasons[tool.name] = "interactive web task";
|
|
21576
|
-
}
|
|
21577
|
-
for (const name of ["proxy_scope", "proxy_flows", "proxy_flow_get", "proxy_sitemap", "proxy_replay", "proxy_intercept", "proxy_clear"]) {
|
|
21578
|
-
selected.add(name);
|
|
21579
|
-
reasons[name] = "managed web proxy";
|
|
21580
|
-
}
|
|
21581
|
-
}
|
|
21582
|
-
if (interactiveWeb || rawHttp) {
|
|
21583
|
-
selected.add("http_request");
|
|
21584
|
-
reasons.http_request = rawHttp ? "explicit HTTP task" : "network assessment task";
|
|
21585
|
-
}
|
|
21586
|
-
if (campaign)
|
|
21587
|
-
for (const name of CAMPAIGN) {
|
|
21588
|
-
selected.add(name);
|
|
21589
|
-
reasons[name] = "active campaign";
|
|
21590
|
-
}
|
|
21591
|
-
if (campaign && input.session.phase === "verification") {
|
|
21592
|
-
selected.add("campaign_dispatch");
|
|
21593
|
-
reasons["campaign_dispatch"] = "campaign verification";
|
|
21594
|
-
}
|
|
21595
|
-
if (!campaign && recon) {
|
|
21596
|
-
selected.add("campaign_create");
|
|
21597
|
-
reasons["campaign_create"] = "campaign can be initialized for assessment work";
|
|
21598
|
-
}
|
|
21599
|
-
if (callback)
|
|
21600
|
-
for (const name of CALLBACK) {
|
|
21601
|
-
selected.add(name);
|
|
21602
|
-
reasons[name] = "callback or OOB task";
|
|
21603
|
-
}
|
|
21604
|
-
if (matches(text, /\b(search the web|web search|research|latest|current|internet|online|source|citation|paper|documentation)\b/)) {
|
|
21605
|
-
selected.add("web_search");
|
|
21606
|
-
selected.add("web_fetch");
|
|
21607
|
-
reasons.web_search = "current web research";
|
|
21608
|
-
reasons.web_fetch = "current web research";
|
|
21609
|
-
}
|
|
21610
|
-
if (matches(text, /\b(image|screenshot|photo|diagram|png|jpe?g|gif|webp)\b/)) {
|
|
21611
|
-
selected.add("image_view");
|
|
21612
|
-
reasons.image_view = "image inspection";
|
|
21613
|
-
}
|
|
21614
|
-
if (matches(text, /\bmcp\b.*\b(resource|resources)\b|\b(resource|resources)\b.*\bmcp\b/)) {
|
|
21615
|
-
selected.add("mcp_resource_list");
|
|
21616
|
-
selected.add("mcp_resource_read");
|
|
21617
|
-
reasons.mcp_resource_list = "MCP resources";
|
|
21618
|
-
reasons.mcp_resource_read = "MCP resources";
|
|
21619
|
-
}
|
|
21620
|
-
if (input.hasActiveJobs || input.hasOutputArtifacts) {
|
|
21621
|
-
for (const name of BACKGROUND) {
|
|
21622
|
-
selected.add(name);
|
|
21623
|
-
reasons[name] = "active or retrievable tool output";
|
|
21624
|
-
}
|
|
21625
|
-
}
|
|
21626
|
-
if (!input.session.parentId && input.hasActiveJobs) {
|
|
21627
|
-
for (const name of ["agent_list", "agent_wait", "agent_message", "agent_followup", "agent_interrupt", "agent_close"]) {
|
|
21628
|
-
selected.add(name);
|
|
21629
|
-
reasons[name] = "active child-agent lifecycle";
|
|
21630
|
-
}
|
|
21631
|
-
}
|
|
21632
|
-
for (const tool of input.tools) {
|
|
21633
|
-
if (exactToolMention(text, tool.name)) {
|
|
21634
|
-
selected.add(tool.name);
|
|
21635
|
-
reasons[tool.name] = "explicit tool mention";
|
|
21636
|
-
}
|
|
21637
|
-
}
|
|
21638
|
-
if (input.invokedTools?.length) {
|
|
21639
|
-
const invoked = new Set(input.invokedTools.map(canonicalToolName));
|
|
21640
|
-
for (const tool of input.tools) {
|
|
21641
|
-
if (BRIDGE.has(tool.name) || selected.has(tool.name) || !invoked.has(tool.name))
|
|
21642
|
-
continue;
|
|
21643
|
-
selected.add(tool.name);
|
|
21644
|
-
reasons[tool.name] = "used earlier this session";
|
|
21645
|
-
}
|
|
21646
|
-
}
|
|
21647
|
-
const direct = input.tools.filter((tool) => selected.has(tool.name) && !BRIDGE.has(tool.name));
|
|
21648
|
-
const deferred = input.tools.filter((tool) => !selected.has(tool.name) && !BRIDGE.has(tool.name));
|
|
21649
|
-
if (deferred.length > 0) {
|
|
21650
|
-
for (const bridge of input.tools.filter((tool) => BRIDGE.has(tool.name))) {
|
|
21651
|
-
direct.push(bridge);
|
|
21652
|
-
reasons[bridge.name] = `${deferred.length} capabilities deferred`;
|
|
21653
|
-
}
|
|
21654
|
-
}
|
|
21655
|
-
direct.sort((a, b) => a.name.localeCompare(b.name));
|
|
21656
|
-
deferred.sort((a, b) => a.name.localeCompare(b.name));
|
|
21503
|
+
const direct = [...input.tools].sort((a, b) => a.name.localeCompare(b.name));
|
|
21657
21504
|
return {
|
|
21658
21505
|
direct,
|
|
21659
|
-
deferred,
|
|
21660
|
-
reasons
|
|
21506
|
+
deferred: [],
|
|
21507
|
+
reasons: Object.fromEntries(direct.map((tool) => [tool.name, "available session capability"]))
|
|
21661
21508
|
};
|
|
21662
21509
|
}
|
|
21663
21510
|
function toolSchemaTokens(tools) {
|
|
@@ -21668,17 +21515,8 @@ function toolSchemaTokens(tools) {
|
|
|
21668
21515
|
}));
|
|
21669
21516
|
return Math.max(0, Math.ceil(Buffer.byteLength(JSON.stringify(payload), "utf8") / 4));
|
|
21670
21517
|
}
|
|
21671
|
-
var BRIDGE, ALWAYS, CODING, RECON, BROWSER_KERNEL, CAMPAIGN, CALLBACK, BACKGROUND;
|
|
21672
21518
|
var init_capability_admission = __esm(() => {
|
|
21673
21519
|
init_tool_names();
|
|
21674
|
-
BRIDGE = new Set(["tool_search", "tool_invoke"]);
|
|
21675
|
-
ALWAYS = new Set(["shell_exec", "fs_read", "fs_grep", "skill_load", "session_rename", "todo_add", "todo_update", "todo_list"]);
|
|
21676
|
-
CODING = new Set(["fs_list", "fs_write", "fs_edit", "patch_apply", "notebook_edit", "git_status", "git_diff", "code_write_script", "lsp_inspect", "worktree_enter", "worktree_exit"]);
|
|
21677
|
-
RECON = new Set(["port_scan", "nmap_scan", "subdomain_enum", "dir_enum", "exploit_search", "kali_tool_search", "notes_add", "evidence_save"]);
|
|
21678
|
-
BROWSER_KERNEL = ["browser_context", "browser_navigate", "browser_snapshot", "browser_find", "browser_click", "browser_fill_form", "browser_type", "browser_press_key", "browser_wait_for", "browser_tabs", "browser_network_requests", "browser_network_request"];
|
|
21679
|
-
CAMPAIGN = new Set(["campaign_asset", "campaign_observe", "campaign_hypothesis", "campaign_search", "campaign_next_action", "campaign_test", "campaign_verify", "report_add_finding"]);
|
|
21680
|
-
CALLBACK = new Set(["callback_host_info", "callback_listen", "callback_oast", "callback_stop"]);
|
|
21681
|
-
BACKGROUND = new Set(["session_poll", "session_stop", "tool_output_read"]);
|
|
21682
21520
|
});
|
|
21683
21521
|
|
|
21684
21522
|
// src/agent-core/context-index.ts
|
|
@@ -23688,26 +23526,6 @@ class ToolExecutionLease {
|
|
|
23688
23526
|
}
|
|
23689
23527
|
}
|
|
23690
23528
|
|
|
23691
|
-
class ToolGateLease {
|
|
23692
|
-
quarantines = [];
|
|
23693
|
-
mirrors = new Set;
|
|
23694
|
-
mirrorTo(target) {
|
|
23695
|
-
if (target !== this)
|
|
23696
|
-
this.mirrors.add(target);
|
|
23697
|
-
}
|
|
23698
|
-
quarantineUntil(running, error) {
|
|
23699
|
-
this.quarantines.push({
|
|
23700
|
-
running,
|
|
23701
|
-
error
|
|
23702
|
-
});
|
|
23703
|
-
for (const target of this.mirrors)
|
|
23704
|
-
target.quarantineUntil(running, error);
|
|
23705
|
-
}
|
|
23706
|
-
takeQuarantines() {
|
|
23707
|
-
return this.quarantines.splice(0);
|
|
23708
|
-
}
|
|
23709
|
-
}
|
|
23710
|
-
|
|
23711
23529
|
class ToolExecutionDeadline {
|
|
23712
23530
|
controller = new AbortController;
|
|
23713
23531
|
constructor(tool, timeoutMs, parentSignal) {
|
|
@@ -23723,21 +23541,16 @@ class ToolExecutionDeadline {
|
|
|
23723
23541
|
once: true
|
|
23724
23542
|
});
|
|
23725
23543
|
}
|
|
23726
|
-
async run(work
|
|
23544
|
+
async run(work) {
|
|
23727
23545
|
this.signal.throwIfAborted();
|
|
23728
|
-
|
|
23729
|
-
|
|
23730
|
-
|
|
23731
|
-
|
|
23732
|
-
|
|
23733
|
-
const running = Promise.resolve().then(work);
|
|
23734
|
-
try {
|
|
23735
|
-
return await abortablePromise(running, this.signal);
|
|
23736
|
-
} catch (error) {
|
|
23737
|
-
if (this.signal.aborted)
|
|
23738
|
-
gateLease.quarantineUntil(running, abortReason(this.signal));
|
|
23739
|
-
throw error;
|
|
23546
|
+
if (Number.isFinite(this.timeoutMs)) {
|
|
23547
|
+
this.timer ??= setTimeout(() => {
|
|
23548
|
+
if (!this.signal.aborted)
|
|
23549
|
+
this.controller.abort(new ToolDeadlineError(this.tool, this.timeoutMs));
|
|
23550
|
+
}, this.timeoutMs);
|
|
23740
23551
|
}
|
|
23552
|
+
this.signal.throwIfAborted();
|
|
23553
|
+
return await abortablePromise(Promise.resolve().then(work), this.signal);
|
|
23741
23554
|
}
|
|
23742
23555
|
dispose() {
|
|
23743
23556
|
if (this.timer)
|
|
@@ -23758,12 +23571,10 @@ class ToolExecutionGate {
|
|
|
23758
23571
|
}
|
|
23759
23572
|
async run(key, parallel, fn, signal) {
|
|
23760
23573
|
const release = await this.acquire(key, parallel ? "read" : "write", signal);
|
|
23761
|
-
const lease = new ToolGateLease;
|
|
23762
23574
|
try {
|
|
23763
23575
|
signal?.throwIfAborted();
|
|
23764
|
-
return await fn(
|
|
23576
|
+
return await fn();
|
|
23765
23577
|
} finally {
|
|
23766
|
-
this.quarantine(key, lease.takeQuarantines());
|
|
23767
23578
|
release();
|
|
23768
23579
|
}
|
|
23769
23580
|
}
|
|
@@ -23776,14 +23587,9 @@ class ToolExecutionGate {
|
|
|
23776
23587
|
const state = this.states.get(key) ?? {
|
|
23777
23588
|
activeReaders: 0,
|
|
23778
23589
|
activeWriter: false,
|
|
23779
|
-
queue: []
|
|
23780
|
-
quarantines: new Set
|
|
23590
|
+
queue: []
|
|
23781
23591
|
};
|
|
23782
23592
|
this.states.set(key, state);
|
|
23783
|
-
if (state.quarantineError) {
|
|
23784
|
-
reject(state.quarantineError);
|
|
23785
|
-
return;
|
|
23786
|
-
}
|
|
23787
23593
|
const waiter = {
|
|
23788
23594
|
mode,
|
|
23789
23595
|
resolve: resolve5,
|
|
@@ -23812,7 +23618,7 @@ class ToolExecutionGate {
|
|
|
23812
23618
|
});
|
|
23813
23619
|
}
|
|
23814
23620
|
drain(key, state) {
|
|
23815
|
-
if (state.activeWriter
|
|
23621
|
+
if (state.activeWriter)
|
|
23816
23622
|
return;
|
|
23817
23623
|
const first = state.queue[0];
|
|
23818
23624
|
if (!first)
|
|
@@ -23840,7 +23646,7 @@ class ToolExecutionGate {
|
|
|
23840
23646
|
}
|
|
23841
23647
|
}
|
|
23842
23648
|
cleanup(key, state) {
|
|
23843
|
-
if (state.activeReaders === 0 && !state.activeWriter && state.queue.length === 0 &&
|
|
23649
|
+
if (state.activeReaders === 0 && !state.activeWriter && state.queue.length === 0 && this.states.get(key) === state) {
|
|
23844
23650
|
this.states.delete(key);
|
|
23845
23651
|
if (this.states.size === 0) {
|
|
23846
23652
|
for (const resolve5 of this.idleResolvers)
|
|
@@ -23858,32 +23664,6 @@ class ToolExecutionGate {
|
|
|
23858
23664
|
waiter.signal.removeEventListener("abort", waiter.onAbort);
|
|
23859
23665
|
delete waiter.onAbort;
|
|
23860
23666
|
}
|
|
23861
|
-
quarantine(key, entries) {
|
|
23862
|
-
if (entries.length === 0)
|
|
23863
|
-
return;
|
|
23864
|
-
const state = this.states.get(key);
|
|
23865
|
-
if (!state)
|
|
23866
|
-
return;
|
|
23867
|
-
state.quarantineError ??= new ToolScopeQuarantinedError(key, entries[0].error);
|
|
23868
|
-
for (const waiter of state.queue.splice(0)) {
|
|
23869
|
-
this.detach(waiter);
|
|
23870
|
-
waiter.reject(state.quarantineError);
|
|
23871
|
-
}
|
|
23872
|
-
for (const entry of entries) {
|
|
23873
|
-
let tracked;
|
|
23874
|
-
tracked = entry.running.catch(() => {
|
|
23875
|
-
return;
|
|
23876
|
-
}).finally(() => {
|
|
23877
|
-
state.quarantines.delete(tracked);
|
|
23878
|
-
if (state.quarantines.size === 0) {
|
|
23879
|
-
delete state.quarantineError;
|
|
23880
|
-
this.drain(key, state);
|
|
23881
|
-
this.cleanup(key, state);
|
|
23882
|
-
}
|
|
23883
|
-
});
|
|
23884
|
-
state.quarantines.add(tracked);
|
|
23885
|
-
}
|
|
23886
|
-
}
|
|
23887
23667
|
}
|
|
23888
23668
|
function leasedToolCapability(target, lease) {
|
|
23889
23669
|
return new Proxy(target, {
|
|
@@ -23899,12 +23679,16 @@ function leasedToolCapability(target, lease) {
|
|
|
23899
23679
|
});
|
|
23900
23680
|
}
|
|
23901
23681
|
function normalizeToolTimeout(timeoutMs) {
|
|
23682
|
+
if (timeoutMs === Number.POSITIVE_INFINITY)
|
|
23683
|
+
return timeoutMs;
|
|
23902
23684
|
if (!Number.isFinite(timeoutMs))
|
|
23903
23685
|
return 120000;
|
|
23904
23686
|
return Math.max(1, Math.floor(timeoutMs));
|
|
23905
23687
|
}
|
|
23906
23688
|
function toolOperationTimeout(timeoutMs) {
|
|
23907
23689
|
const deadline = normalizeToolTimeout(timeoutMs);
|
|
23690
|
+
if (!Number.isFinite(deadline))
|
|
23691
|
+
return deadline;
|
|
23908
23692
|
const handoffGrace = Math.min(5000, Math.max(50, Math.floor(deadline * 0.05)));
|
|
23909
23693
|
return Math.max(1, deadline - handoffGrace);
|
|
23910
23694
|
}
|
|
@@ -23958,7 +23742,7 @@ function toolSchedulingDefinition(tool, args, session) {
|
|
|
23958
23742
|
const targetName = canonicalToolName(String(args.name ?? ""));
|
|
23959
23743
|
return getTool(targetName, session) ?? tool;
|
|
23960
23744
|
}
|
|
23961
|
-
var ToolDeadlineError
|
|
23745
|
+
var ToolDeadlineError;
|
|
23962
23746
|
var init_tool_execution_control = __esm(() => {
|
|
23963
23747
|
init_registry4();
|
|
23964
23748
|
init_tool_names();
|
|
@@ -23970,14 +23754,6 @@ var init_tool_execution_control = __esm(() => {
|
|
|
23970
23754
|
this.name = "ToolDeadlineError";
|
|
23971
23755
|
}
|
|
23972
23756
|
};
|
|
23973
|
-
ToolScopeQuarantinedError = class ToolScopeQuarantinedError extends Error {
|
|
23974
|
-
constructor(scope, cause) {
|
|
23975
|
-
super(`Tool concurrency scope ${scope} is quarantined after: ${cause.message}`);
|
|
23976
|
-
this.scope = scope;
|
|
23977
|
-
this.cause = cause;
|
|
23978
|
-
this.name = "ToolScopeQuarantinedError";
|
|
23979
|
-
}
|
|
23980
|
-
};
|
|
23981
23757
|
});
|
|
23982
23758
|
|
|
23983
23759
|
// src/agent-core/tool-input-validation.ts
|
|
@@ -24105,7 +23881,8 @@ function normalizeToolResult(result, input) {
|
|
|
24105
23881
|
return normalized;
|
|
24106
23882
|
const rawOutput = normalized.output;
|
|
24107
23883
|
const sanitizedOutput = sanitizeToolOutput(rawOutput);
|
|
24108
|
-
|
|
23884
|
+
const binaryLike = isBinaryLike(rawOutput);
|
|
23885
|
+
if (!binaryLike && Buffer.byteLength(sanitizedOutput, "utf8") <= TOOL_OUTPUT_LIMITS.bytes) {
|
|
24109
23886
|
return sanitizedOutput === rawOutput ? normalized : {
|
|
24110
23887
|
...normalized,
|
|
24111
23888
|
output: sanitizedOutput
|
|
@@ -24116,6 +23893,20 @@ function normalizeToolResult(result, input) {
|
|
|
24116
23893
|
toolCallId: input.toolCallId,
|
|
24117
23894
|
content: rawOutput
|
|
24118
23895
|
});
|
|
23896
|
+
if (binaryLike && Buffer.byteLength(sanitizedOutput, "utf8") <= TOOL_OUTPUT_LIMITS.bytes) {
|
|
23897
|
+
return {
|
|
23898
|
+
...normalized,
|
|
23899
|
+
output: `${sanitizedOutput}
|
|
23900
|
+
|
|
23901
|
+
[full raw output stored as artifact ${artifact.id}; read it with tool_output_read]`,
|
|
23902
|
+
outputArtifactId: artifact.id,
|
|
23903
|
+
metadata: {
|
|
23904
|
+
...normalized.metadata ?? {},
|
|
23905
|
+
outputArtifact: artifact,
|
|
23906
|
+
binaryLike: true
|
|
23907
|
+
}
|
|
23908
|
+
};
|
|
23909
|
+
}
|
|
24119
23910
|
const head = takeBytes(sanitizedOutput, TOOL_OUTPUT_LIMITS.headBytes, "head");
|
|
24120
23911
|
const tail = takeBytes(sanitizedOutput, TOOL_OUTPUT_LIMITS.tailBytes, "tail");
|
|
24121
23912
|
return {
|
|
@@ -24551,7 +24342,11 @@ class AgentRuntime {
|
|
|
24551
24342
|
});
|
|
24552
24343
|
}
|
|
24553
24344
|
if (!session.archivedAt) {
|
|
24554
|
-
this.userInputs.recover(session.id, this.store.listEvents(session.id, 1e4));
|
|
24345
|
+
const pendingUserInput = this.userInputs.recover(session.id, this.store.listEvents(session.id, 1e4));
|
|
24346
|
+
const latestInterruptedTurn = [...turns].reverse().find((turn) => newlyInterruptedTurns.has(turn.id));
|
|
24347
|
+
if (latestInterruptedTurn && !pendingUserInput && !this.mailbox.hasQueued(session.id)) {
|
|
24348
|
+
this.inputQueue.enqueueFollowup(session.id, ["Continue the task that was interrupted by the runtime restart.", "Use the durable transcript and tool results as the source of truth.", "Do not blindly replay mutating calls; inspect current state first, then resume from the next useful action."].join(" "), "plain", `runtime-recovery:${latestInterruptedTurn.id}`);
|
|
24349
|
+
}
|
|
24555
24350
|
}
|
|
24556
24351
|
}
|
|
24557
24352
|
for (const job of this.store.listRecoverableJobs()) {
|
|
@@ -24564,7 +24359,7 @@ class AgentRuntime {
|
|
|
24564
24359
|
continue;
|
|
24565
24360
|
}
|
|
24566
24361
|
}
|
|
24567
|
-
this.jobs.markLost(job.id, "Background execution owner was lost during runtime restart.
|
|
24362
|
+
this.jobs.markLost(job.id, "Background execution owner was lost during runtime restart. Durable session work is resumable, but the original in-memory process cannot be reattached.", job.agentMode !== "attached");
|
|
24568
24363
|
}
|
|
24569
24364
|
for (const job of this.store.listTerminalJobsMissingMailbox())
|
|
24570
24365
|
this.jobs.repairTerminalMailbox(job.id);
|
|
@@ -25551,8 +25346,8 @@ class AgentRuntime {
|
|
|
25551
25346
|
});
|
|
25552
25347
|
let autoContinueStreak = 0;
|
|
25553
25348
|
let resumeAfterCompaction = false;
|
|
25554
|
-
const maxSteps =
|
|
25555
|
-
const maxTurnMs =
|
|
25349
|
+
const maxSteps = this.maxSteps;
|
|
25350
|
+
const maxTurnMs = this.maxTurnMs;
|
|
25556
25351
|
const loopStartedAt = Date.now();
|
|
25557
25352
|
let timeBudgetWarned = false;
|
|
25558
25353
|
let loopError;
|
|
@@ -25590,7 +25385,7 @@ class AgentRuntime {
|
|
|
25590
25385
|
responses.push(...await this.forceTimeLimitWrapUp(session, turn, assistantMessage, planner, maxTurnMs));
|
|
25591
25386
|
break;
|
|
25592
25387
|
}
|
|
25593
|
-
if (!timeBudgetWarned && elapsedMs >= maxTurnMs * 0.75) {
|
|
25388
|
+
if (Number.isFinite(maxTurnMs) && !timeBudgetWarned && elapsedMs >= maxTurnMs * 0.75) {
|
|
25594
25389
|
timeBudgetWarned = true;
|
|
25595
25390
|
const secondsLeft = Math.max(1, Math.ceil((maxTurnMs - elapsedMs) / 1000));
|
|
25596
25391
|
const queue = this.pendingSteeringContext.get(session.id) ?? [];
|
|
@@ -25601,10 +25396,7 @@ class AgentRuntime {
|
|
|
25601
25396
|
stepCount: step + 1
|
|
25602
25397
|
});
|
|
25603
25398
|
if (step >= maxSteps) {
|
|
25604
|
-
|
|
25605
|
-
responses.push(...await this.forceStepLimitWrapUp(session, turn, assistantMessage, planner, maxSteps));
|
|
25606
|
-
else
|
|
25607
|
-
this.stopTurn(turn, "completed", "no_actions");
|
|
25399
|
+
responses.push(...await this.forceStepLimitWrapUp(session, turn, assistantMessage, planner, maxSteps));
|
|
25608
25400
|
break;
|
|
25609
25401
|
}
|
|
25610
25402
|
const compactResult = await this.maybeAutoCompact(session, planner);
|
|
@@ -25697,7 +25489,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
25697
25489
|
contextWindow: resolveContextWindow(planner.contextWindow),
|
|
25698
25490
|
maxOutputTokens: resolveMaxOutputTokens(planner.maxOutputTokens),
|
|
25699
25491
|
...this.contextBudgetInput(),
|
|
25700
|
-
toolsEnabled:
|
|
25492
|
+
toolsEnabled: true,
|
|
25701
25493
|
extraBlocks: [...passiveCompletions ? [{
|
|
25702
25494
|
title: "Completed Background Work",
|
|
25703
25495
|
body: passiveCompletions,
|
|
@@ -25770,14 +25562,14 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
25770
25562
|
contextBlocks: context.contextBlocks,
|
|
25771
25563
|
tools: context.tools,
|
|
25772
25564
|
toolCatalog: context.toolCatalog,
|
|
25773
|
-
toolChoice:
|
|
25565
|
+
toolChoice: "auto"
|
|
25774
25566
|
};
|
|
25775
25567
|
resumeAfterCompaction = false;
|
|
25776
25568
|
const autoContinue = {
|
|
25777
25569
|
streak: autoContinueStreak
|
|
25778
25570
|
};
|
|
25779
25571
|
const remainingTurnMs = Number.isFinite(maxTurnMs) ? Math.max(0, maxTurnMs - (Date.now() - loopStartedAt)) : undefined;
|
|
25780
|
-
const control = chatProvider ? await this.streamStep(chatProvider, plannerInput, session, turn, assistantMessage, planner.name, step, context.manifest, responses, autoContinue, userAuthored, remainingTurnMs) : await this.batchStep(planner, plannerInput, session, turn, assistantMessage, step, context.manifest, responses, autoContinue,
|
|
25572
|
+
const control = chatProvider ? await this.streamStep(chatProvider, plannerInput, session, turn, assistantMessage, planner.name, step, context.manifest, responses, autoContinue, userAuthored, remainingTurnMs) : await this.batchStep(planner, plannerInput, session, turn, assistantMessage, step, context.manifest, responses, autoContinue, remainingTurnMs);
|
|
25781
25573
|
autoContinueStreak = autoContinue.streak;
|
|
25782
25574
|
if (control.cancelled)
|
|
25783
25575
|
return responses.join(`
|
|
@@ -25900,10 +25692,10 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
25900
25692
|
latencyMs
|
|
25901
25693
|
});
|
|
25902
25694
|
}
|
|
25903
|
-
async batchStep(planner, plannerInput, session, turn, assistantMessage, step, context, responses, autoContinue,
|
|
25695
|
+
async batchStep(planner, plannerInput, session, turn, assistantMessage, step, context, responses, autoContinue, modelTimeoutMs) {
|
|
25904
25696
|
let actions;
|
|
25905
25697
|
try {
|
|
25906
|
-
actions = await this.planWithRetry(planner, plannerInput, session, turn, assistantMessage, context, modelTimeoutMs
|
|
25698
|
+
actions = await this.planWithRetry(planner, plannerInput, session, turn, assistantMessage, context, modelTimeoutMs);
|
|
25907
25699
|
} catch (error) {
|
|
25908
25700
|
if (error instanceof ModelCallDeadlineError)
|
|
25909
25701
|
return {
|
|
@@ -25947,10 +25739,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
25947
25739
|
};
|
|
25948
25740
|
for (const action of actions) {
|
|
25949
25741
|
if (action.kind === "tool") {
|
|
25950
|
-
|
|
25951
|
-
toolBatch.push(action);
|
|
25952
|
-
else
|
|
25953
|
-
this.recordDisabledToolCall(session, turn, assistantMessage, step, action.toolCallId ?? action.tool, action.tool, action.args);
|
|
25742
|
+
toolBatch.push(action);
|
|
25954
25743
|
continue;
|
|
25955
25744
|
}
|
|
25956
25745
|
if (await flushToolBatch())
|
|
@@ -26090,10 +25879,6 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
26090
25879
|
this.recordToolParseError(session, turn, assistantMessage, step, toolCallId ?? toolName, toolName, error instanceof Error ? error.message : String(error), event.arguments);
|
|
26091
25880
|
continue;
|
|
26092
25881
|
}
|
|
26093
|
-
if (!userAuthored) {
|
|
26094
|
-
this.recordDisabledToolCall(session, turn, assistantMessage, step, toolCallId ?? toolName, toolName, args);
|
|
26095
|
-
continue;
|
|
26096
|
-
}
|
|
26097
25882
|
const action = {
|
|
26098
25883
|
kind: "tool",
|
|
26099
25884
|
tool: toolName,
|
|
@@ -26398,10 +26183,6 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
26398
26183
|
}
|
|
26399
26184
|
}
|
|
26400
26185
|
async applyRespond(session, turn, assistantMessage, plannerName, text, truncated, recoverable, responses, autoContinue) {
|
|
26401
|
-
if (this.isRedundantAgentTaskResponse(session.id, turn.id, text)) {
|
|
26402
|
-
this.discardStreamingText(turn.id);
|
|
26403
|
-
return false;
|
|
26404
|
-
}
|
|
26405
26186
|
responses.push(text);
|
|
26406
26187
|
const streamed = this.streamingParts.get(turn.id);
|
|
26407
26188
|
if (streamed?.textPartId) {
|
|
@@ -26421,29 +26202,11 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
26421
26202
|
recoverable
|
|
26422
26203
|
});
|
|
26423
26204
|
if (truncated || recoverable) {
|
|
26424
|
-
|
|
26425
|
-
|
|
26426
|
-
return true;
|
|
26427
|
-
}
|
|
26428
|
-
const reason = truncated ? `kept getting cut off by its token limit ${MAX_RECOVERABLE_AUTO_CONTINUE} times in a row. Consider raising maxOutputTokens in ~/.local/pajarori/farai/config.toml or asking a smaller follow-up question` : `kept failing to produce a usable response ${MAX_RECOVERABLE_AUTO_CONTINUE} times in a row`;
|
|
26429
|
-
const notice = `(Model ${reason} \u2014 stopping auto-continue.)`;
|
|
26430
|
-
responses.push(notice);
|
|
26431
|
-
this.persistTextPart(session.id, turn.id, assistantMessage.id, notice);
|
|
26432
|
-
this.event(session.id, "text", {
|
|
26433
|
-
role: "assistant",
|
|
26434
|
-
text: notice,
|
|
26435
|
-
planner: plannerName
|
|
26436
|
-
});
|
|
26205
|
+
autoContinue.streak += 1;
|
|
26206
|
+
return true;
|
|
26437
26207
|
}
|
|
26438
26208
|
return false;
|
|
26439
26209
|
}
|
|
26440
|
-
isRedundantAgentTaskResponse(sessionId, turnId, text) {
|
|
26441
|
-
const candidate2 = comparableProse(text);
|
|
26442
|
-
if (candidate2.length < 40)
|
|
26443
|
-
return false;
|
|
26444
|
-
const outputs = this.store.listMessages(sessionId, 200).flatMap((message) => message.parts).filter((part) => part.turnId === turnId && part.type === "tool_result").flatMap((part) => agentTaskOutput(part.payload));
|
|
26445
|
-
return outputs.some((output) => substantiallySameProse(candidate2, comparableProse(output)));
|
|
26446
|
-
}
|
|
26447
26210
|
recordToolParseError(session, turn, assistantMessage, step, toolCallId, tool, error, rawArguments) {
|
|
26448
26211
|
const text = `Could not parse arguments for ${tool}: ${error}. Raw: ${rawArguments.slice(0, 500)}`;
|
|
26449
26212
|
this.store.addPart({
|
|
@@ -26486,48 +26249,6 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
26486
26249
|
payload
|
|
26487
26250
|
});
|
|
26488
26251
|
}
|
|
26489
|
-
recordDisabledToolCall(session, turn, assistantMessage, step, toolCallId, tool, args) {
|
|
26490
|
-
const text = `Tool ${tool} was not executed because this is a bounded text-only completion turn.`;
|
|
26491
|
-
this.store.addPart({
|
|
26492
|
-
sessionId: session.id,
|
|
26493
|
-
turnId: turn.id,
|
|
26494
|
-
messageId: assistantMessage.id,
|
|
26495
|
-
type: "tool_call",
|
|
26496
|
-
payload: {
|
|
26497
|
-
record: {
|
|
26498
|
-
id: toolCallId,
|
|
26499
|
-
tool,
|
|
26500
|
-
args
|
|
26501
|
-
}
|
|
26502
|
-
}
|
|
26503
|
-
});
|
|
26504
|
-
this.store.addPart({
|
|
26505
|
-
sessionId: session.id,
|
|
26506
|
-
turnId: turn.id,
|
|
26507
|
-
messageId: assistantMessage.id,
|
|
26508
|
-
type: "tool_result",
|
|
26509
|
-
payload: {
|
|
26510
|
-
toolCallId,
|
|
26511
|
-
tool,
|
|
26512
|
-
result: text
|
|
26513
|
-
}
|
|
26514
|
-
});
|
|
26515
|
-
const payload = {
|
|
26516
|
-
turnId: turn.id,
|
|
26517
|
-
step,
|
|
26518
|
-
tool,
|
|
26519
|
-
error: text,
|
|
26520
|
-
recoverable: false
|
|
26521
|
-
};
|
|
26522
|
-
this.event(session.id, "planner_error", payload);
|
|
26523
|
-
this.store.addPart({
|
|
26524
|
-
sessionId: session.id,
|
|
26525
|
-
turnId: turn.id,
|
|
26526
|
-
messageId: assistantMessage.id,
|
|
26527
|
-
type: "planner_error",
|
|
26528
|
-
payload
|
|
26529
|
-
});
|
|
26530
|
-
}
|
|
26531
26252
|
async forceStepLimitWrapUp(session, turn, assistantMessage, planner, maxSteps) {
|
|
26532
26253
|
return this.forceTextOnlyWrapUp({
|
|
26533
26254
|
session,
|
|
@@ -26680,25 +26401,6 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
26680
26401
|
shouldContinue: !sawResponse
|
|
26681
26402
|
};
|
|
26682
26403
|
}
|
|
26683
|
-
if (action.tool === "subdomain_enum") {
|
|
26684
|
-
const duplicate = this.store.listToolCalls(session.id, 200).find((call2) => call2.turnId === turn.id && call2.tool === action.tool && (call2.status === "done" || call2.status === "error") && stableValue(call2.args) === stableValue(action.args));
|
|
26685
|
-
if (duplicate) {
|
|
26686
|
-
const text = `Equivalent ${action.tool} already finished in this turn as ${duplicate.id}; reuse its source statuses and names instead of retrying.`;
|
|
26687
|
-
this.event(session.id, "planner_error", {
|
|
26688
|
-
turnId: turn.id,
|
|
26689
|
-
step,
|
|
26690
|
-
tool: action.tool,
|
|
26691
|
-
error: text,
|
|
26692
|
-
recoverable: true,
|
|
26693
|
-
policy: "duplicate_terminal_tool",
|
|
26694
|
-
duplicateSuppressed: true,
|
|
26695
|
-
duplicateToolCallId: duplicate.id
|
|
26696
|
-
});
|
|
26697
|
-
return {
|
|
26698
|
-
shouldContinue: true
|
|
26699
|
-
};
|
|
26700
|
-
}
|
|
26701
|
-
}
|
|
26702
26404
|
const validationError = validateToolArgs(tool.inputSchema, action.args);
|
|
26703
26405
|
if (validationError) {
|
|
26704
26406
|
const toolCallId = action.toolCallId ?? action.tool;
|
|
@@ -27256,25 +26958,18 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27256
26958
|
const workspaceTransition = isWorkspaceTransitionTool(schedulingTool);
|
|
27257
26959
|
const gateSignal = signal ? AbortSignal.any([signal, this.shutdownController.signal]) : this.shutdownController.signal;
|
|
27258
26960
|
try {
|
|
27259
|
-
return await this.workspaceBindingGate.run(`session-workspace:${session.id}`, !workspaceTransition, async (
|
|
26961
|
+
return await this.workspaceBindingGate.run(`session-workspace:${session.id}`, !workspaceTransition, async () => {
|
|
27260
26962
|
session = this.store.loadSession(session.id);
|
|
27261
26963
|
tool = toolForExecution(session, toolName);
|
|
27262
26964
|
schedulingTool = toolSchedulingDefinition(tool, args, session);
|
|
27263
|
-
return await this.toolExecutionGate.run(toolConcurrencyKey(schedulingTool, session, session.workspace), schedulingTool.parallel, async (
|
|
27264
|
-
gateLease.mirrorTo(bindingLease);
|
|
26965
|
+
return await this.toolExecutionGate.run(toolConcurrencyKey(schedulingTool, session, session.workspace), schedulingTool.parallel, async () => {
|
|
27265
26966
|
gateSignal.throwIfAborted();
|
|
27266
26967
|
if (owner && this.store.loadTurn(owner.turn.id).status === "cancelled")
|
|
27267
26968
|
throw new Error("turn cancelled before tool start");
|
|
27268
|
-
return await this.runToolUnderGate(session, tool, args, owner, providerToolCallId
|
|
26969
|
+
return await this.runToolUnderGate(session, tool, args, owner, providerToolCallId);
|
|
27269
26970
|
}, gateSignal);
|
|
27270
26971
|
}, gateSignal);
|
|
27271
26972
|
} catch (error) {
|
|
27272
|
-
if (error instanceof ToolScopeQuarantinedError) {
|
|
27273
|
-
return this.recordRejectedToolCall(session, tool, args, error.message, {
|
|
27274
|
-
quarantined: true,
|
|
27275
|
-
reason: "concurrency_scope_quarantined"
|
|
27276
|
-
}, owner, providerToolCallId);
|
|
27277
|
-
}
|
|
27278
26973
|
const ownerCancelled = owner ? this.store.loadTurn(owner.turn.id).status === "cancelled" : false;
|
|
27279
26974
|
if (gateSignal.aborted || ownerCancelled) {
|
|
27280
26975
|
const message = gateSignal.reason ? String(gateSignal.reason) : "turn cancelled before tool start";
|
|
@@ -27304,7 +26999,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27304
26999
|
});
|
|
27305
27000
|
return this.toolCalls.settleError(toolCall, message, state);
|
|
27306
27001
|
}
|
|
27307
|
-
async runToolUnderGate(session, tool, args, owner, providerToolCallId
|
|
27002
|
+
async runToolUnderGate(session, tool, args, owner, providerToolCallId) {
|
|
27308
27003
|
const toolCall = this.toolCalls.begin({
|
|
27309
27004
|
sessionId: session.id,
|
|
27310
27005
|
tool: tool.name,
|
|
@@ -27319,7 +27014,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27319
27014
|
providerToolCallId
|
|
27320
27015
|
} : {}
|
|
27321
27016
|
});
|
|
27322
|
-
await this.executeToolUnderGate(session, toolCall.id, owner
|
|
27017
|
+
await this.executeToolUnderGate(session, toolCall.id, owner);
|
|
27323
27018
|
return this.store.loadToolCall(toolCall.id);
|
|
27324
27019
|
}
|
|
27325
27020
|
async executeTool(session, toolCallId, owner) {
|
|
@@ -27337,29 +27032,18 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27337
27032
|
const gateController = turnId ? this.registerTurnController(turnId) : undefined;
|
|
27338
27033
|
const gateSignal = gateController ? AbortSignal.any([gateController.signal, this.shutdownController.signal]) : this.shutdownController.signal;
|
|
27339
27034
|
try {
|
|
27340
|
-
return await this.workspaceBindingGate.run(`session-workspace:${session.id}`, !workspaceTransition, async (
|
|
27035
|
+
return await this.workspaceBindingGate.run(`session-workspace:${session.id}`, !workspaceTransition, async () => {
|
|
27341
27036
|
session = this.store.loadSession(session.id);
|
|
27342
27037
|
tool = toolForExecution(session, toolCall.tool);
|
|
27343
27038
|
schedulingTool = toolSchedulingDefinition(tool, toolCall.args, session);
|
|
27344
|
-
return await this.toolExecutionGate.run(toolConcurrencyKey(schedulingTool, session, session.workspace), schedulingTool.parallel, async (
|
|
27345
|
-
gateLease.mirrorTo(bindingLease);
|
|
27039
|
+
return await this.toolExecutionGate.run(toolConcurrencyKey(schedulingTool, session, session.workspace), schedulingTool.parallel, async () => {
|
|
27346
27040
|
gateSignal.throwIfAborted();
|
|
27347
27041
|
if (turnId && this.store.loadTurn(turnId).status === "cancelled")
|
|
27348
27042
|
throw new Error("turn cancelled before tool start");
|
|
27349
|
-
return await this.executeToolUnderGate(session, toolCallId, owner
|
|
27043
|
+
return await this.executeToolUnderGate(session, toolCallId, owner);
|
|
27350
27044
|
}, gateSignal);
|
|
27351
27045
|
}, gateSignal);
|
|
27352
27046
|
} catch (error) {
|
|
27353
|
-
if (error instanceof ToolScopeQuarantinedError) {
|
|
27354
|
-
const rejected = this.store.loadToolCall(toolCallId);
|
|
27355
|
-
if (rejected.status === "pending") {
|
|
27356
|
-
return this.toolCalls.settleError(rejected, error.message, {
|
|
27357
|
-
quarantined: true,
|
|
27358
|
-
reason: "concurrency_scope_quarantined"
|
|
27359
|
-
});
|
|
27360
|
-
}
|
|
27361
|
-
return rejected;
|
|
27362
|
-
}
|
|
27363
27047
|
if (!gateSignal.aborted && (!turnId || this.store.loadTurn(turnId).status !== "cancelled"))
|
|
27364
27048
|
throw error;
|
|
27365
27049
|
const cancelled = this.store.loadToolCall(toolCallId);
|
|
@@ -27376,7 +27060,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27376
27060
|
gateController?.release();
|
|
27377
27061
|
}
|
|
27378
27062
|
}
|
|
27379
|
-
async executeToolUnderGate(session, toolCallId, owner
|
|
27063
|
+
async executeToolUnderGate(session, toolCallId, owner) {
|
|
27380
27064
|
let toolCall = this.store.loadToolCall(toolCallId);
|
|
27381
27065
|
if (toolCall.status !== "pending")
|
|
27382
27066
|
return toolCall;
|
|
@@ -27664,7 +27348,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27664
27348
|
...nestedContext,
|
|
27665
27349
|
signal: nestedDeadline.signal,
|
|
27666
27350
|
timeoutMs: toolOperationTimeout(target.timeoutMs)
|
|
27667
|
-
})
|
|
27351
|
+
}));
|
|
27668
27352
|
lease.assertActive();
|
|
27669
27353
|
await this.fireHooks(session, "tool.post", canonicalName, {
|
|
27670
27354
|
tool: canonicalName,
|
|
@@ -27724,22 +27408,10 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27724
27408
|
linkToolCall = true
|
|
27725
27409
|
}) => {
|
|
27726
27410
|
lease.assertActive();
|
|
27727
|
-
let depth = 0;
|
|
27728
|
-
let parentId = session.parentId;
|
|
27729
|
-
while (parentId) {
|
|
27730
|
-
depth += 1;
|
|
27731
|
-
parentId = this.store.loadSession(parentId).parentId;
|
|
27732
|
-
if (depth > 4)
|
|
27733
|
-
break;
|
|
27734
|
-
}
|
|
27735
|
-
if (depth >= 1)
|
|
27736
|
-
throw new Error("nested campaign delegation is disabled; child workers are leaf agents");
|
|
27737
|
-
if (resumeSessionId && (lane || tools || model))
|
|
27738
|
-
throw new Error("resumed subagents preserve their original lane, model, and tool scope");
|
|
27739
27411
|
const previousJob = resumeSessionId ? this.store.listJobs(session.id, 1e4).find((job2) => job2.kind === "agent" && job2.childSessionId === resumeSessionId) : undefined;
|
|
27740
27412
|
const effectiveLane = lane ?? previousJob?.lane;
|
|
27741
27413
|
const laneDef = effectiveLane ? resolveLane(this.workspace, effectiveLane) : undefined;
|
|
27742
|
-
if (
|
|
27414
|
+
if (effectiveLane && !laneDef)
|
|
27743
27415
|
throw new Error(`unknown subagent lane: ${effectiveLane}`);
|
|
27744
27416
|
let child;
|
|
27745
27417
|
let scopedTools;
|
|
@@ -27753,10 +27425,22 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27753
27425
|
const active = this.store.listJobs(session.id, 1e4).some((job2) => job2.kind === "agent" && job2.childSessionId === child.id && ["created", "starting", "running", "cancelling"].includes(job2.status));
|
|
27754
27426
|
if (active || this.hasRunningTurn(child.id))
|
|
27755
27427
|
throw new Error(`subagent session ${child.id} is already running`);
|
|
27756
|
-
|
|
27428
|
+
const requestedTools = tools ?? laneDef?.tools;
|
|
27429
|
+
scopedTools = requestedTools ? resolveSubagentToolScope({
|
|
27430
|
+
parent: session,
|
|
27431
|
+
availableTools: listToolsForSession(session),
|
|
27432
|
+
requestedTools
|
|
27433
|
+
}) : child.toolScope;
|
|
27757
27434
|
editsSharedWorkspace = hasSharedWorkspaceEdits(scopedTools);
|
|
27758
|
-
|
|
27759
|
-
|
|
27435
|
+
const childModel = model ?? laneDef?.model;
|
|
27436
|
+
if (childModel && childModel !== child.model)
|
|
27437
|
+
child = this.updateSession(child.id, {
|
|
27438
|
+
model: childModel
|
|
27439
|
+
});
|
|
27440
|
+
if (requestedTools && scopedTools?.length)
|
|
27441
|
+
child = this.updateSession(child.id, {
|
|
27442
|
+
toolScope: scopedTools
|
|
27443
|
+
});
|
|
27760
27444
|
} else {
|
|
27761
27445
|
const requestedTools = tools ?? laneDef?.tools;
|
|
27762
27446
|
scopedTools = resolveSubagentToolScope({
|
|
@@ -27767,8 +27451,6 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27767
27451
|
} : {}
|
|
27768
27452
|
});
|
|
27769
27453
|
editsSharedWorkspace = hasSharedWorkspaceEdits(scopedTools);
|
|
27770
|
-
if (mode === "detached" && editsSharedWorkspace)
|
|
27771
|
-
throw new Error("detached subagents cannot hold shared workspace edit tools; use an attached code worker or a read-only lane");
|
|
27772
27454
|
const childModel = model ?? laneDef?.model;
|
|
27773
27455
|
child = await this.store.forkSession(session.id, title);
|
|
27774
27456
|
this.recordSession(child);
|
|
@@ -27876,8 +27558,8 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27876
27558
|
this.jobs.completeAgent(job.id, response, mode === "detached");
|
|
27877
27559
|
return response;
|
|
27878
27560
|
};
|
|
27879
|
-
const gated = () => this.subagentGate.run(execute, agentController.signal);
|
|
27880
|
-
return editsSharedWorkspace ? await this.subagentWorkspaceMutationGate.run(gated, agentController.signal) : await gated();
|
|
27561
|
+
const gated = () => session.parentId ? execute() : this.subagentGate.run(execute, agentController.signal);
|
|
27562
|
+
return editsSharedWorkspace && !session.parentId ? await this.subagentWorkspaceMutationGate.run(gated, agentController.signal) : await gated();
|
|
27881
27563
|
} catch (error) {
|
|
27882
27564
|
if (this.store.isOpen() && this.hasJob(job.id)) {
|
|
27883
27565
|
const current = this.store.loadJob(job.id);
|
|
@@ -27931,7 +27613,6 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27931
27613
|
this.turnControllers.delete(turnId);
|
|
27932
27614
|
};
|
|
27933
27615
|
let result;
|
|
27934
|
-
const duplicate = findEquivalentBackgroundJob(activeBackgroundJobs(this.store.listToolCalls(session.id, 200)), toolCall.tool, toolCall.args);
|
|
27935
27616
|
await this.fireHooks(session, "tool.pre", toolCall.tool, {
|
|
27936
27617
|
tool: toolCall.tool,
|
|
27937
27618
|
toolCallId: toolCall.id,
|
|
@@ -27939,16 +27620,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27939
27620
|
});
|
|
27940
27621
|
try {
|
|
27941
27622
|
lease.assertActive();
|
|
27942
|
-
result =
|
|
27943
|
-
ok: true,
|
|
27944
|
-
summary: `Equivalent background job already running: processId=${duplicate.processId}`,
|
|
27945
|
-
output: `Reusing ${duplicate.toolCallId}. Poll ${duplicate.processId} with session_poll instead of starting it again.`,
|
|
27946
|
-
status: "running_background",
|
|
27947
|
-
processId: duplicate.processId,
|
|
27948
|
-
metadata: {
|
|
27949
|
-
reusedBackgroundToolCallId: duplicate.toolCallId
|
|
27950
|
-
}
|
|
27951
|
-
} : await deadline.run(() => tool.run(toolCall.args, context), gateLease);
|
|
27623
|
+
result = await deadline.run(() => tool.run(toolCall.args, context));
|
|
27952
27624
|
} catch (error) {
|
|
27953
27625
|
settleLiveOutput();
|
|
27954
27626
|
releaseController();
|
|
@@ -28609,38 +28281,6 @@ function completedToolCallStatus(result) {
|
|
|
28609
28281
|
return "running_background";
|
|
28610
28282
|
return result.ok ? "done" : "error";
|
|
28611
28283
|
}
|
|
28612
|
-
function agentTaskOutput(payload) {
|
|
28613
|
-
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
28614
|
-
return [];
|
|
28615
|
-
const record2 = payload;
|
|
28616
|
-
if (!["agent_task", "agent_spawn", "agent_followup"].includes(String(record2.tool)))
|
|
28617
|
-
return [];
|
|
28618
|
-
const toolResult = record2.toolResult;
|
|
28619
|
-
if (!toolResult || typeof toolResult !== "object" || Array.isArray(toolResult))
|
|
28620
|
-
return [];
|
|
28621
|
-
const output = toolResult.output;
|
|
28622
|
-
return typeof output === "string" && output.trim() ? [output] : [];
|
|
28623
|
-
}
|
|
28624
|
-
function comparableProse(value) {
|
|
28625
|
-
return value.toLowerCase().replace(/[`*_>#|()[\]{}]/g, " ").replace(/[^a-z0-9./:_-]+/g, " ").replace(/\s+/g, " ").trim();
|
|
28626
|
-
}
|
|
28627
|
-
function substantiallySameProse(left, right) {
|
|
28628
|
-
if (right.length < 40)
|
|
28629
|
-
return false;
|
|
28630
|
-
const shorter = Math.min(left.length, right.length);
|
|
28631
|
-
const longer = Math.max(left.length, right.length);
|
|
28632
|
-
if ((left.includes(right) || right.includes(left)) && shorter / longer >= 0.72)
|
|
28633
|
-
return true;
|
|
28634
|
-
const leftWords = new Set(left.split(" ").filter((word) => word.length > 2));
|
|
28635
|
-
const rightWords = new Set(right.split(" ").filter((word) => word.length > 2));
|
|
28636
|
-
if (leftWords.size < 8 || rightWords.size < 8)
|
|
28637
|
-
return false;
|
|
28638
|
-
let shared = 0;
|
|
28639
|
-
for (const word of leftWords)
|
|
28640
|
-
if (rightWords.has(word))
|
|
28641
|
-
shared += 1;
|
|
28642
|
-
return shared / leftWords.size >= 0.88 && shared / rightWords.size >= 0.88;
|
|
28643
|
-
}
|
|
28644
28284
|
function recoveredJobSummary(job) {
|
|
28645
28285
|
if (job.result && typeof job.result === "object") {
|
|
28646
28286
|
if ("response" in job.result)
|
|
@@ -28765,7 +28405,7 @@ function sameExistingPath(left, right) {
|
|
|
28765
28405
|
return false;
|
|
28766
28406
|
}
|
|
28767
28407
|
}
|
|
28768
|
-
var REASONING_MAX_BYTES2, STREAM_RENDER_INTERVAL_MS = 100, STREAM_PERSIST_INTERVAL_MS = 2000,
|
|
28408
|
+
var REASONING_MAX_BYTES2, STREAM_RENDER_INTERVAL_MS = 100, STREAM_PERSIST_INTERVAL_MS = 2000, LIVE_OUTPUT_MAX_BYTES, LIVE_OUTPUT_FLUSH_INTERVAL_MS = 150, LIVE_OUTPUT_INITIAL_DELAY_MS = 320, TOOL_HUMAN_RESULT_MAX_BYTES, LOOP_SUPERVISION_NO_PROGRESS_STEPS = 12, LOOP_SUPERVISION_STEER_INTERVAL = 5, LOOP_PATTERN_MAX_PERIOD = 8, PROGRESS_ACTION_TOOLS, AUTO_COMPACTION_CONTINUATION = "[internal continuation after context compaction: Continue the active user task from the compacted prior context. Do not repeat, regenerate, or explain the summary. Resume with the exact next useful action.]", WRAPUP_MODEL_TIMEOUT_MS = 15000, DEFAULT_SHUTDOWN_GRACE_PERIOD_MS = 2000, RUNTIME_LEASE_MS = 60000, RUNTIME_HEARTBEAT_MS = 15000, RESTART_TOOL_ERROR = "Interrupted by runtime restart; tool execution was not replayed.", STEP_LIMIT_WRAPUP_DIRECTIVE = "You have reached the maximum number of steps allowed for this turn, so tools are no longer available. Do not attempt to call any tool. In a few sentences, summarize what you accomplished, the key findings or evidence so far, any blockers, and the single most useful next step. This is your final message for this turn.", TIME_LIMIT_WRAPUP_DIRECTIVE = "You have reached the interactive wall-clock budget for this turn, so tools are no longer available. Do not attempt to call any tool. Concisely summarize completed work, proven evidence, active background jobs, remaining uncertainty, and the single best next action. This is your final message for this turn.", IneffectiveCompactionError, ModelCallDeadlineError, PlannerOutputValidationError;
|
|
28769
28409
|
var init_runtime = __esm(() => {
|
|
28770
28410
|
init_sqlite_store();
|
|
28771
28411
|
init_registry4();
|
|
@@ -28832,6 +28472,24 @@ var init_runtime = __esm(() => {
|
|
|
28832
28472
|
};
|
|
28833
28473
|
});
|
|
28834
28474
|
|
|
28475
|
+
// src/branding.ts
|
|
28476
|
+
import figlet from "figlet";
|
|
28477
|
+
function renderFaraiBanner() {
|
|
28478
|
+
try {
|
|
28479
|
+
return figlet.textSync("farai", {
|
|
28480
|
+
font: "Ogre"
|
|
28481
|
+
}).trimEnd();
|
|
28482
|
+
} catch {
|
|
28483
|
+
return "farai";
|
|
28484
|
+
}
|
|
28485
|
+
}
|
|
28486
|
+
var FARAI_BANNER, FARAI_BANNER_LINES;
|
|
28487
|
+
var init_branding = __esm(() => {
|
|
28488
|
+
FARAI_BANNER = renderFaraiBanner();
|
|
28489
|
+
FARAI_BANNER_LINES = FARAI_BANNER.split(`
|
|
28490
|
+
`);
|
|
28491
|
+
});
|
|
28492
|
+
|
|
28835
28493
|
// src/agent-knowledge/pack.ts
|
|
28836
28494
|
import { existsSync as existsSync13, mkdirSync as mkdirSync7, readdirSync as readdirSync4, readFileSync as readFileSync12, renameSync as renameSync4, rmSync, writeFileSync as writeFileSync8 } from "fs";
|
|
28837
28495
|
import { createHash as createHash5 } from "crypto";
|
|
@@ -28934,10 +28592,10 @@ function extractEntities(recordId2, text) {
|
|
|
28934
28592
|
const found = new Set;
|
|
28935
28593
|
const out = [];
|
|
28936
28594
|
for (const pattern of ENTITY_PATTERNS) {
|
|
28937
|
-
const
|
|
28938
|
-
if (!
|
|
28595
|
+
const matches = text.match(pattern.re);
|
|
28596
|
+
if (!matches)
|
|
28939
28597
|
continue;
|
|
28940
|
-
for (const raw of
|
|
28598
|
+
for (const raw of matches) {
|
|
28941
28599
|
const value = pattern.normalize ? pattern.normalize(raw) : raw;
|
|
28942
28600
|
const key = `${pattern.type}:${value}`;
|
|
28943
28601
|
if (found.has(key))
|
|
@@ -33838,7 +33496,8 @@ function createRequestUserInputUiState(request) {
|
|
|
33838
33496
|
answers: {},
|
|
33839
33497
|
drafts: {},
|
|
33840
33498
|
textModeQuestionId: first && !first.choices?.length ? first.id : undefined,
|
|
33841
|
-
submitting: false
|
|
33499
|
+
submitting: false,
|
|
33500
|
+
dismissed: false
|
|
33842
33501
|
};
|
|
33843
33502
|
}
|
|
33844
33503
|
function syncRequestUserInputUiState(current, request) {
|
|
@@ -34043,7 +33702,8 @@ function initialStore(workspace) {
|
|
|
34043
33702
|
sessionStats: {},
|
|
34044
33703
|
agentThreads: [],
|
|
34045
33704
|
lastError: undefined,
|
|
34046
|
-
requestUserInput: undefined
|
|
33705
|
+
requestUserInput: undefined,
|
|
33706
|
+
updateNotice: undefined
|
|
34047
33707
|
}
|
|
34048
33708
|
};
|
|
34049
33709
|
}
|
|
@@ -34465,6 +34125,9 @@ function createActions(store, setStore) {
|
|
|
34465
34125
|
statusDetailSet(detail) {
|
|
34466
34126
|
setStore("ui", "statusDetail", detail);
|
|
34467
34127
|
},
|
|
34128
|
+
updateNoticeSet(notice) {
|
|
34129
|
+
setStore("ui", "updateNotice", notice);
|
|
34130
|
+
},
|
|
34468
34131
|
contextUsageUpdated(usage2) {
|
|
34469
34132
|
if (!usage2) {
|
|
34470
34133
|
setStore("ui", "contextUsage", undefined);
|
|
@@ -34722,6 +34385,20 @@ function createActions(store, setStore) {
|
|
|
34722
34385
|
return;
|
|
34723
34386
|
s.ui.requestUserInput.submitting = submitting;
|
|
34724
34387
|
}));
|
|
34388
|
+
},
|
|
34389
|
+
requestUserInputDismissedSet(dismissed) {
|
|
34390
|
+
setStore(produce((s) => {
|
|
34391
|
+
if (!s.ui.requestUserInput)
|
|
34392
|
+
return;
|
|
34393
|
+
s.ui.requestUserInput.dismissed = dismissed;
|
|
34394
|
+
if (dismissed)
|
|
34395
|
+
s.ui.requestUserInput.textModeQuestionId = undefined;
|
|
34396
|
+
else {
|
|
34397
|
+
const question = s.snapshot.pendingUserInput?.questions[s.ui.requestUserInput.questionIndex];
|
|
34398
|
+
if (question && !question.choices?.length)
|
|
34399
|
+
s.ui.requestUserInput.textModeQuestionId = question.id;
|
|
34400
|
+
}
|
|
34401
|
+
}));
|
|
34725
34402
|
}
|
|
34726
34403
|
};
|
|
34727
34404
|
}
|
|
@@ -36829,6 +36506,14 @@ function TuiStoreProvider(props) {
|
|
|
36829
36506
|
let sessionSelectionIntent = 0;
|
|
36830
36507
|
let mcpOverlayGeneration = 0;
|
|
36831
36508
|
let disposed = false;
|
|
36509
|
+
if (props.updateCheck?.cachedNotice)
|
|
36510
|
+
actions.updateNoticeSet(props.updateCheck.cachedNotice);
|
|
36511
|
+
if (props.updateCheck?.refresh) {
|
|
36512
|
+
props.updateCheck.refresh.then((notice) => {
|
|
36513
|
+
if (!disposed)
|
|
36514
|
+
actions.updateNoticeSet(notice);
|
|
36515
|
+
});
|
|
36516
|
+
}
|
|
36832
36517
|
const timelineRows = createMemo(() => projectMessagesToRows(store.snapshot.messages, Math.max(1, dims().width - 4), store.snapshot.runningTurnId, store.snapshot.toolCalls, store.snapshot.toolInputPreviews));
|
|
36833
36518
|
function setStatusDetail(detail, timeoutMs) {
|
|
36834
36519
|
if (disposed)
|
|
@@ -37208,19 +36893,6 @@ function TuiStoreProvider(props) {
|
|
|
37208
36893
|
const sid = store.activeSessionId;
|
|
37209
36894
|
if (!sid || !text.trim())
|
|
37210
36895
|
return false;
|
|
37211
|
-
if (store.snapshot.pendingUserInput) {
|
|
37212
|
-
actions.promptHistoryAdd(text);
|
|
37213
|
-
(async () => {
|
|
37214
|
-
try {
|
|
37215
|
-
await port.answerUserInput(sid, text);
|
|
37216
|
-
await requestSnapshotRefresh(sid);
|
|
37217
|
-
} catch (error) {
|
|
37218
|
-
if (!disposed && store.activeSessionId === sid)
|
|
37219
|
-
actions.errorSet(error instanceof Error ? error.message : String(error));
|
|
37220
|
-
}
|
|
37221
|
-
})();
|
|
37222
|
-
return true;
|
|
37223
|
-
}
|
|
37224
36896
|
if (promptSubmissions.has(sid) || isAgentBusy(store) || port.getRunningTurnId(sid)) {
|
|
37225
36897
|
if (port.steer?.(sid, text)) {
|
|
37226
36898
|
actions.promptHistoryAdd(text);
|
|
@@ -37320,12 +36992,6 @@ function TuiStoreProvider(props) {
|
|
|
37320
36992
|
actions.snapshotPatched({
|
|
37321
36993
|
pendingUserInput: undefined
|
|
37322
36994
|
});
|
|
37323
|
-
const turnId = store.snapshot.runningTurnId ?? port.getRunningTurnId(sid);
|
|
37324
|
-
if (turnId && capabilities.cancel) {
|
|
37325
|
-
try {
|
|
37326
|
-
await port.cancelTurn(turnId, "user input cancelled");
|
|
37327
|
-
} catch {}
|
|
37328
|
-
}
|
|
37329
36995
|
await requestSnapshotRefresh(sid);
|
|
37330
36996
|
} catch (error) {
|
|
37331
36997
|
if (!disposed && store.activeSessionId === sid)
|
|
@@ -37979,14 +37645,14 @@ function filterOptions(options, needle) {
|
|
|
37979
37645
|
score: score2
|
|
37980
37646
|
}));
|
|
37981
37647
|
}
|
|
37982
|
-
function groupByCategory(
|
|
37648
|
+
function groupByCategory(matches, needleActive) {
|
|
37983
37649
|
if (needleActive)
|
|
37984
37650
|
return [{
|
|
37985
37651
|
category: undefined,
|
|
37986
|
-
matches: [...
|
|
37652
|
+
matches: [...matches]
|
|
37987
37653
|
}];
|
|
37988
37654
|
const buckets = new Map;
|
|
37989
|
-
for (const match of
|
|
37655
|
+
for (const match of matches) {
|
|
37990
37656
|
const key = match.option.category;
|
|
37991
37657
|
const list2 = buckets.get(key) ?? [];
|
|
37992
37658
|
list2.push(match);
|
|
@@ -38407,6 +38073,10 @@ function routeRequestUserInput(key, state) {
|
|
|
38407
38073
|
if (state.submitting)
|
|
38408
38074
|
return consumed();
|
|
38409
38075
|
if (key.ctrl && key.name === "c")
|
|
38076
|
+
return consumed({
|
|
38077
|
+
kind: "requestUserInput.dismiss"
|
|
38078
|
+
});
|
|
38079
|
+
if (key.ctrl && key.name === "x")
|
|
38410
38080
|
return consumed({
|
|
38411
38081
|
kind: "requestUserInput.cancel"
|
|
38412
38082
|
});
|
|
@@ -38425,7 +38095,7 @@ function routeRequestUserInput(key, state) {
|
|
|
38425
38095
|
if (state.textMode) {
|
|
38426
38096
|
if (key.name === "escape")
|
|
38427
38097
|
return consumed({
|
|
38428
|
-
kind: state.canExitTextMode ? "requestUserInput.textModeExit" : "requestUserInput.
|
|
38098
|
+
kind: state.canExitTextMode ? "requestUserInput.textModeExit" : "requestUserInput.dismiss"
|
|
38429
38099
|
});
|
|
38430
38100
|
if (key.name === "tab" && state.canExitTextMode)
|
|
38431
38101
|
return consumed({
|
|
@@ -38440,7 +38110,7 @@ function routeRequestUserInput(key, state) {
|
|
|
38440
38110
|
switch (key.name) {
|
|
38441
38111
|
case "escape":
|
|
38442
38112
|
return consumed({
|
|
38443
|
-
kind: "requestUserInput.
|
|
38113
|
+
kind: "requestUserInput.dismiss"
|
|
38444
38114
|
});
|
|
38445
38115
|
case "up":
|
|
38446
38116
|
return consumed({
|
|
@@ -38765,6 +38435,10 @@ function routeBase(key, ctx) {
|
|
|
38765
38435
|
return consumed({
|
|
38766
38436
|
kind: "composer.copyLast"
|
|
38767
38437
|
});
|
|
38438
|
+
case "q":
|
|
38439
|
+
return ctx.pendingUserInput ? consumed({
|
|
38440
|
+
kind: "requestUserInput.show"
|
|
38441
|
+
}) : PASSTHROUGH;
|
|
38768
38442
|
case "l":
|
|
38769
38443
|
return consumed({
|
|
38770
38444
|
kind: "transcript.clear"
|
|
@@ -39127,7 +38801,8 @@ function KeyboardController() {
|
|
|
39127
38801
|
historySearchActive: Boolean(tui.store.ui.historySearch),
|
|
39128
38802
|
queuedCount: tui.store.snapshot.queuedPrompts.length,
|
|
39129
38803
|
activeMainTab: tui.store.ui.activeMainTab,
|
|
39130
|
-
|
|
38804
|
+
pendingUserInput: Boolean(pendingRequest),
|
|
38805
|
+
...pendingRequest && requestState && pendingQuestion && !requestState.dismissed ? {
|
|
39131
38806
|
requestUserInput: {
|
|
39132
38807
|
textMode: requestState.textModeQuestionId === pendingQuestion.id,
|
|
39133
38808
|
canExitTextMode: Boolean(pendingQuestion.choices?.length),
|
|
@@ -39228,6 +38903,13 @@ function KeyboardController() {
|
|
|
39228
38903
|
await tui.answerUserInputQuestion(current.question.id, draft);
|
|
39229
38904
|
return;
|
|
39230
38905
|
}
|
|
38906
|
+
case "requestUserInput.dismiss":
|
|
38907
|
+
tui.actions.requestUserInputDismissedSet(true);
|
|
38908
|
+
composer.focus();
|
|
38909
|
+
return;
|
|
38910
|
+
case "requestUserInput.show":
|
|
38911
|
+
tui.actions.requestUserInputDismissedSet(false);
|
|
38912
|
+
return;
|
|
39231
38913
|
case "requestUserInput.cancel":
|
|
39232
38914
|
await tui.cancelUserInput();
|
|
39233
38915
|
return;
|
|
@@ -40069,8 +39751,8 @@ function KeyboardController() {
|
|
|
40069
39751
|
const search = tui.store.ui.historySearch;
|
|
40070
39752
|
if (!search)
|
|
40071
39753
|
return;
|
|
40072
|
-
const
|
|
40073
|
-
const preview =
|
|
39754
|
+
const matches = currentHistoryMatches();
|
|
39755
|
+
const preview = matches[search.index] ?? search.originalDraft;
|
|
40074
39756
|
composer.setDraft(preview);
|
|
40075
39757
|
}
|
|
40076
39758
|
async function openExternalEditor() {
|
|
@@ -42575,21 +42257,35 @@ function Transcript() {
|
|
|
42575
42257
|
},
|
|
42576
42258
|
get fallback() {
|
|
42577
42259
|
return (() => {
|
|
42578
|
-
var _el$2 = createElement("box"), _el$3 = createElement("text"), _el$
|
|
42260
|
+
var _el$2 = createElement("box"), _el$3 = createElement("box"), _el$4 = createElement("text"), _el$6 = createElement("text");
|
|
42579
42261
|
insertNode(_el$2, _el$3);
|
|
42580
|
-
insertNode(_el$2, _el$
|
|
42262
|
+
insertNode(_el$2, _el$4);
|
|
42263
|
+
insertNode(_el$2, _el$6);
|
|
42581
42264
|
setProp(_el$2, "style", {
|
|
42582
42265
|
flexDirection: "column",
|
|
42583
42266
|
marginTop: 1,
|
|
42584
42267
|
paddingLeft: 1,
|
|
42585
42268
|
paddingRight: 1
|
|
42586
42269
|
});
|
|
42587
|
-
|
|
42588
|
-
|
|
42270
|
+
setProp(_el$3, "style", {
|
|
42271
|
+
flexDirection: "column",
|
|
42272
|
+
marginBottom: 1
|
|
42273
|
+
});
|
|
42274
|
+
insert(_el$3, createComponent2(For, {
|
|
42275
|
+
each: FARAI_BANNER_LINES,
|
|
42276
|
+
children: (line) => (() => {
|
|
42277
|
+
var _el$8 = createElement("text");
|
|
42278
|
+
insert(_el$8, () => truncateLine2(line, Math.max(1, dims().width - 4)));
|
|
42279
|
+
effect((_$p) => setProp(_el$8, "fg", COLOR.dim, _$p));
|
|
42280
|
+
return _el$8;
|
|
42281
|
+
})()
|
|
42282
|
+
}));
|
|
42283
|
+
insertNode(_el$4, createTextNode(`\u203A message farai to get started`));
|
|
42284
|
+
insertNode(_el$6, createTextNode(` / opens commands \xB7 ? shows shortcuts`));
|
|
42589
42285
|
effect((_p$) => {
|
|
42590
42286
|
var _v$ = COLOR.dim, _v$2 = COLOR.dim;
|
|
42591
|
-
_v$ !== _p$.e && (_p$.e = setProp(_el$
|
|
42592
|
-
_v$2 !== _p$.t && (_p$.t = setProp(_el$
|
|
42287
|
+
_v$ !== _p$.e && (_p$.e = setProp(_el$4, "fg", _v$, _p$.e));
|
|
42288
|
+
_v$2 !== _p$.t && (_p$.t = setProp(_el$6, "fg", _v$2, _p$.t));
|
|
42593
42289
|
return _p$;
|
|
42594
42290
|
}, {
|
|
42595
42291
|
e: undefined,
|
|
@@ -42621,26 +42317,26 @@ function Transcript() {
|
|
|
42621
42317
|
return rawRows();
|
|
42622
42318
|
},
|
|
42623
42319
|
children: (row) => (() => {
|
|
42624
|
-
var _el$
|
|
42625
|
-
setProp(_el$
|
|
42320
|
+
var _el$9 = createElement("box");
|
|
42321
|
+
setProp(_el$9, "style", {
|
|
42626
42322
|
flexDirection: "column",
|
|
42627
42323
|
marginBottom: 1,
|
|
42628
42324
|
paddingLeft: 1,
|
|
42629
42325
|
paddingRight: 1
|
|
42630
42326
|
});
|
|
42631
|
-
insert(_el$
|
|
42327
|
+
insert(_el$9, createComponent2(For, {
|
|
42632
42328
|
get each() {
|
|
42633
42329
|
return row.split(`
|
|
42634
42330
|
`);
|
|
42635
42331
|
},
|
|
42636
42332
|
children: (line) => (() => {
|
|
42637
|
-
var _el$
|
|
42638
|
-
insert(_el$
|
|
42639
|
-
effect((_$p) => setProp(_el$
|
|
42640
|
-
return _el$
|
|
42333
|
+
var _el$0 = createElement("text");
|
|
42334
|
+
insert(_el$0, () => truncateLine2(line, Math.max(1, dims().width - 4)));
|
|
42335
|
+
effect((_$p) => setProp(_el$0, "fg", COLOR.dim, _$p));
|
|
42336
|
+
return _el$0;
|
|
42641
42337
|
})()
|
|
42642
42338
|
}));
|
|
42643
|
-
return _el$
|
|
42339
|
+
return _el$9;
|
|
42644
42340
|
})()
|
|
42645
42341
|
});
|
|
42646
42342
|
}
|
|
@@ -42723,19 +42419,19 @@ function valuesEqual(left, right, seen) {
|
|
|
42723
42419
|
function TranscriptRow(props) {
|
|
42724
42420
|
const isUser = props.row.kind === "user";
|
|
42725
42421
|
return (() => {
|
|
42726
|
-
var _el$
|
|
42727
|
-
setProp(_el$
|
|
42422
|
+
var _el$1 = createElement("box");
|
|
42423
|
+
setProp(_el$1, "style", {
|
|
42728
42424
|
flexDirection: "column",
|
|
42729
42425
|
paddingLeft: isUser ? 0 : 1,
|
|
42730
42426
|
paddingRight: isUser ? 0 : 1
|
|
42731
42427
|
});
|
|
42732
|
-
insert(_el$
|
|
42428
|
+
insert(_el$1, createComponent2(FaraiRow, {
|
|
42733
42429
|
get row() {
|
|
42734
42430
|
return props.row;
|
|
42735
42431
|
}
|
|
42736
42432
|
}));
|
|
42737
|
-
effect((_$p) => setProp(_el$
|
|
42738
|
-
return _el$
|
|
42433
|
+
effect((_$p) => setProp(_el$1, "id", props.row.id, _$p));
|
|
42434
|
+
return _el$1;
|
|
42739
42435
|
})();
|
|
42740
42436
|
}
|
|
42741
42437
|
var init_transcript = __esm(() => {
|
|
@@ -42754,6 +42450,7 @@ var init_transcript = __esm(() => {
|
|
|
42754
42450
|
init_compaction();
|
|
42755
42451
|
init_theme();
|
|
42756
42452
|
init_cells();
|
|
42453
|
+
init_branding();
|
|
42757
42454
|
});
|
|
42758
42455
|
|
|
42759
42456
|
// src/agent-tui/surfaces/center-surface.tsx
|
|
@@ -44685,8 +44382,15 @@ function instructionalFooterLines(state) {
|
|
|
44685
44382
|
function contextualFooter(state) {
|
|
44686
44383
|
return state.context;
|
|
44687
44384
|
}
|
|
44688
|
-
function footerRightItems(backgroundActivities, subagents, browserContexts, queueSize, statusDetail, contextUsage) {
|
|
44385
|
+
function footerRightItems(backgroundActivities, subagents, browserContexts, queueSize, statusDetail, contextUsage, updateNotice) {
|
|
44689
44386
|
const items = [];
|
|
44387
|
+
if (updateNotice) {
|
|
44388
|
+
items.push({
|
|
44389
|
+
id: "update",
|
|
44390
|
+
kind: "update",
|
|
44391
|
+
text: `update ${updateNotice.latestVersion}`
|
|
44392
|
+
});
|
|
44393
|
+
}
|
|
44690
44394
|
if (contextUsage && contextUsage.tokens >= 0) {
|
|
44691
44395
|
items.push({
|
|
44692
44396
|
id: "context",
|
|
@@ -44880,10 +44584,12 @@ function Footer(props) {
|
|
|
44880
44584
|
budget
|
|
44881
44585
|
};
|
|
44882
44586
|
};
|
|
44883
|
-
const rightItems = createMemo(() => footerRightItems(tui.store.snapshot.backgroundActivities, tui.store.snapshot.subagents, tui.store.snapshot.browserContexts, tui.store.snapshot.queuedPrompts.length, tui.store.ui.statusDetail, contextUsage()));
|
|
44587
|
+
const rightItems = createMemo(() => footerRightItems(tui.store.snapshot.backgroundActivities, tui.store.snapshot.subagents, tui.store.snapshot.browserContexts, tui.store.snapshot.queuedPrompts.length, tui.store.ui.statusDetail, contextUsage(), tui.store.ui.updateNotice));
|
|
44884
44588
|
const firstLine = () => fitFooterLine(left(), rightItems(), Math.max(0, dims().width - 4));
|
|
44589
|
+
const updateText = () => tui.store.ui.updateNotice ? `update ${tui.store.ui.updateNotice.latestVersion}` : undefined;
|
|
44590
|
+
const rightLine = createMemo(() => splitUpdateWarning(firstLine().right, updateText()));
|
|
44885
44591
|
return (() => {
|
|
44886
|
-
var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$4 = createElement("text");
|
|
44592
|
+
var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$4 = createElement("text"), _el$5 = createElement("span"), _el$6 = createElement("span");
|
|
44887
44593
|
insertNode(_el$, _el$2);
|
|
44888
44594
|
setProp(_el$, "style", {
|
|
44889
44595
|
flexShrink: 0,
|
|
@@ -44897,33 +44603,64 @@ function Footer(props) {
|
|
|
44897
44603
|
justifyContent: "space-between"
|
|
44898
44604
|
});
|
|
44899
44605
|
insert(_el$3, () => firstLine().left);
|
|
44900
|
-
|
|
44606
|
+
insertNode(_el$4, _el$5);
|
|
44607
|
+
insertNode(_el$4, _el$6);
|
|
44608
|
+
insert(_el$5, () => rightLine().warning);
|
|
44609
|
+
insert(_el$6, () => rightLine().rest);
|
|
44901
44610
|
insert(_el$, createComponent2(ShowShortcutLines, {
|
|
44902
44611
|
get lines() {
|
|
44903
44612
|
return lines().slice(1).map((line) => line.toLowerCase());
|
|
44904
44613
|
}
|
|
44905
44614
|
}), null);
|
|
44906
44615
|
effect((_p$) => {
|
|
44907
|
-
var _v$ = historySearch() ? COLOR.accent : COLOR.dim, _v$2 =
|
|
44616
|
+
var _v$ = historySearch() ? COLOR.accent : COLOR.dim, _v$2 = {
|
|
44617
|
+
fg: COLOR.warning
|
|
44618
|
+
}, _v$3 = {
|
|
44619
|
+
fg: COLOR.dim
|
|
44620
|
+
};
|
|
44908
44621
|
_v$ !== _p$.e && (_p$.e = setProp(_el$3, "fg", _v$, _p$.e));
|
|
44909
|
-
_v$2 !== _p$.t && (_p$.t = setProp(_el$
|
|
44622
|
+
_v$2 !== _p$.t && (_p$.t = setProp(_el$5, "style", _v$2, _p$.t));
|
|
44623
|
+
_v$3 !== _p$.a && (_p$.a = setProp(_el$6, "style", _v$3, _p$.a));
|
|
44910
44624
|
return _p$;
|
|
44911
44625
|
}, {
|
|
44912
44626
|
e: undefined,
|
|
44913
|
-
t: undefined
|
|
44627
|
+
t: undefined,
|
|
44628
|
+
a: undefined
|
|
44914
44629
|
});
|
|
44915
44630
|
return _el$;
|
|
44916
44631
|
})();
|
|
44917
44632
|
}
|
|
44633
|
+
function splitUpdateWarning(line, updateText) {
|
|
44634
|
+
if (!line || !updateText)
|
|
44635
|
+
return {
|
|
44636
|
+
warning: "",
|
|
44637
|
+
rest: line
|
|
44638
|
+
};
|
|
44639
|
+
if (line.startsWith(updateText))
|
|
44640
|
+
return {
|
|
44641
|
+
warning: updateText,
|
|
44642
|
+
rest: line.slice(updateText.length)
|
|
44643
|
+
};
|
|
44644
|
+
const visible = line.endsWith("\u2026") ? line.slice(0, -1) : line;
|
|
44645
|
+
if (visible && updateText.startsWith(visible))
|
|
44646
|
+
return {
|
|
44647
|
+
warning: line,
|
|
44648
|
+
rest: ""
|
|
44649
|
+
};
|
|
44650
|
+
return {
|
|
44651
|
+
warning: "",
|
|
44652
|
+
rest: line
|
|
44653
|
+
};
|
|
44654
|
+
}
|
|
44918
44655
|
function displayModel(sessionModel, workspace) {
|
|
44919
44656
|
return displayModelSelection(workspace, sessionModel);
|
|
44920
44657
|
}
|
|
44921
44658
|
function ShowShortcutLines(props) {
|
|
44922
44659
|
return memo2(() => props.lines.map((line) => (() => {
|
|
44923
|
-
var _el$
|
|
44924
|
-
insert(_el$
|
|
44925
|
-
effect((_$p) => setProp(_el$
|
|
44926
|
-
return _el$
|
|
44660
|
+
var _el$7 = createElement("text");
|
|
44661
|
+
insert(_el$7, line);
|
|
44662
|
+
effect((_$p) => setProp(_el$7, "fg", COLOR.dim, _$p));
|
|
44663
|
+
return _el$7;
|
|
44927
44664
|
})()));
|
|
44928
44665
|
}
|
|
44929
44666
|
var init_footer = __esm(() => {
|
|
@@ -45081,15 +44818,15 @@ var init_status_indicator = __esm(() => {
|
|
|
45081
44818
|
});
|
|
45082
44819
|
|
|
45083
44820
|
// src/agent-tui/dialog/list-selection.ts
|
|
45084
|
-
function selectableIndex(
|
|
45085
|
-
const enabled =
|
|
44821
|
+
function selectableIndex(matches, requested) {
|
|
44822
|
+
const enabled = matches.filter((match) => !match.option.disabled);
|
|
45086
44823
|
if (enabled.length === 0)
|
|
45087
44824
|
return -1;
|
|
45088
44825
|
return Math.max(0, Math.min(requested, enabled.length - 1));
|
|
45089
44826
|
}
|
|
45090
|
-
function selectedOptionId(
|
|
45091
|
-
const index = selectableIndex(
|
|
45092
|
-
return index < 0 ? undefined :
|
|
44827
|
+
function selectedOptionId(matches, requested) {
|
|
44828
|
+
const index = selectableIndex(matches, requested);
|
|
44829
|
+
return index < 0 ? undefined : matches.filter((match) => !match.option.disabled)[index]?.option.id;
|
|
45093
44830
|
}
|
|
45094
44831
|
function scrollWindowStart(total, cap, selectedIndex) {
|
|
45095
44832
|
if (total <= cap || selectedIndex < 0)
|
|
@@ -45098,8 +44835,8 @@ function scrollWindowStart(total, cap, selectedIndex) {
|
|
|
45098
44835
|
return 0;
|
|
45099
44836
|
return Math.min(selectedIndex - cap + 1, total - cap);
|
|
45100
44837
|
}
|
|
45101
|
-
function displayRows(
|
|
45102
|
-
return
|
|
44838
|
+
function displayRows(matches, selectedId) {
|
|
44839
|
+
return matches.map((match) => ({
|
|
45103
44840
|
option: match.option,
|
|
45104
44841
|
matched: match.score > 0,
|
|
45105
44842
|
disabled: Boolean(match.option.disabled),
|
|
@@ -45190,9 +44927,9 @@ var init_selection_row = __esm(() => {
|
|
|
45190
44927
|
function ListOverlay(props) {
|
|
45191
44928
|
const tui = useTuiStore();
|
|
45192
44929
|
const dims = useTerminalDimensions();
|
|
45193
|
-
const
|
|
45194
|
-
const groups = createMemo(() => groupByCategory(
|
|
45195
|
-
const selectedId = createMemo(() => selectedOptionId(
|
|
44930
|
+
const matches = createMemo(() => filterOptions(props.options, props.frame.query));
|
|
44931
|
+
const groups = createMemo(() => groupByCategory(matches(), props.frame.query.trim() !== ""));
|
|
44932
|
+
const selectedId = createMemo(() => selectedOptionId(matches(), props.frame.index));
|
|
45196
44933
|
const rows = createMemo(() => selectionRows(groups(), selectedId()));
|
|
45197
44934
|
const width = () => Math.max(30, dims().width);
|
|
45198
44935
|
const maxRows = () => overlayMaxRows(props.frame.kind, dims().height);
|
|
@@ -45209,7 +44946,7 @@ function ListOverlay(props) {
|
|
|
45209
44946
|
const descCol = () => descriptionColumn(visibleRows(), width());
|
|
45210
44947
|
const subtitle = () => overlaySubtitle(props.frame.kind);
|
|
45211
44948
|
const selectOption = (id2) => {
|
|
45212
|
-
const enabled =
|
|
44949
|
+
const enabled = matches().filter((match) => !match.option.disabled);
|
|
45213
44950
|
const index = enabled.findIndex((match) => match.option.id === id2);
|
|
45214
44951
|
if (index >= 0)
|
|
45215
44952
|
tui.actions.overlaySetIndex(index, enabled.length);
|
|
@@ -45258,7 +44995,7 @@ function ListOverlay(props) {
|
|
|
45258
44995
|
return dims().height;
|
|
45259
44996
|
},
|
|
45260
44997
|
get matches() {
|
|
45261
|
-
return
|
|
44998
|
+
return matches();
|
|
45262
44999
|
},
|
|
45263
45000
|
get selectedId() {
|
|
45264
45001
|
return selectedId();
|
|
@@ -45295,14 +45032,14 @@ function ListOverlay(props) {
|
|
|
45295
45032
|
})());
|
|
45296
45033
|
insert(_el$6, (() => {
|
|
45297
45034
|
var _c$2 = memo2(() => !!props.frame.query);
|
|
45298
|
-
return () => _c$2() ? `${
|
|
45035
|
+
return () => _c$2() ? `${matches().length}/${props.options.length}` : memo2(() => matches().length > 0)() ? `${Math.min(props.frame.index + 1, matches().length)}/${matches().length}` : "";
|
|
45299
45036
|
})());
|
|
45300
45037
|
setProp(_el$7, "style", {
|
|
45301
45038
|
flexDirection: "column"
|
|
45302
45039
|
});
|
|
45303
45040
|
insert(_el$7, createComponent2(Show, {
|
|
45304
45041
|
get when() {
|
|
45305
|
-
return
|
|
45042
|
+
return matches().length > 0;
|
|
45306
45043
|
},
|
|
45307
45044
|
get fallback() {
|
|
45308
45045
|
return (() => {
|
|
@@ -45346,7 +45083,7 @@ function ListOverlay(props) {
|
|
|
45346
45083
|
}));
|
|
45347
45084
|
insert(_el$, createComponent2(SelectionMenuHint, {
|
|
45348
45085
|
get text() {
|
|
45349
|
-
return overlayHint(props.frame,
|
|
45086
|
+
return overlayHint(props.frame, matches(), tui.store.ui.modelProviders);
|
|
45350
45087
|
}
|
|
45351
45088
|
}), null);
|
|
45352
45089
|
effect((_p$) => {
|
|
@@ -45915,10 +45652,10 @@ function overlaySubtitle(kind) {
|
|
|
45915
45652
|
return "inspect durable memory";
|
|
45916
45653
|
return "";
|
|
45917
45654
|
}
|
|
45918
|
-
function overlayHint(frame,
|
|
45655
|
+
function overlayHint(frame, matches, providers) {
|
|
45919
45656
|
if (frame.kind !== "model")
|
|
45920
45657
|
return "press enter to confirm or esc to go back";
|
|
45921
|
-
const enabled =
|
|
45658
|
+
const enabled = matches.filter((match) => !match.option.disabled);
|
|
45922
45659
|
const selected = enabled[frame.index]?.option.value;
|
|
45923
45660
|
if (selected?.kind === "model_action")
|
|
45924
45661
|
return "enter add provider \xB7 ctrl+a add \xB7 esc back";
|
|
@@ -46226,20 +45963,20 @@ function requestStatusDetail(width, progress, countdown) {
|
|
|
46226
45963
|
function requestUserInputHint(width, textMode, hasChoices) {
|
|
46227
45964
|
if (textMode) {
|
|
46228
45965
|
if (width >= 76)
|
|
46229
|
-
return `enter continue \xB7 ${hasChoices ? "tab choices \xB7 " : ""}ctrl+p/n questions \xB7 esc ${hasChoices ? "choices" : "
|
|
45966
|
+
return `enter continue \xB7 ${hasChoices ? "tab choices \xB7 " : ""}ctrl+p/n questions \xB7 esc ${hasChoices ? "choices" : "chat"} \xB7 ctrl+x cancel`;
|
|
46230
45967
|
if (width >= 52)
|
|
46231
|
-
return `enter \xB7 ctrl+p/n questions \xB7 esc ${hasChoices ? "choices" : "
|
|
46232
|
-
return `enter \xB7 esc ${hasChoices ? "choices" : "
|
|
45968
|
+
return `enter \xB7 ctrl+p/n questions \xB7 esc ${hasChoices ? "choices" : "chat"}`;
|
|
45969
|
+
return `enter \xB7 esc ${hasChoices ? "choices" : "chat"}`;
|
|
46233
45970
|
}
|
|
46234
45971
|
if (width >= 86)
|
|
46235
|
-
return "\u2191\u2193 select \xB7 1-9 choose \xB7 \u2190\u2192/ctrl+p/n questions \xB7 tab other \xB7 esc cancel";
|
|
45972
|
+
return "\u2191\u2193 select \xB7 1-9 choose \xB7 \u2190\u2192/ctrl+p/n questions \xB7 tab other \xB7 esc chat \xB7 ctrl+x cancel";
|
|
46236
45973
|
if (width >= 58)
|
|
46237
|
-
return "\u2191\u2193 select \xB7 enter \xB7 \u2190\u2192 questions \xB7 tab other \xB7 esc
|
|
45974
|
+
return "\u2191\u2193 select \xB7 enter \xB7 \u2190\u2192 questions \xB7 tab other \xB7 esc chat";
|
|
46238
45975
|
if (width >= 42)
|
|
46239
|
-
return "\u2191\u2193 select \xB7 enter \xB7 tab other \xB7 esc
|
|
45976
|
+
return "\u2191\u2193 select \xB7 enter \xB7 tab other \xB7 esc chat";
|
|
46240
45977
|
if (width >= 34)
|
|
46241
|
-
return "\u2191\u2193 select \xB7 enter \xB7 esc
|
|
46242
|
-
return "\u2191\u2193 \xB7 enter \xB7 esc";
|
|
45978
|
+
return "\u2191\u2193 select \xB7 enter \xB7 esc chat";
|
|
45979
|
+
return "\u2191\u2193 \xB7 enter \xB7 esc chat";
|
|
46243
45980
|
}
|
|
46244
45981
|
function requestOptionRows(question) {
|
|
46245
45982
|
if (!question?.choices?.length)
|
|
@@ -46815,7 +46552,8 @@ function BottomPane() {
|
|
|
46815
46552
|
const proxyTabActive = () => slot() === "proxy_tab";
|
|
46816
46553
|
const providerWizardActive = () => Boolean(tui.store.ui.modelProviderWizard);
|
|
46817
46554
|
const providerRemovalActive = () => Boolean(tui.store.ui.modelProviderRemoval);
|
|
46818
|
-
const inputRequestActive = () => Boolean(tui.store.snapshot.pendingUserInput);
|
|
46555
|
+
const inputRequestActive = () => Boolean(tui.store.snapshot.pendingUserInput && !tui.store.ui.requestUserInput?.dismissed);
|
|
46556
|
+
const inputRequestPending = () => Boolean(tui.store.snapshot.pendingUserInput);
|
|
46819
46557
|
const footerHidden = () => Boolean(frame()) || Boolean(centerFrame()) || slashPanelActive() || proxyTabActive() || inputRequestActive() || providerWizardActive() || providerRemovalActive();
|
|
46820
46558
|
const inlineStatusDetail = () => {
|
|
46821
46559
|
const detail = tui.store.ui.statusDetail;
|
|
@@ -46875,10 +46613,10 @@ function BottomPane() {
|
|
|
46875
46613
|
return tui.store.ui.lastError;
|
|
46876
46614
|
},
|
|
46877
46615
|
children: (error) => (() => {
|
|
46878
|
-
var _el$
|
|
46879
|
-
insert(_el$
|
|
46880
|
-
effect((_$p) => setProp(_el$
|
|
46881
|
-
return _el$
|
|
46616
|
+
var _el$4 = createElement("text");
|
|
46617
|
+
insert(_el$4, () => `\u2022 error \xB7 ${truncateLine2(error(), 160)}`);
|
|
46618
|
+
effect((_$p) => setProp(_el$4, "fg", COLOR.error, _$p));
|
|
46619
|
+
return _el$4;
|
|
46882
46620
|
})()
|
|
46883
46621
|
}), null);
|
|
46884
46622
|
insert(_el$, createComponent2(Show, {
|
|
@@ -46889,6 +46627,17 @@ function BottomPane() {
|
|
|
46889
46627
|
return createComponent2(PendingInputPreview, {});
|
|
46890
46628
|
}
|
|
46891
46629
|
}), null);
|
|
46630
|
+
insert(_el$, createComponent2(Show, {
|
|
46631
|
+
get when() {
|
|
46632
|
+
return memo2(() => !!(!inputRequestActive() && inputRequestPending() && !providerWizardActive()))() && !providerRemovalActive();
|
|
46633
|
+
},
|
|
46634
|
+
get children() {
|
|
46635
|
+
var _el$2 = createElement("text");
|
|
46636
|
+
insertNode(_el$2, createTextNode(` question pending \xB7 ctrl+q answer \xB7 chat input remains available`));
|
|
46637
|
+
effect((_$p) => setProp(_el$2, "fg", COLOR.warning, _$p));
|
|
46638
|
+
return _el$2;
|
|
46639
|
+
}
|
|
46640
|
+
}), null);
|
|
46892
46641
|
insert(_el$, createComponent2(Show, {
|
|
46893
46642
|
get when() {
|
|
46894
46643
|
return tui.store.ui.modelProviderRemoval;
|
|
@@ -46901,7 +46650,7 @@ function BottomPane() {
|
|
|
46901
46650
|
get fallback() {
|
|
46902
46651
|
return createComponent2(Show, {
|
|
46903
46652
|
get when() {
|
|
46904
|
-
return tui.store.snapshot.pendingUserInput;
|
|
46653
|
+
return memo2(() => !!inputRequestActive())() ? tui.store.snapshot.pendingUserInput : undefined;
|
|
46905
46654
|
},
|
|
46906
46655
|
get fallback() {
|
|
46907
46656
|
return createComponent2(Show, {
|
|
@@ -46978,54 +46727,54 @@ function BottomPane() {
|
|
|
46978
46727
|
function ProxyTabFooter() {
|
|
46979
46728
|
const tui = useTuiStore();
|
|
46980
46729
|
return (() => {
|
|
46981
|
-
var _el$
|
|
46982
|
-
insertNode(_el$
|
|
46983
|
-
insertNode(_el$
|
|
46984
|
-
setProp(_el$
|
|
46730
|
+
var _el$5 = createElement("box"), _el$6 = createElement("text"), _el$8 = createElement("text");
|
|
46731
|
+
insertNode(_el$5, _el$6);
|
|
46732
|
+
insertNode(_el$5, _el$8);
|
|
46733
|
+
setProp(_el$5, "style", {
|
|
46985
46734
|
height: 1,
|
|
46986
46735
|
flexDirection: "row",
|
|
46987
46736
|
justifyContent: "space-between",
|
|
46988
46737
|
paddingLeft: 1,
|
|
46989
46738
|
paddingRight: 1
|
|
46990
46739
|
});
|
|
46991
|
-
insertNode(_el$
|
|
46992
|
-
insert(_el$
|
|
46740
|
+
insertNode(_el$6, createTextNode(`\u2191\u2193 flow \xB7 tab detail \xB7 p/n ws msg \xB7 \u2190\u2192 filter \xB7 a/h/w tabs \xB7 ctrl+1 chat`));
|
|
46741
|
+
insert(_el$8, () => `proxy \xB7 ${tui.store.ui.proxyFilter}`);
|
|
46993
46742
|
effect((_p$) => {
|
|
46994
46743
|
var _v$ = COLOR.dim, _v$2 = COLOR.dim;
|
|
46995
|
-
_v$ !== _p$.e && (_p$.e = setProp(_el$
|
|
46996
|
-
_v$2 !== _p$.t && (_p$.t = setProp(_el$
|
|
46744
|
+
_v$ !== _p$.e && (_p$.e = setProp(_el$6, "fg", _v$, _p$.e));
|
|
46745
|
+
_v$2 !== _p$.t && (_p$.t = setProp(_el$8, "fg", _v$2, _p$.t));
|
|
46997
46746
|
return _p$;
|
|
46998
46747
|
}, {
|
|
46999
46748
|
e: undefined,
|
|
47000
46749
|
t: undefined
|
|
47001
46750
|
});
|
|
47002
|
-
return _el$
|
|
46751
|
+
return _el$5;
|
|
47003
46752
|
})();
|
|
47004
46753
|
}
|
|
47005
46754
|
function CenterSurfaceFooter(props) {
|
|
47006
46755
|
return (() => {
|
|
47007
|
-
var _el$
|
|
47008
|
-
insertNode(_el$
|
|
47009
|
-
insertNode(_el$
|
|
47010
|
-
setProp(_el$
|
|
46756
|
+
var _el$9 = createElement("box"), _el$0 = createElement("text"), _el$1 = createElement("text");
|
|
46757
|
+
insertNode(_el$9, _el$0);
|
|
46758
|
+
insertNode(_el$9, _el$1);
|
|
46759
|
+
setProp(_el$9, "style", {
|
|
47011
46760
|
height: 1,
|
|
47012
46761
|
flexDirection: "row",
|
|
47013
46762
|
justifyContent: "space-between",
|
|
47014
46763
|
paddingLeft: 1,
|
|
47015
46764
|
paddingRight: 1
|
|
47016
46765
|
});
|
|
47017
|
-
insert(_el$
|
|
47018
|
-
insert(_el$
|
|
46766
|
+
insert(_el$0, () => centerSurfaceFooter(props.frame).toLowerCase());
|
|
46767
|
+
insert(_el$1, () => props.frame.kind.toLowerCase());
|
|
47019
46768
|
effect((_p$) => {
|
|
47020
46769
|
var _v$3 = COLOR.dim, _v$4 = COLOR.dim;
|
|
47021
|
-
_v$3 !== _p$.e && (_p$.e = setProp(_el$
|
|
47022
|
-
_v$4 !== _p$.t && (_p$.t = setProp(_el$
|
|
46770
|
+
_v$3 !== _p$.e && (_p$.e = setProp(_el$0, "fg", _v$3, _p$.e));
|
|
46771
|
+
_v$4 !== _p$.t && (_p$.t = setProp(_el$1, "fg", _v$4, _p$.t));
|
|
47023
46772
|
return _p$;
|
|
47024
46773
|
}, {
|
|
47025
46774
|
e: undefined,
|
|
47026
46775
|
t: undefined
|
|
47027
46776
|
});
|
|
47028
|
-
return _el$
|
|
46777
|
+
return _el$9;
|
|
47029
46778
|
})();
|
|
47030
46779
|
}
|
|
47031
46780
|
var init_bottom_pane = __esm(() => {
|
|
@@ -47242,6 +46991,191 @@ var init_app = __esm(() => {
|
|
|
47242
46991
|
init_app_shell();
|
|
47243
46992
|
});
|
|
47244
46993
|
|
|
46994
|
+
// src/agent-tui/update-check.ts
|
|
46995
|
+
import { mkdirSync as mkdirSync13, readFileSync as readFileSync19, renameSync as renameSync7, writeFileSync as writeFileSync14 } from "fs";
|
|
46996
|
+
import { dirname as dirname8, join as join26 } from "path";
|
|
46997
|
+
function prepareUpdateCheck(options = {}) {
|
|
46998
|
+
if (updateCheckDisabled())
|
|
46999
|
+
return {
|
|
47000
|
+
cachedNotice: undefined,
|
|
47001
|
+
refresh: undefined
|
|
47002
|
+
};
|
|
47003
|
+
const currentVersion = options.currentVersion ?? readCurrentVersion();
|
|
47004
|
+
if (!currentVersion)
|
|
47005
|
+
return {
|
|
47006
|
+
cachedNotice: undefined,
|
|
47007
|
+
refresh: undefined
|
|
47008
|
+
};
|
|
47009
|
+
const now = options.now ?? Date.now();
|
|
47010
|
+
const cachePath = options.cachePath ?? updateCachePath();
|
|
47011
|
+
const cache = readUpdateCache(cachePath);
|
|
47012
|
+
const cachedNotice = cache ? createUpdateNotice(currentVersion, cache.latestVersion) : undefined;
|
|
47013
|
+
if (cache && isFreshCache(cache, now))
|
|
47014
|
+
return {
|
|
47015
|
+
cachedNotice,
|
|
47016
|
+
refresh: undefined
|
|
47017
|
+
};
|
|
47018
|
+
return {
|
|
47019
|
+
cachedNotice,
|
|
47020
|
+
refresh: refreshUpdateNotice({
|
|
47021
|
+
cachePath,
|
|
47022
|
+
currentVersion,
|
|
47023
|
+
fetcher: options.fetcher ?? fetch,
|
|
47024
|
+
now,
|
|
47025
|
+
timeoutMs: options.timeoutMs ?? UPDATE_CHECK_TIMEOUT_MS,
|
|
47026
|
+
fallback: cachedNotice
|
|
47027
|
+
})
|
|
47028
|
+
};
|
|
47029
|
+
}
|
|
47030
|
+
function createUpdateNotice(currentVersion, latestVersion) {
|
|
47031
|
+
if (compareSemver(latestVersion, currentVersion) <= 0)
|
|
47032
|
+
return;
|
|
47033
|
+
return {
|
|
47034
|
+
currentVersion,
|
|
47035
|
+
latestVersion,
|
|
47036
|
+
updateCommand: "npm install -g farai@latest"
|
|
47037
|
+
};
|
|
47038
|
+
}
|
|
47039
|
+
function compareSemver(left, right) {
|
|
47040
|
+
const a = parseSemver(left);
|
|
47041
|
+
const b = parseSemver(right);
|
|
47042
|
+
if (!a || !b)
|
|
47043
|
+
return 0;
|
|
47044
|
+
for (let index = 0;index < 3; index += 1) {
|
|
47045
|
+
const delta = a.core[index] - b.core[index];
|
|
47046
|
+
if (delta !== 0)
|
|
47047
|
+
return delta < 0 ? -1 : 1;
|
|
47048
|
+
}
|
|
47049
|
+
if (a.prerelease.length === 0 || b.prerelease.length === 0) {
|
|
47050
|
+
if (a.prerelease.length === b.prerelease.length)
|
|
47051
|
+
return 0;
|
|
47052
|
+
return a.prerelease.length === 0 ? 1 : -1;
|
|
47053
|
+
}
|
|
47054
|
+
const length = Math.max(a.prerelease.length, b.prerelease.length);
|
|
47055
|
+
for (let index = 0;index < length; index += 1) {
|
|
47056
|
+
const aPart = a.prerelease[index];
|
|
47057
|
+
const bPart = b.prerelease[index];
|
|
47058
|
+
if (aPart === undefined || bPart === undefined)
|
|
47059
|
+
return aPart === undefined ? -1 : 1;
|
|
47060
|
+
if (aPart === bPart)
|
|
47061
|
+
continue;
|
|
47062
|
+
const aNumber = numericIdentifier(aPart);
|
|
47063
|
+
const bNumber = numericIdentifier(bPart);
|
|
47064
|
+
if (aNumber !== undefined && bNumber !== undefined)
|
|
47065
|
+
return aNumber < bNumber ? -1 : 1;
|
|
47066
|
+
if (aNumber !== undefined || bNumber !== undefined)
|
|
47067
|
+
return aNumber !== undefined ? -1 : 1;
|
|
47068
|
+
return aPart < bPart ? -1 : 1;
|
|
47069
|
+
}
|
|
47070
|
+
return 0;
|
|
47071
|
+
}
|
|
47072
|
+
function readUpdateCache(path = updateCachePath()) {
|
|
47073
|
+
try {
|
|
47074
|
+
const parsed = JSON.parse(readFileSync19(path, "utf8"));
|
|
47075
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
47076
|
+
return;
|
|
47077
|
+
const value = parsed;
|
|
47078
|
+
if (typeof value.checkedAt !== "number" || !Number.isFinite(value.checkedAt))
|
|
47079
|
+
return;
|
|
47080
|
+
if (typeof value.latestVersion !== "string" || !parseSemver(value.latestVersion))
|
|
47081
|
+
return;
|
|
47082
|
+
return {
|
|
47083
|
+
checkedAt: value.checkedAt,
|
|
47084
|
+
latestVersion: value.latestVersion
|
|
47085
|
+
};
|
|
47086
|
+
} catch {
|
|
47087
|
+
return;
|
|
47088
|
+
}
|
|
47089
|
+
}
|
|
47090
|
+
function updateCachePath() {
|
|
47091
|
+
return join26(globalDataDir(), "update.json");
|
|
47092
|
+
}
|
|
47093
|
+
function readCurrentVersion() {
|
|
47094
|
+
try {
|
|
47095
|
+
const packagePath = join26(import.meta.dir, "..", "..", "package.json");
|
|
47096
|
+
const parsed = JSON.parse(readFileSync19(packagePath, "utf8"));
|
|
47097
|
+
return typeof parsed.version === "string" && parseSemver(parsed.version) ? parsed.version : undefined;
|
|
47098
|
+
} catch {
|
|
47099
|
+
return;
|
|
47100
|
+
}
|
|
47101
|
+
}
|
|
47102
|
+
async function refreshUpdateNotice(input) {
|
|
47103
|
+
try {
|
|
47104
|
+
const latestVersion = await fetchLatestVersion(input.fetcher, input.timeoutMs);
|
|
47105
|
+
writeUpdateCache(input.cachePath, {
|
|
47106
|
+
checkedAt: input.now,
|
|
47107
|
+
latestVersion
|
|
47108
|
+
});
|
|
47109
|
+
return createUpdateNotice(input.currentVersion, latestVersion);
|
|
47110
|
+
} catch {
|
|
47111
|
+
return input.fallback;
|
|
47112
|
+
}
|
|
47113
|
+
}
|
|
47114
|
+
async function fetchLatestVersion(fetcher, timeoutMs) {
|
|
47115
|
+
const controller = new AbortController;
|
|
47116
|
+
const timer = setTimeout(() => controller.abort(), Math.max(1, timeoutMs));
|
|
47117
|
+
timer.unref?.();
|
|
47118
|
+
try {
|
|
47119
|
+
const response = await fetcher(UPDATE_REGISTRY_URL, {
|
|
47120
|
+
headers: {
|
|
47121
|
+
accept: "application/json"
|
|
47122
|
+
},
|
|
47123
|
+
signal: controller.signal
|
|
47124
|
+
});
|
|
47125
|
+
if (!response.ok)
|
|
47126
|
+
throw new Error(`npm registry returned ${response.status}`);
|
|
47127
|
+
const parsed = await response.json();
|
|
47128
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
47129
|
+
throw new Error("invalid npm registry response");
|
|
47130
|
+
const version = parsed.version;
|
|
47131
|
+
if (typeof version !== "string" || !parseSemver(version))
|
|
47132
|
+
throw new Error("invalid npm package version");
|
|
47133
|
+
return version;
|
|
47134
|
+
} finally {
|
|
47135
|
+
clearTimeout(timer);
|
|
47136
|
+
}
|
|
47137
|
+
}
|
|
47138
|
+
function writeUpdateCache(path, cache) {
|
|
47139
|
+
try {
|
|
47140
|
+
mkdirSync13(dirname8(path), {
|
|
47141
|
+
recursive: true
|
|
47142
|
+
});
|
|
47143
|
+
const temporary = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
47144
|
+
writeFileSync14(temporary, `${JSON.stringify(cache)}
|
|
47145
|
+
`, "utf8");
|
|
47146
|
+
renameSync7(temporary, path);
|
|
47147
|
+
} catch {}
|
|
47148
|
+
}
|
|
47149
|
+
function isFreshCache(cache, now) {
|
|
47150
|
+
const age = now - cache.checkedAt;
|
|
47151
|
+
return age >= 0 && age < UPDATE_CACHE_TTL_MS;
|
|
47152
|
+
}
|
|
47153
|
+
function updateCheckDisabled() {
|
|
47154
|
+
return envEnabled(process.env.FARAI_DISABLE_UPDATE_CHECK) || envEnabled(process.env.NO_UPDATE_NOTIFIER);
|
|
47155
|
+
}
|
|
47156
|
+
function envEnabled(value) {
|
|
47157
|
+
return value === "1" || value?.toLowerCase() === "true" || value?.toLowerCase() === "yes";
|
|
47158
|
+
}
|
|
47159
|
+
function parseSemver(value) {
|
|
47160
|
+
const match = value.trim().match(/^(?:v)?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/);
|
|
47161
|
+
if (!match)
|
|
47162
|
+
return;
|
|
47163
|
+
return {
|
|
47164
|
+
core: [Number(match[1]), Number(match[2]), Number(match[3])],
|
|
47165
|
+
prerelease: match[4]?.split(".") ?? []
|
|
47166
|
+
};
|
|
47167
|
+
}
|
|
47168
|
+
function numericIdentifier(value) {
|
|
47169
|
+
if (!/^(0|[1-9]\d*)$/.test(value))
|
|
47170
|
+
return;
|
|
47171
|
+
return Number(value);
|
|
47172
|
+
}
|
|
47173
|
+
var UPDATE_CACHE_TTL_MS, UPDATE_CHECK_TIMEOUT_MS = 4000, UPDATE_REGISTRY_URL = "https://registry.npmjs.org/farai/latest";
|
|
47174
|
+
var init_update_check = __esm(() => {
|
|
47175
|
+
init_config();
|
|
47176
|
+
UPDATE_CACHE_TTL_MS = 20 * 60 * 60 * 1000;
|
|
47177
|
+
});
|
|
47178
|
+
|
|
47245
47179
|
// src/agent-tui/index.tsx
|
|
47246
47180
|
var exports_agent_tui = {};
|
|
47247
47181
|
__export(exports_agent_tui, {
|
|
@@ -47265,6 +47199,7 @@ async function runOpenTui(input) {
|
|
|
47265
47199
|
let handleRendererDestroy;
|
|
47266
47200
|
const managedRenderer = await createManagedRenderer(() => handleRendererDestroy?.());
|
|
47267
47201
|
const renderer = managedRenderer.renderer;
|
|
47202
|
+
const updateCheck = prepareUpdateCheck();
|
|
47268
47203
|
let done;
|
|
47269
47204
|
const finished = new Promise((resolve5) => {
|
|
47270
47205
|
done = resolve5;
|
|
@@ -47326,6 +47261,7 @@ async function runOpenTui(input) {
|
|
|
47326
47261
|
get children() {
|
|
47327
47262
|
return createComponent2(TuiStoreProvider, {
|
|
47328
47263
|
initialSessionId,
|
|
47264
|
+
updateCheck,
|
|
47329
47265
|
onActiveSessionChange: (sessionId, title) => {
|
|
47330
47266
|
activeSessionId = sessionId;
|
|
47331
47267
|
renderer.setTerminalTitle(`farai \xB7 ${title?.trim() || DEFAULT_SESSION_TITLE}`);
|
|
@@ -47397,9 +47333,9 @@ async function ensureSession(input) {
|
|
|
47397
47333
|
} catch {
|
|
47398
47334
|
const needle = input.sessionId.trim().toLowerCase();
|
|
47399
47335
|
const sessions2 = await input.runtime.listSessions();
|
|
47400
|
-
const
|
|
47401
|
-
if (
|
|
47402
|
-
return
|
|
47336
|
+
const matches = sessions2.filter((session) => session.id.toLowerCase().startsWith(needle) || session.title?.trim().toLowerCase() === needle);
|
|
47337
|
+
if (matches.length === 1)
|
|
47338
|
+
return matches[0].id;
|
|
47403
47339
|
throw new SessionResolutionError(input.sessionId, input.workspace, sessions2);
|
|
47404
47340
|
}
|
|
47405
47341
|
}
|
|
@@ -47450,6 +47386,7 @@ var init_agent_tui = __esm(() => {
|
|
|
47450
47386
|
init_runtime_port();
|
|
47451
47387
|
init_session_title();
|
|
47452
47388
|
init_session_catalog();
|
|
47389
|
+
init_update_check();
|
|
47453
47390
|
SessionResolutionError = class SessionResolutionError extends Error {
|
|
47454
47391
|
constructor(query, workspace, sessions2) {
|
|
47455
47392
|
const recent = sessions2.slice(0, 5).map((session) => ` ${session.id} ${session.title?.trim() || DEFAULT_SESSION_TITLE}`).join(`
|
|
@@ -47726,8 +47663,8 @@ var init_csi_cybench_33 = __esm(() => {
|
|
|
47726
47663
|
|
|
47727
47664
|
// src/agent-benchmark/hash.ts
|
|
47728
47665
|
import { createHash as createHash7 } from "crypto";
|
|
47729
|
-
import { readFileSync as
|
|
47730
|
-
import { join as
|
|
47666
|
+
import { readFileSync as readFileSync20, readdirSync as readdirSync9, statSync as statSync7 } from "fs";
|
|
47667
|
+
import { join as join27, relative as relative9 } from "path";
|
|
47731
47668
|
function stableStringify(value) {
|
|
47732
47669
|
return JSON.stringify(sortValue(value));
|
|
47733
47670
|
}
|
|
@@ -47737,10 +47674,10 @@ function sha256(value) {
|
|
|
47737
47674
|
function hashPath(path) {
|
|
47738
47675
|
const stat = statSync7(path);
|
|
47739
47676
|
if (stat.isFile())
|
|
47740
|
-
return sha256(
|
|
47677
|
+
return sha256(readFileSync20(path));
|
|
47741
47678
|
if (!stat.isDirectory())
|
|
47742
47679
|
throw new Error(`unsupported benchmark input type: ${path}`);
|
|
47743
|
-
const entries = walk(path).map((entry) => `${relative9(path, entry).replace(/\\/g, "/")}\x00${sha256(
|
|
47680
|
+
const entries = walk(path).map((entry) => `${relative9(path, entry).replace(/\\/g, "/")}\x00${sha256(readFileSync20(entry))}`);
|
|
47744
47681
|
return sha256(entries.join(`
|
|
47745
47682
|
`));
|
|
47746
47683
|
}
|
|
@@ -47798,7 +47735,7 @@ function sortValue(value) {
|
|
|
47798
47735
|
function walk(root) {
|
|
47799
47736
|
const out = [];
|
|
47800
47737
|
for (const name of readdirSync9(root).sort()) {
|
|
47801
|
-
const path =
|
|
47738
|
+
const path = join27(root, name);
|
|
47802
47739
|
const stat = statSync7(path);
|
|
47803
47740
|
if (stat.isDirectory())
|
|
47804
47741
|
out.push(...walk(path));
|
|
@@ -48144,8 +48081,8 @@ __export(exports_csi_suite, {
|
|
|
48144
48081
|
loadCsiCampaignConfig: () => loadCsiCampaignConfig,
|
|
48145
48082
|
generateCsiBenchmarkSuite: () => generateCsiBenchmarkSuite
|
|
48146
48083
|
});
|
|
48147
|
-
import { existsSync as existsSync20, readFileSync as
|
|
48148
|
-
import { dirname as
|
|
48084
|
+
import { existsSync as existsSync20, readFileSync as readFileSync21, readdirSync as readdirSync10, statSync as statSync8, writeFileSync as writeFileSync15 } from "fs";
|
|
48085
|
+
import { dirname as dirname9, isAbsolute as isAbsolute6, join as join28, relative as relative10, resolve as resolve5 } from "path";
|
|
48149
48086
|
async function loadCsiCampaignConfig(path) {
|
|
48150
48087
|
return normalizeCsiCampaignConfig(JSON.parse(await Bun.file(path).text()));
|
|
48151
48088
|
}
|
|
@@ -48172,7 +48109,7 @@ async function generateCsiBenchmarkSuite(configInput, materialRoot) {
|
|
|
48172
48109
|
const promptPath = protectedPath(root, material.promptFile, `${challenge.id}.promptFile`);
|
|
48173
48110
|
if (!existsSync20(promptPath) || !statSync8(promptPath).isFile())
|
|
48174
48111
|
throw new Error(`missing prompt file for csi challenge: ${challenge.id}`);
|
|
48175
|
-
const prompt =
|
|
48112
|
+
const prompt = readFileSync21(promptPath, "utf8").trim();
|
|
48176
48113
|
if (!prompt)
|
|
48177
48114
|
throw new Error(`empty prompt file for csi challenge: ${challenge.id}`);
|
|
48178
48115
|
const files = material.files?.map((file, index) => {
|
|
@@ -48279,10 +48216,10 @@ async function generateCsiBenchmarkSuite(configInput, materialRoot) {
|
|
|
48279
48216
|
});
|
|
48280
48217
|
}
|
|
48281
48218
|
function writeCsiBenchmarkSuite(suite, path) {
|
|
48282
|
-
const directory =
|
|
48219
|
+
const directory = dirname9(resolve5(path));
|
|
48283
48220
|
if (!existsSync20(directory))
|
|
48284
48221
|
throw new Error(`suite output directory does not exist: ${directory}`);
|
|
48285
|
-
|
|
48222
|
+
writeFileSync15(path, `${JSON.stringify(suite, null, 2)}
|
|
48286
48223
|
`);
|
|
48287
48224
|
}
|
|
48288
48225
|
function normalizeCsiCampaignConfig(value) {
|
|
@@ -48417,7 +48354,7 @@ function protectedPath(root, path, name) {
|
|
|
48417
48354
|
function listFiles(rootPath) {
|
|
48418
48355
|
if (!statSync8(rootPath).isDirectory())
|
|
48419
48356
|
return [rootPath];
|
|
48420
|
-
return readdirSync10(rootPath).flatMap((name) => listFiles(
|
|
48357
|
+
return readdirSync10(rootPath).flatMap((name) => listFiles(join28(rootPath, name)));
|
|
48421
48358
|
}
|
|
48422
48359
|
function object2(value, name) {
|
|
48423
48360
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
@@ -48475,30 +48412,30 @@ var init_csi_suite = __esm(() => {
|
|
|
48475
48412
|
|
|
48476
48413
|
// src/agent-benchmark/bundle.ts
|
|
48477
48414
|
import { createHash as createHash8 } from "crypto";
|
|
48478
|
-
import { chmodSync as chmodSync3, mkdirSync as
|
|
48479
|
-
import { join as
|
|
48415
|
+
import { chmodSync as chmodSync3, mkdirSync as mkdirSync14, readFileSync as readFileSync22, writeFileSync as writeFileSync16 } from "fs";
|
|
48416
|
+
import { join as join29 } from "path";
|
|
48480
48417
|
function writeBenchmarkBundle(bundle, directory) {
|
|
48481
|
-
|
|
48418
|
+
mkdirSync14(directory, {
|
|
48482
48419
|
recursive: true
|
|
48483
48420
|
});
|
|
48484
48421
|
const files = new Map([["manifest.json", json(redactManifest(bundle.manifest))], ["result.json", json(bundle.result)], ["environment.json", json(bundle.result.frozen)], ["sessions.jsonl", jsonl(bundle.sessions)], ["turns.jsonl", jsonl(bundle.turns)], ["messages.jsonl", jsonl(bundle.messages)], ["events.jsonl", jsonl(bundle.events)], ["tool-calls.jsonl", jsonl(bundle.toolCalls)], ["jobs.jsonl", jsonl(bundle.jobs)], ["usage.jsonl", jsonl(bundle.usage)], ["compactions.jsonl", jsonl(bundle.compactions)], ["evidence.jsonl", jsonl(bundle.evidence)]]);
|
|
48485
48422
|
for (const [name, content] of files)
|
|
48486
|
-
|
|
48487
|
-
const checksums = [...files.keys()].sort().map((name) => `${sha2562(
|
|
48423
|
+
writeFileSync16(join29(directory, name), content);
|
|
48424
|
+
const checksums = [...files.keys()].sort().map((name) => `${sha2562(readFileSync22(join29(directory, name)))} ${name}`).join(`
|
|
48488
48425
|
`);
|
|
48489
|
-
|
|
48426
|
+
writeFileSync16(join29(directory, "checksums.sha256"), `${checksums}
|
|
48490
48427
|
`);
|
|
48491
48428
|
for (const name of [...files.keys(), "checksums.sha256"])
|
|
48492
|
-
chmodSync3(
|
|
48429
|
+
chmodSync3(join29(directory, name), 292);
|
|
48493
48430
|
return directory;
|
|
48494
48431
|
}
|
|
48495
48432
|
function writeBenchmarkResult(result, path) {
|
|
48496
48433
|
const directory = path.slice(0, Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\")));
|
|
48497
48434
|
if (directory)
|
|
48498
|
-
|
|
48435
|
+
mkdirSync14(directory, {
|
|
48499
48436
|
recursive: true
|
|
48500
48437
|
});
|
|
48501
|
-
|
|
48438
|
+
writeFileSync16(path, json(result));
|
|
48502
48439
|
}
|
|
48503
48440
|
function redactManifest(manifest) {
|
|
48504
48441
|
return canonicalBenchmarkManifest(manifest);
|
|
@@ -48720,26 +48657,26 @@ __export(exports_runner, {
|
|
|
48720
48657
|
normalizeBenchmarkManifest: () => normalizeBenchmarkManifest,
|
|
48721
48658
|
loadBenchmarkManifest: () => loadBenchmarkManifest
|
|
48722
48659
|
});
|
|
48723
|
-
import { cpSync, existsSync as existsSync22, mkdirSync as
|
|
48660
|
+
import { cpSync, existsSync as existsSync22, mkdirSync as mkdirSync15, mkdtempSync as mkdtempSync3, readFileSync as readFileSync23, readdirSync as readdirSync11, statSync as statSync9 } from "fs";
|
|
48724
48661
|
import { arch, platform, tmpdir as tmpdir4 } from "os";
|
|
48725
|
-
import { dirname as
|
|
48662
|
+
import { dirname as dirname10, join as join30, relative as relative11, resolve as resolve7 } from "path";
|
|
48726
48663
|
async function runBenchmark(input, options = {}) {
|
|
48727
48664
|
const manifest = normalizeBenchmarkManifest(input);
|
|
48728
48665
|
assertExecutableIsolation(manifest);
|
|
48729
|
-
const workspace = options.workspace ?? mkdtempSync3(
|
|
48730
|
-
const artifactsRoot = options.artifactsDir ?? mkdtempSync3(
|
|
48666
|
+
const workspace = options.workspace ?? mkdtempSync3(join30(tmpdir4(), "farai-benchmark-"));
|
|
48667
|
+
const artifactsRoot = options.artifactsDir ?? mkdtempSync3(join30(tmpdir4(), "farai-benchmark-artifacts-"));
|
|
48731
48668
|
const repetition = options.repetition ?? 1;
|
|
48732
|
-
|
|
48669
|
+
mkdirSync15(workspace, {
|
|
48733
48670
|
recursive: true
|
|
48734
48671
|
});
|
|
48735
48672
|
assertCleanWorkspace(workspace);
|
|
48736
48673
|
assertArtifactsOutsideWorkspace(workspace, artifactsRoot);
|
|
48737
|
-
|
|
48674
|
+
mkdirSync15(artifactsRoot, {
|
|
48738
48675
|
recursive: true
|
|
48739
48676
|
});
|
|
48740
48677
|
stageFiles(manifest, workspace);
|
|
48741
48678
|
const runId = id();
|
|
48742
|
-
const bundlePath =
|
|
48679
|
+
const bundlePath = join30(artifactsRoot, `${safeName2(manifest.challenge.id)}-r${repetition}-${runId}`);
|
|
48743
48680
|
const provider = options.provider ?? await createChatProviderForSession(syntheticSession(workspace, manifest));
|
|
48744
48681
|
assertProvider(manifest, provider);
|
|
48745
48682
|
const dockerLifecycle = manifest.isolation.backend === "docker" ? new BenchmarkDockerLifecycle(manifest, workspace, runId, options.dockerProcessRunner) : undefined;
|
|
@@ -49026,7 +48963,7 @@ function stageFiles(manifest, workspace) {
|
|
|
49026
48963
|
const target = resolve7(workspace, file.destination);
|
|
49027
48964
|
if (relative11(workspace, target).startsWith(".."))
|
|
49028
48965
|
throw new Error(`benchmark destination escapes scratch workspace: ${file.destination}`);
|
|
49029
|
-
|
|
48966
|
+
mkdirSync15(dirname10(target), {
|
|
49030
48967
|
recursive: true
|
|
49031
48968
|
});
|
|
49032
48969
|
cpSync(source, target, {
|
|
@@ -49043,7 +48980,7 @@ function listFiles2(rootPath) {
|
|
|
49043
48980
|
return [];
|
|
49044
48981
|
if (!statSync9(rootPath).isDirectory())
|
|
49045
48982
|
return [rootPath];
|
|
49046
|
-
return readdirSync11(rootPath).flatMap((name) => listFiles2(
|
|
48983
|
+
return readdirSync11(rootPath).flatMap((name) => listFiles2(join30(rootPath, name)));
|
|
49047
48984
|
}
|
|
49048
48985
|
|
|
49049
48986
|
class BenchmarkHostBackend {
|
|
@@ -49087,7 +49024,7 @@ class BenchmarkHostBackend {
|
|
|
49087
49024
|
hostPath(path) {
|
|
49088
49025
|
if (path === "/workspace")
|
|
49089
49026
|
return this.workspace;
|
|
49090
|
-
return
|
|
49027
|
+
return join30(this.workspace, path.slice("/workspace/".length));
|
|
49091
49028
|
}
|
|
49092
49029
|
}
|
|
49093
49030
|
function freezeRun(manifest, session, tools, faraiRoot, provider, kaliImageId) {
|
|
@@ -49141,7 +49078,7 @@ function freezeRun(manifest, session, tools, faraiRoot, provider, kaliImageId) {
|
|
|
49141
49078
|
})),
|
|
49142
49079
|
kaliImage: kaliImageId ?? DEFAULT_KALI_IMAGE,
|
|
49143
49080
|
kaliContract: KALI_IMAGE_CONTRACT,
|
|
49144
|
-
kaliToolManifestHash: sha256(
|
|
49081
|
+
kaliToolManifestHash: sha256(readFileSync23(KALI_TOOL_MANIFEST_PATH)),
|
|
49145
49082
|
...manifest.challenge.targetImage ? {
|
|
49146
49083
|
targetImage: manifest.challenge.targetImage
|
|
49147
49084
|
} : {},
|
|
@@ -49355,16 +49292,16 @@ __export(exports_suite, {
|
|
|
49355
49292
|
normalizeBenchmarkSuiteManifest: () => normalizeBenchmarkSuiteManifest,
|
|
49356
49293
|
loadBenchmarkSuiteManifest: () => loadBenchmarkSuiteManifest
|
|
49357
49294
|
});
|
|
49358
|
-
import { mkdirSync as
|
|
49295
|
+
import { mkdirSync as mkdirSync16, mkdtempSync as mkdtempSync4, writeFileSync as writeFileSync17 } from "fs";
|
|
49359
49296
|
import { tmpdir as tmpdir5 } from "os";
|
|
49360
|
-
import { join as
|
|
49297
|
+
import { join as join31 } from "path";
|
|
49361
49298
|
async function runBenchmarkSuite(input, options = {}) {
|
|
49362
49299
|
const manifest = normalizeBenchmarkSuiteManifest(input);
|
|
49363
49300
|
const campaignId = id();
|
|
49364
|
-
const root = options.artifactsDir ?? mkdtempSync4(
|
|
49365
|
-
const bundlePath =
|
|
49366
|
-
const runsPath =
|
|
49367
|
-
|
|
49301
|
+
const root = options.artifactsDir ?? mkdtempSync4(join31(tmpdir5(), "farai-benchmark-campaign-"));
|
|
49302
|
+
const bundlePath = join31(root, `${safeName3(manifest.id)}-${campaignId}`);
|
|
49303
|
+
const runsPath = join31(bundlePath, "runs");
|
|
49304
|
+
mkdirSync16(runsPath, {
|
|
49368
49305
|
recursive: true
|
|
49369
49306
|
});
|
|
49370
49307
|
const attempts = [];
|
|
@@ -49457,9 +49394,9 @@ async function runBenchmarkSuite(input, options = {}) {
|
|
|
49457
49394
|
error: outcome.error
|
|
49458
49395
|
})
|
|
49459
49396
|
};
|
|
49460
|
-
|
|
49397
|
+
writeFileSync17(join31(bundlePath, "campaign.json"), `${JSON.stringify(result, null, 2)}
|
|
49461
49398
|
`);
|
|
49462
|
-
|
|
49399
|
+
writeFileSync17(join31(bundlePath, "suite.sha256"), `${result.manifestHash}
|
|
49463
49400
|
`);
|
|
49464
49401
|
return result;
|
|
49465
49402
|
}
|
|
@@ -49511,8 +49448,9 @@ init_model_catalog();
|
|
|
49511
49448
|
init_model_profiles();
|
|
49512
49449
|
init_global_config();
|
|
49513
49450
|
init_config();
|
|
49514
|
-
|
|
49515
|
-
import {
|
|
49451
|
+
init_branding();
|
|
49452
|
+
import { readFileSync as readFileSync24 } from "fs";
|
|
49453
|
+
import { join as join32 } from "path";
|
|
49516
49454
|
var [, , command, ...args2] = process.argv;
|
|
49517
49455
|
if (command === "--version" || command === "-v" || command === "version") {
|
|
49518
49456
|
console.log(packageVersion());
|
|
@@ -49626,7 +49564,9 @@ async function setup(args3) {
|
|
|
49626
49564
|
const baseUrl = flag(args3, "--base-url") ?? flag(args3, "--baseURL");
|
|
49627
49565
|
const apiKeyEnv = flag(args3, "--api-key-env");
|
|
49628
49566
|
const setDefault = args3.includes("--set-default") || args3.includes("--default") || Boolean(model);
|
|
49629
|
-
console.log(
|
|
49567
|
+
console.log(FARAI_BANNER);
|
|
49568
|
+
console.log();
|
|
49569
|
+
console.log("[*] setting up farai");
|
|
49630
49570
|
console.log(`[+] config: ${globalConfigPath()}`);
|
|
49631
49571
|
console.log(`[+] auth: ${authPath("global")}`);
|
|
49632
49572
|
if (model) {
|
|
@@ -49926,7 +49866,7 @@ function parseProviderModel(value) {
|
|
|
49926
49866
|
}
|
|
49927
49867
|
function packageVersion() {
|
|
49928
49868
|
try {
|
|
49929
|
-
const raw =
|
|
49869
|
+
const raw = readFileSync24(join32(import.meta.dir, "..", "..", "package.json"), "utf8");
|
|
49930
49870
|
const parsed = JSON.parse(raw);
|
|
49931
49871
|
return typeof parsed.version === "string" ? parsed.version : "0.0.0";
|
|
49932
49872
|
} catch {
|
|
@@ -50027,5 +49967,5 @@ Examples:
|
|
|
50027
49967
|
`);
|
|
50028
49968
|
}
|
|
50029
49969
|
|
|
50030
|
-
//# debugId=
|
|
49970
|
+
//# debugId=231A9E4A58F7AFCB64756E2164756E21
|
|
50031
49971
|
//# sourceMappingURL=index.js.map
|