farai 0.1.5 → 0.1.7
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 +724 -672
- package/dist/cli/index.js.map +32 -30
- package/package.json +2 -2
package/dist/cli/index.js
CHANGED
|
@@ -3958,7 +3958,6 @@ 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
3963
|
transparent = true
|
|
@@ -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
|
|
23727
|
-
this.signal.throwIfAborted();
|
|
23728
|
-
this.timer ??= setTimeout(() => {
|
|
23729
|
-
if (!this.signal.aborted)
|
|
23730
|
-
this.controller.abort(new ToolDeadlineError(this.tool, this.timeoutMs));
|
|
23731
|
-
}, this.timeoutMs);
|
|
23544
|
+
async run(work) {
|
|
23732
23545
|
this.signal.throwIfAborted();
|
|
23733
|
-
|
|
23734
|
-
|
|
23735
|
-
|
|
23736
|
-
|
|
23737
|
-
|
|
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 {
|
|
@@ -24312,6 +24103,17 @@ var init_tool_call_journal = () => {};
|
|
|
24312
24103
|
import { createHash as createHash4 } from "crypto";
|
|
24313
24104
|
import { existsSync as existsSync12, mkdirSync as mkdirSync6, realpathSync as realpathSync3, writeFileSync as writeFileSync7 } from "fs";
|
|
24314
24105
|
import { isAbsolute as isAbsolute4, join as join16, relative as relative6 } from "path";
|
|
24106
|
+
function shouldBufferInitialTextStream(text) {
|
|
24107
|
+
if (isInternalMetaReasoning(text))
|
|
24108
|
+
return true;
|
|
24109
|
+
if (text.includes(`
|
|
24110
|
+
`))
|
|
24111
|
+
return false;
|
|
24112
|
+
const normalized = text.trimStart().toLowerCase().replace(/\s+/g, " ");
|
|
24113
|
+
if (!normalized)
|
|
24114
|
+
return false;
|
|
24115
|
+
return INTERNAL_META_STREAM_PREFIXES.some((prefix) => prefix.startsWith(normalized));
|
|
24116
|
+
}
|
|
24315
24117
|
|
|
24316
24118
|
class AgentRuntime {
|
|
24317
24119
|
turnControllers = new Map;
|
|
@@ -24551,7 +24353,11 @@ class AgentRuntime {
|
|
|
24551
24353
|
});
|
|
24552
24354
|
}
|
|
24553
24355
|
if (!session.archivedAt) {
|
|
24554
|
-
this.userInputs.recover(session.id, this.store.listEvents(session.id, 1e4));
|
|
24356
|
+
const pendingUserInput = this.userInputs.recover(session.id, this.store.listEvents(session.id, 1e4));
|
|
24357
|
+
const latestInterruptedTurn = [...turns].reverse().find((turn) => newlyInterruptedTurns.has(turn.id));
|
|
24358
|
+
if (latestInterruptedTurn && !pendingUserInput && !this.mailbox.hasQueued(session.id)) {
|
|
24359
|
+
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}`);
|
|
24360
|
+
}
|
|
24555
24361
|
}
|
|
24556
24362
|
}
|
|
24557
24363
|
for (const job of this.store.listRecoverableJobs()) {
|
|
@@ -24564,7 +24370,7 @@ class AgentRuntime {
|
|
|
24564
24370
|
continue;
|
|
24565
24371
|
}
|
|
24566
24372
|
}
|
|
24567
|
-
this.jobs.markLost(job.id, "Background execution owner was lost during runtime restart.
|
|
24373
|
+
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
24374
|
}
|
|
24569
24375
|
for (const job of this.store.listTerminalJobsMissingMailbox())
|
|
24570
24376
|
this.jobs.repairTerminalMailbox(job.id);
|
|
@@ -25242,7 +25048,7 @@ class AgentRuntime {
|
|
|
25242
25048
|
this.autoCompactFailures.delete(sessionId);
|
|
25243
25049
|
this.fileState.clear(sessionId);
|
|
25244
25050
|
for (const turn of this.store.listTurns(sessionId, 1000)) {
|
|
25245
|
-
this.
|
|
25051
|
+
this.deleteStreamingParts(turn.id);
|
|
25246
25052
|
}
|
|
25247
25053
|
return this.store.clearSessionChat(sessionId);
|
|
25248
25054
|
}
|
|
@@ -25551,8 +25357,8 @@ class AgentRuntime {
|
|
|
25551
25357
|
});
|
|
25552
25358
|
let autoContinueStreak = 0;
|
|
25553
25359
|
let resumeAfterCompaction = false;
|
|
25554
|
-
const maxSteps =
|
|
25555
|
-
const maxTurnMs =
|
|
25360
|
+
const maxSteps = this.maxSteps;
|
|
25361
|
+
const maxTurnMs = this.maxTurnMs;
|
|
25556
25362
|
const loopStartedAt = Date.now();
|
|
25557
25363
|
let timeBudgetWarned = false;
|
|
25558
25364
|
let loopError;
|
|
@@ -25590,7 +25396,7 @@ class AgentRuntime {
|
|
|
25590
25396
|
responses.push(...await this.forceTimeLimitWrapUp(session, turn, assistantMessage, planner, maxTurnMs));
|
|
25591
25397
|
break;
|
|
25592
25398
|
}
|
|
25593
|
-
if (!timeBudgetWarned && elapsedMs >= maxTurnMs * 0.75) {
|
|
25399
|
+
if (Number.isFinite(maxTurnMs) && !timeBudgetWarned && elapsedMs >= maxTurnMs * 0.75) {
|
|
25594
25400
|
timeBudgetWarned = true;
|
|
25595
25401
|
const secondsLeft = Math.max(1, Math.ceil((maxTurnMs - elapsedMs) / 1000));
|
|
25596
25402
|
const queue = this.pendingSteeringContext.get(session.id) ?? [];
|
|
@@ -25601,10 +25407,7 @@ class AgentRuntime {
|
|
|
25601
25407
|
stepCount: step + 1
|
|
25602
25408
|
});
|
|
25603
25409
|
if (step >= maxSteps) {
|
|
25604
|
-
|
|
25605
|
-
responses.push(...await this.forceStepLimitWrapUp(session, turn, assistantMessage, planner, maxSteps));
|
|
25606
|
-
else
|
|
25607
|
-
this.stopTurn(turn, "completed", "no_actions");
|
|
25410
|
+
responses.push(...await this.forceStepLimitWrapUp(session, turn, assistantMessage, planner, maxSteps));
|
|
25608
25411
|
break;
|
|
25609
25412
|
}
|
|
25610
25413
|
const compactResult = await this.maybeAutoCompact(session, planner);
|
|
@@ -25697,7 +25500,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
25697
25500
|
contextWindow: resolveContextWindow(planner.contextWindow),
|
|
25698
25501
|
maxOutputTokens: resolveMaxOutputTokens(planner.maxOutputTokens),
|
|
25699
25502
|
...this.contextBudgetInput(),
|
|
25700
|
-
toolsEnabled:
|
|
25503
|
+
toolsEnabled: true,
|
|
25701
25504
|
extraBlocks: [...passiveCompletions ? [{
|
|
25702
25505
|
title: "Completed Background Work",
|
|
25703
25506
|
body: passiveCompletions,
|
|
@@ -25770,14 +25573,14 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
25770
25573
|
contextBlocks: context.contextBlocks,
|
|
25771
25574
|
tools: context.tools,
|
|
25772
25575
|
toolCatalog: context.toolCatalog,
|
|
25773
|
-
toolChoice:
|
|
25576
|
+
toolChoice: "auto"
|
|
25774
25577
|
};
|
|
25775
25578
|
resumeAfterCompaction = false;
|
|
25776
25579
|
const autoContinue = {
|
|
25777
25580
|
streak: autoContinueStreak
|
|
25778
25581
|
};
|
|
25779
25582
|
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,
|
|
25583
|
+
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
25584
|
autoContinueStreak = autoContinue.streak;
|
|
25782
25585
|
if (control.cancelled)
|
|
25783
25586
|
return responses.join(`
|
|
@@ -25900,10 +25703,10 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
25900
25703
|
latencyMs
|
|
25901
25704
|
});
|
|
25902
25705
|
}
|
|
25903
|
-
async batchStep(planner, plannerInput, session, turn, assistantMessage, step, context, responses, autoContinue,
|
|
25706
|
+
async batchStep(planner, plannerInput, session, turn, assistantMessage, step, context, responses, autoContinue, modelTimeoutMs) {
|
|
25904
25707
|
let actions;
|
|
25905
25708
|
try {
|
|
25906
|
-
actions = await this.planWithRetry(planner, plannerInput, session, turn, assistantMessage, context, modelTimeoutMs
|
|
25709
|
+
actions = await this.planWithRetry(planner, plannerInput, session, turn, assistantMessage, context, modelTimeoutMs);
|
|
25907
25710
|
} catch (error) {
|
|
25908
25711
|
if (error instanceof ModelCallDeadlineError)
|
|
25909
25712
|
return {
|
|
@@ -25947,10 +25750,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
25947
25750
|
};
|
|
25948
25751
|
for (const action of actions) {
|
|
25949
25752
|
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);
|
|
25753
|
+
toolBatch.push(action);
|
|
25954
25754
|
continue;
|
|
25955
25755
|
}
|
|
25956
25756
|
if (await flushToolBatch())
|
|
@@ -25963,7 +25763,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
25963
25763
|
} else if (action.kind === "respond") {
|
|
25964
25764
|
sawResponse = true;
|
|
25965
25765
|
if (hasToolAction && isInternalMetaReasoning(action.text)) {
|
|
25966
|
-
this.discardStreamingText(turn.id);
|
|
25766
|
+
this.discardStreamingText(session.id, turn.id);
|
|
25967
25767
|
shouldContinue = true;
|
|
25968
25768
|
continue;
|
|
25969
25769
|
}
|
|
@@ -26000,7 +25800,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
26000
25800
|
});
|
|
26001
25801
|
const request = buildChatRequest(plannerInput, requestController.signal);
|
|
26002
25802
|
let lastError = "";
|
|
26003
|
-
this.
|
|
25803
|
+
this.deleteStreamingParts(turn.id);
|
|
26004
25804
|
try {
|
|
26005
25805
|
for (let attempt = 1;; attempt += 1) {
|
|
26006
25806
|
this.emitPlannerAttempt(session, turn, assistantMessage, plannerName, attempt, plannerInput, context);
|
|
@@ -26090,10 +25890,6 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
26090
25890
|
this.recordToolParseError(session, turn, assistantMessage, step, toolCallId ?? toolName, toolName, error instanceof Error ? error.message : String(error), event.arguments);
|
|
26091
25891
|
continue;
|
|
26092
25892
|
}
|
|
26093
|
-
if (!userAuthored) {
|
|
26094
|
-
this.recordDisabledToolCall(session, turn, assistantMessage, step, toolCallId ?? toolName, toolName, args);
|
|
26095
|
-
continue;
|
|
26096
|
-
}
|
|
26097
25893
|
const action = {
|
|
26098
25894
|
kind: "tool",
|
|
26099
25895
|
tool: toolName,
|
|
@@ -26143,7 +25939,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
26143
25939
|
shouldContinue: shouldContinue2
|
|
26144
25940
|
};
|
|
26145
25941
|
}
|
|
26146
|
-
this.prepareStreamingRetry(turn.id);
|
|
25942
|
+
this.prepareStreamingRetry(session.id, turn.id);
|
|
26147
25943
|
const retry = plannerRetryState(error, attempt, dispatched.length === 0);
|
|
26148
25944
|
const errorPayload = {
|
|
26149
25945
|
turnId: turn.id,
|
|
@@ -26212,7 +26008,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
26212
26008
|
const rawRespondText = content.trim();
|
|
26213
26009
|
const respondText = dispatched.length > 0 && isInternalMetaReasoning(rawRespondText) ? "" : rawRespondText;
|
|
26214
26010
|
if (!respondText && rawRespondText && dispatched.length > 0)
|
|
26215
|
-
this.discardStreamingText(turn.id);
|
|
26011
|
+
this.discardStreamingText(session.id, turn.id);
|
|
26216
26012
|
const outcomes = await Promise.all(dispatched);
|
|
26217
26013
|
const toolCancelled = outcomes.some((outcome) => outcome.cancelled);
|
|
26218
26014
|
for (const outcome of outcomes) {
|
|
@@ -26366,9 +26162,11 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
26366
26162
|
rationale: ""
|
|
26367
26163
|
});
|
|
26368
26164
|
if (state) {
|
|
26165
|
+
state.reasoningAccum = "";
|
|
26166
|
+
this.publishStreamingReasoning(session.id, turn.id, state);
|
|
26369
26167
|
delete state.reasoningPartId;
|
|
26370
26168
|
delete state.reasoningAccum;
|
|
26371
|
-
delete state.
|
|
26169
|
+
delete state.lastReasoningPersist;
|
|
26372
26170
|
}
|
|
26373
26171
|
return;
|
|
26374
26172
|
}
|
|
@@ -26377,13 +26175,15 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
26377
26175
|
rationale
|
|
26378
26176
|
});
|
|
26379
26177
|
if (state?.reasoningPartId) {
|
|
26178
|
+
state.reasoningAccum = rationale;
|
|
26179
|
+
this.publishStreamingReasoning(session.id, turn.id, state);
|
|
26380
26180
|
this.store.updatePartPayload(state.reasoningPartId, {
|
|
26381
26181
|
planner: plannerName,
|
|
26382
26182
|
rationale
|
|
26383
26183
|
});
|
|
26384
26184
|
delete state.reasoningPartId;
|
|
26385
26185
|
delete state.reasoningAccum;
|
|
26386
|
-
delete state.
|
|
26186
|
+
delete state.lastReasoningPersist;
|
|
26387
26187
|
} else {
|
|
26388
26188
|
this.store.addPart({
|
|
26389
26189
|
sessionId: session.id,
|
|
@@ -26398,13 +26198,11 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
26398
26198
|
}
|
|
26399
26199
|
}
|
|
26400
26200
|
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
26201
|
responses.push(text);
|
|
26406
26202
|
const streamed = this.streamingParts.get(turn.id);
|
|
26407
26203
|
if (streamed?.textPartId) {
|
|
26204
|
+
streamed.textAccum = text;
|
|
26205
|
+
this.publishStreamingText(session.id, turn.id, streamed);
|
|
26408
26206
|
this.store.updatePartPayload(streamed.textPartId, {
|
|
26409
26207
|
text
|
|
26410
26208
|
});
|
|
@@ -26421,29 +26219,11 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
26421
26219
|
recoverable
|
|
26422
26220
|
});
|
|
26423
26221
|
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
|
-
});
|
|
26222
|
+
autoContinue.streak += 1;
|
|
26223
|
+
return true;
|
|
26437
26224
|
}
|
|
26438
26225
|
return false;
|
|
26439
26226
|
}
|
|
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
26227
|
recordToolParseError(session, turn, assistantMessage, step, toolCallId, tool, error, rawArguments) {
|
|
26448
26228
|
const text = `Could not parse arguments for ${tool}: ${error}. Raw: ${rawArguments.slice(0, 500)}`;
|
|
26449
26229
|
this.store.addPart({
|
|
@@ -26486,48 +26266,6 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
26486
26266
|
payload
|
|
26487
26267
|
});
|
|
26488
26268
|
}
|
|
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
26269
|
async forceStepLimitWrapUp(session, turn, assistantMessage, planner, maxSteps) {
|
|
26532
26270
|
return this.forceTextOnlyWrapUp({
|
|
26533
26271
|
session,
|
|
@@ -26680,25 +26418,6 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
26680
26418
|
shouldContinue: !sawResponse
|
|
26681
26419
|
};
|
|
26682
26420
|
}
|
|
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
26421
|
const validationError = validateToolArgs(tool.inputSchema, action.args);
|
|
26703
26422
|
if (validationError) {
|
|
26704
26423
|
const toolCallId = action.toolCallId ?? action.tool;
|
|
@@ -26967,6 +26686,40 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
26967
26686
|
};
|
|
26968
26687
|
}
|
|
26969
26688
|
}
|
|
26689
|
+
deleteStreamingParts(turnId) {
|
|
26690
|
+
this.streamingParts.delete(turnId);
|
|
26691
|
+
}
|
|
26692
|
+
publishStreamingText(sessionId, turnId, state) {
|
|
26693
|
+
if (!state.textPartId)
|
|
26694
|
+
return;
|
|
26695
|
+
this.store.publishTransientEvent({
|
|
26696
|
+
id: id(),
|
|
26697
|
+
sessionId,
|
|
26698
|
+
type: "stream_text",
|
|
26699
|
+
payload: {
|
|
26700
|
+
turnId,
|
|
26701
|
+
partId: state.textPartId,
|
|
26702
|
+
text: state.textAccum
|
|
26703
|
+
},
|
|
26704
|
+
createdAt: nowIso()
|
|
26705
|
+
});
|
|
26706
|
+
}
|
|
26707
|
+
publishStreamingReasoning(sessionId, turnId, state) {
|
|
26708
|
+
if (!state.reasoningPartId)
|
|
26709
|
+
return;
|
|
26710
|
+
const rationale = normalizeReasoningSummary(state.reasoningAccum ?? "");
|
|
26711
|
+
this.store.publishTransientEvent({
|
|
26712
|
+
id: id(),
|
|
26713
|
+
sessionId,
|
|
26714
|
+
type: "stream_reasoning",
|
|
26715
|
+
payload: {
|
|
26716
|
+
turnId,
|
|
26717
|
+
partId: state.reasoningPartId,
|
|
26718
|
+
rationale
|
|
26719
|
+
},
|
|
26720
|
+
createdAt: nowIso()
|
|
26721
|
+
});
|
|
26722
|
+
}
|
|
26970
26723
|
applyStreamEvent(session, turn, assistantMessage, event) {
|
|
26971
26724
|
const now = Date.now();
|
|
26972
26725
|
const state = this.streamingParts.get(turn.id) ?? {
|
|
@@ -26991,26 +26744,39 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
26991
26744
|
}
|
|
26992
26745
|
});
|
|
26993
26746
|
state.reasoningPartId = part.id;
|
|
26994
|
-
state.
|
|
26995
|
-
} else if (now - (state.
|
|
26747
|
+
state.lastReasoningPersist = now;
|
|
26748
|
+
} else if (now - (state.lastReasoningPersist ?? 0) >= STREAM_PERSIST_INTERVAL_MS) {
|
|
26996
26749
|
this.store.updatePartPayload(state.reasoningPartId, {
|
|
26997
26750
|
rationale
|
|
26998
26751
|
});
|
|
26999
|
-
state.
|
|
26752
|
+
state.lastReasoningPersist = now;
|
|
27000
26753
|
}
|
|
26754
|
+
this.publishStreamingReasoning(session.id, turn.id, state);
|
|
27001
26755
|
this.streamingParts.set(turn.id, state);
|
|
27002
26756
|
return;
|
|
27003
26757
|
}
|
|
27004
26758
|
state.textAccum += event.delta;
|
|
27005
26759
|
if (!state.textPartId) {
|
|
27006
|
-
if (
|
|
26760
|
+
if (shouldBufferInitialTextStream(state.textAccum)) {
|
|
27007
26761
|
this.streamingParts.set(turn.id, state);
|
|
27008
26762
|
return;
|
|
27009
26763
|
}
|
|
27010
|
-
if (state.
|
|
27011
|
-
|
|
27012
|
-
|
|
27013
|
-
|
|
26764
|
+
if (!state.reasoningPartId && state.reasoningAccum) {
|
|
26765
|
+
const rationale = normalizeReasoningSummary(state.reasoningAccum);
|
|
26766
|
+
if (rationale) {
|
|
26767
|
+
const reasoningPart = this.store.addPart({
|
|
26768
|
+
sessionId: session.id,
|
|
26769
|
+
turnId: turn.id,
|
|
26770
|
+
messageId: assistantMessage.id,
|
|
26771
|
+
type: "reasoning_summary",
|
|
26772
|
+
payload: {
|
|
26773
|
+
rationale
|
|
26774
|
+
}
|
|
26775
|
+
});
|
|
26776
|
+
state.reasoningPartId = reasoningPart.id;
|
|
26777
|
+
state.lastReasoningPersist = now;
|
|
26778
|
+
this.publishStreamingReasoning(session.id, turn.id, state);
|
|
26779
|
+
}
|
|
27014
26780
|
}
|
|
27015
26781
|
const part = this.store.addPart({
|
|
27016
26782
|
sessionId: session.id,
|
|
@@ -27022,30 +26788,17 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27022
26788
|
}
|
|
27023
26789
|
});
|
|
27024
26790
|
state.textPartId = part.id;
|
|
27025
|
-
state.lastTextRender = now;
|
|
27026
26791
|
state.lastTextPersist = now;
|
|
27027
26792
|
} else if (now - (state.lastTextPersist ?? 0) >= STREAM_PERSIST_INTERVAL_MS) {
|
|
27028
26793
|
this.store.updatePartPayload(state.textPartId, {
|
|
27029
26794
|
text: state.textAccum
|
|
27030
26795
|
});
|
|
27031
|
-
state.lastTextRender = now;
|
|
27032
26796
|
state.lastTextPersist = now;
|
|
27033
|
-
} else if (now - (state.lastTextRender ?? 0) >= STREAM_RENDER_INTERVAL_MS) {
|
|
27034
|
-
this.store.publishTransientEvent({
|
|
27035
|
-
id: id(),
|
|
27036
|
-
sessionId: session.id,
|
|
27037
|
-
type: "stream_text",
|
|
27038
|
-
payload: {
|
|
27039
|
-
partId: state.textPartId,
|
|
27040
|
-
text: state.textAccum
|
|
27041
|
-
},
|
|
27042
|
-
createdAt: nowIso()
|
|
27043
|
-
});
|
|
27044
|
-
state.lastTextRender = now;
|
|
27045
26797
|
}
|
|
26798
|
+
this.publishStreamingText(session.id, turn.id, state);
|
|
27046
26799
|
this.streamingParts.set(turn.id, state);
|
|
27047
26800
|
}
|
|
27048
|
-
discardStreamingText(turnId) {
|
|
26801
|
+
discardStreamingText(sessionId, turnId) {
|
|
27049
26802
|
const state = this.streamingParts.get(turnId);
|
|
27050
26803
|
if (!state)
|
|
27051
26804
|
return;
|
|
@@ -27054,11 +26807,11 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27054
26807
|
text: ""
|
|
27055
26808
|
});
|
|
27056
26809
|
state.textAccum = "";
|
|
26810
|
+
this.publishStreamingText(sessionId, turnId, state);
|
|
27057
26811
|
delete state.textPartId;
|
|
27058
|
-
delete state.lastTextRender;
|
|
27059
26812
|
delete state.lastTextPersist;
|
|
27060
26813
|
}
|
|
27061
|
-
prepareStreamingRetry(turnId) {
|
|
26814
|
+
prepareStreamingRetry(sessionId, turnId) {
|
|
27062
26815
|
const state = this.streamingParts.get(turnId);
|
|
27063
26816
|
if (!state)
|
|
27064
26817
|
return;
|
|
@@ -27071,11 +26824,13 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27071
26824
|
rationale: ""
|
|
27072
26825
|
});
|
|
27073
26826
|
state.textAccum = "";
|
|
26827
|
+
state.reasoningAccum = "";
|
|
26828
|
+
this.publishStreamingText(sessionId, turnId, state);
|
|
26829
|
+
this.publishStreamingReasoning(sessionId, turnId, state);
|
|
27074
26830
|
delete state.reasoningPartId;
|
|
27075
26831
|
delete state.reasoningAccum;
|
|
27076
|
-
delete state.
|
|
27077
|
-
state.
|
|
27078
|
-
state.lastTextPersist = state.lastTextRender;
|
|
26832
|
+
delete state.lastReasoningPersist;
|
|
26833
|
+
state.lastTextPersist = Date.now();
|
|
27079
26834
|
}
|
|
27080
26835
|
async planWithRetry(planner, input, session, turn, assistantMessage, context, timeoutMs, allowEmpty = false) {
|
|
27081
26836
|
let lastError = "";
|
|
@@ -27084,7 +26839,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27084
26839
|
release,
|
|
27085
26840
|
timedOut
|
|
27086
26841
|
} = this.registerTurnController(turn.id, timeoutMs);
|
|
27087
|
-
this.
|
|
26842
|
+
this.deleteStreamingParts(turn.id);
|
|
27088
26843
|
const onStreamEvent = (event) => this.applyStreamEvent(session, turn, assistantMessage, event);
|
|
27089
26844
|
try {
|
|
27090
26845
|
for (let attempt = 1;; attempt += 1) {
|
|
@@ -27111,7 +26866,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27111
26866
|
lastError = "cancelled";
|
|
27112
26867
|
break;
|
|
27113
26868
|
}
|
|
27114
|
-
this.prepareStreamingRetry(turn.id);
|
|
26869
|
+
this.prepareStreamingRetry(session.id, turn.id);
|
|
27115
26870
|
const retry = plannerRetryState(error, attempt, true);
|
|
27116
26871
|
const errorPayload = {
|
|
27117
26872
|
turnId: turn.id,
|
|
@@ -27233,7 +26988,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27233
26988
|
} : {}
|
|
27234
26989
|
});
|
|
27235
26990
|
this.modelCallsByTurn.delete(turn.id);
|
|
27236
|
-
this.
|
|
26991
|
+
this.deleteStreamingParts(turn.id);
|
|
27237
26992
|
this.fireHooks({
|
|
27238
26993
|
id: turn.sessionId
|
|
27239
26994
|
}, "turn.stop", undefined, {
|
|
@@ -27256,25 +27011,18 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27256
27011
|
const workspaceTransition = isWorkspaceTransitionTool(schedulingTool);
|
|
27257
27012
|
const gateSignal = signal ? AbortSignal.any([signal, this.shutdownController.signal]) : this.shutdownController.signal;
|
|
27258
27013
|
try {
|
|
27259
|
-
return await this.workspaceBindingGate.run(`session-workspace:${session.id}`, !workspaceTransition, async (
|
|
27014
|
+
return await this.workspaceBindingGate.run(`session-workspace:${session.id}`, !workspaceTransition, async () => {
|
|
27260
27015
|
session = this.store.loadSession(session.id);
|
|
27261
27016
|
tool = toolForExecution(session, toolName);
|
|
27262
27017
|
schedulingTool = toolSchedulingDefinition(tool, args, session);
|
|
27263
|
-
return await this.toolExecutionGate.run(toolConcurrencyKey(schedulingTool, session, session.workspace), schedulingTool.parallel, async (
|
|
27264
|
-
gateLease.mirrorTo(bindingLease);
|
|
27018
|
+
return await this.toolExecutionGate.run(toolConcurrencyKey(schedulingTool, session, session.workspace), schedulingTool.parallel, async () => {
|
|
27265
27019
|
gateSignal.throwIfAborted();
|
|
27266
27020
|
if (owner && this.store.loadTurn(owner.turn.id).status === "cancelled")
|
|
27267
27021
|
throw new Error("turn cancelled before tool start");
|
|
27268
|
-
return await this.runToolUnderGate(session, tool, args, owner, providerToolCallId
|
|
27022
|
+
return await this.runToolUnderGate(session, tool, args, owner, providerToolCallId);
|
|
27269
27023
|
}, gateSignal);
|
|
27270
27024
|
}, gateSignal);
|
|
27271
27025
|
} 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
27026
|
const ownerCancelled = owner ? this.store.loadTurn(owner.turn.id).status === "cancelled" : false;
|
|
27279
27027
|
if (gateSignal.aborted || ownerCancelled) {
|
|
27280
27028
|
const message = gateSignal.reason ? String(gateSignal.reason) : "turn cancelled before tool start";
|
|
@@ -27304,7 +27052,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27304
27052
|
});
|
|
27305
27053
|
return this.toolCalls.settleError(toolCall, message, state);
|
|
27306
27054
|
}
|
|
27307
|
-
async runToolUnderGate(session, tool, args, owner, providerToolCallId
|
|
27055
|
+
async runToolUnderGate(session, tool, args, owner, providerToolCallId) {
|
|
27308
27056
|
const toolCall = this.toolCalls.begin({
|
|
27309
27057
|
sessionId: session.id,
|
|
27310
27058
|
tool: tool.name,
|
|
@@ -27319,7 +27067,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27319
27067
|
providerToolCallId
|
|
27320
27068
|
} : {}
|
|
27321
27069
|
});
|
|
27322
|
-
await this.executeToolUnderGate(session, toolCall.id, owner
|
|
27070
|
+
await this.executeToolUnderGate(session, toolCall.id, owner);
|
|
27323
27071
|
return this.store.loadToolCall(toolCall.id);
|
|
27324
27072
|
}
|
|
27325
27073
|
async executeTool(session, toolCallId, owner) {
|
|
@@ -27337,29 +27085,18 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27337
27085
|
const gateController = turnId ? this.registerTurnController(turnId) : undefined;
|
|
27338
27086
|
const gateSignal = gateController ? AbortSignal.any([gateController.signal, this.shutdownController.signal]) : this.shutdownController.signal;
|
|
27339
27087
|
try {
|
|
27340
|
-
return await this.workspaceBindingGate.run(`session-workspace:${session.id}`, !workspaceTransition, async (
|
|
27088
|
+
return await this.workspaceBindingGate.run(`session-workspace:${session.id}`, !workspaceTransition, async () => {
|
|
27341
27089
|
session = this.store.loadSession(session.id);
|
|
27342
27090
|
tool = toolForExecution(session, toolCall.tool);
|
|
27343
27091
|
schedulingTool = toolSchedulingDefinition(tool, toolCall.args, session);
|
|
27344
|
-
return await this.toolExecutionGate.run(toolConcurrencyKey(schedulingTool, session, session.workspace), schedulingTool.parallel, async (
|
|
27345
|
-
gateLease.mirrorTo(bindingLease);
|
|
27092
|
+
return await this.toolExecutionGate.run(toolConcurrencyKey(schedulingTool, session, session.workspace), schedulingTool.parallel, async () => {
|
|
27346
27093
|
gateSignal.throwIfAborted();
|
|
27347
27094
|
if (turnId && this.store.loadTurn(turnId).status === "cancelled")
|
|
27348
27095
|
throw new Error("turn cancelled before tool start");
|
|
27349
|
-
return await this.executeToolUnderGate(session, toolCallId, owner
|
|
27096
|
+
return await this.executeToolUnderGate(session, toolCallId, owner);
|
|
27350
27097
|
}, gateSignal);
|
|
27351
27098
|
}, gateSignal);
|
|
27352
27099
|
} 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
27100
|
if (!gateSignal.aborted && (!turnId || this.store.loadTurn(turnId).status !== "cancelled"))
|
|
27364
27101
|
throw error;
|
|
27365
27102
|
const cancelled = this.store.loadToolCall(toolCallId);
|
|
@@ -27376,7 +27113,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27376
27113
|
gateController?.release();
|
|
27377
27114
|
}
|
|
27378
27115
|
}
|
|
27379
|
-
async executeToolUnderGate(session, toolCallId, owner
|
|
27116
|
+
async executeToolUnderGate(session, toolCallId, owner) {
|
|
27380
27117
|
let toolCall = this.store.loadToolCall(toolCallId);
|
|
27381
27118
|
if (toolCall.status !== "pending")
|
|
27382
27119
|
return toolCall;
|
|
@@ -27664,7 +27401,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27664
27401
|
...nestedContext,
|
|
27665
27402
|
signal: nestedDeadline.signal,
|
|
27666
27403
|
timeoutMs: toolOperationTimeout(target.timeoutMs)
|
|
27667
|
-
})
|
|
27404
|
+
}));
|
|
27668
27405
|
lease.assertActive();
|
|
27669
27406
|
await this.fireHooks(session, "tool.post", canonicalName, {
|
|
27670
27407
|
tool: canonicalName,
|
|
@@ -27724,22 +27461,10 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27724
27461
|
linkToolCall = true
|
|
27725
27462
|
}) => {
|
|
27726
27463
|
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
27464
|
const previousJob = resumeSessionId ? this.store.listJobs(session.id, 1e4).find((job2) => job2.kind === "agent" && job2.childSessionId === resumeSessionId) : undefined;
|
|
27740
27465
|
const effectiveLane = lane ?? previousJob?.lane;
|
|
27741
27466
|
const laneDef = effectiveLane ? resolveLane(this.workspace, effectiveLane) : undefined;
|
|
27742
|
-
if (
|
|
27467
|
+
if (effectiveLane && !laneDef)
|
|
27743
27468
|
throw new Error(`unknown subagent lane: ${effectiveLane}`);
|
|
27744
27469
|
let child;
|
|
27745
27470
|
let scopedTools;
|
|
@@ -27753,10 +27478,22 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27753
27478
|
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
27479
|
if (active || this.hasRunningTurn(child.id))
|
|
27755
27480
|
throw new Error(`subagent session ${child.id} is already running`);
|
|
27756
|
-
|
|
27481
|
+
const requestedTools = tools ?? laneDef?.tools;
|
|
27482
|
+
scopedTools = requestedTools ? resolveSubagentToolScope({
|
|
27483
|
+
parent: session,
|
|
27484
|
+
availableTools: listToolsForSession(session),
|
|
27485
|
+
requestedTools
|
|
27486
|
+
}) : child.toolScope;
|
|
27757
27487
|
editsSharedWorkspace = hasSharedWorkspaceEdits(scopedTools);
|
|
27758
|
-
|
|
27759
|
-
|
|
27488
|
+
const childModel = model ?? laneDef?.model;
|
|
27489
|
+
if (childModel && childModel !== child.model)
|
|
27490
|
+
child = this.updateSession(child.id, {
|
|
27491
|
+
model: childModel
|
|
27492
|
+
});
|
|
27493
|
+
if (requestedTools && scopedTools?.length)
|
|
27494
|
+
child = this.updateSession(child.id, {
|
|
27495
|
+
toolScope: scopedTools
|
|
27496
|
+
});
|
|
27760
27497
|
} else {
|
|
27761
27498
|
const requestedTools = tools ?? laneDef?.tools;
|
|
27762
27499
|
scopedTools = resolveSubagentToolScope({
|
|
@@ -27767,8 +27504,6 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27767
27504
|
} : {}
|
|
27768
27505
|
});
|
|
27769
27506
|
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
27507
|
const childModel = model ?? laneDef?.model;
|
|
27773
27508
|
child = await this.store.forkSession(session.id, title);
|
|
27774
27509
|
this.recordSession(child);
|
|
@@ -27876,8 +27611,8 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27876
27611
|
this.jobs.completeAgent(job.id, response, mode === "detached");
|
|
27877
27612
|
return response;
|
|
27878
27613
|
};
|
|
27879
|
-
const gated = () => this.subagentGate.run(execute, agentController.signal);
|
|
27880
|
-
return editsSharedWorkspace ? await this.subagentWorkspaceMutationGate.run(gated, agentController.signal) : await gated();
|
|
27614
|
+
const gated = () => session.parentId ? execute() : this.subagentGate.run(execute, agentController.signal);
|
|
27615
|
+
return editsSharedWorkspace && !session.parentId ? await this.subagentWorkspaceMutationGate.run(gated, agentController.signal) : await gated();
|
|
27881
27616
|
} catch (error) {
|
|
27882
27617
|
if (this.store.isOpen() && this.hasJob(job.id)) {
|
|
27883
27618
|
const current = this.store.loadJob(job.id);
|
|
@@ -27931,7 +27666,6 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27931
27666
|
this.turnControllers.delete(turnId);
|
|
27932
27667
|
};
|
|
27933
27668
|
let result;
|
|
27934
|
-
const duplicate = findEquivalentBackgroundJob(activeBackgroundJobs(this.store.listToolCalls(session.id, 200)), toolCall.tool, toolCall.args);
|
|
27935
27669
|
await this.fireHooks(session, "tool.pre", toolCall.tool, {
|
|
27936
27670
|
tool: toolCall.tool,
|
|
27937
27671
|
toolCallId: toolCall.id,
|
|
@@ -27939,16 +27673,7 @@ This completion is already terminal and was delivered automatically. Do not call
|
|
|
27939
27673
|
});
|
|
27940
27674
|
try {
|
|
27941
27675
|
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);
|
|
27676
|
+
result = await deadline.run(() => tool.run(toolCall.args, context));
|
|
27952
27677
|
} catch (error) {
|
|
27953
27678
|
settleLiveOutput();
|
|
27954
27679
|
releaseController();
|
|
@@ -28609,38 +28334,6 @@ function completedToolCallStatus(result) {
|
|
|
28609
28334
|
return "running_background";
|
|
28610
28335
|
return result.ok ? "done" : "error";
|
|
28611
28336
|
}
|
|
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
28337
|
function recoveredJobSummary(job) {
|
|
28645
28338
|
if (job.result && typeof job.result === "object") {
|
|
28646
28339
|
if ("response" in job.result)
|
|
@@ -28765,7 +28458,7 @@ function sameExistingPath(left, right) {
|
|
|
28765
28458
|
return false;
|
|
28766
28459
|
}
|
|
28767
28460
|
}
|
|
28768
|
-
var REASONING_MAX_BYTES2,
|
|
28461
|
+
var REASONING_MAX_BYTES2, 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.", INTERNAL_META_STREAM_PREFIXES, IneffectiveCompactionError, ModelCallDeadlineError, PlannerOutputValidationError;
|
|
28769
28462
|
var init_runtime = __esm(() => {
|
|
28770
28463
|
init_sqlite_store();
|
|
28771
28464
|
init_registry4();
|
|
@@ -28813,6 +28506,7 @@ var init_runtime = __esm(() => {
|
|
|
28813
28506
|
LIVE_OUTPUT_MAX_BYTES = 2 * 1024;
|
|
28814
28507
|
TOOL_HUMAN_RESULT_MAX_BYTES = 24 * 1024;
|
|
28815
28508
|
PROGRESS_ACTION_TOOLS = new Set(["http_request", "subdomain_enum", "dir_enum", "port_scan", "nmap_scan", "fs_edit", "fs_write", "patch_apply", "code_write_script", "campaign_verify", "campaign_test", "callback_oast", "exploit_search"]);
|
|
28509
|
+
INTERNAL_META_STREAM_PREFIXES = ["the user", "user asked", "user wants", "user requested", "user said", "per my", "per the", "my communication", "my instruction", "my instructions", "my rules", "my task", "according to my", "according to the", "the tool search", "tool_search", "there's no dedicated tool", "there is no dedicated tool", "there's no specific tool", "there is no specific tool", "there's no matching tool", "there is no matching tool", "i have access to", "let me inspect the available tool", "let me check the available tool", "let me search the available tool", "i should report", "i will report", "i need to report", "i must report", "i can report", "we should report", "we will report", "we need to report", "we must report", "we can report"];
|
|
28816
28510
|
IneffectiveCompactionError = class IneffectiveCompactionError extends Error {
|
|
28817
28511
|
constructor(preTokens, postTokens) {
|
|
28818
28512
|
super(`compaction did not reduce active context: ${preTokens} -> ${postTokens} estimated tokens`);
|
|
@@ -28952,10 +28646,10 @@ function extractEntities(recordId2, text) {
|
|
|
28952
28646
|
const found = new Set;
|
|
28953
28647
|
const out = [];
|
|
28954
28648
|
for (const pattern of ENTITY_PATTERNS) {
|
|
28955
|
-
const
|
|
28956
|
-
if (!
|
|
28649
|
+
const matches = text.match(pattern.re);
|
|
28650
|
+
if (!matches)
|
|
28957
28651
|
continue;
|
|
28958
|
-
for (const raw of
|
|
28652
|
+
for (const raw of matches) {
|
|
28959
28653
|
const value = pattern.normalize ? pattern.normalize(raw) : raw;
|
|
28960
28654
|
const key = `${pattern.type}:${value}`;
|
|
28961
28655
|
if (found.has(key))
|
|
@@ -33004,6 +32698,17 @@ function createRuntimePort(runtime, options = {}) {
|
|
|
33004
32698
|
return;
|
|
33005
32699
|
if (evt.type === "snapshot.changed" && pending.some((item) => item.type === "snapshot.changed" && item.sessionId === evt.sessionId))
|
|
33006
32700
|
return;
|
|
32701
|
+
const streamIdentity = transientStreamIdentity(evt);
|
|
32702
|
+
if (streamIdentity) {
|
|
32703
|
+
const existing = pending.length - 1;
|
|
32704
|
+
if (existing >= 0 && transientStreamIdentity(pending[existing]) === streamIdentity)
|
|
32705
|
+
pending[existing] = evt;
|
|
32706
|
+
else
|
|
32707
|
+
pending.push(evt);
|
|
32708
|
+
if (!flushTimer)
|
|
32709
|
+
flushTimer = setTimeout(flush, BATCH_DEBOUNCE_MS);
|
|
32710
|
+
return;
|
|
32711
|
+
}
|
|
33007
32712
|
if (evt.type === "activity.changed") {
|
|
33008
32713
|
const existing = pending.findIndex((item) => item.type === "activity.changed" && item.sessionId === evt.sessionId);
|
|
33009
32714
|
if (existing >= 0)
|
|
@@ -33609,6 +33314,16 @@ function createRuntimePort(runtime, options = {}) {
|
|
|
33609
33314
|
}
|
|
33610
33315
|
};
|
|
33611
33316
|
}
|
|
33317
|
+
function transientStreamIdentity(event) {
|
|
33318
|
+
if (event.type !== "event.appended")
|
|
33319
|
+
return;
|
|
33320
|
+
if (event.event.type !== "stream_text" && event.event.type !== "stream_reasoning")
|
|
33321
|
+
return;
|
|
33322
|
+
const payload = event.event.payload;
|
|
33323
|
+
if (typeof payload?.turnId !== "string" || typeof payload.partId !== "string")
|
|
33324
|
+
return;
|
|
33325
|
+
return `${event.sessionId}:${payload.turnId}:${event.event.type}:${payload.partId}`;
|
|
33326
|
+
}
|
|
33612
33327
|
function storeChangeIdentity(change) {
|
|
33613
33328
|
switch (change.kind) {
|
|
33614
33329
|
case "part":
|
|
@@ -33856,7 +33571,8 @@ function createRequestUserInputUiState(request) {
|
|
|
33856
33571
|
answers: {},
|
|
33857
33572
|
drafts: {},
|
|
33858
33573
|
textModeQuestionId: first && !first.choices?.length ? first.id : undefined,
|
|
33859
|
-
submitting: false
|
|
33574
|
+
submitting: false,
|
|
33575
|
+
dismissed: false
|
|
33860
33576
|
};
|
|
33861
33577
|
}
|
|
33862
33578
|
function syncRequestUserInputUiState(current, request) {
|
|
@@ -34651,19 +34367,6 @@ function createActions(store, setStore) {
|
|
|
34651
34367
|
errorSet(message) {
|
|
34652
34368
|
setStore("ui", "lastError", message);
|
|
34653
34369
|
},
|
|
34654
|
-
streamTextUpdated(partId, text) {
|
|
34655
|
-
setStore(produce((s) => {
|
|
34656
|
-
for (const message of s.snapshot.messages) {
|
|
34657
|
-
const part = message.parts.find((item) => item.id === partId);
|
|
34658
|
-
if (!part)
|
|
34659
|
-
continue;
|
|
34660
|
-
part.payload = {
|
|
34661
|
-
text
|
|
34662
|
-
};
|
|
34663
|
-
return;
|
|
34664
|
-
}
|
|
34665
|
-
}));
|
|
34666
|
-
},
|
|
34667
34370
|
toolInputPreviewUpdated(preview) {
|
|
34668
34371
|
setStore(produce((s) => {
|
|
34669
34372
|
const index = s.snapshot.toolInputPreviews.findIndex((item) => item.id === preview.id);
|
|
@@ -34744,6 +34447,20 @@ function createActions(store, setStore) {
|
|
|
34744
34447
|
return;
|
|
34745
34448
|
s.ui.requestUserInput.submitting = submitting;
|
|
34746
34449
|
}));
|
|
34450
|
+
},
|
|
34451
|
+
requestUserInputDismissedSet(dismissed) {
|
|
34452
|
+
setStore(produce((s) => {
|
|
34453
|
+
if (!s.ui.requestUserInput)
|
|
34454
|
+
return;
|
|
34455
|
+
s.ui.requestUserInput.dismissed = dismissed;
|
|
34456
|
+
if (dismissed)
|
|
34457
|
+
s.ui.requestUserInput.textModeQuestionId = undefined;
|
|
34458
|
+
else {
|
|
34459
|
+
const question = s.snapshot.pendingUserInput?.questions[s.ui.requestUserInput.questionIndex];
|
|
34460
|
+
if (question && !question.choices?.length)
|
|
34461
|
+
s.ui.requestUserInput.textModeQuestionId = question.id;
|
|
34462
|
+
}
|
|
34463
|
+
}));
|
|
34747
34464
|
}
|
|
34748
34465
|
};
|
|
34749
34466
|
}
|
|
@@ -35091,9 +34808,11 @@ async function handleTuiEvent(evt, ctx) {
|
|
|
35091
34808
|
}
|
|
35092
34809
|
switch (evt.type) {
|
|
35093
34810
|
case "turn.started":
|
|
34811
|
+
ctx.onTurnStarted?.(evt.turnId);
|
|
35094
34812
|
ctx.actions.turnStarted(evt.turnId, evt.startedAt);
|
|
35095
34813
|
break;
|
|
35096
34814
|
case "turn.finished":
|
|
34815
|
+
ctx.onTurnFinished?.(evt.turnId);
|
|
35097
34816
|
ctx.actions.turnFinished(evt.turnId);
|
|
35098
34817
|
await refreshSnapshot(evt.sessionId);
|
|
35099
34818
|
queueMicrotask(() => {
|
|
@@ -35124,8 +34843,14 @@ async function handleTuiEvent(evt, ctx) {
|
|
|
35124
34843
|
}
|
|
35125
34844
|
if (evt.event.type === "stream_text") {
|
|
35126
34845
|
const payload = evt.event.payload;
|
|
35127
|
-
if (typeof payload?.partId === "string" && typeof payload.text === "string") {
|
|
35128
|
-
ctx.
|
|
34846
|
+
if (typeof payload?.turnId === "string" && typeof payload.partId === "string" && typeof payload.text === "string") {
|
|
34847
|
+
ctx.onStreamText?.(payload.partId, payload.text, payload.turnId);
|
|
34848
|
+
}
|
|
34849
|
+
}
|
|
34850
|
+
if (evt.event.type === "stream_reasoning") {
|
|
34851
|
+
const payload = evt.event.payload;
|
|
34852
|
+
if (typeof payload?.turnId === "string" && typeof payload.partId === "string" && typeof payload.rationale === "string") {
|
|
34853
|
+
ctx.onStreamReasoning?.(payload.partId, payload.rationale, payload.turnId);
|
|
35129
34854
|
}
|
|
35130
34855
|
}
|
|
35131
34856
|
const detail = statusDetailFor(evt.event);
|
|
@@ -35331,6 +35056,243 @@ var init_runtime2 = __esm(() => {
|
|
|
35331
35056
|
RuntimeContext = createContext();
|
|
35332
35057
|
});
|
|
35333
35058
|
|
|
35059
|
+
// src/agent-tui/transcript/stream-engine.ts
|
|
35060
|
+
function createTranscriptStreamEngine() {
|
|
35061
|
+
const [state, setState] = createStore({
|
|
35062
|
+
text: {},
|
|
35063
|
+
reasoning: {},
|
|
35064
|
+
diagnostics: {
|
|
35065
|
+
revision: 0,
|
|
35066
|
+
flushes: 0,
|
|
35067
|
+
coalescedUpdates: 0,
|
|
35068
|
+
lastIntervalMs: BASE_FRAME_INTERVAL_MS,
|
|
35069
|
+
lastFlushAt: undefined
|
|
35070
|
+
}
|
|
35071
|
+
});
|
|
35072
|
+
const pending = new Map;
|
|
35073
|
+
let activeTurnId;
|
|
35074
|
+
let timer;
|
|
35075
|
+
let timerDueAt = 0;
|
|
35076
|
+
let lastFlushAt = 0;
|
|
35077
|
+
let updatesSinceFlush = 0;
|
|
35078
|
+
let coalescedUpdates = 0;
|
|
35079
|
+
let currentInterval = BASE_FRAME_INTERVAL_MS;
|
|
35080
|
+
let acceptingUpdates = false;
|
|
35081
|
+
let disposed = false;
|
|
35082
|
+
function key(kind, partId) {
|
|
35083
|
+
return `${kind}:${partId}`;
|
|
35084
|
+
}
|
|
35085
|
+
function intervalFor(contentLength, updateCount) {
|
|
35086
|
+
const sizePressure = Math.log2(1 + contentLength / 4096) * 8;
|
|
35087
|
+
const updatePressure = Math.max(0, updateCount - 3) * 2;
|
|
35088
|
+
return Math.round(Math.max(MIN_FRAME_INTERVAL_MS, Math.min(MAX_FRAME_INTERVAL_MS, BASE_FRAME_INTERVAL_MS + sizePressure + updatePressure)));
|
|
35089
|
+
}
|
|
35090
|
+
function schedule() {
|
|
35091
|
+
if (disposed || pending.size === 0)
|
|
35092
|
+
return;
|
|
35093
|
+
const now = Date.now();
|
|
35094
|
+
const maxLength = Math.max(0, ...[...pending.values()].map((entry) => entry.content.length));
|
|
35095
|
+
const interval = intervalFor(maxLength, updatesSinceFlush);
|
|
35096
|
+
currentInterval = interval;
|
|
35097
|
+
const elapsed = now - lastFlushAt;
|
|
35098
|
+
if (lastFlushAt === 0 || elapsed >= interval) {
|
|
35099
|
+
flush();
|
|
35100
|
+
return;
|
|
35101
|
+
}
|
|
35102
|
+
const dueAt = lastFlushAt + interval;
|
|
35103
|
+
if (timer && dueAt <= timerDueAt)
|
|
35104
|
+
return;
|
|
35105
|
+
if (timer)
|
|
35106
|
+
clearTimeout(timer);
|
|
35107
|
+
timerDueAt = dueAt;
|
|
35108
|
+
timer = setTimeout(flush, Math.max(0, dueAt - now));
|
|
35109
|
+
}
|
|
35110
|
+
function flush() {
|
|
35111
|
+
if (disposed)
|
|
35112
|
+
return;
|
|
35113
|
+
if (timer) {
|
|
35114
|
+
clearTimeout(timer);
|
|
35115
|
+
timer = undefined;
|
|
35116
|
+
}
|
|
35117
|
+
timerDueAt = 0;
|
|
35118
|
+
if (pending.size === 0)
|
|
35119
|
+
return;
|
|
35120
|
+
const entries = [...pending.values()];
|
|
35121
|
+
pending.clear();
|
|
35122
|
+
const now = Date.now();
|
|
35123
|
+
lastFlushAt = now;
|
|
35124
|
+
updatesSinceFlush = 0;
|
|
35125
|
+
setState(produce((draft) => {
|
|
35126
|
+
draft.diagnostics.revision += 1;
|
|
35127
|
+
draft.diagnostics.flushes += 1;
|
|
35128
|
+
draft.diagnostics.coalescedUpdates = coalescedUpdates;
|
|
35129
|
+
draft.diagnostics.lastIntervalMs = currentInterval;
|
|
35130
|
+
draft.diagnostics.lastFlushAt = now;
|
|
35131
|
+
const revision = draft.diagnostics.revision;
|
|
35132
|
+
for (const entry of entries) {
|
|
35133
|
+
const target = entry.kind === "text" ? draft.text : draft.reasoning;
|
|
35134
|
+
target[entry.partId] = {
|
|
35135
|
+
content: entry.content,
|
|
35136
|
+
revision
|
|
35137
|
+
};
|
|
35138
|
+
}
|
|
35139
|
+
}));
|
|
35140
|
+
}
|
|
35141
|
+
function clearVisible() {
|
|
35142
|
+
pending.clear();
|
|
35143
|
+
updatesSinceFlush = 0;
|
|
35144
|
+
if (timer) {
|
|
35145
|
+
clearTimeout(timer);
|
|
35146
|
+
timer = undefined;
|
|
35147
|
+
}
|
|
35148
|
+
timerDueAt = 0;
|
|
35149
|
+
setState(produce((draft) => {
|
|
35150
|
+
draft.text = {};
|
|
35151
|
+
draft.reasoning = {};
|
|
35152
|
+
}));
|
|
35153
|
+
}
|
|
35154
|
+
function update(kind, partId, content, turnId) {
|
|
35155
|
+
if (disposed || !acceptingUpdates || turnId !== activeTurnId)
|
|
35156
|
+
return;
|
|
35157
|
+
const pendingKey = key(kind, partId);
|
|
35158
|
+
const existing = pending.get(pendingKey);
|
|
35159
|
+
const visible = kind === "text" ? state.text[partId]?.content : state.reasoning[partId]?.content;
|
|
35160
|
+
if (existing?.content === content || !existing && visible === content)
|
|
35161
|
+
return;
|
|
35162
|
+
if (existing)
|
|
35163
|
+
coalescedUpdates += 1;
|
|
35164
|
+
pending.set(pendingKey, {
|
|
35165
|
+
kind,
|
|
35166
|
+
partId,
|
|
35167
|
+
content
|
|
35168
|
+
});
|
|
35169
|
+
updatesSinceFlush += 1;
|
|
35170
|
+
schedule();
|
|
35171
|
+
}
|
|
35172
|
+
function durableContent(messages, partId, kind) {
|
|
35173
|
+
for (const message of messages) {
|
|
35174
|
+
const part = message.parts.find((candidate2) => candidate2.id === partId);
|
|
35175
|
+
if (!part)
|
|
35176
|
+
continue;
|
|
35177
|
+
if (kind === "text") {
|
|
35178
|
+
if (typeof part.payload === "string")
|
|
35179
|
+
return part.payload;
|
|
35180
|
+
if (part.payload && typeof part.payload === "object" && "text" in part.payload) {
|
|
35181
|
+
const value = part.payload.text;
|
|
35182
|
+
return typeof value === "string" ? value : undefined;
|
|
35183
|
+
}
|
|
35184
|
+
return;
|
|
35185
|
+
}
|
|
35186
|
+
if (part.payload && typeof part.payload === "object") {
|
|
35187
|
+
const payload = part.payload;
|
|
35188
|
+
if (typeof payload.rationale === "string")
|
|
35189
|
+
return payload.rationale;
|
|
35190
|
+
if (typeof payload.text === "string")
|
|
35191
|
+
return payload.text;
|
|
35192
|
+
}
|
|
35193
|
+
return typeof part.payload === "string" ? part.payload : undefined;
|
|
35194
|
+
}
|
|
35195
|
+
return;
|
|
35196
|
+
}
|
|
35197
|
+
function reconciliationRemovals(kind, visible, messages) {
|
|
35198
|
+
const removals = [];
|
|
35199
|
+
for (const [partId, entry] of Object.entries(visible)) {
|
|
35200
|
+
const durable = durableContent(messages, partId, kind);
|
|
35201
|
+
if (durable === entry.content)
|
|
35202
|
+
removals.push(partId);
|
|
35203
|
+
}
|
|
35204
|
+
return removals;
|
|
35205
|
+
}
|
|
35206
|
+
function reconcile2(messages, runningTurnId) {
|
|
35207
|
+
if (disposed)
|
|
35208
|
+
return;
|
|
35209
|
+
if (!runningTurnId) {
|
|
35210
|
+
clearVisible();
|
|
35211
|
+
activeTurnId = undefined;
|
|
35212
|
+
acceptingUpdates = false;
|
|
35213
|
+
return;
|
|
35214
|
+
}
|
|
35215
|
+
if (activeTurnId && activeTurnId !== runningTurnId) {
|
|
35216
|
+
clearVisible();
|
|
35217
|
+
lastFlushAt = 0;
|
|
35218
|
+
currentInterval = BASE_FRAME_INTERVAL_MS;
|
|
35219
|
+
}
|
|
35220
|
+
if (activeTurnId !== runningTurnId)
|
|
35221
|
+
acceptingUpdates = true;
|
|
35222
|
+
activeTurnId = runningTurnId;
|
|
35223
|
+
const textRemovals = reconciliationRemovals("text", state.text, messages);
|
|
35224
|
+
const reasoningRemovals = reconciliationRemovals("reasoning", state.reasoning, messages);
|
|
35225
|
+
if (textRemovals.length === 0 && reasoningRemovals.length === 0)
|
|
35226
|
+
return;
|
|
35227
|
+
setState(produce((draft) => {
|
|
35228
|
+
for (const partId of textRemovals)
|
|
35229
|
+
delete draft.text[partId];
|
|
35230
|
+
for (const partId of reasoningRemovals)
|
|
35231
|
+
delete draft.reasoning[partId];
|
|
35232
|
+
}));
|
|
35233
|
+
}
|
|
35234
|
+
function beginTurn(turnId) {
|
|
35235
|
+
if (disposed)
|
|
35236
|
+
return;
|
|
35237
|
+
if (activeTurnId !== turnId) {
|
|
35238
|
+
clearVisible();
|
|
35239
|
+
lastFlushAt = 0;
|
|
35240
|
+
currentInterval = BASE_FRAME_INTERVAL_MS;
|
|
35241
|
+
}
|
|
35242
|
+
activeTurnId = turnId;
|
|
35243
|
+
acceptingUpdates = true;
|
|
35244
|
+
}
|
|
35245
|
+
function finishTurn(turnId) {
|
|
35246
|
+
if (disposed || activeTurnId !== turnId)
|
|
35247
|
+
return;
|
|
35248
|
+
flush();
|
|
35249
|
+
acceptingUpdates = false;
|
|
35250
|
+
}
|
|
35251
|
+
function reset2() {
|
|
35252
|
+
if (disposed)
|
|
35253
|
+
return;
|
|
35254
|
+
clearVisible();
|
|
35255
|
+
activeTurnId = undefined;
|
|
35256
|
+
acceptingUpdates = false;
|
|
35257
|
+
lastFlushAt = 0;
|
|
35258
|
+
coalescedUpdates = 0;
|
|
35259
|
+
currentInterval = BASE_FRAME_INTERVAL_MS;
|
|
35260
|
+
setState("diagnostics", {
|
|
35261
|
+
revision: 0,
|
|
35262
|
+
flushes: 0,
|
|
35263
|
+
coalescedUpdates: 0,
|
|
35264
|
+
lastIntervalMs: BASE_FRAME_INTERVAL_MS,
|
|
35265
|
+
lastFlushAt: undefined
|
|
35266
|
+
});
|
|
35267
|
+
}
|
|
35268
|
+
function dispose2() {
|
|
35269
|
+
if (disposed)
|
|
35270
|
+
return;
|
|
35271
|
+
clearVisible();
|
|
35272
|
+
disposed = true;
|
|
35273
|
+
}
|
|
35274
|
+
return {
|
|
35275
|
+
state,
|
|
35276
|
+
beginTurn,
|
|
35277
|
+
finishTurn,
|
|
35278
|
+
update,
|
|
35279
|
+
updateText(partId, content, turnId) {
|
|
35280
|
+
update("text", partId, content, turnId);
|
|
35281
|
+
},
|
|
35282
|
+
updateReasoning(partId, content, turnId) {
|
|
35283
|
+
update("reasoning", partId, content, turnId);
|
|
35284
|
+
},
|
|
35285
|
+
reconcile: reconcile2,
|
|
35286
|
+
reset: reset2,
|
|
35287
|
+
flush,
|
|
35288
|
+
dispose: dispose2
|
|
35289
|
+
};
|
|
35290
|
+
}
|
|
35291
|
+
var MIN_FRAME_INTERVAL_MS = 24, BASE_FRAME_INTERVAL_MS = 24, MAX_FRAME_INTERVAL_MS = 96;
|
|
35292
|
+
var init_stream_engine = __esm(() => {
|
|
35293
|
+
init_store2();
|
|
35294
|
+
});
|
|
35295
|
+
|
|
35334
35296
|
// src/agent-tui/reasoning.ts
|
|
35335
35297
|
function parseReasoning(text) {
|
|
35336
35298
|
const trimmed = normalizeReasoningSummary(text);
|
|
@@ -36820,6 +36782,34 @@ var init_renderers2 = __esm(() => {
|
|
|
36820
36782
|
TOOL_DETAIL_MAX_BYTES = 32 * 1024;
|
|
36821
36783
|
});
|
|
36822
36784
|
|
|
36785
|
+
// src/agent-tui/transcript/projection.ts
|
|
36786
|
+
function createTranscriptProjection(store, width) {
|
|
36787
|
+
const historyMessages = createMemo(() => {
|
|
36788
|
+
const runningTurnId = store.snapshot.runningTurnId;
|
|
36789
|
+
return runningTurnId ? store.snapshot.messages.filter((message) => message.turnId !== runningTurnId) : store.snapshot.messages;
|
|
36790
|
+
});
|
|
36791
|
+
const activeMessages = createMemo(() => {
|
|
36792
|
+
const runningTurnId = store.snapshot.runningTurnId;
|
|
36793
|
+
return runningTurnId ? store.snapshot.messages.filter((message) => message.turnId === runningTurnId) : [];
|
|
36794
|
+
});
|
|
36795
|
+
const activeMessageIds = createMemo(() => new Set(activeMessages().map((message) => message.id)));
|
|
36796
|
+
const historyToolCalls = createMemo(() => {
|
|
36797
|
+
const runningTurnId = store.snapshot.runningTurnId;
|
|
36798
|
+
return runningTurnId ? store.snapshot.toolCalls.filter((toolCall) => toolCall.turnId !== runningTurnId && (!toolCall.messageId || !activeMessageIds().has(toolCall.messageId))) : store.snapshot.toolCalls;
|
|
36799
|
+
});
|
|
36800
|
+
const activeToolCalls = createMemo(() => {
|
|
36801
|
+
const runningTurnId = store.snapshot.runningTurnId;
|
|
36802
|
+
return runningTurnId ? store.snapshot.toolCalls.filter((toolCall) => toolCall.turnId === runningTurnId || (toolCall.messageId ? activeMessageIds().has(toolCall.messageId) : toolCall.turnId === undefined)) : [];
|
|
36803
|
+
});
|
|
36804
|
+
const historyRows = createMemo(() => projectMessagesToRows(historyMessages(), width(), undefined, historyToolCalls()));
|
|
36805
|
+
const activeRows = createMemo(() => projectMessagesToRows(activeMessages(), width(), store.snapshot.runningTurnId, activeToolCalls(), store.snapshot.toolInputPreviews));
|
|
36806
|
+
return createMemo(() => [...historyRows(), ...activeRows()]);
|
|
36807
|
+
}
|
|
36808
|
+
var init_projection = __esm(() => {
|
|
36809
|
+
init_solid();
|
|
36810
|
+
init_renderers2();
|
|
36811
|
+
});
|
|
36812
|
+
|
|
36823
36813
|
// src/agent-tui/context/store.tsx
|
|
36824
36814
|
function proxyRefreshQuery() {
|
|
36825
36815
|
return {
|
|
@@ -36838,6 +36828,7 @@ function TuiStoreProvider(props) {
|
|
|
36838
36828
|
setStore: _set,
|
|
36839
36829
|
actions
|
|
36840
36830
|
} = createFaraiStore(workspace);
|
|
36831
|
+
const transcript = createTranscriptStreamEngine();
|
|
36841
36832
|
let statusTimer;
|
|
36842
36833
|
const proxyRefreshes = new Map;
|
|
36843
36834
|
const mcpRefreshes = new Map;
|
|
@@ -36859,7 +36850,7 @@ function TuiStoreProvider(props) {
|
|
|
36859
36850
|
actions.updateNoticeSet(notice);
|
|
36860
36851
|
});
|
|
36861
36852
|
}
|
|
36862
|
-
const timelineRows =
|
|
36853
|
+
const timelineRows = createTranscriptProjection(store, () => Math.max(1, dims().width - 4));
|
|
36863
36854
|
function setStatusDetail(detail, timeoutMs) {
|
|
36864
36855
|
if (disposed)
|
|
36865
36856
|
return;
|
|
@@ -36927,6 +36918,7 @@ function TuiStoreProvider(props) {
|
|
|
36927
36918
|
if (disposed || store.activeSessionId !== sid || snapshotGenerations.get(sid) !== generation)
|
|
36928
36919
|
return;
|
|
36929
36920
|
actions.snapshotApplied(snapshot);
|
|
36921
|
+
transcript.reconcile(snapshot.messages, snapshot.runningTurnId);
|
|
36930
36922
|
for (const entry2 of promptHistoryFromMessages(snapshot.messages)) {
|
|
36931
36923
|
actions.promptHistoryAdd(entry2, "session");
|
|
36932
36924
|
}
|
|
@@ -36956,12 +36948,16 @@ function TuiStoreProvider(props) {
|
|
|
36956
36948
|
return;
|
|
36957
36949
|
if (sessionId === store.activeSessionId)
|
|
36958
36950
|
return;
|
|
36951
|
+
transcript.reset();
|
|
36959
36952
|
actions.activeSessionSet(sessionId);
|
|
36960
36953
|
const pendingSubmission = promptSubmissions.get(sessionId);
|
|
36961
36954
|
if (pendingSubmission)
|
|
36962
36955
|
pendingSubmission.generation = actions.promptSubmissionStarted();
|
|
36963
36956
|
props.onActiveSessionChange?.(sessionId);
|
|
36964
36957
|
port.setActiveSession(sessionId);
|
|
36958
|
+
const runningTurnId = port.getRunningTurnId?.(sessionId);
|
|
36959
|
+
if (runningTurnId)
|
|
36960
|
+
transcript.beginTurn(runningTurnId);
|
|
36965
36961
|
try {
|
|
36966
36962
|
await requestSnapshotRefresh(sessionId);
|
|
36967
36963
|
} catch (error) {
|
|
@@ -37238,19 +37234,6 @@ function TuiStoreProvider(props) {
|
|
|
37238
37234
|
const sid = store.activeSessionId;
|
|
37239
37235
|
if (!sid || !text.trim())
|
|
37240
37236
|
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
37237
|
if (promptSubmissions.has(sid) || isAgentBusy(store) || port.getRunningTurnId(sid)) {
|
|
37255
37238
|
if (port.steer?.(sid, text)) {
|
|
37256
37239
|
actions.promptHistoryAdd(text);
|
|
@@ -37350,12 +37333,6 @@ function TuiStoreProvider(props) {
|
|
|
37350
37333
|
actions.snapshotPatched({
|
|
37351
37334
|
pendingUserInput: undefined
|
|
37352
37335
|
});
|
|
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
37336
|
await requestSnapshotRefresh(sid);
|
|
37360
37337
|
} catch (error) {
|
|
37361
37338
|
if (!disposed && store.activeSessionId === sid)
|
|
@@ -37477,9 +37454,11 @@ function TuiStoreProvider(props) {
|
|
|
37477
37454
|
setStatusDetail,
|
|
37478
37455
|
refreshSnapshot: requestSnapshotRefresh,
|
|
37479
37456
|
refreshSessions,
|
|
37480
|
-
onSnapshot: () =>
|
|
37481
|
-
|
|
37482
|
-
|
|
37457
|
+
onSnapshot: (snapshot) => transcript.reconcile(snapshot.messages, snapshot.runningTurnId),
|
|
37458
|
+
onTurnStarted: (turnId) => transcript.beginTurn(turnId),
|
|
37459
|
+
onTurnFinished: (turnId) => transcript.finishTurn(turnId),
|
|
37460
|
+
onStreamText: (partId, text, turnId) => transcript.updateText(partId, text, turnId),
|
|
37461
|
+
onStreamReasoning: (partId, rationale, turnId) => transcript.updateReasoning(partId, rationale, turnId)
|
|
37483
37462
|
}, (error) => {
|
|
37484
37463
|
if (!disposed)
|
|
37485
37464
|
actions.errorSet(error instanceof Error ? error.message : String(error));
|
|
@@ -37487,8 +37466,9 @@ function TuiStoreProvider(props) {
|
|
|
37487
37466
|
const off = port.event.on((event) => {
|
|
37488
37467
|
if (disposed)
|
|
37489
37468
|
return;
|
|
37490
|
-
if (event.type === "store.changed" || event.type === "store.batch")
|
|
37469
|
+
if (event.type === "store.changed" || event.type === "store.batch" || event.type === "turn.started") {
|
|
37491
37470
|
invalidateSnapshot(event.sessionId);
|
|
37471
|
+
}
|
|
37492
37472
|
eventDispatcher.dispatch(event);
|
|
37493
37473
|
});
|
|
37494
37474
|
onCleanup(() => {
|
|
@@ -37499,6 +37479,7 @@ function TuiStoreProvider(props) {
|
|
|
37499
37479
|
mcpOverlayGeneration += 1;
|
|
37500
37480
|
off();
|
|
37501
37481
|
eventDispatcher.dispose();
|
|
37482
|
+
transcript.dispose();
|
|
37502
37483
|
if (statusTimer)
|
|
37503
37484
|
clearTimeout(statusTimer);
|
|
37504
37485
|
});
|
|
@@ -37544,6 +37525,7 @@ function TuiStoreProvider(props) {
|
|
|
37544
37525
|
store,
|
|
37545
37526
|
actions,
|
|
37546
37527
|
timelineRows,
|
|
37528
|
+
transcript,
|
|
37547
37529
|
submitPrompt,
|
|
37548
37530
|
submitUserInput,
|
|
37549
37531
|
answerUserInputQuestion,
|
|
@@ -37628,7 +37610,8 @@ var init_store4 = __esm(() => {
|
|
|
37628
37610
|
init_solid();
|
|
37629
37611
|
init_store3();
|
|
37630
37612
|
init_runtime2();
|
|
37631
|
-
|
|
37613
|
+
init_stream_engine();
|
|
37614
|
+
init_projection();
|
|
37632
37615
|
StoreContext = createContext();
|
|
37633
37616
|
});
|
|
37634
37617
|
|
|
@@ -38009,14 +37992,14 @@ function filterOptions(options, needle) {
|
|
|
38009
37992
|
score: score2
|
|
38010
37993
|
}));
|
|
38011
37994
|
}
|
|
38012
|
-
function groupByCategory(
|
|
37995
|
+
function groupByCategory(matches, needleActive) {
|
|
38013
37996
|
if (needleActive)
|
|
38014
37997
|
return [{
|
|
38015
37998
|
category: undefined,
|
|
38016
|
-
matches: [...
|
|
37999
|
+
matches: [...matches]
|
|
38017
38000
|
}];
|
|
38018
38001
|
const buckets = new Map;
|
|
38019
|
-
for (const match of
|
|
38002
|
+
for (const match of matches) {
|
|
38020
38003
|
const key = match.option.category;
|
|
38021
38004
|
const list2 = buckets.get(key) ?? [];
|
|
38022
38005
|
list2.push(match);
|
|
@@ -38437,6 +38420,10 @@ function routeRequestUserInput(key, state) {
|
|
|
38437
38420
|
if (state.submitting)
|
|
38438
38421
|
return consumed();
|
|
38439
38422
|
if (key.ctrl && key.name === "c")
|
|
38423
|
+
return consumed({
|
|
38424
|
+
kind: "requestUserInput.dismiss"
|
|
38425
|
+
});
|
|
38426
|
+
if (key.ctrl && key.name === "x")
|
|
38440
38427
|
return consumed({
|
|
38441
38428
|
kind: "requestUserInput.cancel"
|
|
38442
38429
|
});
|
|
@@ -38455,7 +38442,7 @@ function routeRequestUserInput(key, state) {
|
|
|
38455
38442
|
if (state.textMode) {
|
|
38456
38443
|
if (key.name === "escape")
|
|
38457
38444
|
return consumed({
|
|
38458
|
-
kind: state.canExitTextMode ? "requestUserInput.textModeExit" : "requestUserInput.
|
|
38445
|
+
kind: state.canExitTextMode ? "requestUserInput.textModeExit" : "requestUserInput.dismiss"
|
|
38459
38446
|
});
|
|
38460
38447
|
if (key.name === "tab" && state.canExitTextMode)
|
|
38461
38448
|
return consumed({
|
|
@@ -38470,7 +38457,7 @@ function routeRequestUserInput(key, state) {
|
|
|
38470
38457
|
switch (key.name) {
|
|
38471
38458
|
case "escape":
|
|
38472
38459
|
return consumed({
|
|
38473
|
-
kind: "requestUserInput.
|
|
38460
|
+
kind: "requestUserInput.dismiss"
|
|
38474
38461
|
});
|
|
38475
38462
|
case "up":
|
|
38476
38463
|
return consumed({
|
|
@@ -38795,6 +38782,10 @@ function routeBase(key, ctx) {
|
|
|
38795
38782
|
return consumed({
|
|
38796
38783
|
kind: "composer.copyLast"
|
|
38797
38784
|
});
|
|
38785
|
+
case "q":
|
|
38786
|
+
return ctx.pendingUserInput ? consumed({
|
|
38787
|
+
kind: "requestUserInput.show"
|
|
38788
|
+
}) : PASSTHROUGH;
|
|
38798
38789
|
case "l":
|
|
38799
38790
|
return consumed({
|
|
38800
38791
|
kind: "transcript.clear"
|
|
@@ -39157,7 +39148,8 @@ function KeyboardController() {
|
|
|
39157
39148
|
historySearchActive: Boolean(tui.store.ui.historySearch),
|
|
39158
39149
|
queuedCount: tui.store.snapshot.queuedPrompts.length,
|
|
39159
39150
|
activeMainTab: tui.store.ui.activeMainTab,
|
|
39160
|
-
|
|
39151
|
+
pendingUserInput: Boolean(pendingRequest),
|
|
39152
|
+
...pendingRequest && requestState && pendingQuestion && !requestState.dismissed ? {
|
|
39161
39153
|
requestUserInput: {
|
|
39162
39154
|
textMode: requestState.textModeQuestionId === pendingQuestion.id,
|
|
39163
39155
|
canExitTextMode: Boolean(pendingQuestion.choices?.length),
|
|
@@ -39258,6 +39250,13 @@ function KeyboardController() {
|
|
|
39258
39250
|
await tui.answerUserInputQuestion(current.question.id, draft);
|
|
39259
39251
|
return;
|
|
39260
39252
|
}
|
|
39253
|
+
case "requestUserInput.dismiss":
|
|
39254
|
+
tui.actions.requestUserInputDismissedSet(true);
|
|
39255
|
+
composer.focus();
|
|
39256
|
+
return;
|
|
39257
|
+
case "requestUserInput.show":
|
|
39258
|
+
tui.actions.requestUserInputDismissedSet(false);
|
|
39259
|
+
return;
|
|
39261
39260
|
case "requestUserInput.cancel":
|
|
39262
39261
|
await tui.cancelUserInput();
|
|
39263
39262
|
return;
|
|
@@ -40099,8 +40098,8 @@ function KeyboardController() {
|
|
|
40099
40098
|
const search = tui.store.ui.historySearch;
|
|
40100
40099
|
if (!search)
|
|
40101
40100
|
return;
|
|
40102
|
-
const
|
|
40103
|
-
const preview =
|
|
40101
|
+
const matches = currentHistoryMatches();
|
|
40102
|
+
const preview = matches[search.index] ?? search.originalDraft;
|
|
40104
40103
|
composer.setDraft(preview);
|
|
40105
40104
|
}
|
|
40106
40105
|
async function openExternalEditor() {
|
|
@@ -40194,7 +40193,10 @@ function KeyboardController() {
|
|
|
40194
40193
|
for (const message of [...tui.store.snapshot.messages].reverse()) {
|
|
40195
40194
|
if (message.role !== "assistant")
|
|
40196
40195
|
continue;
|
|
40197
|
-
const parts = message.parts.map((part) => {
|
|
40196
|
+
const parts = message.parts.filter((part) => part.type === "text").map((part) => {
|
|
40197
|
+
const streamed = tui.transcript.state.text[part.id]?.content;
|
|
40198
|
+
if (streamed !== undefined)
|
|
40199
|
+
return streamed;
|
|
40198
40200
|
if (typeof part.payload === "string")
|
|
40199
40201
|
return part.payload;
|
|
40200
40202
|
if (part.payload && typeof part.payload === "object" && "text" in part.payload) {
|
|
@@ -40592,11 +40594,13 @@ function AssistantMessage(props) {
|
|
|
40592
40594
|
insertNode(_el$, _el$2);
|
|
40593
40595
|
setProp(_el$, "style", {
|
|
40594
40596
|
flexDirection: "column",
|
|
40597
|
+
flexShrink: 0,
|
|
40595
40598
|
marginBottom: 1
|
|
40596
40599
|
});
|
|
40597
40600
|
insertNode(_el$2, _el$3);
|
|
40598
40601
|
setProp(_el$2, "style", {
|
|
40599
40602
|
flexDirection: "row",
|
|
40603
|
+
flexShrink: 0,
|
|
40600
40604
|
minWidth: 0
|
|
40601
40605
|
});
|
|
40602
40606
|
insert(_el$2, createComponent2(TranscriptMarker, {
|
|
@@ -40612,23 +40616,23 @@ function AssistantMessage(props) {
|
|
|
40612
40616
|
minWidth: 0
|
|
40613
40617
|
});
|
|
40614
40618
|
setProp(_el$4, "width", "100%");
|
|
40619
|
+
setProp(_el$4, "streaming", true);
|
|
40620
|
+
setProp(_el$4, "internalBlockMode", "top-level");
|
|
40615
40621
|
setProp(_el$4, "tableOptions", {
|
|
40616
40622
|
style: "columns",
|
|
40617
40623
|
widthMode: "content",
|
|
40618
40624
|
cellPaddingX: 1
|
|
40619
40625
|
});
|
|
40620
40626
|
effect((_p$) => {
|
|
40621
|
-
var _v$ = props.
|
|
40627
|
+
var _v$ = props.streamedText ?? props.row.text, _v$2 = syntax(), _v$3 = COLOR.markdownText;
|
|
40622
40628
|
_v$ !== _p$.e && (_p$.e = setProp(_el$4, "content", _v$, _p$.e));
|
|
40623
|
-
_v$2 !== _p$.t && (_p$.t = setProp(_el$4, "
|
|
40624
|
-
_v$3 !== _p$.a && (_p$.a = setProp(_el$4, "
|
|
40625
|
-
_v$4 !== _p$.o && (_p$.o = setProp(_el$4, "fg", _v$4, _p$.o));
|
|
40629
|
+
_v$2 !== _p$.t && (_p$.t = setProp(_el$4, "syntaxStyle", _v$2, _p$.t));
|
|
40630
|
+
_v$3 !== _p$.a && (_p$.a = setProp(_el$4, "fg", _v$3, _p$.a));
|
|
40626
40631
|
return _p$;
|
|
40627
40632
|
}, {
|
|
40628
40633
|
e: undefined,
|
|
40629
40634
|
t: undefined,
|
|
40630
|
-
a: undefined
|
|
40631
|
-
o: undefined
|
|
40635
|
+
a: undefined
|
|
40632
40636
|
});
|
|
40633
40637
|
return _el$;
|
|
40634
40638
|
})();
|
|
@@ -40707,18 +40711,24 @@ function ReasoningRow(props) {
|
|
|
40707
40711
|
const tui = useTuiStore();
|
|
40708
40712
|
const dims = useTerminalDimensions();
|
|
40709
40713
|
const expanded = () => Boolean(tui.store.ui.expandedCells[props.row.id]);
|
|
40710
|
-
const
|
|
40714
|
+
const content = createMemo(() => props.streamedReasoning === undefined ? {
|
|
40715
|
+
title: props.row.title,
|
|
40716
|
+
body: props.row.body
|
|
40717
|
+
} : parseReasoning(props.streamedReasoning));
|
|
40718
|
+
const label = () => truncateLine2(content().title === "reasoning" ? "thinking" : content().title, Math.max(1, dims().width - 4));
|
|
40711
40719
|
const toggleClick = createPrimaryClickGesture(() => tui.actions.cellExpandedToggle(props.row.id));
|
|
40712
40720
|
return (() => {
|
|
40713
40721
|
var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text");
|
|
40714
40722
|
insertNode(_el$, _el$2);
|
|
40715
40723
|
setProp(_el$, "style", {
|
|
40716
40724
|
flexDirection: "column",
|
|
40725
|
+
flexShrink: 0,
|
|
40717
40726
|
marginBottom: 1
|
|
40718
40727
|
});
|
|
40719
40728
|
insertNode(_el$2, _el$3);
|
|
40720
40729
|
setProp(_el$2, "style", {
|
|
40721
|
-
flexDirection: "row"
|
|
40730
|
+
flexDirection: "row",
|
|
40731
|
+
flexShrink: 0
|
|
40722
40732
|
});
|
|
40723
40733
|
spread(_el$2, toggleClick, true);
|
|
40724
40734
|
insert(_el$2, createComponent2(TranscriptMarker, {
|
|
@@ -40732,24 +40742,24 @@ function ReasoningRow(props) {
|
|
|
40732
40742
|
insert(_el$3, label);
|
|
40733
40743
|
insert(_el$, createComponent2(Show, {
|
|
40734
40744
|
get when() {
|
|
40735
|
-
return memo2(() => !!expanded())() &&
|
|
40745
|
+
return memo2(() => !!expanded())() && content().body.trim();
|
|
40736
40746
|
},
|
|
40737
40747
|
get children() {
|
|
40738
40748
|
return createComponent2(ExpandedPanel, {
|
|
40739
40749
|
get children() {
|
|
40740
40750
|
var _el$4 = createElement("markdown");
|
|
40751
|
+
setProp(_el$4, "streaming", true);
|
|
40752
|
+
setProp(_el$4, "internalBlockMode", "top-level");
|
|
40741
40753
|
effect((_p$) => {
|
|
40742
|
-
var _v$ =
|
|
40754
|
+
var _v$ = content().body, _v$2 = syntax(), _v$3 = COLOR.dim;
|
|
40743
40755
|
_v$ !== _p$.e && (_p$.e = setProp(_el$4, "content", _v$, _p$.e));
|
|
40744
|
-
_v$2 !== _p$.t && (_p$.t = setProp(_el$4, "
|
|
40745
|
-
_v$3 !== _p$.a && (_p$.a = setProp(_el$4, "
|
|
40746
|
-
_v$4 !== _p$.o && (_p$.o = setProp(_el$4, "fg", _v$4, _p$.o));
|
|
40756
|
+
_v$2 !== _p$.t && (_p$.t = setProp(_el$4, "syntaxStyle", _v$2, _p$.t));
|
|
40757
|
+
_v$3 !== _p$.a && (_p$.a = setProp(_el$4, "fg", _v$3, _p$.a));
|
|
40747
40758
|
return _p$;
|
|
40748
40759
|
}, {
|
|
40749
40760
|
e: undefined,
|
|
40750
40761
|
t: undefined,
|
|
40751
|
-
a: undefined
|
|
40752
|
-
o: undefined
|
|
40762
|
+
a: undefined
|
|
40753
40763
|
});
|
|
40754
40764
|
return _el$4;
|
|
40755
40765
|
}
|
|
@@ -40778,6 +40788,7 @@ var init_reasoning_cell = __esm(() => {
|
|
|
40778
40788
|
init_expanded_panel();
|
|
40779
40789
|
init_transcript_marker();
|
|
40780
40790
|
init_mouse();
|
|
40791
|
+
init_reasoning();
|
|
40781
40792
|
});
|
|
40782
40793
|
|
|
40783
40794
|
// src/agent-tui/filetype.ts
|
|
@@ -42442,12 +42453,18 @@ function FaraiRow(props) {
|
|
|
42442
42453
|
return createComponent2(AssistantMessage, {
|
|
42443
42454
|
get row() {
|
|
42444
42455
|
return props.row;
|
|
42456
|
+
},
|
|
42457
|
+
get streamedText() {
|
|
42458
|
+
return props.streamedText;
|
|
42445
42459
|
}
|
|
42446
42460
|
});
|
|
42447
42461
|
case "thinking":
|
|
42448
42462
|
return createComponent2(ReasoningRow, {
|
|
42449
42463
|
get row() {
|
|
42450
42464
|
return props.row;
|
|
42465
|
+
},
|
|
42466
|
+
get streamedReasoning() {
|
|
42467
|
+
return props.streamedReasoning;
|
|
42451
42468
|
}
|
|
42452
42469
|
});
|
|
42453
42470
|
case "tool":
|
|
@@ -42573,7 +42590,16 @@ function Transcript() {
|
|
|
42573
42590
|
byId
|
|
42574
42591
|
};
|
|
42575
42592
|
});
|
|
42576
|
-
const rawRows = createMemo(() => tui.store.snapshot.messages.flatMap((message) => message.parts.map((part) =>
|
|
42593
|
+
const rawRows = createMemo(() => tui.store.snapshot.messages.flatMap((message) => message.parts.map((part) => {
|
|
42594
|
+
const streamText = tui.transcript.state.text[part.id]?.content;
|
|
42595
|
+
const streamReasoning = tui.transcript.state.reasoning[part.id]?.content;
|
|
42596
|
+
const payload = streamText !== undefined ? {
|
|
42597
|
+
text: streamText
|
|
42598
|
+
} : streamReasoning !== undefined ? {
|
|
42599
|
+
rationale: streamReasoning
|
|
42600
|
+
} : part.payload;
|
|
42601
|
+
return `${message.role}.${part.type} ${formatPayload(payload, 16000)}`;
|
|
42602
|
+
})));
|
|
42577
42603
|
createEffect(() => {
|
|
42578
42604
|
const request = tui.store.ui.messageNavigation;
|
|
42579
42605
|
const userIds = transcriptRows().filter((row) => row.kind === "user").map((row) => row.id);
|
|
@@ -42590,6 +42616,7 @@ function Transcript() {
|
|
|
42590
42616
|
typeof _ref$ === "function" ? use(_ref$, _el$) : scrollRef = _el$;
|
|
42591
42617
|
setProp(_el$, "stickyScroll", true);
|
|
42592
42618
|
setProp(_el$, "stickyStart", "bottom");
|
|
42619
|
+
setProp(_el$, "viewportCulling", true);
|
|
42593
42620
|
setProp(_el$, "scrollbarOptions", {
|
|
42594
42621
|
visible: false
|
|
42595
42622
|
});
|
|
@@ -42655,6 +42682,12 @@ function Transcript() {
|
|
|
42655
42682
|
children: (id2) => createComponent2(TranscriptRow, {
|
|
42656
42683
|
get row() {
|
|
42657
42684
|
return indexedRows().byId.get(id2);
|
|
42685
|
+
},
|
|
42686
|
+
get streamedText() {
|
|
42687
|
+
return tui.transcript.state.text[id2]?.content;
|
|
42688
|
+
},
|
|
42689
|
+
get streamedReasoning() {
|
|
42690
|
+
return tui.transcript.state.reasoning[id2]?.content;
|
|
42658
42691
|
}
|
|
42659
42692
|
})
|
|
42660
42693
|
});
|
|
@@ -42770,12 +42803,19 @@ function TranscriptRow(props) {
|
|
|
42770
42803
|
var _el$1 = createElement("box");
|
|
42771
42804
|
setProp(_el$1, "style", {
|
|
42772
42805
|
flexDirection: "column",
|
|
42806
|
+
flexShrink: 0,
|
|
42773
42807
|
paddingLeft: isUser ? 0 : 1,
|
|
42774
42808
|
paddingRight: isUser ? 0 : 1
|
|
42775
42809
|
});
|
|
42776
42810
|
insert(_el$1, createComponent2(FaraiRow, {
|
|
42777
42811
|
get row() {
|
|
42778
42812
|
return props.row;
|
|
42813
|
+
},
|
|
42814
|
+
get streamedText() {
|
|
42815
|
+
return props.streamedText;
|
|
42816
|
+
},
|
|
42817
|
+
get streamedReasoning() {
|
|
42818
|
+
return props.streamedReasoning;
|
|
42779
42819
|
}
|
|
42780
42820
|
}));
|
|
42781
42821
|
effect((_$p) => setProp(_el$1, "id", props.row.id, _$p));
|
|
@@ -45166,15 +45206,15 @@ var init_status_indicator = __esm(() => {
|
|
|
45166
45206
|
});
|
|
45167
45207
|
|
|
45168
45208
|
// src/agent-tui/dialog/list-selection.ts
|
|
45169
|
-
function selectableIndex(
|
|
45170
|
-
const enabled =
|
|
45209
|
+
function selectableIndex(matches, requested) {
|
|
45210
|
+
const enabled = matches.filter((match) => !match.option.disabled);
|
|
45171
45211
|
if (enabled.length === 0)
|
|
45172
45212
|
return -1;
|
|
45173
45213
|
return Math.max(0, Math.min(requested, enabled.length - 1));
|
|
45174
45214
|
}
|
|
45175
|
-
function selectedOptionId(
|
|
45176
|
-
const index = selectableIndex(
|
|
45177
|
-
return index < 0 ? undefined :
|
|
45215
|
+
function selectedOptionId(matches, requested) {
|
|
45216
|
+
const index = selectableIndex(matches, requested);
|
|
45217
|
+
return index < 0 ? undefined : matches.filter((match) => !match.option.disabled)[index]?.option.id;
|
|
45178
45218
|
}
|
|
45179
45219
|
function scrollWindowStart(total, cap, selectedIndex) {
|
|
45180
45220
|
if (total <= cap || selectedIndex < 0)
|
|
@@ -45183,8 +45223,8 @@ function scrollWindowStart(total, cap, selectedIndex) {
|
|
|
45183
45223
|
return 0;
|
|
45184
45224
|
return Math.min(selectedIndex - cap + 1, total - cap);
|
|
45185
45225
|
}
|
|
45186
|
-
function displayRows(
|
|
45187
|
-
return
|
|
45226
|
+
function displayRows(matches, selectedId) {
|
|
45227
|
+
return matches.map((match) => ({
|
|
45188
45228
|
option: match.option,
|
|
45189
45229
|
matched: match.score > 0,
|
|
45190
45230
|
disabled: Boolean(match.option.disabled),
|
|
@@ -45275,9 +45315,9 @@ var init_selection_row = __esm(() => {
|
|
|
45275
45315
|
function ListOverlay(props) {
|
|
45276
45316
|
const tui = useTuiStore();
|
|
45277
45317
|
const dims = useTerminalDimensions();
|
|
45278
|
-
const
|
|
45279
|
-
const groups = createMemo(() => groupByCategory(
|
|
45280
|
-
const selectedId = createMemo(() => selectedOptionId(
|
|
45318
|
+
const matches = createMemo(() => filterOptions(props.options, props.frame.query));
|
|
45319
|
+
const groups = createMemo(() => groupByCategory(matches(), props.frame.query.trim() !== ""));
|
|
45320
|
+
const selectedId = createMemo(() => selectedOptionId(matches(), props.frame.index));
|
|
45281
45321
|
const rows = createMemo(() => selectionRows(groups(), selectedId()));
|
|
45282
45322
|
const width = () => Math.max(30, dims().width);
|
|
45283
45323
|
const maxRows = () => overlayMaxRows(props.frame.kind, dims().height);
|
|
@@ -45294,7 +45334,7 @@ function ListOverlay(props) {
|
|
|
45294
45334
|
const descCol = () => descriptionColumn(visibleRows(), width());
|
|
45295
45335
|
const subtitle = () => overlaySubtitle(props.frame.kind);
|
|
45296
45336
|
const selectOption = (id2) => {
|
|
45297
|
-
const enabled =
|
|
45337
|
+
const enabled = matches().filter((match) => !match.option.disabled);
|
|
45298
45338
|
const index = enabled.findIndex((match) => match.option.id === id2);
|
|
45299
45339
|
if (index >= 0)
|
|
45300
45340
|
tui.actions.overlaySetIndex(index, enabled.length);
|
|
@@ -45343,7 +45383,7 @@ function ListOverlay(props) {
|
|
|
45343
45383
|
return dims().height;
|
|
45344
45384
|
},
|
|
45345
45385
|
get matches() {
|
|
45346
|
-
return
|
|
45386
|
+
return matches();
|
|
45347
45387
|
},
|
|
45348
45388
|
get selectedId() {
|
|
45349
45389
|
return selectedId();
|
|
@@ -45380,14 +45420,14 @@ function ListOverlay(props) {
|
|
|
45380
45420
|
})());
|
|
45381
45421
|
insert(_el$6, (() => {
|
|
45382
45422
|
var _c$2 = memo2(() => !!props.frame.query);
|
|
45383
|
-
return () => _c$2() ? `${
|
|
45423
|
+
return () => _c$2() ? `${matches().length}/${props.options.length}` : memo2(() => matches().length > 0)() ? `${Math.min(props.frame.index + 1, matches().length)}/${matches().length}` : "";
|
|
45384
45424
|
})());
|
|
45385
45425
|
setProp(_el$7, "style", {
|
|
45386
45426
|
flexDirection: "column"
|
|
45387
45427
|
});
|
|
45388
45428
|
insert(_el$7, createComponent2(Show, {
|
|
45389
45429
|
get when() {
|
|
45390
|
-
return
|
|
45430
|
+
return matches().length > 0;
|
|
45391
45431
|
},
|
|
45392
45432
|
get fallback() {
|
|
45393
45433
|
return (() => {
|
|
@@ -45431,7 +45471,7 @@ function ListOverlay(props) {
|
|
|
45431
45471
|
}));
|
|
45432
45472
|
insert(_el$, createComponent2(SelectionMenuHint, {
|
|
45433
45473
|
get text() {
|
|
45434
|
-
return overlayHint(props.frame,
|
|
45474
|
+
return overlayHint(props.frame, matches(), tui.store.ui.modelProviders);
|
|
45435
45475
|
}
|
|
45436
45476
|
}), null);
|
|
45437
45477
|
effect((_p$) => {
|
|
@@ -46000,10 +46040,10 @@ function overlaySubtitle(kind) {
|
|
|
46000
46040
|
return "inspect durable memory";
|
|
46001
46041
|
return "";
|
|
46002
46042
|
}
|
|
46003
|
-
function overlayHint(frame,
|
|
46043
|
+
function overlayHint(frame, matches, providers) {
|
|
46004
46044
|
if (frame.kind !== "model")
|
|
46005
46045
|
return "press enter to confirm or esc to go back";
|
|
46006
|
-
const enabled =
|
|
46046
|
+
const enabled = matches.filter((match) => !match.option.disabled);
|
|
46007
46047
|
const selected = enabled[frame.index]?.option.value;
|
|
46008
46048
|
if (selected?.kind === "model_action")
|
|
46009
46049
|
return "enter add provider \xB7 ctrl+a add \xB7 esc back";
|
|
@@ -46311,20 +46351,20 @@ function requestStatusDetail(width, progress, countdown) {
|
|
|
46311
46351
|
function requestUserInputHint(width, textMode, hasChoices) {
|
|
46312
46352
|
if (textMode) {
|
|
46313
46353
|
if (width >= 76)
|
|
46314
|
-
return `enter continue \xB7 ${hasChoices ? "tab choices \xB7 " : ""}ctrl+p/n questions \xB7 esc ${hasChoices ? "choices" : "
|
|
46354
|
+
return `enter continue \xB7 ${hasChoices ? "tab choices \xB7 " : ""}ctrl+p/n questions \xB7 esc ${hasChoices ? "choices" : "chat"} \xB7 ctrl+x cancel`;
|
|
46315
46355
|
if (width >= 52)
|
|
46316
|
-
return `enter \xB7 ctrl+p/n questions \xB7 esc ${hasChoices ? "choices" : "
|
|
46317
|
-
return `enter \xB7 esc ${hasChoices ? "choices" : "
|
|
46356
|
+
return `enter \xB7 ctrl+p/n questions \xB7 esc ${hasChoices ? "choices" : "chat"}`;
|
|
46357
|
+
return `enter \xB7 esc ${hasChoices ? "choices" : "chat"}`;
|
|
46318
46358
|
}
|
|
46319
46359
|
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";
|
|
46360
|
+
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
46361
|
if (width >= 58)
|
|
46322
|
-
return "\u2191\u2193 select \xB7 enter \xB7 \u2190\u2192 questions \xB7 tab other \xB7 esc
|
|
46362
|
+
return "\u2191\u2193 select \xB7 enter \xB7 \u2190\u2192 questions \xB7 tab other \xB7 esc chat";
|
|
46323
46363
|
if (width >= 42)
|
|
46324
|
-
return "\u2191\u2193 select \xB7 enter \xB7 tab other \xB7 esc
|
|
46364
|
+
return "\u2191\u2193 select \xB7 enter \xB7 tab other \xB7 esc chat";
|
|
46325
46365
|
if (width >= 34)
|
|
46326
|
-
return "\u2191\u2193 select \xB7 enter \xB7 esc
|
|
46327
|
-
return "\u2191\u2193 \xB7 enter \xB7 esc";
|
|
46366
|
+
return "\u2191\u2193 select \xB7 enter \xB7 esc chat";
|
|
46367
|
+
return "\u2191\u2193 \xB7 enter \xB7 esc chat";
|
|
46328
46368
|
}
|
|
46329
46369
|
function requestOptionRows(question) {
|
|
46330
46370
|
if (!question?.choices?.length)
|
|
@@ -46900,7 +46940,8 @@ function BottomPane() {
|
|
|
46900
46940
|
const proxyTabActive = () => slot() === "proxy_tab";
|
|
46901
46941
|
const providerWizardActive = () => Boolean(tui.store.ui.modelProviderWizard);
|
|
46902
46942
|
const providerRemovalActive = () => Boolean(tui.store.ui.modelProviderRemoval);
|
|
46903
|
-
const inputRequestActive = () => Boolean(tui.store.snapshot.pendingUserInput);
|
|
46943
|
+
const inputRequestActive = () => Boolean(tui.store.snapshot.pendingUserInput && !tui.store.ui.requestUserInput?.dismissed);
|
|
46944
|
+
const inputRequestPending = () => Boolean(tui.store.snapshot.pendingUserInput);
|
|
46904
46945
|
const footerHidden = () => Boolean(frame()) || Boolean(centerFrame()) || slashPanelActive() || proxyTabActive() || inputRequestActive() || providerWizardActive() || providerRemovalActive();
|
|
46905
46946
|
const inlineStatusDetail = () => {
|
|
46906
46947
|
const detail = tui.store.ui.statusDetail;
|
|
@@ -46960,10 +47001,10 @@ function BottomPane() {
|
|
|
46960
47001
|
return tui.store.ui.lastError;
|
|
46961
47002
|
},
|
|
46962
47003
|
children: (error) => (() => {
|
|
46963
|
-
var _el$
|
|
46964
|
-
insert(_el$
|
|
46965
|
-
effect((_$p) => setProp(_el$
|
|
46966
|
-
return _el$
|
|
47004
|
+
var _el$4 = createElement("text");
|
|
47005
|
+
insert(_el$4, () => `\u2022 error \xB7 ${truncateLine2(error(), 160)}`);
|
|
47006
|
+
effect((_$p) => setProp(_el$4, "fg", COLOR.error, _$p));
|
|
47007
|
+
return _el$4;
|
|
46967
47008
|
})()
|
|
46968
47009
|
}), null);
|
|
46969
47010
|
insert(_el$, createComponent2(Show, {
|
|
@@ -46974,6 +47015,17 @@ function BottomPane() {
|
|
|
46974
47015
|
return createComponent2(PendingInputPreview, {});
|
|
46975
47016
|
}
|
|
46976
47017
|
}), null);
|
|
47018
|
+
insert(_el$, createComponent2(Show, {
|
|
47019
|
+
get when() {
|
|
47020
|
+
return memo2(() => !!(!inputRequestActive() && inputRequestPending() && !providerWizardActive()))() && !providerRemovalActive();
|
|
47021
|
+
},
|
|
47022
|
+
get children() {
|
|
47023
|
+
var _el$2 = createElement("text");
|
|
47024
|
+
insertNode(_el$2, createTextNode(` question pending \xB7 ctrl+q answer \xB7 chat input remains available`));
|
|
47025
|
+
effect((_$p) => setProp(_el$2, "fg", COLOR.warning, _$p));
|
|
47026
|
+
return _el$2;
|
|
47027
|
+
}
|
|
47028
|
+
}), null);
|
|
46977
47029
|
insert(_el$, createComponent2(Show, {
|
|
46978
47030
|
get when() {
|
|
46979
47031
|
return tui.store.ui.modelProviderRemoval;
|
|
@@ -46986,7 +47038,7 @@ function BottomPane() {
|
|
|
46986
47038
|
get fallback() {
|
|
46987
47039
|
return createComponent2(Show, {
|
|
46988
47040
|
get when() {
|
|
46989
|
-
return tui.store.snapshot.pendingUserInput;
|
|
47041
|
+
return memo2(() => !!inputRequestActive())() ? tui.store.snapshot.pendingUserInput : undefined;
|
|
46990
47042
|
},
|
|
46991
47043
|
get fallback() {
|
|
46992
47044
|
return createComponent2(Show, {
|
|
@@ -47063,54 +47115,54 @@ function BottomPane() {
|
|
|
47063
47115
|
function ProxyTabFooter() {
|
|
47064
47116
|
const tui = useTuiStore();
|
|
47065
47117
|
return (() => {
|
|
47066
|
-
var _el$
|
|
47067
|
-
insertNode(_el$
|
|
47068
|
-
insertNode(_el$
|
|
47069
|
-
setProp(_el$
|
|
47118
|
+
var _el$5 = createElement("box"), _el$6 = createElement("text"), _el$8 = createElement("text");
|
|
47119
|
+
insertNode(_el$5, _el$6);
|
|
47120
|
+
insertNode(_el$5, _el$8);
|
|
47121
|
+
setProp(_el$5, "style", {
|
|
47070
47122
|
height: 1,
|
|
47071
47123
|
flexDirection: "row",
|
|
47072
47124
|
justifyContent: "space-between",
|
|
47073
47125
|
paddingLeft: 1,
|
|
47074
47126
|
paddingRight: 1
|
|
47075
47127
|
});
|
|
47076
|
-
insertNode(_el$
|
|
47077
|
-
insert(_el$
|
|
47128
|
+
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`));
|
|
47129
|
+
insert(_el$8, () => `proxy \xB7 ${tui.store.ui.proxyFilter}`);
|
|
47078
47130
|
effect((_p$) => {
|
|
47079
47131
|
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$
|
|
47132
|
+
_v$ !== _p$.e && (_p$.e = setProp(_el$6, "fg", _v$, _p$.e));
|
|
47133
|
+
_v$2 !== _p$.t && (_p$.t = setProp(_el$8, "fg", _v$2, _p$.t));
|
|
47082
47134
|
return _p$;
|
|
47083
47135
|
}, {
|
|
47084
47136
|
e: undefined,
|
|
47085
47137
|
t: undefined
|
|
47086
47138
|
});
|
|
47087
|
-
return _el$
|
|
47139
|
+
return _el$5;
|
|
47088
47140
|
})();
|
|
47089
47141
|
}
|
|
47090
47142
|
function CenterSurfaceFooter(props) {
|
|
47091
47143
|
return (() => {
|
|
47092
|
-
var _el$
|
|
47093
|
-
insertNode(_el$
|
|
47094
|
-
insertNode(_el$
|
|
47095
|
-
setProp(_el$
|
|
47144
|
+
var _el$9 = createElement("box"), _el$0 = createElement("text"), _el$1 = createElement("text");
|
|
47145
|
+
insertNode(_el$9, _el$0);
|
|
47146
|
+
insertNode(_el$9, _el$1);
|
|
47147
|
+
setProp(_el$9, "style", {
|
|
47096
47148
|
height: 1,
|
|
47097
47149
|
flexDirection: "row",
|
|
47098
47150
|
justifyContent: "space-between",
|
|
47099
47151
|
paddingLeft: 1,
|
|
47100
47152
|
paddingRight: 1
|
|
47101
47153
|
});
|
|
47102
|
-
insert(_el$
|
|
47103
|
-
insert(_el$
|
|
47154
|
+
insert(_el$0, () => centerSurfaceFooter(props.frame).toLowerCase());
|
|
47155
|
+
insert(_el$1, () => props.frame.kind.toLowerCase());
|
|
47104
47156
|
effect((_p$) => {
|
|
47105
47157
|
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$
|
|
47158
|
+
_v$3 !== _p$.e && (_p$.e = setProp(_el$0, "fg", _v$3, _p$.e));
|
|
47159
|
+
_v$4 !== _p$.t && (_p$.t = setProp(_el$1, "fg", _v$4, _p$.t));
|
|
47108
47160
|
return _p$;
|
|
47109
47161
|
}, {
|
|
47110
47162
|
e: undefined,
|
|
47111
47163
|
t: undefined
|
|
47112
47164
|
});
|
|
47113
|
-
return _el$
|
|
47165
|
+
return _el$9;
|
|
47114
47166
|
})();
|
|
47115
47167
|
}
|
|
47116
47168
|
var init_bottom_pane = __esm(() => {
|
|
@@ -47669,9 +47721,9 @@ async function ensureSession(input) {
|
|
|
47669
47721
|
} catch {
|
|
47670
47722
|
const needle = input.sessionId.trim().toLowerCase();
|
|
47671
47723
|
const sessions2 = await input.runtime.listSessions();
|
|
47672
|
-
const
|
|
47673
|
-
if (
|
|
47674
|
-
return
|
|
47724
|
+
const matches = sessions2.filter((session) => session.id.toLowerCase().startsWith(needle) || session.title?.trim().toLowerCase() === needle);
|
|
47725
|
+
if (matches.length === 1)
|
|
47726
|
+
return matches[0].id;
|
|
47675
47727
|
throw new SessionResolutionError(input.sessionId, input.workspace, sessions2);
|
|
47676
47728
|
}
|
|
47677
47729
|
}
|
|
@@ -50303,5 +50355,5 @@ Examples:
|
|
|
50303
50355
|
`);
|
|
50304
50356
|
}
|
|
50305
50357
|
|
|
50306
|
-
//# debugId=
|
|
50358
|
+
//# debugId=90702708DD95C63C64756E2164756E21
|
|
50307
50359
|
//# sourceMappingURL=index.js.map
|