farai 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -3635,7 +3635,7 @@ function proxyConfig(value) {
3635
3635
  }
3636
3636
  function resolveProxyConfig(config) {
3637
3637
  return {
3638
- transparent: config.proxy?.transparent !== false,
3638
+ transparent: config.proxy?.transparent === true,
3639
3639
  ports: config.proxy?.ports?.length ? config.proxy.ports : DEFAULT_TRANSPARENT_PROXY_PORTS
3640
3640
  };
3641
3641
  }
@@ -3958,10 +3958,9 @@ function atomicWriteFile(path, content, mode) {
3958
3958
  }
3959
3959
  }
3960
3960
  var DEFAULT_TRANSPARENT_PROXY_PORTS, LSP_SERVER_IDS, DEFAULT_CONFIG_TEMPLATE = `model = "big-pickle"
3961
- max_turn_seconds = 900
3962
3961
 
3963
3962
  [proxy]
3964
- transparent = true
3963
+ transparent = false
3965
3964
 
3966
3965
  [mcp_servers.mitmproxy-mcp]
3967
3966
  command = "mitmproxy-mcp"
@@ -5018,14 +5017,45 @@ function sanitizeToolOutput(value) {
5018
5017
  if (http && isBinaryLike(http.body)) {
5019
5018
  return `${sanitizeText(http.head).trimEnd()}
5020
5019
 
5021
- [binary body suppressed: ${byteLength(http.body)} bytes]`;
5020
+ ${binaryPreview(http.body, "binary body")}`;
5022
5021
  }
