farai 0.1.5 → 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 +262 -598
- package/dist/cli/index.js.map +23 -23
- package/docker/kali/farai-proxy-init.sh +0 -2
- package/docker/kali/farai-proxy-teardown.sh +0 -1
- package/package.json +1 -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();
|
|
@@ -28952,10 +28592,10 @@ function extractEntities(recordId2, text) {
|
|
|
28952
28592
|
const found = new Set;
|
|
28953
28593
|
const out = [];
|
|
28954
28594
|
for (const pattern of ENTITY_PATTERNS) {
|
|
28955
|
-
const
|
|
28956
|
-
if (!
|
|
28595
|
+
const matches = text.match(pattern.re);
|
|
28596
|
+
if (!matches)
|
|
28957
28597
|
continue;
|
|
28958
|
-
for (const raw of
|
|
28598
|
+
for (const raw of matches) {
|
|
28959
28599
|
const value = pattern.normalize ? pattern.normalize(raw) : raw;
|
|
28960
28600
|
const key = `${pattern.type}:${value}`;
|
|
28961
28601
|
if (found.has(key))
|
|
@@ -33856,7 +33496,8 @@ function createRequestUserInputUiState(request) {
|
|
|
33856
33496
|
answers: {},
|
|
33857
33497
|
drafts: {},
|
|
33858
33498
|
textModeQuestionId: first && !first.choices?.length ? first.id : undefined,
|
|
33859
|
-
submitting: false
|
|
33499
|
+
submitting: false,
|
|
33500
|
+
dismissed: false
|
|
33860
33501
|
};
|
|
33861
33502
|
}
|
|
33862
33503
|
function syncRequestUserInputUiState(current, request) {
|
|
@@ -34744,6 +34385,20 @@ function createActions(store, setStore) {
|
|
|
34744
34385
|
return;
|
|
34745
34386
|
s.ui.requestUserInput.submitting = submitting;
|
|
34746
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
|
+
}));
|
|
34747
34402
|
}
|
|
34748
34403
|
};
|
|
34749
34404
|
}
|
|
@@ -37238,19 +36893,6 @@ function TuiStoreProvider(props) {
|
|
|
37238
36893
|
const sid = store.activeSessionId;
|
|
37239
36894
|
if (!sid || !text.trim())
|
|
37240
36895
|
return false;
|
|
37241
|
-
if (store.snapshot.pendingUserInput) {
|
|
37242
|
-
actions.promptHistoryAdd(text);
|
|
37243
|
-
(async () => {
|
|
37244
|
-
try {
|
|
37245
|
-
await port.answerUserInput(sid, text);
|
|
37246
|
-
await requestSnapshotRefresh(sid);
|
|
37247
|
-
} catch (error) {
|
|
37248
|
-
if (!disposed && store.activeSessionId === sid)
|
|
37249
|
-
actions.errorSet(error instanceof Error ? error.message : String(error));
|
|
37250
|
-
}
|
|
37251
|
-
})();
|
|
37252
|
-
return true;
|
|
37253
|
-
}
|
|
37254
36896
|
if (promptSubmissions.has(sid) || isAgentBusy(store) || port.getRunningTurnId(sid)) {
|
|
37255
36897
|
if (port.steer?.(sid, text)) {
|
|
37256
36898
|
actions.promptHistoryAdd(text);
|
|
@@ -37350,12 +36992,6 @@ function TuiStoreProvider(props) {
|
|
|
37350
36992
|
actions.snapshotPatched({
|
|
37351
36993
|
pendingUserInput: undefined
|
|
37352
36994
|
});
|
|
37353
|
-
const turnId = store.snapshot.runningTurnId ?? port.getRunningTurnId(sid);
|
|
37354
|
-
if (turnId && capabilities.cancel) {
|
|
37355
|
-
try {
|
|
37356
|
-
await port.cancelTurn(turnId, "user input cancelled");
|
|
37357
|
-
} catch {}
|
|
37358
|
-
}
|
|
37359
36995
|
await requestSnapshotRefresh(sid);
|
|
37360
36996
|
} catch (error) {
|
|
37361
36997
|
if (!disposed && store.activeSessionId === sid)
|
|
@@ -38009,14 +37645,14 @@ function filterOptions(options, needle) {
|
|
|
38009
37645
|
score: score2
|
|
38010
37646
|
}));
|
|
38011
37647
|
}
|
|
38012
|
-
function groupByCategory(
|
|
37648
|
+
function groupByCategory(matches, needleActive) {
|
|
38013
37649
|
if (needleActive)
|
|
38014
37650
|
return [{
|
|
38015
37651
|
category: undefined,
|
|
38016
|
-
matches: [...
|
|
37652
|
+
matches: [...matches]
|
|
38017
37653
|
}];
|
|
38018
37654
|
const buckets = new Map;
|
|
38019
|
-
for (const match of
|
|
37655
|
+
for (const match of matches) {
|
|
38020
37656
|
const key = match.option.category;
|
|
38021
37657
|
const list2 = buckets.get(key) ?? [];
|
|
38022
37658
|
list2.push(match);
|
|
@@ -38437,6 +38073,10 @@ function routeRequestUserInput(key, state) {
|
|
|
38437
38073
|
if (state.submitting)
|
|
38438
38074
|
return consumed();
|
|
38439
38075
|
if (key.ctrl && key.name === "c")
|
|
38076
|
+
return consumed({
|
|
38077
|
+
kind: "requestUserInput.dismiss"
|
|
38078
|
+
});
|
|
38079
|
+
if (key.ctrl && key.name === "x")
|
|
38440
38080
|
return consumed({
|
|
38441
38081
|
kind: "requestUserInput.cancel"
|
|
38442
38082
|
});
|
|
@@ -38455,7 +38095,7 @@ function routeRequestUserInput(key, state) {
|
|
|
38455
38095
|
if (state.textMode) {
|
|
38456
38096
|
if (key.name === "escape")
|
|
38457
38097
|
return consumed({
|
|
38458
|
-
kind: state.canExitTextMode ? "requestUserInput.textModeExit" : "requestUserInput.
|
|
38098
|
+
kind: state.canExitTextMode ? "requestUserInput.textModeExit" : "requestUserInput.dismiss"
|
|
38459
38099
|
});
|
|
38460
38100
|
if (key.name === "tab" && state.canExitTextMode)
|
|
38461
38101
|
return consumed({
|
|
@@ -38470,7 +38110,7 @@ function routeRequestUserInput(key, state) {
|
|
|
38470
38110
|
switch (key.name) {
|
|
38471
38111
|
case "escape":
|
|
38472
38112
|
return consumed({
|
|
38473
|
-
kind: "requestUserInput.
|
|
38113
|
+
kind: "requestUserInput.dismiss"
|
|
38474
38114
|
});
|
|
38475
38115
|
case "up":
|
|
38476
38116
|
return consumed({
|
|
@@ -38795,6 +38435,10 @@ function routeBase(key, ctx) {
|
|
|
38795
38435
|
return consumed({
|
|
38796
38436
|
kind: "composer.copyLast"
|
|
38797
38437
|
});
|
|
38438
|
+
case "q":
|
|
38439
|
+
return ctx.pendingUserInput ? consumed({
|
|
38440
|
+
kind: "requestUserInput.show"
|
|
38441
|
+
}) : PASSTHROUGH;
|
|
38798
38442
|
case "l":
|
|
38799
38443
|
return consumed({
|
|
38800
38444
|
kind: "transcript.clear"
|
|
@@ -39157,7 +38801,8 @@ function KeyboardController() {
|
|
|
39157
38801
|
historySearchActive: Boolean(tui.store.ui.historySearch),
|
|
39158
38802
|
queuedCount: tui.store.snapshot.queuedPrompts.length,
|
|
39159
38803
|
activeMainTab: tui.store.ui.activeMainTab,
|
|
39160
|
-
|
|
38804
|
+
pendingUserInput: Boolean(pendingRequest),
|
|
38805
|
+
...pendingRequest && requestState && pendingQuestion && !requestState.dismissed ? {
|
|
39161
38806
|
requestUserInput: {
|
|
39162
38807
|
textMode: requestState.textModeQuestionId === pendingQuestion.id,
|
|
39163
38808
|
canExitTextMode: Boolean(pendingQuestion.choices?.length),
|
|
@@ -39258,6 +38903,13 @@ function KeyboardController() {
|
|
|
39258
38903
|
await tui.answerUserInputQuestion(current.question.id, draft);
|
|
39259
38904
|
return;
|
|
39260
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;
|
|
39261
38913
|
case "requestUserInput.cancel":
|
|
39262
38914
|
await tui.cancelUserInput();
|
|
39263
38915
|
return;
|
|
@@ -40099,8 +39751,8 @@ function KeyboardController() {
|
|
|
40099
39751
|
const search = tui.store.ui.historySearch;
|
|
40100
39752
|
if (!search)
|
|
40101
39753
|
return;
|
|
40102
|
-
const
|
|
40103
|
-
const preview =
|
|
39754
|
+
const matches = currentHistoryMatches();
|
|
39755
|
+
const preview = matches[search.index] ?? search.originalDraft;
|
|
40104
39756
|
composer.setDraft(preview);
|
|
40105
39757
|
}
|
|
40106
39758
|
async function openExternalEditor() {
|
|
@@ -45166,15 +44818,15 @@ var init_status_indicator = __esm(() => {
|
|
|
45166
44818
|
});
|
|
45167
44819
|
|
|
45168
44820
|
// src/agent-tui/dialog/list-selection.ts
|
|
45169
|
-
function selectableIndex(
|
|
45170
|
-
const enabled =
|
|
44821
|
+
function selectableIndex(matches, requested) {
|
|
44822
|
+
const enabled = matches.filter((match) => !match.option.disabled);
|
|
45171
44823
|
if (enabled.length === 0)
|
|
45172
44824
|
return -1;
|
|
45173
44825
|
return Math.max(0, Math.min(requested, enabled.length - 1));
|
|
45174
44826
|
}
|
|
45175
|
-
function selectedOptionId(
|
|
45176
|
-
const index = selectableIndex(
|
|
45177
|
-
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;
|
|
45178
44830
|
}
|
|
45179
44831
|
function scrollWindowStart(total, cap, selectedIndex) {
|
|
45180
44832
|
if (total <= cap || selectedIndex < 0)
|
|
@@ -45183,8 +44835,8 @@ function scrollWindowStart(total, cap, selectedIndex) {
|
|
|
45183
44835
|
return 0;
|
|
45184
44836
|
return Math.min(selectedIndex - cap + 1, total - cap);
|
|
45185
44837
|
}
|
|
45186
|
-
function displayRows(
|
|
45187
|
-
return
|
|
44838
|
+
function displayRows(matches, selectedId) {
|
|
44839
|
+
return matches.map((match) => ({
|
|
45188
44840
|
option: match.option,
|
|
45189
44841
|
matched: match.score > 0,
|
|
45190
44842
|
disabled: Boolean(match.option.disabled),
|
|
@@ -45275,9 +44927,9 @@ var init_selection_row = __esm(() => {
|
|
|
45275
44927
|
function ListOverlay(props) {
|
|
45276
44928
|
const tui = useTuiStore();
|
|
45277
44929
|
const dims = useTerminalDimensions();
|
|
45278
|
-
const
|
|
45279
|
-
const groups = createMemo(() => groupByCategory(
|
|
45280
|
-
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));
|
|
45281
44933
|
const rows = createMemo(() => selectionRows(groups(), selectedId()));
|
|
45282
44934
|
const width = () => Math.max(30, dims().width);
|
|
45283
44935
|
const maxRows = () => overlayMaxRows(props.frame.kind, dims().height);
|
|
@@ -45294,7 +44946,7 @@ function ListOverlay(props) {
|
|
|
45294
44946
|
const descCol = () => descriptionColumn(visibleRows(), width());
|
|
45295
44947
|
const subtitle = () => overlaySubtitle(props.frame.kind);
|
|
45296
44948
|
const selectOption = (id2) => {
|
|
45297
|
-
const enabled =
|
|
44949
|
+
const enabled = matches().filter((match) => !match.option.disabled);
|
|
45298
44950
|
const index = enabled.findIndex((match) => match.option.id === id2);
|
|
45299
44951
|
if (index >= 0)
|
|
45300
44952
|
tui.actions.overlaySetIndex(index, enabled.length);
|
|
@@ -45343,7 +44995,7 @@ function ListOverlay(props) {
|
|
|
45343
44995
|
return dims().height;
|
|
45344
44996
|
},
|
|
45345
44997
|
get matches() {
|
|
45346
|
-
return
|
|
44998
|
+
return matches();
|
|
45347
44999
|
},
|
|
45348
45000
|
get selectedId() {
|
|
45349
45001
|
return selectedId();
|
|
@@ -45380,14 +45032,14 @@ function ListOverlay(props) {
|
|
|
45380
45032
|
})());
|
|
45381
45033
|
insert(_el$6, (() => {
|
|
45382
45034
|
var _c$2 = memo2(() => !!props.frame.query);
|
|
45383
|
-
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}` : "";
|
|
45384
45036
|
})());
|
|
45385
45037
|
setProp(_el$7, "style", {
|
|
45386
45038
|
flexDirection: "column"
|
|
45387
45039
|
});
|
|
45388
45040
|
insert(_el$7, createComponent2(Show, {
|
|
45389
45041
|
get when() {
|
|
45390
|
-
return
|
|
45042
|
+
return matches().length > 0;
|
|
45391
45043
|
},
|
|
45392
45044
|
get fallback() {
|
|
45393
45045
|
return (() => {
|
|
@@ -45431,7 +45083,7 @@ function ListOverlay(props) {
|
|
|
45431
45083
|
}));
|
|
45432
45084
|
insert(_el$, createComponent2(SelectionMenuHint, {
|
|
45433
45085
|
get text() {
|
|
45434
|
-
return overlayHint(props.frame,
|
|
45086
|
+
return overlayHint(props.frame, matches(), tui.store.ui.modelProviders);
|
|
45435
45087
|
}
|
|
45436
45088
|
}), null);
|
|
45437
45089
|
effect((_p$) => {
|
|
@@ -46000,10 +45652,10 @@ function overlaySubtitle(kind) {
|
|
|
46000
45652
|
return "inspect durable memory";
|
|
46001
45653
|
return "";
|
|
46002
45654
|
}
|
|
46003
|
-
function overlayHint(frame,
|
|
45655
|
+
function overlayHint(frame, matches, providers) {
|
|
46004
45656
|
if (frame.kind !== "model")
|
|
46005
45657
|
return "press enter to confirm or esc to go back";
|
|
46006
|
-
const enabled =
|
|
45658
|
+
const enabled = matches.filter((match) => !match.option.disabled);
|
|
46007
45659
|
const selected = enabled[frame.index]?.option.value;
|
|
46008
45660
|
if (selected?.kind === "model_action")
|
|
46009
45661
|
return "enter add provider \xB7 ctrl+a add \xB7 esc back";
|
|
@@ -46311,20 +45963,20 @@ function requestStatusDetail(width, progress, countdown) {
|
|
|
46311
45963
|
function requestUserInputHint(width, textMode, hasChoices) {
|
|
46312
45964
|
if (textMode) {
|
|
46313
45965
|
if (width >= 76)
|
|
46314
|
-
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`;
|
|
46315
45967
|
if (width >= 52)
|
|
46316
|
-
return `enter \xB7 ctrl+p/n questions \xB7 esc ${hasChoices ? "choices" : "
|
|
46317
|
-
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"}`;
|
|
46318
45970
|
}
|
|
46319
45971
|
if (width >= 86)
|
|
46320
|
-
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";
|
|
46321
45973
|
if (width >= 58)
|
|
46322
|
-
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";
|
|
46323
45975
|
if (width >= 42)
|
|
46324
|
-
return "\u2191\u2193 select \xB7 enter \xB7 tab other \xB7 esc
|
|
45976
|
+
return "\u2191\u2193 select \xB7 enter \xB7 tab other \xB7 esc chat";
|
|
46325
45977
|
if (width >= 34)
|
|
46326
|
-
return "\u2191\u2193 select \xB7 enter \xB7 esc
|
|
46327
|
-
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";
|
|
46328
45980
|
}
|
|
46329
45981
|
function requestOptionRows(question) {
|
|
46330
45982
|
if (!question?.choices?.length)
|
|
@@ -46900,7 +46552,8 @@ function BottomPane() {
|
|
|
46900
46552
|
const proxyTabActive = () => slot() === "proxy_tab";
|
|
46901
46553
|
const providerWizardActive = () => Boolean(tui.store.ui.modelProviderWizard);
|
|
46902
46554
|
const providerRemovalActive = () => Boolean(tui.store.ui.modelProviderRemoval);
|
|
46903
|
-
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);
|
|
46904
46557
|
const footerHidden = () => Boolean(frame()) || Boolean(centerFrame()) || slashPanelActive() || proxyTabActive() || inputRequestActive() || providerWizardActive() || providerRemovalActive();
|
|
46905
46558
|
const inlineStatusDetail = () => {
|
|
46906
46559
|
const detail = tui.store.ui.statusDetail;
|
|
@@ -46960,10 +46613,10 @@ function BottomPane() {
|
|
|
46960
46613
|
return tui.store.ui.lastError;
|
|
46961
46614
|
},
|
|
46962
46615
|
children: (error) => (() => {
|
|
46963
|
-
var _el$
|
|
46964
|
-
insert(_el$
|
|
46965
|
-
effect((_$p) => setProp(_el$
|
|
46966
|
-
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;
|
|
46967
46620
|
})()
|
|
46968
46621
|
}), null);
|
|
46969
46622
|
insert(_el$, createComponent2(Show, {
|
|
@@ -46974,6 +46627,17 @@ function BottomPane() {
|
|
|
46974
46627
|
return createComponent2(PendingInputPreview, {});
|
|
46975
46628
|
}
|
|
46976
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);
|
|
46977
46641
|
insert(_el$, createComponent2(Show, {
|
|
46978
46642
|
get when() {
|
|
46979
46643
|
return tui.store.ui.modelProviderRemoval;
|
|
@@ -46986,7 +46650,7 @@ function BottomPane() {
|
|
|
46986
46650
|
get fallback() {
|
|
46987
46651
|
return createComponent2(Show, {
|
|
46988
46652
|
get when() {
|
|
46989
|
-
return tui.store.snapshot.pendingUserInput;
|
|
46653
|
+
return memo2(() => !!inputRequestActive())() ? tui.store.snapshot.pendingUserInput : undefined;
|
|
46990
46654
|
},
|
|
46991
46655
|
get fallback() {
|
|
46992
46656
|
return createComponent2(Show, {
|
|
@@ -47063,54 +46727,54 @@ function BottomPane() {
|
|
|
47063
46727
|
function ProxyTabFooter() {
|
|
47064
46728
|
const tui = useTuiStore();
|
|
47065
46729
|
return (() => {
|
|
47066
|
-
var _el$
|
|
47067
|
-
insertNode(_el$
|
|
47068
|
-
insertNode(_el$
|
|
47069
|
-
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", {
|
|
47070
46734
|
height: 1,
|
|
47071
46735
|
flexDirection: "row",
|
|
47072
46736
|
justifyContent: "space-between",
|
|
47073
46737
|
paddingLeft: 1,
|
|
47074
46738
|
paddingRight: 1
|
|
47075
46739
|
});
|
|
47076
|
-
insertNode(_el$
|
|
47077
|
-
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}`);
|
|
47078
46742
|
effect((_p$) => {
|
|
47079
46743
|
var _v$ = COLOR.dim, _v$2 = COLOR.dim;
|
|
47080
|
-
_v$ !== _p$.e && (_p$.e = setProp(_el$
|
|
47081
|
-
_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));
|
|
47082
46746
|
return _p$;
|
|
47083
46747
|
}, {
|
|
47084
46748
|
e: undefined,
|
|
47085
46749
|
t: undefined
|
|
47086
46750
|
});
|
|
47087
|
-
return _el$
|
|
46751
|
+
return _el$5;
|
|
47088
46752
|
})();
|
|
47089
46753
|
}
|
|
47090
46754
|
function CenterSurfaceFooter(props) {
|
|
47091
46755
|
return (() => {
|
|
47092
|
-
var _el$
|
|
47093
|
-
insertNode(_el$
|
|
47094
|
-
insertNode(_el$
|
|
47095
|
-
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", {
|
|
47096
46760
|
height: 1,
|
|
47097
46761
|
flexDirection: "row",
|
|
47098
46762
|
justifyContent: "space-between",
|
|
47099
46763
|
paddingLeft: 1,
|
|
47100
46764
|
paddingRight: 1
|
|
47101
46765
|
});
|
|
47102
|
-
insert(_el$
|
|
47103
|
-
insert(_el$
|
|
46766
|
+
insert(_el$0, () => centerSurfaceFooter(props.frame).toLowerCase());
|
|
46767
|
+
insert(_el$1, () => props.frame.kind.toLowerCase());
|
|
47104
46768
|
effect((_p$) => {
|
|
47105
46769
|
var _v$3 = COLOR.dim, _v$4 = COLOR.dim;
|
|
47106
|
-
_v$3 !== _p$.e && (_p$.e = setProp(_el$
|
|
47107
|
-
_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));
|
|
47108
46772
|
return _p$;
|
|
47109
46773
|
}, {
|
|
47110
46774
|
e: undefined,
|
|
47111
46775
|
t: undefined
|
|
47112
46776
|
});
|
|
47113
|
-
return _el$
|
|
46777
|
+
return _el$9;
|
|
47114
46778
|
})();
|
|
47115
46779
|
}
|
|
47116
46780
|
var init_bottom_pane = __esm(() => {
|
|
@@ -47669,9 +47333,9 @@ async function ensureSession(input) {
|
|
|
47669
47333
|
} catch {
|
|
47670
47334
|
const needle = input.sessionId.trim().toLowerCase();
|
|
47671
47335
|
const sessions2 = await input.runtime.listSessions();
|
|
47672
|
-
const
|
|
47673
|
-
if (
|
|
47674
|
-
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;
|
|
47675
47339
|
throw new SessionResolutionError(input.sessionId, input.workspace, sessions2);
|
|
47676
47340
|
}
|
|
47677
47341
|
}
|
|
@@ -50303,5 +49967,5 @@ Examples:
|
|
|
50303
49967
|
`);
|
|
50304
49968
|
}
|
|
50305
49969
|
|
|
50306
|
-
//# debugId=
|
|
49970
|
+
//# debugId=231A9E4A58F7AFCB64756E2164756E21
|
|
50307
49971
|
//# sourceMappingURL=index.js.map
|