5023
5022
  if (isBinaryLike(value)) {
5024
- return `[binary output suppressed: ${byteLength(value)} bytes]
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, intent) {
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 = {".git", ".farai", "node_modules"}
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 = {".git", ".farai", "node_modules"}
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)).filter((name) => !NON_DELEGABLE_TOOLS.has(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 leaf 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 only on this bounded task. do not delegate again, ask the user questions, repeat parent work, broaden scope, or write a user-facing answer.", "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(`
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 NON_DELEGABLE_TOOLS, SHARED_WORKSPACE_EDIT_TOOLS, TOOL_SCOPE_ALIASES;
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 up to three bounded 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.",
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: 120000,
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
- const scope = resolveSubagentToolScope({
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
- return DEFAULT_MITMPROXY_PORT;
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: 900000,
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 bounded leaf subagent. Attached waits; detached runs independently and requires a lane or tool scope.",
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: 900000,
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: false,
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 = 120, DEFAULT_MAX_TURN_SECONDS;
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
- DEFAULT_MAX_TURN_SECONDS = 15 * 60;
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 text = input.userText ?? "";
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, gateLease) {
23544
+ async run(work) {
23727
23545
  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);
23732
- this.signal.throwIfAborted();
23733
- const running = Promise.resolve().then(work);
23734
- try {
23735
- return await abortablePromise(running, this.signal);
23736
- } catch (error) {
23737
- if (this.signal.aborted)
23738
- gateLease.quarantineUntil(running, abortReason(this.signal));
23739
- throw error;
23546
+ if (Number.isFinite(this.timeoutMs)) {
23547
+ this.timer ??= setTimeout(() => {
23548
+ if (!this.signal.aborted)
23549
+ this.controller.abort(new ToolDeadlineError(this.tool, this.timeoutMs));
23550
+ }, this.timeoutMs);
23740
23551
  }
23552
+ this.signal.throwIfAborted();
23553
+ return await abortablePromise(Promise.resolve().then(work), this.signal);
23741
23554
  }
23742
23555
  dispose() {
23743
23556
  if (this.timer)
@@ -23758,12 +23571,10 @@ class ToolExecutionGate {
23758
23571
  }
23759
23572
  async run(key, parallel, fn, signal) {
23760
23573
  const release = await this.acquire(key, parallel ? "read" : "write", signal);
23761
- const lease = new ToolGateLease;
23762
23574
  try {
23763
23575
  signal?.throwIfAborted();
23764
- return await fn(lease);
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 || state.quarantineError)
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 && state.quarantines.size === 0 && this.states.get(key) === state) {
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, ToolScopeQuarantinedError;
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
- if (Buffer.byteLength(sanitizedOutput, "utf8") <= TOOL_OUTPUT_LIMITS.bytes) {
23884
+ const binaryLike = isBinaryLike(rawOutput);
23885
+ if (!binaryLike && Buffer.byteLength(sanitizedOutput, "utf8") <= TOOL_OUTPUT_LIMITS.bytes) {
24109
23886
  return sanitizedOutput === rawOutput ? normalized : {
24110
23887
  ...normalized,
24111
23888
  output: sanitizedOutput
@@ -24116,6 +23893,20 @@ function normalizeToolResult(result, input) {
24116
23893
  toolCallId: input.toolCallId,
24117
23894
  content: rawOutput
24118
23895
  });
23896
+ if (binaryLike && Buffer.byteLength(sanitizedOutput, "utf8") <= TOOL_OUTPUT_LIMITS.bytes) {
23897
+ return {
23898
+ ...normalized,
23899
+ output: `${sanitizedOutput}
23900
+
23901
+ [full raw output stored as artifact ${artifact.id}; read it with tool_output_read]`,
23902
+ outputArtifactId: artifact.id,
23903
+ metadata: {
23904
+ ...normalized.metadata ?? {},
23905
+ outputArtifact: artifact,
23906
+ binaryLike: true
23907
+ }
23908
+ };
23909
+ }
24119
23910
  const head = takeBytes(sanitizedOutput, TOOL_OUTPUT_LIMITS.headBytes, "head");
24120
23911
  const tail = takeBytes(sanitizedOutput, TOOL_OUTPUT_LIMITS.tailBytes, "tail");
24121
23912
  return {
@@ -24551,7 +24342,11 @@ class AgentRuntime {
24551
24342
  });
24552
24343
  }
24553
24344
  if (!session.archivedAt) {
24554
- this.userInputs.recover(session.id, this.store.listEvents(session.id, 1e4));
24345
+ const pendingUserInput = this.userInputs.recover(session.id, this.store.listEvents(session.id, 1e4));
24346
+ const latestInterruptedTurn = [...turns].reverse().find((turn) => newlyInterruptedTurns.has(turn.id));
24347
+ if (latestInterruptedTurn && !pendingUserInput && !this.mailbox.hasQueued(session.id)) {
24348
+ this.inputQueue.enqueueFollowup(session.id, ["Continue the task that was interrupted by the runtime restart.", "Use the durable transcript and tool results as the source of truth.", "Do not blindly replay mutating calls; inspect current state first, then resume from the next useful action."].join(" "), "plain", `runtime-recovery:${latestInterruptedTurn.id}`);
24349
+ }
24555
24350
  }
24556
24351
  }
24557
24352
  for (const job of this.store.listRecoverableJobs()) {
@@ -24564,7 +24359,7 @@ class AgentRuntime {
24564
24359
  continue;
24565
24360
  }
24566
24361
  }
24567
- this.jobs.markLost(job.id, "Background execution owner was lost during runtime restart. The original work was not replayed.", job.agentMode !== "attached");
24362
+ this.jobs.markLost(job.id, "Background execution owner was lost during runtime restart. Durable session work is resumable, but the original in-memory process cannot be reattached.", job.agentMode !== "attached");
24568
24363
  }
24569
24364
  for (const job of this.store.listTerminalJobsMissingMailbox())
24570
24365
  this.jobs.repairTerminalMailbox(job.id);
@@ -25551,8 +25346,8 @@ class AgentRuntime {
25551
25346
  });
25552
25347
  let autoContinueStreak = 0;
25553
25348
  let resumeAfterCompaction = false;
25554
- const maxSteps = userAuthored ? this.maxSteps : BACKGROUND_COMPLETION_MAX_STEPS;
25555
- const maxTurnMs = userAuthored ? this.maxTurnMs : Number.POSITIVE_INFINITY;
25349
+ const maxSteps = this.maxSteps;
25350
+ const maxTurnMs = this.maxTurnMs;
25556
25351
  const loopStartedAt = Date.now();
25557
25352
  let timeBudgetWarned = false;
25558
25353
  let loopError;
@@ -25590,7 +25385,7 @@ class AgentRuntime {
25590
25385
  responses.push(...await this.forceTimeLimitWrapUp(session, turn, assistantMessage, planner, maxTurnMs));
25591
25386
  break;
25592
25387
  }
25593
- if (!timeBudgetWarned && elapsedMs >= maxTurnMs * 0.75) {
25388
+ if (Number.isFinite(maxTurnMs) && !timeBudgetWarned && elapsedMs >= maxTurnMs * 0.75) {
25594
25389
  timeBudgetWarned = true;
25595
25390
  const secondsLeft = Math.max(1, Math.ceil((maxTurnMs - elapsedMs) / 1000));
25596
25391
  const queue = this.pendingSteeringContext.get(session.id) ?? [];
@@ -25601,10 +25396,7 @@ class AgentRuntime {
25601
25396
  stepCount: step + 1
25602
25397
  });
25603
25398
  if (step >= maxSteps) {
25604
- if (userAuthored)
25605
- responses.push(...await this.forceStepLimitWrapUp(session, turn, assistantMessage, planner, maxSteps));
25606
- else
25607
- this.stopTurn(turn, "completed", "no_actions");
25399
+ responses.push(...await this.forceStepLimitWrapUp(session, turn, assistantMessage, planner, maxSteps));
25608
25400
  break;
25609
25401
  }
25610
25402
  const compactResult = await this.maybeAutoCompact(session, planner);
@@ -25697,7 +25489,7 @@ This completion is already terminal and was delivered automatically. Do not call
25697
25489
  contextWindow: resolveContextWindow(planner.contextWindow),
25698
25490
  maxOutputTokens: resolveMaxOutputTokens(planner.maxOutputTokens),
25699
25491
  ...this.contextBudgetInput(),
25700
- toolsEnabled: userAuthored,
25492
+ toolsEnabled: true,
25701
25493
  extraBlocks: [...passiveCompletions ? [{
25702
25494
  title: "Completed Background Work",
25703
25495
  body: passiveCompletions,
@@ -25770,14 +25562,14 @@ This completion is already terminal and was delivered automatically. Do not call
25770
25562
  contextBlocks: context.contextBlocks,
25771
25563
  tools: context.tools,
25772
25564
  toolCatalog: context.toolCatalog,
25773
- toolChoice: userAuthored ? "auto" : "none"
25565
+ toolChoice: "auto"
25774
25566
  };
25775
25567
  resumeAfterCompaction = false;
25776
25568
  const autoContinue = {
25777
25569
  streak: autoContinueStreak
25778
25570
  };
25779
25571
  const remainingTurnMs = Number.isFinite(maxTurnMs) ? Math.max(0, maxTurnMs - (Date.now() - loopStartedAt)) : undefined;
25780
- const control = chatProvider ? await this.streamStep(chatProvider, plannerInput, session, turn, assistantMessage, planner.name, step, context.manifest, responses, autoContinue, userAuthored, remainingTurnMs) : await this.batchStep(planner, plannerInput, session, turn, assistantMessage, step, context.manifest, responses, autoContinue, userAuthored, remainingTurnMs);
25572
+ const control = chatProvider ? await this.streamStep(chatProvider, plannerInput, session, turn, assistantMessage, planner.name, step, context.manifest, responses, autoContinue, userAuthored, remainingTurnMs) : await this.batchStep(planner, plannerInput, session, turn, assistantMessage, step, context.manifest, responses, autoContinue, remainingTurnMs);
25781
25573
  autoContinueStreak = autoContinue.streak;
25782
25574
  if (control.cancelled)
25783
25575
  return responses.join(`
@@ -25900,10 +25692,10 @@ This completion is already terminal and was delivered automatically. Do not call
25900
25692
  latencyMs
25901
25693
  });
25902
25694
  }
25903
- async batchStep(planner, plannerInput, session, turn, assistantMessage, step, context, responses, autoContinue, toolsAllowed, modelTimeoutMs) {
25695
+ async batchStep(planner, plannerInput, session, turn, assistantMessage, step, context, responses, autoContinue, modelTimeoutMs) {
25904
25696
  let actions;
25905
25697
  try {
25906
- actions = await this.planWithRetry(planner, plannerInput, session, turn, assistantMessage, context, modelTimeoutMs, !toolsAllowed);
25698
+ actions = await this.planWithRetry(planner, plannerInput, session, turn, assistantMessage, context, modelTimeoutMs);
25907
25699
  } catch (error) {
25908
25700
  if (error instanceof ModelCallDeadlineError)
25909
25701
  return {
@@ -25947,10 +25739,7 @@ This completion is already terminal and was delivered automatically. Do not call
25947
25739
  };
25948
25740
  for (const action of actions) {
25949
25741
  if (action.kind === "tool") {
25950
- if (toolsAllowed)
25951
- toolBatch.push(action);
25952
- else
25953
- this.recordDisabledToolCall(session, turn, assistantMessage, step, action.toolCallId ?? action.tool, action.tool, action.args);
25742
+ toolBatch.push(action);
25954
25743
  continue;
25955
25744
  }
25956
25745
  if (await flushToolBatch())
@@ -26090,10 +25879,6 @@ This completion is already terminal and was delivered automatically. Do not call
26090
25879
  this.recordToolParseError(session, turn, assistantMessage, step, toolCallId ?? toolName, toolName, error instanceof Error ? error.message : String(error), event.arguments);
26091
25880
  continue;
26092
25881
  }
26093
- if (!userAuthored) {
26094
- this.recordDisabledToolCall(session, turn, assistantMessage, step, toolCallId ?? toolName, toolName, args);
26095
- continue;
26096
- }
26097
25882
  const action = {
26098
25883
  kind: "tool",
26099
25884
  tool: toolName,
@@ -26398,10 +26183,6 @@ This completion is already terminal and was delivered automatically. Do not call
26398
26183
  }
26399
26184
  }
26400
26185
  async applyRespond(session, turn, assistantMessage, plannerName, text, truncated, recoverable, responses, autoContinue) {
26401
- if (this.isRedundantAgentTaskResponse(session.id, turn.id, text)) {
26402
- this.discardStreamingText(turn.id);
26403
- return false;
26404
- }
26405
26186
  responses.push(text);
26406
26187
  const streamed = this.streamingParts.get(turn.id);
26407
26188
  if (streamed?.textPartId) {
@@ -26421,29 +26202,11 @@ This completion is already terminal and was delivered automatically. Do not call
26421
26202
  recoverable
26422
26203
  });
26423
26204
  if (truncated || recoverable) {
26424
- if (autoContinue.streak < MAX_RECOVERABLE_AUTO_CONTINUE) {
26425
- autoContinue.streak += 1;
26426
- return true;
26427
- }
26428
- const reason = truncated ? `kept getting cut off by its token limit ${MAX_RECOVERABLE_AUTO_CONTINUE} times in a row. Consider raising maxOutputTokens in ~/.local/pajarori/farai/config.toml or asking a smaller follow-up question` : `kept failing to produce a usable response ${MAX_RECOVERABLE_AUTO_CONTINUE} times in a row`;
26429
- const notice = `(Model ${reason} \u2014 stopping auto-continue.)`;
26430
- responses.push(notice);
26431
- this.persistTextPart(session.id, turn.id, assistantMessage.id, notice);
26432
- this.event(session.id, "text", {
26433
- role: "assistant",
26434
- text: notice,
26435
- planner: plannerName
26436
- });
26205
+ autoContinue.streak += 1;
26206
+ return true;
26437
26207
  }
26438
26208
  return false;
26439
26209
  }
26440
- isRedundantAgentTaskResponse(sessionId, turnId, text) {
26441
- const candidate2 = comparableProse(text);
26442
- if (candidate2.length < 40)
26443
- return false;
26444
- const outputs = this.store.listMessages(sessionId, 200).flatMap((message) => message.parts).filter((part) => part.turnId === turnId && part.type === "tool_result").flatMap((part) => agentTaskOutput(part.payload));
26445
- return outputs.some((output) => substantiallySameProse(candidate2, comparableProse(output)));
26446
- }
26447
26210
  recordToolParseError(session, turn, assistantMessage, step, toolCallId, tool, error, rawArguments) {
26448
26211
  const text = `Could not parse arguments for ${tool}: ${error}. Raw: ${rawArguments.slice(0, 500)}`;
26449
26212
  this.store.addPart({
@@ -26486,48 +26249,6 @@ This completion is already terminal and was delivered automatically. Do not call
26486
26249
  payload
26487
26250
  });
26488
26251
  }
26489
- recordDisabledToolCall(session, turn, assistantMessage, step, toolCallId, tool, args) {
26490
- const text = `Tool ${tool} was not executed because this is a bounded text-only completion turn.`;
26491
- this.store.addPart({
26492
- sessionId: session.id,
26493
- turnId: turn.id,
26494
- messageId: assistantMessage.id,
26495
- type: "tool_call",
26496
- payload: {
26497
- record: {
26498
- id: toolCallId,
26499
- tool,
26500
- args
26501
- }
26502
- }
26503
- });
26504
- this.store.addPart({
26505
- sessionId: session.id,
26506
- turnId: turn.id,
26507
- messageId: assistantMessage.id,
26508
- type: "tool_result",
26509
- payload: {
26510
- toolCallId,
26511
- tool,
26512
- result: text
26513
- }
26514
- });
26515
- const payload = {
26516
- turnId: turn.id,
26517
- step,
26518
- tool,
26519
- error: text,
26520
- recoverable: false
26521
- };
26522
- this.event(session.id, "planner_error", payload);
26523
- this.store.addPart({
26524
- sessionId: session.id,
26525
- turnId: turn.id,
26526
- messageId: assistantMessage.id,
26527
- type: "planner_error",
26528
- payload
26529
- });
26530
- }
26531
26252
  async forceStepLimitWrapUp(session, turn, assistantMessage, planner, maxSteps) {
26532
26253
  return this.forceTextOnlyWrapUp({
26533
26254
  session,
@@ -26680,25 +26401,6 @@ This completion is already terminal and was delivered automatically. Do not call
26680
26401
  shouldContinue: !sawResponse
26681
26402
  };
26682
26403
  }
26683
- if (action.tool === "subdomain_enum") {
26684
- const duplicate = this.store.listToolCalls(session.id, 200).find((call2) => call2.turnId === turn.id && call2.tool === action.tool && (call2.status === "done" || call2.status === "error") && stableValue(call2.args) === stableValue(action.args));
26685
- if (duplicate) {
26686
- const text = `Equivalent ${action.tool} already finished in this turn as ${duplicate.id}; reuse its source statuses and names instead of retrying.`;
26687
- this.event(session.id, "planner_error", {
26688
- turnId: turn.id,
26689
- step,
26690
- tool: action.tool,
26691
- error: text,
26692
- recoverable: true,
26693
- policy: "duplicate_terminal_tool",
26694
- duplicateSuppressed: true,
26695
- duplicateToolCallId: duplicate.id
26696
- });
26697
- return {
26698
- shouldContinue: true
26699
- };
26700
- }
26701
- }
26702
26404
  const validationError = validateToolArgs(tool.inputSchema, action.args);
26703
26405
  if (validationError) {
26704
26406
  const toolCallId = action.toolCallId ?? action.tool;
@@ -27256,25 +26958,18 @@ This completion is already terminal and was delivered automatically. Do not call
27256
26958
  const workspaceTransition = isWorkspaceTransitionTool(schedulingTool);
27257
26959
  const gateSignal = signal ? AbortSignal.any([signal, this.shutdownController.signal]) : this.shutdownController.signal;
27258
26960
  try {
27259
- return await this.workspaceBindingGate.run(`session-workspace:${session.id}`, !workspaceTransition, async (bindingLease) => {
26961
+ return await this.workspaceBindingGate.run(`session-workspace:${session.id}`, !workspaceTransition, async () => {
27260
26962
  session = this.store.loadSession(session.id);
27261
26963
  tool = toolForExecution(session, toolName);
27262
26964
  schedulingTool = toolSchedulingDefinition(tool, args, session);
27263
- return await this.toolExecutionGate.run(toolConcurrencyKey(schedulingTool, session, session.workspace), schedulingTool.parallel, async (gateLease) => {
27264
- gateLease.mirrorTo(bindingLease);
26965
+ return await this.toolExecutionGate.run(toolConcurrencyKey(schedulingTool, session, session.workspace), schedulingTool.parallel, async () => {
27265
26966
  gateSignal.throwIfAborted();
27266
26967
  if (owner && this.store.loadTurn(owner.turn.id).status === "cancelled")
27267
26968
  throw new Error("turn cancelled before tool start");
27268
- return await this.runToolUnderGate(session, tool, args, owner, providerToolCallId, gateLease);
26969
+ return await this.runToolUnderGate(session, tool, args, owner, providerToolCallId);
27269
26970
  }, gateSignal);
27270
26971
  }, gateSignal);
27271
26972
  } catch (error) {
27272
- if (error instanceof ToolScopeQuarantinedError) {
27273
- return this.recordRejectedToolCall(session, tool, args, error.message, {
27274
- quarantined: true,
27275
- reason: "concurrency_scope_quarantined"
27276
- }, owner, providerToolCallId);
27277
- }
27278
26973
  const ownerCancelled = owner ? this.store.loadTurn(owner.turn.id).status === "cancelled" : false;
27279
26974
  if (gateSignal.aborted || ownerCancelled) {
27280
26975
  const message = gateSignal.reason ? String(gateSignal.reason) : "turn cancelled before tool start";
@@ -27304,7 +26999,7 @@ This completion is already terminal and was delivered automatically. Do not call
27304
26999
  });
27305
27000
  return this.toolCalls.settleError(toolCall, message, state);
27306
27001
  }
27307
- async runToolUnderGate(session, tool, args, owner, providerToolCallId, gateLease) {
27002
+ async runToolUnderGate(session, tool, args, owner, providerToolCallId) {
27308
27003
  const toolCall = this.toolCalls.begin({
27309
27004
  sessionId: session.id,
27310
27005
  tool: tool.name,
@@ -27319,7 +27014,7 @@ This completion is already terminal and was delivered automatically. Do not call
27319
27014
  providerToolCallId
27320
27015
  } : {}
27321
27016
  });
27322
- await this.executeToolUnderGate(session, toolCall.id, owner, gateLease);
27017
+ await this.executeToolUnderGate(session, toolCall.id, owner);
27323
27018
  return this.store.loadToolCall(toolCall.id);
27324
27019
  }
27325
27020
  async executeTool(session, toolCallId, owner) {
@@ -27337,29 +27032,18 @@ This completion is already terminal and was delivered automatically. Do not call
27337
27032
  const gateController = turnId ? this.registerTurnController(turnId) : undefined;
27338
27033
  const gateSignal = gateController ? AbortSignal.any([gateController.signal, this.shutdownController.signal]) : this.shutdownController.signal;
27339
27034
  try {
27340
- return await this.workspaceBindingGate.run(`session-workspace:${session.id}`, !workspaceTransition, async (bindingLease) => {
27035
+ return await this.workspaceBindingGate.run(`session-workspace:${session.id}`, !workspaceTransition, async () => {
27341
27036
  session = this.store.loadSession(session.id);
27342
27037
  tool = toolForExecution(session, toolCall.tool);
27343
27038
  schedulingTool = toolSchedulingDefinition(tool, toolCall.args, session);
27344
- return await this.toolExecutionGate.run(toolConcurrencyKey(schedulingTool, session, session.workspace), schedulingTool.parallel, async (gateLease) => {
27345
- gateLease.mirrorTo(bindingLease);
27039
+ return await this.toolExecutionGate.run(toolConcurrencyKey(schedulingTool, session, session.workspace), schedulingTool.parallel, async () => {
27346
27040
  gateSignal.throwIfAborted();
27347
27041
  if (turnId && this.store.loadTurn(turnId).status === "cancelled")
27348
27042
  throw new Error("turn cancelled before tool start");
27349
- return await this.executeToolUnderGate(session, toolCallId, owner, gateLease);
27043
+ return await this.executeToolUnderGate(session, toolCallId, owner);
27350
27044
  }, gateSignal);
27351
27045
  }, gateSignal);
27352
27046
  } catch (error) {
27353
- if (error instanceof ToolScopeQuarantinedError) {
27354
- const rejected = this.store.loadToolCall(toolCallId);
27355
- if (rejected.status === "pending") {
27356
- return this.toolCalls.settleError(rejected, error.message, {
27357
- quarantined: true,
27358
- reason: "concurrency_scope_quarantined"
27359
- });
27360
- }
27361
- return rejected;
27362
- }
27363
27047
  if (!gateSignal.aborted && (!turnId || this.store.loadTurn(turnId).status !== "cancelled"))
27364
27048
  throw error;
27365
27049
  const cancelled = this.store.loadToolCall(toolCallId);
@@ -27376,7 +27060,7 @@ This completion is already terminal and was delivered automatically. Do not call
27376
27060
  gateController?.release();
27377
27061
  }
27378
27062
  }
27379
- async executeToolUnderGate(session, toolCallId, owner, gateLease = new ToolGateLease) {
27063
+ async executeToolUnderGate(session, toolCallId, owner) {
27380
27064
  let toolCall = this.store.loadToolCall(toolCallId);
27381
27065
  if (toolCall.status !== "pending")
27382
27066
  return toolCall;
@@ -27664,7 +27348,7 @@ This completion is already terminal and was delivered automatically. Do not call
27664
27348
  ...nestedContext,
27665
27349
  signal: nestedDeadline.signal,
27666
27350
  timeoutMs: toolOperationTimeout(target.timeoutMs)
27667
- }), gateLease);
27351
+ }));
27668
27352
  lease.assertActive();
27669
27353
  await this.fireHooks(session, "tool.post", canonicalName, {
27670
27354
  tool: canonicalName,
@@ -27724,22 +27408,10 @@ This completion is already terminal and was delivered automatically. Do not call
27724
27408
  linkToolCall = true
27725
27409
  }) => {
27726
27410
  lease.assertActive();
27727
- let depth = 0;
27728
- let parentId = session.parentId;
27729
- while (parentId) {
27730
- depth += 1;
27731
- parentId = this.store.loadSession(parentId).parentId;
27732
- if (depth > 4)
27733
- break;
27734
- }
27735
- if (depth >= 1)
27736
- throw new Error("nested campaign delegation is disabled; child workers are leaf agents");
27737
- if (resumeSessionId && (lane || tools || model))
27738
- throw new Error("resumed subagents preserve their original lane, model, and tool scope");
27739
27411
  const previousJob = resumeSessionId ? this.store.listJobs(session.id, 1e4).find((job2) => job2.kind === "agent" && job2.childSessionId === resumeSessionId) : undefined;
27740
27412
  const effectiveLane = lane ?? previousJob?.lane;
27741
27413
  const laneDef = effectiveLane ? resolveLane(this.workspace, effectiveLane) : undefined;
27742
- if (!resumeSessionId && effectiveLane && !laneDef)
27414
+ if (effectiveLane && !laneDef)
27743
27415
  throw new Error(`unknown subagent lane: ${effectiveLane}`);
27744
27416
  let child;
27745
27417
  let scopedTools;
@@ -27753,10 +27425,22 @@ This completion is already terminal and was delivered automatically. Do not call
27753
27425
  const active = this.store.listJobs(session.id, 1e4).some((job2) => job2.kind === "agent" && job2.childSessionId === child.id && ["created", "starting", "running", "cancelling"].includes(job2.status));
27754
27426
  if (active || this.hasRunningTurn(child.id))
27755
27427
  throw new Error(`subagent session ${child.id} is already running`);
27756
- scopedTools = child.toolScope;
27428
+ const requestedTools = tools ?? laneDef?.tools;
27429
+ scopedTools = requestedTools ? resolveSubagentToolScope({
27430
+ parent: session,
27431
+ availableTools: listToolsForSession(session),
27432
+ requestedTools
27433
+ }) : child.toolScope;
27757
27434
  editsSharedWorkspace = hasSharedWorkspaceEdits(scopedTools);
27758
- if (mode === "detached" && editsSharedWorkspace)
27759
- throw new Error("detached subagents cannot hold shared workspace edit tools; use an attached code worker or a read-only lane");
27435
+ const childModel = model ?? laneDef?.model;
27436
+ if (childModel && childModel !== child.model)
27437
+ child = this.updateSession(child.id, {
27438
+ model: childModel
27439
+ });
27440
+ if (requestedTools && scopedTools?.length)
27441
+ child = this.updateSession(child.id, {
27442
+ toolScope: scopedTools
27443
+ });
27760
27444
  } else {
27761
27445
  const requestedTools = tools ?? laneDef?.tools;
27762
27446
  scopedTools = resolveSubagentToolScope({
@@ -27767,8 +27451,6 @@ This completion is already terminal and was delivered automatically. Do not call
27767
27451
  } : {}
27768
27452
  });
27769
27453
  editsSharedWorkspace = hasSharedWorkspaceEdits(scopedTools);
27770
- if (mode === "detached" && editsSharedWorkspace)
27771
- throw new Error("detached subagents cannot hold shared workspace edit tools; use an attached code worker or a read-only lane");
27772
27454
  const childModel = model ?? laneDef?.model;
27773
27455
  child = await this.store.forkSession(session.id, title);
27774
27456
  this.recordSession(child);
@@ -27876,8 +27558,8 @@ This completion is already terminal and was delivered automatically. Do not call
27876
27558
  this.jobs.completeAgent(job.id, response, mode === "detached");
27877
27559
  return response;
27878
27560
  };
27879
- const gated = () => this.subagentGate.run(execute, agentController.signal);
27880
- return editsSharedWorkspace ? await this.subagentWorkspaceMutationGate.run(gated, agentController.signal) : await gated();
27561
+ const gated = () => session.parentId ? execute() : this.subagentGate.run(execute, agentController.signal);
27562
+ return editsSharedWorkspace && !session.parentId ? await this.subagentWorkspaceMutationGate.run(gated, agentController.signal) : await gated();
27881
27563
  } catch (error) {
27882
27564
  if (this.store.isOpen() && this.hasJob(job.id)) {
27883
27565
  const current = this.store.loadJob(job.id);
@@ -27931,7 +27613,6 @@ This completion is already terminal and was delivered automatically. Do not call
27931
27613
  this.turnControllers.delete(turnId);
27932
27614
  };
27933
27615
  let result;
27934
- const duplicate = findEquivalentBackgroundJob(activeBackgroundJobs(this.store.listToolCalls(session.id, 200)), toolCall.tool, toolCall.args);
27935
27616
  await this.fireHooks(session, "tool.pre", toolCall.tool, {
27936
27617
  tool: toolCall.tool,
27937
27618
  toolCallId: toolCall.id,
@@ -27939,16 +27620,7 @@ This completion is already terminal and was delivered automatically. Do not call
27939
27620
  });
27940
27621
  try {
27941
27622
  lease.assertActive();
27942
- result = duplicate && sessionManager.isTracked(duplicate.processId) ? {
27943
- ok: true,
27944
- summary: `Equivalent background job already running: processId=${duplicate.processId}`,
27945
- output: `Reusing ${duplicate.toolCallId}. Poll ${duplicate.processId} with session_poll instead of starting it again.`,
27946
- status: "running_background",
27947
- processId: duplicate.processId,
27948
- metadata: {
27949
- reusedBackgroundToolCallId: duplicate.toolCallId
27950
- }
27951
- } : await deadline.run(() => tool.run(toolCall.args, context), gateLease);
27623
+ result = await deadline.run(() => tool.run(toolCall.args, context));
27952
27624
  } catch (error) {
27953
27625
  settleLiveOutput();
27954
27626
  releaseController();
@@ -28609,38 +28281,6 @@ function completedToolCallStatus(result) {
28609
28281
  return "running_background";
28610
28282
  return result.ok ? "done" : "error";
28611
28283
  }
28612
- function agentTaskOutput(payload) {
28613
- if (!payload || typeof payload !== "object" || Array.isArray(payload))
28614
- return [];
28615
- const record2 = payload;
28616
- if (!["agent_task", "agent_spawn", "agent_followup"].includes(String(record2.tool)))
28617
- return [];
28618
- const toolResult = record2.toolResult;
28619
- if (!toolResult || typeof toolResult !== "object" || Array.isArray(toolResult))
28620
- return [];
28621
- const output = toolResult.output;
28622
- return typeof output === "string" && output.trim() ? [output] : [];
28623
- }
28624
- function comparableProse(value) {
28625
- return value.toLowerCase().replace(/[`*_>#|()[\]{}]/g, " ").replace(/[^a-z0-9./:_-]+/g, " ").replace(/\s+/g, " ").trim();
28626
- }
28627
- function substantiallySameProse(left, right) {
28628
- if (right.length < 40)
28629
- return false;
28630
- const shorter = Math.min(left.length, right.length);
28631
- const longer = Math.max(left.length, right.length);
28632
- if ((left.includes(right) || right.includes(left)) && shorter / longer >= 0.72)
28633
- return true;
28634
- const leftWords = new Set(left.split(" ").filter((word) => word.length > 2));
28635
- const rightWords = new Set(right.split(" ").filter((word) => word.length > 2));
28636
- if (leftWords.size < 8 || rightWords.size < 8)
28637
- return false;
28638
- let shared = 0;
28639
- for (const word of leftWords)
28640
- if (rightWords.has(word))
28641
- shared += 1;
28642
- return shared / leftWords.size >= 0.88 && shared / rightWords.size >= 0.88;
28643
- }
28644
28284
  function recoveredJobSummary(job) {
28645
28285
  if (job.result && typeof job.result === "object") {
28646
28286
  if ("response" in job.result)
@@ -28765,7 +28405,7 @@ function sameExistingPath(left, right) {
28765
28405
  return false;
28766
28406
  }
28767
28407
  }
28768
- var REASONING_MAX_BYTES2, STREAM_RENDER_INTERVAL_MS = 100, STREAM_PERSIST_INTERVAL_MS = 2000, MAX_RECOVERABLE_AUTO_CONTINUE = 3, LIVE_OUTPUT_MAX_BYTES, LIVE_OUTPUT_FLUSH_INTERVAL_MS = 150, LIVE_OUTPUT_INITIAL_DELAY_MS = 320, TOOL_HUMAN_RESULT_MAX_BYTES, BACKGROUND_COMPLETION_MAX_STEPS = 1, LOOP_SUPERVISION_NO_PROGRESS_STEPS = 12, LOOP_SUPERVISION_STEER_INTERVAL = 5, LOOP_PATTERN_MAX_PERIOD = 8, PROGRESS_ACTION_TOOLS, AUTO_COMPACTION_CONTINUATION = "[internal continuation after context compaction: Continue the active user task from the compacted prior context. Do not repeat, regenerate, or explain the summary. Resume with the exact next useful action.]", WRAPUP_MODEL_TIMEOUT_MS = 15000, DEFAULT_SHUTDOWN_GRACE_PERIOD_MS = 2000, RUNTIME_LEASE_MS = 60000, RUNTIME_HEARTBEAT_MS = 15000, RESTART_TOOL_ERROR = "Interrupted by runtime restart; tool execution was not replayed.", STEP_LIMIT_WRAPUP_DIRECTIVE = "You have reached the maximum number of steps allowed for this turn, so tools are no longer available. Do not attempt to call any tool. In a few sentences, summarize what you accomplished, the key findings or evidence so far, any blockers, and the single most useful next step. This is your final message for this turn.", TIME_LIMIT_WRAPUP_DIRECTIVE = "You have reached the interactive wall-clock budget for this turn, so tools are no longer available. Do not attempt to call any tool. Concisely summarize completed work, proven evidence, active background jobs, remaining uncertainty, and the single best next action. This is your final message for this turn.", IneffectiveCompactionError, ModelCallDeadlineError, PlannerOutputValidationError;
28408
+ var REASONING_MAX_BYTES2, STREAM_RENDER_INTERVAL_MS = 100, STREAM_PERSIST_INTERVAL_MS = 2000, LIVE_OUTPUT_MAX_BYTES, LIVE_OUTPUT_FLUSH_INTERVAL_MS = 150, LIVE_OUTPUT_INITIAL_DELAY_MS = 320, TOOL_HUMAN_RESULT_MAX_BYTES, LOOP_SUPERVISION_NO_PROGRESS_STEPS = 12, LOOP_SUPERVISION_STEER_INTERVAL = 5, LOOP_PATTERN_MAX_PERIOD = 8, PROGRESS_ACTION_TOOLS, AUTO_COMPACTION_CONTINUATION = "[internal continuation after context compaction: Continue the active user task from the compacted prior context. Do not repeat, regenerate, or explain the summary. Resume with the exact next useful action.]", WRAPUP_MODEL_TIMEOUT_MS = 15000, DEFAULT_SHUTDOWN_GRACE_PERIOD_MS = 2000, RUNTIME_LEASE_MS = 60000, RUNTIME_HEARTBEAT_MS = 15000, RESTART_TOOL_ERROR = "Interrupted by runtime restart; tool execution was not replayed.", STEP_LIMIT_WRAPUP_DIRECTIVE = "You have reached the maximum number of steps allowed for this turn, so tools are no longer available. Do not attempt to call any tool. In a few sentences, summarize what you accomplished, the key findings or evidence so far, any blockers, and the single most useful next step. This is your final message for this turn.", TIME_LIMIT_WRAPUP_DIRECTIVE = "You have reached the interactive wall-clock budget for this turn, so tools are no longer available. Do not attempt to call any tool. Concisely summarize completed work, proven evidence, active background jobs, remaining uncertainty, and the single best next action. This is your final message for this turn.", IneffectiveCompactionError, ModelCallDeadlineError, PlannerOutputValidationError;
28769
28409
  var init_runtime = __esm(() => {
28770
28410
  init_sqlite_store();
28771
28411
  init_registry4();
@@ -28952,10 +28592,10 @@ function extractEntities(recordId2, text) {
28952
28592
  const found = new Set;
28953
28593
  const out = [];
28954
28594
  for (const pattern of ENTITY_PATTERNS) {
28955
- const matches2 = text.match(pattern.re);
28956
- if (!matches2)
28595
+ const matches = text.match(pattern.re);
28596
+ if (!matches)
28957
28597
  continue;
28958
- for (const raw of matches2) {
28598
+ for (const raw of matches) {
28959
28599
  const value = pattern.normalize ? pattern.normalize(raw) : raw;
28960
28600
  const key = `${pattern.type}:${value}`;
28961
28601
  if (found.has(key))
@@ -33856,7 +33496,8 @@ function createRequestUserInputUiState(request) {
33856
33496
  answers: {},
33857
33497
  drafts: {},
33858
33498
  textModeQuestionId: first && !first.choices?.length ? first.id : undefined,
33859
- submitting: false
33499
+ submitting: false,
33500
+ dismissed: false
33860
33501
  };
33861
33502
  }
33862
33503
  function syncRequestUserInputUiState(current, request) {
@@ -34744,6 +34385,20 @@ function createActions(store, setStore) {
34744
34385
  return;
34745
34386
  s.ui.requestUserInput.submitting = submitting;
34746
34387
  }));
34388
+ },
34389
+ requestUserInputDismissedSet(dismissed) {
34390
+ setStore(produce((s) => {
34391
+ if (!s.ui.requestUserInput)
34392
+ return;
34393
+ s.ui.requestUserInput.dismissed = dismissed;
34394
+ if (dismissed)
34395
+ s.ui.requestUserInput.textModeQuestionId = undefined;
34396
+ else {
34397
+ const question = s.snapshot.pendingUserInput?.questions[s.ui.requestUserInput.questionIndex];
34398
+ if (question && !question.choices?.length)
34399
+ s.ui.requestUserInput.textModeQuestionId = question.id;
34400
+ }
34401
+ }));
34747
34402
  }
34748
34403
  };
34749
34404
  }
@@ -37238,19 +36893,6 @@ function TuiStoreProvider(props) {
37238
36893
  const sid = store.activeSessionId;
37239
36894
  if (!sid || !text.trim())
37240
36895
  return false;
37241
- if (store.snapshot.pendingUserInput) {
37242
- actions.promptHistoryAdd(text);
37243
- (async () => {
37244
- try {
37245
- await port.answerUserInput(sid, text);
37246
- await requestSnapshotRefresh(sid);
37247
- } catch (error) {
37248
- if (!disposed && store.activeSessionId === sid)
37249
- actions.errorSet(error instanceof Error ? error.message : String(error));
37250
- }
37251
- })();
37252
- return true;
37253
- }
37254
36896
  if (promptSubmissions.has(sid) || isAgentBusy(store) || port.getRunningTurnId(sid)) {
37255
36897
  if (port.steer?.(sid, text)) {
37256
36898
  actions.promptHistoryAdd(text);
@@ -37350,12 +36992,6 @@ function TuiStoreProvider(props) {
37350
36992
  actions.snapshotPatched({
37351
36993
  pendingUserInput: undefined
37352
36994
  });
37353
- const turnId = store.snapshot.runningTurnId ?? port.getRunningTurnId(sid);
37354
- if (turnId && capabilities.cancel) {
37355
- try {
37356
- await port.cancelTurn(turnId, "user input cancelled");
37357
- } catch {}
37358
- }
37359
36995
  await requestSnapshotRefresh(sid);
37360
36996
  } catch (error) {
37361
36997
  if (!disposed && store.activeSessionId === sid)
@@ -38009,14 +37645,14 @@ function filterOptions(options, needle) {
38009
37645
  score: score2
38010
37646
  }));
38011
37647
  }
38012
- function groupByCategory(matches2, needleActive) {
37648
+ function groupByCategory(matches, needleActive) {
38013
37649
  if (needleActive)
38014
37650
  return [{
38015
37651
  category: undefined,
38016
- matches: [...matches2]
37652
+ matches: [...matches]
38017
37653
  }];
38018
37654
  const buckets = new Map;
38019
- for (const match of matches2) {
37655
+ for (const match of matches) {
38020
37656
  const key = match.option.category;
38021
37657
  const list2 = buckets.get(key) ?? [];
38022
37658
  list2.push(match);
@@ -38437,6 +38073,10 @@ function routeRequestUserInput(key, state) {
38437
38073
  if (state.submitting)
38438
38074
  return consumed();
38439
38075
  if (key.ctrl && key.name === "c")
38076
+ return consumed({
38077
+ kind: "requestUserInput.dismiss"
38078
+ });
38079
+ if (key.ctrl && key.name === "x")
38440
38080
  return consumed({
38441
38081
  kind: "requestUserInput.cancel"
38442
38082
  });
@@ -38455,7 +38095,7 @@ function routeRequestUserInput(key, state) {
38455
38095
  if (state.textMode) {
38456
38096
  if (key.name === "escape")
38457
38097
  return consumed({
38458
- kind: state.canExitTextMode ? "requestUserInput.textModeExit" : "requestUserInput.cancel"
38098
+ kind: state.canExitTextMode ? "requestUserInput.textModeExit" : "requestUserInput.dismiss"
38459
38099
  });
38460
38100
  if (key.name === "tab" && state.canExitTextMode)
38461
38101
  return consumed({
@@ -38470,7 +38110,7 @@ function routeRequestUserInput(key, state) {
38470
38110
  switch (key.name) {
38471
38111
  case "escape":
38472
38112
  return consumed({
38473
- kind: "requestUserInput.cancel"
38113
+ kind: "requestUserInput.dismiss"
38474
38114
  });
38475
38115
  case "up":
38476
38116
  return consumed({
@@ -38795,6 +38435,10 @@ function routeBase(key, ctx) {
38795
38435
  return consumed({
38796
38436
  kind: "composer.copyLast"
38797
38437
  });
38438
+ case "q":
38439
+ return ctx.pendingUserInput ? consumed({
38440
+ kind: "requestUserInput.show"
38441
+ }) : PASSTHROUGH;
38798
38442
  case "l":
38799
38443
  return consumed({
38800
38444
  kind: "transcript.clear"
@@ -39157,7 +38801,8 @@ function KeyboardController() {
39157
38801
  historySearchActive: Boolean(tui.store.ui.historySearch),
39158
38802
  queuedCount: tui.store.snapshot.queuedPrompts.length,
39159
38803
  activeMainTab: tui.store.ui.activeMainTab,
39160
- ...pendingRequest && requestState && pendingQuestion ? {
38804
+ pendingUserInput: Boolean(pendingRequest),
38805
+ ...pendingRequest && requestState && pendingQuestion && !requestState.dismissed ? {
39161
38806
  requestUserInput: {
39162
38807
  textMode: requestState.textModeQuestionId === pendingQuestion.id,
39163
38808
  canExitTextMode: Boolean(pendingQuestion.choices?.length),
@@ -39258,6 +38903,13 @@ function KeyboardController() {
39258
38903
  await tui.answerUserInputQuestion(current.question.id, draft);
39259
38904
  return;
39260
38905
  }
38906
+ case "requestUserInput.dismiss":
38907
+ tui.actions.requestUserInputDismissedSet(true);
38908
+ composer.focus();
38909
+ return;
38910
+ case "requestUserInput.show":
38911
+ tui.actions.requestUserInputDismissedSet(false);
38912
+ return;
39261
38913
  case "requestUserInput.cancel":
39262
38914
  await tui.cancelUserInput();
39263
38915
  return;
@@ -40099,8 +39751,8 @@ function KeyboardController() {
40099
39751
  const search = tui.store.ui.historySearch;
40100
39752
  if (!search)
40101
39753
  return;
40102
- const matches2 = currentHistoryMatches();
40103
- const preview = matches2[search.index] ?? search.originalDraft;
39754
+ const matches = currentHistoryMatches();
39755
+ const preview = matches[search.index] ?? search.originalDraft;
40104
39756
  composer.setDraft(preview);
40105
39757
  }
40106
39758
  async function openExternalEditor() {
@@ -45166,15 +44818,15 @@ var init_status_indicator = __esm(() => {
45166
44818
  });
45167
44819
 
45168
44820
  // src/agent-tui/dialog/list-selection.ts
45169
- function selectableIndex(matches2, requested) {
45170
- const enabled = matches2.filter((match) => !match.option.disabled);
44821
+ function selectableIndex(matches, requested) {
44822
+ const enabled = matches.filter((match) => !match.option.disabled);
45171
44823
  if (enabled.length === 0)
45172
44824
  return -1;
45173
44825
  return Math.max(0, Math.min(requested, enabled.length - 1));
45174
44826
  }
45175
- function selectedOptionId(matches2, requested) {
45176
- const index = selectableIndex(matches2, requested);
45177
- return index < 0 ? undefined : matches2.filter((match) => !match.option.disabled)[index]?.option.id;
44827
+ function selectedOptionId(matches, requested) {
44828
+ const index = selectableIndex(matches, requested);
44829
+ return index < 0 ? undefined : matches.filter((match) => !match.option.disabled)[index]?.option.id;
45178
44830
  }
45179
44831
  function scrollWindowStart(total, cap, selectedIndex) {
45180
44832
  if (total <= cap || selectedIndex < 0)
@@ -45183,8 +44835,8 @@ function scrollWindowStart(total, cap, selectedIndex) {
45183
44835
  return 0;
45184
44836
  return Math.min(selectedIndex - cap + 1, total - cap);
45185
44837
  }
45186
- function displayRows(matches2, selectedId) {
45187
- return matches2.map((match) => ({
44838
+ function displayRows(matches, selectedId) {
44839
+ return matches.map((match) => ({
45188
44840
  option: match.option,
45189
44841
  matched: match.score > 0,
45190
44842
  disabled: Boolean(match.option.disabled),
@@ -45275,9 +44927,9 @@ var init_selection_row = __esm(() => {
45275
44927
  function ListOverlay(props) {
45276
44928
  const tui = useTuiStore();
45277
44929
  const dims = useTerminalDimensions();
45278
- const matches2 = createMemo(() => filterOptions(props.options, props.frame.query));
45279
- const groups = createMemo(() => groupByCategory(matches2(), props.frame.query.trim() !== ""));
45280
- const selectedId = createMemo(() => selectedOptionId(matches2(), props.frame.index));
44930
+ const matches = createMemo(() => filterOptions(props.options, props.frame.query));
44931
+ const groups = createMemo(() => groupByCategory(matches(), props.frame.query.trim() !== ""));
44932
+ const selectedId = createMemo(() => selectedOptionId(matches(), props.frame.index));
45281
44933
  const rows = createMemo(() => selectionRows(groups(), selectedId()));
45282
44934
  const width = () => Math.max(30, dims().width);
45283
44935
  const maxRows = () => overlayMaxRows(props.frame.kind, dims().height);
@@ -45294,7 +44946,7 @@ function ListOverlay(props) {
45294
44946
  const descCol = () => descriptionColumn(visibleRows(), width());
45295
44947
  const subtitle = () => overlaySubtitle(props.frame.kind);
45296
44948
  const selectOption = (id2) => {
45297
- const enabled = matches2().filter((match) => !match.option.disabled);
44949
+ const enabled = matches().filter((match) => !match.option.disabled);
45298
44950
  const index = enabled.findIndex((match) => match.option.id === id2);
45299
44951
  if (index >= 0)
45300
44952
  tui.actions.overlaySetIndex(index, enabled.length);
@@ -45343,7 +44995,7 @@ function ListOverlay(props) {
45343
44995
  return dims().height;
45344
44996
  },
45345
44997
  get matches() {
45346
- return matches2();
44998
+ return matches();
45347
44999
  },
45348
45000
  get selectedId() {
45349
45001
  return selectedId();
@@ -45380,14 +45032,14 @@ function ListOverlay(props) {
45380
45032
  })());
45381
45033
  insert(_el$6, (() => {
45382
45034
  var _c$2 = memo2(() => !!props.frame.query);
45383
- return () => _c$2() ? `${matches2().length}/${props.options.length}` : memo2(() => matches2().length > 0)() ? `${Math.min(props.frame.index + 1, matches2().length)}/${matches2().length}` : "";
45035
+ return () => _c$2() ? `${matches().length}/${props.options.length}` : memo2(() => matches().length > 0)() ? `${Math.min(props.frame.index + 1, matches().length)}/${matches().length}` : "";
45384
45036
  })());
45385
45037
  setProp(_el$7, "style", {
45386
45038
  flexDirection: "column"
45387
45039
  });
45388
45040
  insert(_el$7, createComponent2(Show, {
45389
45041
  get when() {
45390
- return matches2().length > 0;
45042
+ return matches().length > 0;
45391
45043
  },
45392
45044
  get fallback() {
45393
45045
  return (() => {
@@ -45431,7 +45083,7 @@ function ListOverlay(props) {
45431
45083
  }));
45432
45084
  insert(_el$, createComponent2(SelectionMenuHint, {
45433
45085
  get text() {
45434
- return overlayHint(props.frame, matches2(), tui.store.ui.modelProviders);
45086
+ return overlayHint(props.frame, matches(), tui.store.ui.modelProviders);
45435
45087
  }
45436
45088
  }), null);
45437
45089
  effect((_p$) => {
@@ -46000,10 +45652,10 @@ function overlaySubtitle(kind) {
46000
45652
  return "inspect durable memory";
46001
45653
  return "";
46002
45654
  }
46003
- function overlayHint(frame, matches2, providers) {
45655
+ function overlayHint(frame, matches, providers) {
46004
45656
  if (frame.kind !== "model")
46005
45657
  return "press enter to confirm or esc to go back";
46006
- const enabled = matches2.filter((match) => !match.option.disabled);
45658
+ const enabled = matches.filter((match) => !match.option.disabled);
46007
45659
  const selected = enabled[frame.index]?.option.value;
46008
45660
  if (selected?.kind === "model_action")
46009
45661
  return "enter add provider \xB7 ctrl+a add \xB7 esc back";
@@ -46311,20 +45963,20 @@ function requestStatusDetail(width, progress, countdown) {
46311
45963
  function requestUserInputHint(width, textMode, hasChoices) {
46312
45964
  if (textMode) {
46313
45965
  if (width >= 76)
46314
- return `enter continue \xB7 ${hasChoices ? "tab choices \xB7 " : ""}ctrl+p/n questions \xB7 esc ${hasChoices ? "choices" : "cancel"}`;
45966
+ return `enter continue \xB7 ${hasChoices ? "tab choices \xB7 " : ""}ctrl+p/n questions \xB7 esc ${hasChoices ? "choices" : "chat"} \xB7 ctrl+x cancel`;
46315
45967
  if (width >= 52)
46316
- return `enter \xB7 ctrl+p/n questions \xB7 esc ${hasChoices ? "choices" : "cancel"}`;
46317
- return `enter \xB7 esc ${hasChoices ? "choices" : "cancel"}`;
45968
+ return `enter \xB7 ctrl+p/n questions \xB7 esc ${hasChoices ? "choices" : "chat"}`;
45969
+ return `enter \xB7 esc ${hasChoices ? "choices" : "chat"}`;
46318
45970
  }
46319
45971
  if (width >= 86)
46320
- return "\u2191\u2193 select \xB7 1-9 choose \xB7 \u2190\u2192/ctrl+p/n questions \xB7 tab other \xB7 esc cancel";
45972
+ return "\u2191\u2193 select \xB7 1-9 choose \xB7 \u2190\u2192/ctrl+p/n questions \xB7 tab other \xB7 esc chat \xB7 ctrl+x cancel";
46321
45973
  if (width >= 58)
46322
- return "\u2191\u2193 select \xB7 enter \xB7 \u2190\u2192 questions \xB7 tab other \xB7 esc cancel";
45974
+ return "\u2191\u2193 select \xB7 enter \xB7 \u2190\u2192 questions \xB7 tab other \xB7 esc chat";
46323
45975
  if (width >= 42)
46324
- return "\u2191\u2193 select \xB7 enter \xB7 tab other \xB7 esc cancel";
45976
+ return "\u2191\u2193 select \xB7 enter \xB7 tab other \xB7 esc chat";
46325
45977
  if (width >= 34)
46326
- return "\u2191\u2193 select \xB7 enter \xB7 esc cancel";
46327
- return "\u2191\u2193 \xB7 enter \xB7 esc";
45978
+ return "\u2191\u2193 select \xB7 enter \xB7 esc chat";
45979
+ return "\u2191\u2193 \xB7 enter \xB7 esc chat";
46328
45980
  }
46329
45981
  function requestOptionRows(question) {
46330
45982
  if (!question?.choices?.length)
@@ -46900,7 +46552,8 @@ function BottomPane() {
46900
46552
  const proxyTabActive = () => slot() === "proxy_tab";
46901
46553
  const providerWizardActive = () => Boolean(tui.store.ui.modelProviderWizard);
46902
46554
  const providerRemovalActive = () => Boolean(tui.store.ui.modelProviderRemoval);
46903
- const inputRequestActive = () => Boolean(tui.store.snapshot.pendingUserInput);
46555
+ const inputRequestActive = () => Boolean(tui.store.snapshot.pendingUserInput && !tui.store.ui.requestUserInput?.dismissed);
46556
+ const inputRequestPending = () => Boolean(tui.store.snapshot.pendingUserInput);
46904
46557
  const footerHidden = () => Boolean(frame()) || Boolean(centerFrame()) || slashPanelActive() || proxyTabActive() || inputRequestActive() || providerWizardActive() || providerRemovalActive();
46905
46558
  const inlineStatusDetail = () => {
46906
46559
  const detail = tui.store.ui.statusDetail;
@@ -46960,10 +46613,10 @@ function BottomPane() {
46960
46613
  return tui.store.ui.lastError;
46961
46614
  },
46962
46615
  children: (error) => (() => {
46963
- var _el$2 = createElement("text");
46964
- insert(_el$2, () => `\u2022 error \xB7 ${truncateLine2(error(), 160)}`);
46965
- effect((_$p) => setProp(_el$2, "fg", COLOR.error, _$p));
46966
- return _el$2;
46616
+ var _el$4 = createElement("text");
46617
+ insert(_el$4, () => `\u2022 error \xB7 ${truncateLine2(error(), 160)}`);
46618
+ effect((_$p) => setProp(_el$4, "fg", COLOR.error, _$p));
46619
+ return _el$4;
46967
46620
  })()
46968
46621
  }), null);
46969
46622
  insert(_el$, createComponent2(Show, {
@@ -46974,6 +46627,17 @@ function BottomPane() {
46974
46627
  return createComponent2(PendingInputPreview, {});
46975
46628
  }
46976
46629
  }), null);
46630
+ insert(_el$, createComponent2(Show, {
46631
+ get when() {
46632
+ return memo2(() => !!(!inputRequestActive() && inputRequestPending() && !providerWizardActive()))() && !providerRemovalActive();
46633
+ },
46634
+ get children() {
46635
+ var _el$2 = createElement("text");
46636
+ insertNode(_el$2, createTextNode(` question pending \xB7 ctrl+q answer \xB7 chat input remains available`));
46637
+ effect((_$p) => setProp(_el$2, "fg", COLOR.warning, _$p));
46638
+ return _el$2;
46639
+ }
46640
+ }), null);
46977
46641
  insert(_el$, createComponent2(Show, {
46978
46642
  get when() {
46979
46643
  return tui.store.ui.modelProviderRemoval;
@@ -46986,7 +46650,7 @@ function BottomPane() {
46986
46650
  get fallback() {
46987
46651
  return createComponent2(Show, {
46988
46652
  get when() {
46989
- return tui.store.snapshot.pendingUserInput;
46653
+ return memo2(() => !!inputRequestActive())() ? tui.store.snapshot.pendingUserInput : undefined;
46990
46654
  },
46991
46655
  get fallback() {
46992
46656
  return createComponent2(Show, {
@@ -47063,54 +46727,54 @@ function BottomPane() {
47063
46727
  function ProxyTabFooter() {
47064
46728
  const tui = useTuiStore();
47065
46729
  return (() => {
47066
- var _el$3 = createElement("box"), _el$4 = createElement("text"), _el$6 = createElement("text");
47067
- insertNode(_el$3, _el$4);
47068
- insertNode(_el$3, _el$6);
47069
- setProp(_el$3, "style", {
46730
+ var _el$5 = createElement("box"), _el$6 = createElement("text"), _el$8 = createElement("text");
46731
+ insertNode(_el$5, _el$6);
46732
+ insertNode(_el$5, _el$8);
46733
+ setProp(_el$5, "style", {
47070
46734
  height: 1,
47071
46735
  flexDirection: "row",
47072
46736
  justifyContent: "space-between",
47073
46737
  paddingLeft: 1,
47074
46738
  paddingRight: 1
47075
46739
  });
47076
- insertNode(_el$4, 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`));
47077
- insert(_el$6, () => `proxy \xB7 ${tui.store.ui.proxyFilter}`);
46740
+ insertNode(_el$6, createTextNode(`\u2191\u2193 flow \xB7 tab detail \xB7 p/n ws msg \xB7 \u2190\u2192 filter \xB7 a/h/w tabs \xB7 ctrl+1 chat`));
46741
+ insert(_el$8, () => `proxy \xB7 ${tui.store.ui.proxyFilter}`);
47078
46742
  effect((_p$) => {
47079
46743
  var _v$ = COLOR.dim, _v$2 = COLOR.dim;
47080
- _v$ !== _p$.e && (_p$.e = setProp(_el$4, "fg", _v$, _p$.e));
47081
- _v$2 !== _p$.t && (_p$.t = setProp(_el$6, "fg", _v$2, _p$.t));
46744
+ _v$ !== _p$.e && (_p$.e = setProp(_el$6, "fg", _v$, _p$.e));
46745
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$8, "fg", _v$2, _p$.t));
47082
46746
  return _p$;
47083
46747
  }, {
47084
46748
  e: undefined,
47085
46749
  t: undefined
47086
46750
  });
47087
- return _el$3;
46751
+ return _el$5;
47088
46752
  })();
47089
46753
  }
47090
46754
  function CenterSurfaceFooter(props) {
47091
46755
  return (() => {
47092
- var _el$7 = createElement("box"), _el$8 = createElement("text"), _el$9 = createElement("text");
47093
- insertNode(_el$7, _el$8);
47094
- insertNode(_el$7, _el$9);
47095
- setProp(_el$7, "style", {
46756
+ var _el$9 = createElement("box"), _el$0 = createElement("text"), _el$1 = createElement("text");
46757
+ insertNode(_el$9, _el$0);
46758
+ insertNode(_el$9, _el$1);
46759
+ setProp(_el$9, "style", {
47096
46760
  height: 1,
47097
46761
  flexDirection: "row",
47098
46762
  justifyContent: "space-between",
47099
46763
  paddingLeft: 1,
47100
46764
  paddingRight: 1
47101
46765
  });
47102
- insert(_el$8, () => centerSurfaceFooter(props.frame).toLowerCase());
47103
- insert(_el$9, () => props.frame.kind.toLowerCase());
46766
+ insert(_el$0, () => centerSurfaceFooter(props.frame).toLowerCase());
46767
+ insert(_el$1, () => props.frame.kind.toLowerCase());
47104
46768
  effect((_p$) => {
47105
46769
  var _v$3 = COLOR.dim, _v$4 = COLOR.dim;
47106
- _v$3 !== _p$.e && (_p$.e = setProp(_el$8, "fg", _v$3, _p$.e));
47107
- _v$4 !== _p$.t && (_p$.t = setProp(_el$9, "fg", _v$4, _p$.t));
46770
+ _v$3 !== _p$.e && (_p$.e = setProp(_el$0, "fg", _v$3, _p$.e));
46771
+ _v$4 !== _p$.t && (_p$.t = setProp(_el$1, "fg", _v$4, _p$.t));
47108
46772
  return _p$;
47109
46773
  }, {
47110
46774
  e: undefined,
47111
46775
  t: undefined
47112
46776
  });
47113
- return _el$7;
46777
+ return _el$9;
47114
46778
  })();
47115
46779
  }
47116
46780
  var init_bottom_pane = __esm(() => {
@@ -47669,9 +47333,9 @@ async function ensureSession(input) {
47669
47333
  } catch {
47670
47334
  const needle = input.sessionId.trim().toLowerCase();
47671
47335
  const sessions2 = await input.runtime.listSessions();
47672
- const matches2 = sessions2.filter((session) => session.id.toLowerCase().startsWith(needle) || session.title?.trim().toLowerCase() === needle);
47673
- if (matches2.length === 1)
47674
- return matches2[0].id;
47336
+ const matches = sessions2.filter((session) => session.id.toLowerCase().startsWith(needle) || session.title?.trim().toLowerCase() === needle);
47337
+ if (matches.length === 1)
47338
+ return matches[0].id;
47675
47339
  throw new SessionResolutionError(input.sessionId, input.workspace, sessions2);
47676
47340
  }
47677
47341
  }
@@ -50303,5 +49967,5 @@ Examples:
50303
49967
  `);
50304
49968
  }
50305
49969
 
50306
- //# debugId=A6CB55D8D7667DD664756E2164756E21
49970
+ //# debugId=231A9E4A58F7AFCB64756E2164756E21
50307
49971
  //# sourceMappingURL=index.js.map