farai 0.3.8 → 0.3.9

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
@@ -810,9 +810,9 @@ class SqliteStore {
810
810
  });
811
811
  try {
812
812
  ensurePrivateRegularFileIfExists(file, "farai database path");
813
+ db.exec("PRAGMA busy_timeout = 5000;");
813
814
  db.exec("PRAGMA journal_mode = WAL;");
814
815
  db.exec("PRAGMA foreign_keys = ON;");
815
- db.exec("PRAGMA busy_timeout = 5000;");
816
816
  this.migrate(db);
817
817
  ensurePrivateSqlitePath(file, "farai database path");
818
818
  this.db = db;
@@ -6436,6 +6436,7 @@ function normalizeConfig(raw) {
6436
6436
  const email = providerMap(raw.email_accounts ?? raw.emailAccounts);
6437
6437
  const modelLimits = modelLimitMap(raw.model_limits ?? raw.modelLimits);
6438
6438
  const recent = raw.recent_models ?? raw.recentModels;
6439
+ const pinnedSkills = raw.pinned_skills ?? raw.pinnedSkills;
6439
6440
  const proxy = proxyConfig(raw.proxy);
6440
6441
  const context = contextConfig(raw.context);
6441
6442
  const lsp = lspConfig(raw.lsp);
@@ -6477,6 +6478,9 @@ function normalizeConfig(raw) {
6477
6478
  ...Array.isArray(recent) ? {
6478
6479
  recentModels: recent.filter((item) => typeof item === "string")
6479
6480
  } : {},
6481
+ ...Array.isArray(pinnedSkills) ? {
6482
+ pinnedSkills: [...new Set(pinnedSkills.filter((item) => typeof item === "string" && Boolean(item.trim())).map((item) => item.trim()))]
6483
+ } : {},
6480
6484
  ...modelLimits ? {
6481
6485
  modelLimits
6482
6486
  } : {},
@@ -6651,6 +6655,9 @@ function mergeConfig(base, over) {
6651
6655
  ...over.recentModels ?? base.recentModels ? {
6652
6656
  recentModels: over.recentModels ?? base.recentModels
6653
6657
  } : {},
6658
+ ...base.pinnedSkills || over.pinnedSkills ? {
6659
+ pinnedSkills: [...new Set([...base.pinnedSkills ?? [], ...over.pinnedSkills ?? []])]
6660
+ } : {},
6654
6661
  ...base.modelLimits || over.modelLimits ? {
6655
6662
  modelLimits: {
6656
6663
  ...base.modelLimits,
@@ -6721,6 +6728,8 @@ function serializeConfigToml(config) {
6721
6728
  lines.push(`model = ${tomlString(config.model)}`);
6722
6729
  if (config.recentModels?.length)
6723
6730
  lines.push(`recent_models = ${tomlArray(config.recentModels)}`);
6731
+ if (config.pinnedSkills?.length)
6732
+ lines.push(`pinned_skills = ${tomlArray(config.pinnedSkills)}`);
6724
6733
  if (config.contextWindow)
6725
6734
  lines.push(`context_window = ${config.contextWindow}`);
6726
6735
  if (config.maxOutputTokens)
@@ -9257,8 +9266,8 @@ var init_renderers = __esm(() => {
9257
9266
 
9258
9267
  // src/version.ts
9259
9268
  function resolveFaraiVersion() {
9260
- if ("0.3.8")
9261
- return "0.3.8";
9269
+ if ("0.3.9")
9270
+ return "0.3.9";
9262
9271
  try {
9263
9272
  const parsed = JSON.parse(readBoundedFileTextSync(new URL("../package.json", import.meta.url), 1024 * 1024, "package metadata"));
9264
9273
  if (typeof parsed.version === "string" && parsed.version)
@@ -15640,49 +15649,50 @@ function buildDnsProbeCommand(args) {
15640
15649
  const recordTypes = requested.length ? requested : ["a", "aaaa", "cname"];
15641
15650
  if (recordTypes.some((value) => !DNS_RECORD_TYPES.includes(value)))
15642
15651
  throw new Error("recordTypes contains an unsupported DNS record type");
15643
- const command = ["-json", "-silent", "-nc", "-duc", "-omit-raw", "-resp", "-timeout", `${integer(args.timeoutSeconds, 5, 1, 30)}s`, "-rl", String(integer(args.rateLimit, 500, 1, 1e4))];
15644
- for (const type of recordTypes)
15645
- command.push(`-${type}`);
15646
- if (args.includeAsn === true)
15647
- command.push("-asn");
15652
+ const timeout = integer(args.timeoutSeconds, 5, 1, 30);
15648
15653
  const resolvers = optionalStringList(args.resolvers, "resolvers", 100);
15649
- if (resolvers.length)
15650
- command.push("-r", resolvers.join(","));
15651
- if (args.wildcard === "auto")
15652
- command.push("-auto-wildcard");
15653
- return inputFileCommand("dnsx", command, names, "-l");
15654
+ const queries = names.flatMap((name) => recordTypes.map((type) => `${name} ${type.toUpperCase()}`));
15655
+ const queryLines = queries.map(shellQuote3).join(" ");
15656
+ const fixed = ["dig", "+noall", "+answer", "+nocomments", "+tries=1", `+time=${timeout}`].join(" ");
15657
+ const server = resolvers[0] ? ` ${shellQuote3(`@${resolvers[0]}`)}` : "";
15658
+ return ['file="$(mktemp /tmp/farai-dig.XXXXXX)" || exit 1', `trap 'rm -f "$file"' EXIT`, `printf '%s\\n' ${queryLines} > "$file"`, `${fixed}${server} -f "$file"`].join(`
15659
+ `);
15654
15660
  }
15655
15661
  function parseDnsProbeOutput(raw) {
15656
- const parsed = parseJsonLines(raw);
15657
- return {
15658
- records: parsed.records.map(normalizeDnsProbe),
15659
- malformed: parsed.malformed
15660
- };
15661
- }
15662
- function normalizeDnsProbe(value) {
15663
- const records = {};
15664
- for (const type of DNS_RECORD_TYPES) {
15665
- const values = textArray(value[type]);
15666
- if (values.length)
15667
- records[type] = values;
15662
+ const byName = new Map;
15663
+ let malformed = 0;
15664
+ for (const line of raw.split(`
15665
+ `)) {
15666
+ const trimmed = line.trim();
15667
+ if (!trimmed || trimmed.startsWith(";"))
15668
+ continue;
15669
+ const fields = trimmed.split(/\s+/);
15670
+ if (fields.length < 5) {
15671
+ malformed += 1;
15672
+ continue;
15673
+ }
15674
+ const owner = fields[0].replace(/\.$/, "").toLowerCase();
15675
+ const type = fields[3].toLowerCase();
15676
+ const value = fields.slice(4).join(" ").replace(/\.$/, "");
15677
+ if (!DNS_RECORD_TYPES.includes(type))
15678
+ continue;
15679
+ const entry = byName.get(owner) ?? {};
15680
+ (entry[type] ??= []).push(value);
15681
+ byName.set(owner, entry);
15668
15682
  }
15669
- const status = text(value.status_code);
15670
- const asn = record(value.asn);
15683
+ const records = [...byName.entries()].map(([name, entryRecords]) => ({
15684
+ name,
15685
+ records: entryRecords,
15686
+ resolver: []
15687
+ }));
15671
15688
  return {
15672
- name: text(value.host) ?? text(value.input) ?? "unknown",
15673
- ...status ? {
15674
- status
15675
- } : {},
15676
15689
  records,
15677
- resolver: textArray(value.resolver),
15678
- ...asn ? {
15679
- asn
15680
- } : {}
15690
+ malformed
15681
15691
  };
15682
15692
  }
15683
15693
  function renderDnsProbe(item) {
15684
15694
  const answers = Object.entries(item.records).flatMap(([type, values]) => values.map((value) => `${type.toUpperCase()} ${value}`));
15685
- return `${item.name}${item.status ? ` \xB7 ${item.status}` : ""}${answers.length ? ` \xB7 ${answers.join(" \xB7 ")}` : " \xB7 no answers"}`;
15695
+ return `${item.name}${answers.length ? ` \xB7 ${answers.join(" \xB7 ")}` : " \xB7 no answers"}`;
15686
15696
  }
15687
15697
  var DNS_RECORD_TYPES, dnsProbeTool;
15688
15698
  var init_dns_probe = __esm(() => {
@@ -15693,7 +15703,7 @@ var init_dns_probe = __esm(() => {
15693
15703
  DNS_RECORD_TYPES = ["a", "aaaa", "cname", "ns", "txt", "srv", "ptr", "mx", "soa", "caa"];
15694
15704
  dnsProbeTool = {
15695
15705
  name: "dns_resolve",
15696
- description: "Resolve and enrich one or many hostnames with ProjectDiscovery dnsx using selected DNS record types, optional custom resolvers, ASN enrichment, and automatic wildcard filtering. Use this to validate candidates from asset_subdomains before HTTP or port probing; it is not a passive discovery source.",
15706
+ description: "Resolve one or many hostnames with dig using selected DNS record types and optional custom resolver. Returns answer records grouped by name (following CNAME targets as their own entries). Use this to validate candidates from asset_subdomains before HTTP or port probing; it is not a passive discovery source.",
15697
15707
  inputSchema: {
15698
15708
  type: "object",
15699
15709
  required: ["names"],
@@ -15730,24 +15740,13 @@ var init_dns_probe = __esm(() => {
15730
15740
  },
15731
15741
  maxItems: 100,
15732
15742
  uniqueItems: true
15733
- }]
15734
- },
15735
- wildcard: {
15736
- type: "string",
15737
- enum: ["off", "auto"]
15738
- },
15739
- includeAsn: {
15740
- type: "boolean"
15743
+ }],
15744
+ description: "optional custom resolver; the first entry is passed to dig as @resolver"
15741
15745
  },
15742
15746
  timeoutSeconds: {
15743
15747
  type: "integer",
15744
15748
  minimum: 1,
15745
15749
  maximum: 30
15746
- },
15747
- rateLimit: {
15748
- type: "integer",
15749
- minimum: 1,
15750
- maximum: 1e4
15751
15750
  }
15752
15751
  },
15753
15752
  additionalProperties: false
@@ -15768,7 +15767,7 @@ var init_dns_probe = __esm(() => {
15768
15767
  const parsed = parseDnsProbeOutput(result.stdout);
15769
15768
  return projectDiscoveryResult(context, {
15770
15769
  tool: "dns_resolve",
15771
- backend: "dnsx",
15770
+ backend: "dig",
15772
15771
  result,
15773
15772
  records: parsed.records,
15774
15773
  malformed: parsed.malformed,
@@ -15783,11 +15782,29 @@ var init_dns_probe = __esm(() => {
15783
15782
  });
15784
15783
 
15785
15784
  // src/agent-tools/recon/http-probe.ts
15785
+ function hasScheme(target) {
15786
+ return /^[a-z][a-z0-9+.-]*:\/\//i.test(target);
15787
+ }
15788
+ function probeSchemes(args) {
15789
+ if (!Array.isArray(args.schemes))
15790
+ return ["https"];
15791
+ const schemes = [...new Set(args.schemes.filter((item) => item === "https" || item === "http"))];
15792
+ return schemes.length ? schemes : ["https"];
15793
+ }
15794
+ function fastProbeTargets(args) {
15795
+ const schemes = probeSchemes(args);
15796
+ return stringList(args.targets, "targets").flatMap((target) => hasScheme(target) ? [target] : schemes.map((scheme) => `${scheme}://${target}`));
15797
+ }
15798
+ function fastHttpProbeBudgetMs(args) {
15799
+ const timeoutSeconds = integer(args.timeoutSeconds, 3, 1, 15);
15800
+ const concurrency = integer(args.concurrency, 100, 1, 200);
15801
+ const waves = Math.max(1, Math.ceil(fastProbeTargets(args).length / concurrency));
15802
+ return Math.min(28000, Math.max(8000, waves * timeoutSeconds * 1000 + 6000));
15803
+ }
15786
15804
  function buildFastHttpProbeCommand(args) {
15787
- const targets = stringList(args.targets, "targets");
15788
15805
  const timeoutSeconds = integer(args.timeoutSeconds, 3, 1, 15);
15789
15806
  const concurrency = integer(args.concurrency, 100, 1, 200);
15790
- const probeTargets = targets.flatMap((target) => /^[a-z][a-z0-9+.-]*:\/\//i.test(target) ? [target] : [`https://${target}`, `http://${target}`]);
15807
+ const probeTargets = fastProbeTargets(args);
15791
15808
  const configLines = probeTargets.flatMap((target) => [`url = "${curlConfigQuote(target)}"`, 'output = "/dev/null"', 'write-out = "%{url}\\t%{url_effective}\\t%{http_code}\\t%{content_type}\\t%{size_download}\\t%{time_total}\\t%{remote_ip}\\t%{exitcode}\\t%{errormsg}\\n"']).map(shellQuote3).join(" ");
15792
15809
  return ['config="$(mktemp /tmp/farai-http-probe.XXXXXX)" || exit 1', `trap 'rm -f "$config"' EXIT`, `printf '%s\\n' ${configLines} > "$config"`, `curl --parallel --parallel-immediate --parallel-max ${concurrency} --silent --show-error --insecure --connect-timeout 1 --max-time ${timeoutSeconds} --config "$config"`].join(`
15793
15810
  `);
@@ -15986,7 +16003,7 @@ var init_http_probe = __esm(() => {
15986
16003
  init_projectdiscovery();
15987
16004
  httpProbeTool = {
15988
16005
  name: "service_probe",
15989
- description: "Probe one or many hosts, IPs, or URLs with a lightweight concurrent HTTP probe and return normalized live service records. The default fast mode returns status, content type, response size, address, and timing; detail mode uses ProjectDiscovery httpx for page title, server, technology, ASN, CDN/WAF, redirect, and TLS enrichment. Use this after subdomain or port discovery; use browser tools for interactive state and http_request for one exact protocol request.",
16006
+ description: 'Probe one or many hosts, IPs, or URLs with a lightweight concurrent HTTP probe and return normalized live service records. Fast mode (default) tries https only for bare hosts and returns status, content type, response size, address, and timing; pass schemes:["https","http"] to also probe http. Detail mode uses ProjectDiscovery httpx for page title, server, technology, ASN, CDN/WAF, redirect, and TLS enrichment. Use this after subdomain or port discovery; use browser tools for interactive state and http_request for one exact protocol request.',
15990
16007
  inputSchema: {
15991
16008
  type: "object",
15992
16009
  required: ["targets"],
@@ -16020,6 +16037,17 @@ var init_http_probe = __esm(() => {
16020
16037
  uniqueItems: true
16021
16038
  }]
16022
16039
  },
16040
+ schemes: {
16041
+ type: "array",
16042
+ items: {
16043
+ type: "string",
16044
+ enum: ["https", "http"]
16045
+ },
16046
+ minItems: 1,
16047
+ maxItems: 2,
16048
+ uniqueItems: true,
16049
+ description: 'schemes to try for bare hosts; defaults to https only for speed, pass ["https","http"] to also probe http'
16050
+ },
16023
16051
  redirects: {
16024
16052
  type: "string",
16025
16053
  enum: ["none", "same_host", "all"]
@@ -16061,7 +16089,7 @@ var init_http_probe = __esm(() => {
16061
16089
  assertObject(args, "args");
16062
16090
  const kali = backend(context);
16063
16091
  const detail = args.mode === "detail";
16064
- const result = await kali.exec(buildHttpProbeCommand(args), detail ? 25000 : 8000, context.signal, 16000000);
16092
+ const result = await kali.exec(buildHttpProbeCommand(args), detail ? 25000 : fastHttpProbeBudgetMs(args), context.signal, 16000000);
16065
16093
  const converted = timeoutBackgroundResult("service_probe", kali, result);
16066
16094
  if (converted)
16067
16095
  return converted;
@@ -19423,6 +19451,29 @@ function loadSkill(name, options = {}) {
19423
19451
  }
19424
19452
  };
19425
19453
  }
19454
+ function renderPinnedSkills(workspace, names, maxChars = 24000) {
19455
+ const unique = [...new Set(names.map((name) => name.trim()).filter(Boolean))];
19456
+ if (!unique.length || maxChars < 200)
19457
+ return;
19458
+ const blocks = [];
19459
+ for (const name of unique) {
19460
+ const skill = loadSkill(name, {
19461
+ workspace
19462
+ });
19463
+ if (!skill)
19464
+ continue;
19465
+ blocks.push(`## pinned skill: ${skill.name}
19466
+ source: ${skill.source} \xB7 sha256: ${skill.hash}
19467
+
19468
+ ${skill.body}`);
19469
+ }
19470
+ if (!blocks.length)
19471
+ return;
19472
+ const header = "these skills are pinned and always active \u2014 follow them as trusted local instructions within the user's current request and higher-priority policy. do not call skill_load for them again.";
19473
+ return compactText([header, ...blocks].join(`
19474
+
19475
+ `), maxChars);
19476
+ }
19426
19477
  function renderSkillCatalog(workspace, maxChars = 8000) {
19427
19478
  const skills = listSkills(workspace);
19428
19479
  if (!skills.length || maxChars < 80)
@@ -22068,6 +22119,28 @@ function loadLanes(workspace) {
22068
22119
  function resolveLane(workspace, id2) {
22069
22120
  return loadLanes(workspace).find((lane) => lane.id === id2);
22070
22121
  }
22122
+ function laneWriteTarget(workspace) {
22123
+ return laneConfigPaths(workspace)[0];
22124
+ }
22125
+ function readLaneFile(path) {
22126
+ if (!existsSync11(path))
22127
+ return [];
22128
+ try {
22129
+ const parsed = JSON.parse(readBoundedFileTextSync(path, LANE_CONFIG_MAX_BYTES, "agent lane config"));
22130
+ if (!Array.isArray(parsed))
22131
+ return [];
22132
+ return parsed.flatMap((entry) => {
22133
+ const lane = normalizeLane(entry);
22134
+ return lane ? [lane] : [];
22135
+ });
22136
+ } catch {
22137
+ return [];
22138
+ }
22139
+ }
22140
+ function writeLaneFile(path, lanes) {
22141
+ atomicWriteFile(path, `${JSON.stringify(lanes, null, 2)}
22142
+ `, 384);
22143
+ }
22071
22144
  function normalizeLane(value) {
22072
22145
  if (!value || typeof value !== "object")
22073
22146
  return;
@@ -22095,32 +22168,15 @@ var LANE_CONFIG_MAX_BYTES, BUILTIN_LANES;
22095
22168
  var init_lanes = __esm(() => {
22096
22169
  init_global_config();
22097
22170
  init_file_read();
22171
+ init_atomic_file();
22098
22172
  LANE_CONFIG_MAX_BYTES = 2 * 1024 * 1024;
22099
22173
  BUILTIN_LANES = [{
22100
- id: "explore",
22101
- description: "read-only workspace exploration and codebase analysis",
22102
- prompt: "Explore the workspace without modifying it. Return only the evidence, file references, conclusions, and unresolved questions needed by the parent.",
22103
- tools: ["file_list", "file_search", "file_read", "git_status", "git_diff", "code_diagnostics", "output_read", "agent_manage"]
22104
- }, {
22105
22174
  id: "recon",
22106
- description: "bounded infrastructure and attack-surface reconnaissance",
22107
- prompt: "Perform only the delegated reconnaissance scope. Prefer typed discovery tools, preserve evidence, avoid duplicate probes, and return deduplicated assets with source status and uncertainty.",
22108
- tools: ["asset_subdomains", "dns_resolve", "network_scan", "service_probe", "tls_inspect", "url_discover", "web_crawl", "vulnerability_scan", "vulnerability_lookup", "web_directory", "kali_search", "command_run", "command_input", "browser_manage", "campaign_manage", "knowledge_manage", "command_poll", "command_stop", "output_read", "agent_manage"]
22109
- }, {
22110
- id: "web",
22111
- description: "web application exploration and verification",
22112
- prompt: "Audit only the delegated web scope. Use browser, HTTP, and shell capabilities as appropriate, preserve exact evidence, and return proven findings separately from uncertainty.",
22113
- tools: ["browser_manage", "mail_manage", "http_request", "service_probe", "tls_inspect", "url_discover", "web_crawl", "vulnerability_scan", "vulnerability_lookup", "web_directory", "kali_search", "command_run", "command_input", "campaign_manage", "knowledge_manage", "command_poll", "command_stop", "output_read", "agent_manage"]
22114
- }, {
22115
- id: "code",
22116
- description: "isolated software implementation, debugging, and review",
22117
- prompt: "Handle only the delegated code task. Inspect before editing, preserve unrelated changes, make the smallest coherent patch, and return changed files, validation, and residual risk.",
22118
- tools: ["file_list", "file_search", "file_read", "file_write", "file_replace", "file_patch", "git_status", "git_diff", "code_diagnostics", "script_write", "command_run", "command_input", "task_manage", "output_read", "agent_manage"]
22119
- }, {
22120
- id: "verify",
22121
- description: "independent verification of evidence and candidate findings",
22122
- prompt: "Independently verify only the delegated claim. Establish a baseline, run the smallest discriminating test, save evidence, and return proven, disproven, or inconclusive with exact reasoning.",
22123
- tools: ["browser_manage", "http_request", "service_probe", "tls_inspect", "vulnerability_scan", "vulnerability_lookup", "command_run", "command_input", "campaign_manage", "finding_manage", "knowledge_manage", "output_read", "agent_manage"]
22175
+ description: "basic attack-surface reconnaissance: subdomains, dns, live probing, and crawling",
22176
+ prompt: ["You are a focused reconnaissance subagent. Map only the delegated target's attack surface using passive and light-active discovery: subdomain enumeration, DNS records, live HTTP/TLS probing, content and URL discovery, and crawling.", "Scope: discovery only. Do NOT exploit, brute-force, run broad port or vulnerability scanners, or perform intrusive testing \u2014 those belong to other lanes. Prefer the typed discovery tools; use shell only to run a recon CLI when no typed tool covers the need.", "Deduplicate assets, preserve exact evidence, and record the source and confidence of each item. Return a structured inventory: subdomains/hosts, resolved DNS, live services with status code and detected technology, discovered URLs and paths, and TLS facts \u2014 separating confirmed from uncertain. End with coverage gaps and the highest-value next recon steps for the parent."].join(`
22177
+
22178
+ `),
22179
+ tools: ["asset_subdomains", "dns_resolve", "service_probe", "tls_inspect", "url_discover", "web_crawl", "web_directory", "kali_search", "command_run", "command_input", "command_poll", "command_stop", "campaign_manage", "knowledge_manage", "output_read", "agent_manage"]
22124
22180
  }];
22125
22181
  });
22126
22182
 
@@ -30670,7 +30726,7 @@ var init_lifecycle2 = __esm(() => {
30670
30726
  },
30671
30727
  lane: {
30672
30728
  type: "string",
30673
- description: "capability profile: explore for read-only inspection, recon for discovery shell, web for browser and HTTP work, code for edits, verify for independent validation, or an explicitly configured specialist lane"
30729
+ description: "capability profile from the available subagent lanes (for example recon for basic attack-surface discovery), or an explicitly configured specialist lane; omit to inherit the full parent scope"
30674
30730
  },
30675
30731
  tools: {
30676
30732
  type: "array",
@@ -31760,6 +31816,11 @@ var init_catalog = __esm(() => {
31760
31816
  function getToolRegistry() {
31761
31817
  return builtinRegistry;
31762
31818
  }
31819
+ function listTools() {
31820
+ const tools = [...builtinRegistry.list().map(toolDefinition), ...listMcpTools()];
31821
+ assertUniqueTools(tools);
31822
+ return tools;
31823
+ }
31763
31824
  function listToolsForSession(session) {
31764
31825
  const tools = [...builtinRegistry.list().map(toolDefinition), ...listMcpTools(session)];
31765
31826
  assertUniqueTools(tools);
@@ -33598,13 +33659,13 @@ async function resolveModelSelection(workspace, selection) {
33598
33659
  if (selection) {
33599
33660
  const selected = (await buildModelCatalog(workspace)).models.find((model) => model.id === selection);
33600
33661
  if (selected)
33601
- return withSavedModelLimits(modelChoiceToResolved(selected), selection, workspace);
33662
+ return withModelsDevLimits(withSavedModelLimits(modelChoiceToResolved(selected), selection, workspace), selection, workspace);
33602
33663
  const profileResolved = await resolveProfileAsync(loadModelProfiles(workspace), selection);
33603
33664
  if (profileResolved?.model)
33604
- return withSavedModelLimits(profileResolved, selection, workspace);
33605
- return withSavedModelLimits(ensureConcrete(resolveModel({
33665
+ return withModelsDevLimits(withSavedModelLimits(profileResolved, selection, workspace), selection, workspace);
33666
+ return withModelsDevLimits(withSavedModelLimits(ensureConcrete(resolveModel({
33606
33667
  model: selection
33607
- })), selection, workspace);
33668
+ })), selection, workspace), selection, workspace);
33608
33669
  }
33609
33670
  return resolveDefaultCatalogModel(workspace);
33610
33671
  }
@@ -33928,6 +33989,22 @@ async function lookupModelsDevLimits(model, providerHint, baseUrlHint) {
33928
33989
  } : {}
33929
33990
  };
33930
33991
  }
33992
+ async function withModelsDevLimits(resolved, selection, workspace) {
33993
+ if (resolved.contextWindow)
33994
+ return resolved;
33995
+ const profile = resolveProfile(loadModelProfiles(workspace), selection);
33996
+ const lookupModel = resolved.model ?? profile?.model ?? selection;
33997
+ const fetched = await lookupModelsDevLimits(lookupModel, profile ? selection : undefined, resolved.baseUrl ?? profile?.baseUrl);
33998
+ if (!fetched?.contextWindow)
33999
+ return resolved;
34000
+ return {
34001
+ ...resolved,
34002
+ contextWindow: fetched.contextWindow,
34003
+ ...resolved.maxOutputTokens ? {} : fetched.maxOutputTokens ? {
34004
+ maxOutputTokens: fetched.maxOutputTokens
34005
+ } : {}
34006
+ };
34007
+ }
33931
34008
  function withSavedModelLimits(resolved, selection, workspace) {
33932
34009
  const saved = loadConfig(workspace).modelLimits?.[selection];
33933
34010
  if (!saved)
@@ -34198,7 +34275,7 @@ function buildSystemPromptBlocks(input) {
34198
34275
  `)
34199
34276
  }, {
34200
34277
  title: "State and Delegation",
34201
- body: ["Treat active jobs as live state: reuse or poll relevant work instead of duplicating it. Completion is delivered automatically.", "Keep the current session name concise and specific. Farai derives an initial name from the first substantive user request; call session_manage once when that fallback is vague or the durable goal materially changes. Do not rename a session for greetings, temporary substeps, or routine follow-ups.", 'Use agent_manage only for bounded work that benefits from independent context, parallel I/O, persistent browser state, specialist tools, or independent verification. Use operation=spawn to start a child, list or wait to inspect it, message to steer active work, followup to continue an idle child, and interrupt or close for lifecycle cleanup. Put operation-specific fields inside args. Children inherit the parent model by default; omit model unless an explicit model override is required. Choose the required lane first: explore is read-only without shell; recon has discovery shell; web has browser, HTTP, and shell; code can edit; verify independently checks with browser, HTTP, and shell. Attached work blocks the parent; detached work must be non-editing and independently useful. Give parallel workers non-overlapping ownership, and keep synthesis and the user-facing answer in the parent. For background delegation, pass args.mode="detached"; never invent detached=true or top-level operation arguments.', "Campaigns are model-decided: for multi-step authorized work needing durable evidence, waves, coordination, verification, or reporting, call campaign_manage with the relevant operation; avoid it for one-off tasks. While active, use campaign_manage operations, define only the requirements that matter for this objective, and checkpoint each wave; the runtime handles leases, recovery, and final validation."].join(`
34278
+ body: ["Treat active jobs as live state: reuse or poll relevant work instead of duplicating it. Completion is delivered automatically.", "Keep the current session name concise and specific. Farai derives an initial name from the first substantive user request; call session_manage once when that fallback is vague or the durable goal materially changes. Do not rename a session for greetings, temporary substeps, or routine follow-ups.", "Work as an orchestrator: delegate whole units of work to subagents and keep this thread coordinating. Judge by the size of the work. A self-contained objective that spans several steps or tools, benefits from its own context, can run in parallel with other work, or needs a specialist role \u2014 for example map and probe an attack surface, test authorization across an app, or review and patch a module \u2014 should be delegated to a subagent (a matching lane from Available Subagent Lanes when one fits, otherwise a plain child that inherits scope). Do NOT spawn a subagent just to run one tool: call that tool directly, and if it is slow, run it in the background and poll rather than wrapping it in a child. Answer trivial requests directly. When you do delegate, keep this thread planning, splitting non-overlapping work, dispatching workers (in parallel when independent), monitoring, and synthesizing results into the user's answer.", 'Use agent_manage for work that benefits from independent context, parallel I/O, persistent browser state, specialist tools, or independent verification. Use operation=spawn to start a child, list or wait to inspect it, message to steer active work, followup to continue an idle child, and interrupt or close for lifecycle cleanup. Put operation-specific fields inside args. Children inherit the parent model by default; omit model unless an explicit model override is required. Pick a lane from the Available Subagent Lanes list (each has a role and tool scope); do not assume only the builtin lanes exist. Prefer detached delegation for work you can run in the background or in parallel: pass args.mode="detached" (never detached=true) so the child runs without blocking you and its result is delivered back automatically on completion \u2014 fan out several non-overlapping detached children and keep working instead of idling on one. Detached children must be non-editing and independently useful; use attached (blocking) mode when you need the result before the next step or when the child edits the shared workspace. While children run, use operation=list or wait to check progress, message to steer an active child, and followup to give an idle child more work. Give parallel workers non-overlapping ownership, and keep synthesis and the user-facing answer in the parent.', "Campaigns are model-decided: for multi-step authorized work needing durable evidence, waves, coordination, verification, or reporting, call campaign_manage with the relevant operation; avoid it for one-off tasks. While active, use campaign_manage operations, define only the requirements that matter for this objective, and checkpoint each wave; the runtime handles leases, recovery, and final validation."].join(`
34202
34279
  `)
34203
34280
  }, {
34204
34281
  title: "Trust Boundary",
@@ -36440,12 +36517,13 @@ function actionsFromMessage(message) {
36440
36517
  } : {}
36441
36518
  });
36442
36519
  } catch (error) {
36520
+ const baseError = error instanceof Error ? error.message : String(error);
36443
36521
  actions.push({
36444
36522
  kind: "tool_parse_error",
36445
36523
  tool: name,
36446
36524
  toolCallId: call2.id ?? name,
36447
36525
  rawArguments: raw,
36448
- error: error instanceof Error ? error.message : String(error)
36526
+ error: isProviderIncompleteFinishReason(finishReason) ? `${baseError} (the provider truncated the response before the tool arguments finished, finish_reason=${finishReason ?? "unknown"}; retry or switch model)` : baseError
36449
36527
  });
36450
36528
  }
36451
36529
  }
@@ -36973,6 +37051,10 @@ function fitToolResultText(text2, maxBytes) {
36973
37051
  var init_history = () => {};
36974
37052
 
36975
37053
  // src/agent-core/history-projection.ts
37054
+ function isLocalCommandInput(text2) {
37055
+ const trimmed = text2.trimStart();
37056
+ return trimmed.startsWith("/") || trimmed.startsWith("!");
37057
+ }
36976
37058
  function projectConversationHistory(messages, options = {}) {
36977
37059
  const overlay = options.overlay;
36978
37060
  const providerToolCallIds = new Map;
@@ -37012,7 +37094,7 @@ function projectConversationHistory(messages, options = {}) {
37012
37094
  continue;
37013
37095
  }
37014
37096
  const text2 = part.payload.text;
37015
- if (typeof text2 === "string" && text2)
37097
+ if (typeof text2 === "string" && text2 && !isLocalCommandInput(text2))
37016
37098
  entries.push({
37017
37099
  role: "user",
37018
37100
  text: text2,
@@ -37316,6 +37398,7 @@ class ContextManager {
37316
37398
  for (const nodeId of chunk.coveredNodeIds)
37317
37399
  covered.add(nodeId);
37318
37400
  const activeJobs = new Set(activeBackgroundJobs(this.store.listToolCalls(session.id, 200)).map((job) => job.toolCallId));
37401
+ const windowTokens = resolveContextWindow(planner.contextWindow);
37319
37402
  for (const policy of this.lanes) {
37320
37403
  if (signal.aborted)
37321
37404
  return;
@@ -37336,7 +37419,7 @@ class ContextManager {
37336
37419
  } : {}
37337
37420
  });
37338
37421
  }
37339
- for (const plan of policy.planChunks(nodes)) {
37422
+ for (const plan of policy.planChunks(nodes, windowTokens)) {
37340
37423
  if (signal.aborted)
37341
37424
  return;
37342
37425
  const chunkNodes = nodes.filter((node) => plan.coveredNodeIds.includes(node.nodeId));
@@ -37499,27 +37582,31 @@ var init_context_manager2 = __esm(() => {
37499
37582
  init_history_projection();
37500
37583
  init_background();
37501
37584
  init_summary_runner();
37585
+ init_model_registry();
37502
37586
  });
37503
37587
 
37504
37588
  // src/agent-context/lanes.ts
37505
37589
  class SummarizeLanePolicy {
37506
37590
  constructor(options = {}) {
37507
37591
  this.lane = options.lane ?? "tool";
37508
- this.chunkSize = Math.max(2, options.chunkSize ?? 5);
37509
- this.triggerUncovered = Math.max(this.chunkSize, options.triggerUncovered ?? 30);
37592
+ this.chunkSize = Math.max(2, options.chunkSize ?? 8);
37510
37593
  this.protectedTail = Math.max(0, options.protectedTail ?? 10);
37594
+ this.triggerFraction = Math.max(0, options.triggerFraction ?? 0.15);
37595
+ this.triggerTokensFloor = Math.max(0, options.triggerTokensFloor ?? 25000);
37511
37596
  }
37512
37597
  classify(entry) {
37513
37598
  return entry.role === "tool";
37514
37599
  }
37515
- planChunks(nodes) {
37600
+ planChunks(nodes, windowTokens) {
37516
37601
  const protectedFrom = Math.max(0, nodes.length - this.protectedTail);
37517
37602
  const eligible = nodes.filter((node, index) => !node.covered && node.settled && index < protectedFrom);
37518
- if (eligible.length < this.triggerUncovered)
37603
+ if (eligible.length < this.chunkSize)
37519
37604
  return [];
37520
- const batch = eligible.slice(0, this.chunkSize);
37521
- if (batch.length < this.chunkSize)
37605
+ const eligibleTokens = eligible.reduce((total, node) => total + Math.ceil(node.bytes / 4), 0);
37606
+ const threshold = Math.max(this.triggerTokensFloor, Math.round(windowTokens * this.triggerFraction));
37607
+ if (eligibleTokens < threshold)
37522
37608
  return [];
37609
+ const batch = eligible.slice(0, this.chunkSize);
37523
37610
  return [{
37524
37611
  lane: this.lane,
37525
37612
  coveredNodeIds: batch.map((node) => node.nodeId),
@@ -39096,6 +39183,35 @@ Phase: ${session.phase}`,
39096
39183
  priority: 95,
39097
39184
  relevance: 1
39098
39185
  }));
39186
+ const pinnedNames = this.skillsEnabled && canLoadSkills ? loadConfig(workspace).pinnedSkills ?? [] : [];
39187
+ const pinnedSkills = pinnedNames.length ? renderPinnedSkills(workspace, pinnedNames) : undefined;
39188
+ if (pinnedSkills)
39189
+ candidates.push(candidate({
39190
+ id: "pinned-skills",
39191
+ class: "instructions",
39192
+ title: "Pinned Skills",
39193
+ source: "agent-skills registry (pinned)",
39194
+ content: pinnedSkills,
39195
+ mandatory: true,
39196
+ stable: true,
39197
+ priority: 98,
39198
+ relevance: 1
39199
+ }));
39200
+ const canDelegate = !session.toolScope?.length || session.toolScope.some((name) => canonicalToolName(name) === "agent_manage");
39201
+ const lanes = canDelegate ? loadLanes(workspace) : [];
39202
+ if (lanes.length)
39203
+ candidates.push(candidate({
39204
+ id: "subagent-lanes",
39205
+ class: "capabilities",
39206
+ title: "Available Subagent Lanes",
39207
+ source: "subagent role registry",
39208
+ content: ["Delegate whole units of work to subagents (agent_manage): a multi-step objective, specialist work, or something that can run in parallel \u2014 pick the matching lane below when one fits, otherwise a plain child. Do not spawn a child just to run one tool (run a single slow tool in the background instead). Keep this thread as the orchestrator that plans, dispatches non-overlapping work in parallel, and owns synthesis. Lanes carry a role prompt and tool scope.", "Available lanes:", ...lanes.map((lane) => `- ${lane.id}: ${lane.description ?? "custom lane"}`)].join(`
39209
+ `),
39210
+ mandatory: false,
39211
+ stable: true,
39212
+ priority: 94,
39213
+ relevance: 1
39214
+ }));
39099
39215
  if (isCodingTask(session, query)) {
39100
39216
  const outline = buildWorkspaceOutline(workspace, query, recentPaths, this.contextBuilder);
39101
39217
  if (outline)
@@ -39475,6 +39591,7 @@ var EPHEMERAL_CONTEXT_MAX_BYTES, WORKING_FILES_COUNT = 6, WORKING_FILE_MAX_BYTES
39475
39591
  var init_context_engine = __esm(() => {
39476
39592
  init_tool_names();
39477
39593
  init_registry2();
39594
+ init_config();
39478
39595
  init_background();
39479
39596
  init_summary_runner();
39480
39597
  init_provider();
@@ -39483,6 +39600,7 @@ var init_context_engine = __esm(() => {
39483
39600
  init_capability_admission();
39484
39601
  init_kali_command_catalog();
39485
39602
  init_kali();
39603
+ init_lanes();
39486
39604
  init_cvss31_guidance();
39487
39605
  EPHEMERAL_CONTEXT_MAX_BYTES = 12 * 1024;
39488
39606
  WORKING_FILE_MAX_BYTES = 12 * 1024;
@@ -39490,7 +39608,7 @@ var init_context_engine = __esm(() => {
39490
39608
  });
39491
39609
 
39492
39610
  // src/agent-core/context-report.ts
39493
- function buildContextReport(manifest, model, columns = 20, rows = 5) {
39611
+ function buildContextReport(manifest, model, columns = 20, cellTokens = CELL_TOKENS) {
39494
39612
  const window = Math.max(1, manifest.contextWindow);
39495
39613
  const used = Math.min(window, Math.max(0, Math.round(manifest.estimatedTokens)));
39496
39614
  const slices = [];
@@ -39505,7 +39623,7 @@ function buildContextReport(manifest, model, columns = 20, rows = 5) {
39505
39623
  });
39506
39624
  }
39507
39625
  const free = Math.max(0, window - used);
39508
- const totalCells = Math.max(1, columns * rows);
39626
+ const totalCells = Math.max(1, Math.min(MAX_CELLS, Math.round(window / Math.max(1, cellTokens))));
39509
39627
  const usedCells = Math.min(totalCells, Math.round(used / window * totalCells));
39510
39628
  const grid = [];
39511
39629
  for (const slice of slices) {
@@ -39528,7 +39646,7 @@ function buildContextReport(manifest, model, columns = 20, rows = 5) {
39528
39646
  columns
39529
39647
  };
39530
39648
  }
39531
- var CATEGORIES;
39649
+ var CATEGORIES, CELL_TOKENS = 3000, MAX_CELLS = 1000;
39532
39650
  var init_context_report = __esm(() => {
39533
39651
  CATEGORIES = [{
39534
39652
  key: "kernel",
@@ -46316,7 +46434,7 @@ This completion is already terminal and was delivered automatically. Do not call
46316
46434
  return text2;
46317
46435
  }
46318
46436
  if (command === "/context") {
46319
- const report = buildContextReport(this.inspectContext(session), session.model ?? "");
46437
+ const report = buildContextReport(await this.inspectContext(session), session.model ?? "");
46320
46438
  this.store.addPart({
46321
46439
  sessionId: session.id,
46322
46440
  turnId: turn.id,
@@ -46528,15 +46646,17 @@ This completion is already terminal and was delivered automatically. Do not call
46528
46646
  }
46529
46647
  return this.toolOperationDrain;
46530
46648
  }
46531
- inspectContext(session, hypotheticalInput) {
46649
+ async inspectContext(session, hypotheticalInput) {
46650
+ const current = this.store.loadSession(session.id);
46651
+ const planner = this.planner ?? await createPlannerForSessionAsync(current, this.workspace);
46532
46652
  return this.assembleContext({
46533
- session: this.store.loadSession(session.id),
46653
+ session: current,
46534
46654
  ...hypotheticalInput?.trim() ? {
46535
46655
  userText: hypotheticalInput.trim()
46536
46656
  } : {},
46537
- availableTools: listToolsForSession(session),
46538
- contextWindow: resolveContextWindow(this.planner?.contextWindow),
46539
- maxOutputTokens: resolveMaxOutputTokens(this.planner?.maxOutputTokens),
46657
+ availableTools: listToolsForSession(current),
46658
+ contextWindow: resolveContextWindow(planner.contextWindow),
46659
+ maxOutputTokens: resolveMaxOutputTokens(planner.maxOutputTokens),
46540
46660
  ...this.contextBudgetInput()
46541
46661
  }).manifest;
46542
46662
  }
@@ -50844,6 +50964,66 @@ var init_model_provider_management = __esm(() => {
50844
50964
  MODEL_PROBE_MAX_BYTES = 8 * 1024 * 1024;
50845
50965
  });
50846
50966
 
50967
+ // src/agent-core/subagents/lane-management.ts
50968
+ function listLanes(workspace) {
50969
+ const paths = laneConfigPaths(workspace);
50970
+ const writeTarget = laneWriteTarget(workspace);
50971
+ const globalIds = new Set(readLaneFile(writeTarget).map((lane) => lane.id));
50972
+ const overrideIds = new Set(paths.filter((path) => path !== writeTarget).flatMap((path) => readLaneFile(path).map((lane) => lane.id)));
50973
+ const builtinIds = new Set(BUILTIN_LANES.map((lane) => lane.id));
50974
+ return loadLanes(workspace).map((lane) => {
50975
+ const source = overrideIds.has(lane.id) ? "workspace" : globalIds.has(lane.id) ? "global" : builtinIds.has(lane.id) ? "builtin" : "global";
50976
+ return {
50977
+ ...lane,
50978
+ source,
50979
+ editable: source !== "workspace"
50980
+ };
50981
+ });
50982
+ }
50983
+ function saveLane(workspace, input) {
50984
+ const id2 = input.id.trim();
50985
+ if (!LANE_ID.test(id2))
50986
+ throw new Error("lane id must match ^[a-z0-9][a-z0-9_-]*$");
50987
+ const tools = input.tools ? [...new Set(input.tools.map((tool) => tool.trim()).filter(Boolean))] : undefined;
50988
+ if (input.tools && !tools?.length)
50989
+ throw new Error("tools must contain at least one non-empty tool name");
50990
+ const definition = {
50991
+ id: id2,
50992
+ ...input.description?.trim() ? {
50993
+ description: input.description.trim()
50994
+ } : {},
50995
+ ...input.prompt?.trim() ? {
50996
+ prompt: input.prompt.trim()
50997
+ } : {},
50998
+ ...tools?.length ? {
50999
+ tools
51000
+ } : {},
51001
+ ...input.model?.trim() ? {
51002
+ model: input.model.trim()
51003
+ } : {}
51004
+ };
51005
+ const target = laneWriteTarget(workspace);
51006
+ const current = readLaneFile(target).filter((lane) => lane.id !== id2);
51007
+ writeLaneFile(target, [...current, definition]);
51008
+ return {
51009
+ ...definition,
51010
+ source: "global",
51011
+ editable: true
51012
+ };
51013
+ }
51014
+ function removeLane(workspace, id2) {
51015
+ const target = laneWriteTarget(workspace);
51016
+ const current = readLaneFile(target);
51017
+ if (!current.some((lane) => lane.id === id2))
51018
+ throw new Error(`no custom lane to remove: ${id2}`);
51019
+ writeLaneFile(target, current.filter((lane) => lane.id !== id2));
51020
+ }
51021
+ var LANE_ID;
51022
+ var init_lane_management = __esm(() => {
51023
+ init_lanes();
51024
+ LANE_ID = /^[a-z0-9][a-z0-9_-]*$/;
51025
+ });
51026
+
50847
51027
  // src/agent-email/oauth-providers.ts
50848
51028
  function emailOAuthProvider(provider) {
50849
51029
  return EMAIL_OAUTH_PROVIDERS[provider];
@@ -51519,6 +51699,20 @@ function createRuntimePort(runtime, options = {}) {
51519
51699
  statuses: listMcpServerStatuses(activeSessionId)
51520
51700
  };
51521
51701
  },
51702
+ async listLanes() {
51703
+ return listLanes(runtime.workspace);
51704
+ },
51705
+ async listToolNames() {
51706
+ return [...new Set(listTools().map((tool) => tool.name))].sort();
51707
+ },
51708
+ async saveLane(input) {
51709
+ saveLane(runtime.workspace, input);
51710
+ return listLanes(runtime.workspace);
51711
+ },
51712
+ async removeLane(id2) {
51713
+ removeLane(runtime.workspace, id2);
51714
+ return listLanes(runtime.workspace);
51715
+ },
51522
51716
  async setMcpServerEnabled(serverID, enabled) {
51523
51717
  await setMcpServerEnabled(runtime.workspace, serverID, enabled);
51524
51718
  if (activeSessionId)
@@ -51590,7 +51784,7 @@ function createRuntimePort(runtime, options = {}) {
51590
51784
  return runtime.takeBackQueuedUserInput(sessionId);
51591
51785
  },
51592
51786
  async inspectContext(sessionId, hypotheticalInput) {
51593
- return runtime.inspectContext(runtime.loadSession(sessionId), hypotheticalInput);
51787
+ return await runtime.inspectContext(runtime.loadSession(sessionId), hypotheticalInput);
51594
51788
  },
51595
51789
  steer(sessionId, input) {
51596
51790
  return runtime.injectUserInput(sessionId, input);
@@ -51943,6 +52137,8 @@ var init_runtime_port = __esm(() => {
51943
52137
  init_model_registry();
51944
52138
  init_context_manager();
51945
52139
  init_mcp_server_management();
52140
+ init_lane_management();
52141
+ init_registry4();
51946
52142
  init_accounts();
51947
52143
  init_oauth_providers();
51948
52144
  init_oauth();
@@ -52076,6 +52272,59 @@ var init_model_provider_state = __esm(() => {
52076
52272
  PROTOCOL_ORDER = ["auto", "openai-chat", "anthropic-messages"];
52077
52273
  });
52078
52274
 
52275
+ // src/agent-tui/lane-state.ts
52276
+ function createLaneWizard(lane) {
52277
+ if (!lane) {
52278
+ return {
52279
+ mode: "add",
52280
+ field: "id",
52281
+ id: "",
52282
+ description: "",
52283
+ prompt: "",
52284
+ tools: [],
52285
+ toolFilter: "",
52286
+ toolCursor: 0,
52287
+ model: "",
52288
+ busy: false,
52289
+ error: undefined
52290
+ };
52291
+ }
52292
+ return {
52293
+ mode: "edit",
52294
+ field: "description",
52295
+ id: lane.id,
52296
+ originalID: lane.id,
52297
+ description: lane.description ?? "",
52298
+ prompt: lane.prompt ?? "",
52299
+ tools: [...lane.tools ?? []],
52300
+ toolFilter: "",
52301
+ toolCursor: 0,
52302
+ model: lane.model ?? "",
52303
+ busy: false,
52304
+ error: undefined
52305
+ };
52306
+ }
52307
+ function laneWizardFieldMove(field, delta) {
52308
+ const index = FIELD_ORDER2.indexOf(field);
52309
+ return FIELD_ORDER2[Math.max(0, Math.min(FIELD_ORDER2.length - 1, index + delta))] ?? field;
52310
+ }
52311
+ function laneWizardStep(field) {
52312
+ return FIELD_ORDER2.indexOf(field) + 1;
52313
+ }
52314
+ function laneWizardStepCount() {
52315
+ return FIELD_ORDER2.length;
52316
+ }
52317
+ function filteredToolNames(all, filter) {
52318
+ const needle = filter.trim().toLowerCase();
52319
+ if (!needle)
52320
+ return all;
52321
+ return all.filter((name) => name.toLowerCase().includes(needle));
52322
+ }
52323
+ var FIELD_ORDER2;
52324
+ var init_lane_state = __esm(() => {
52325
+ FIELD_ORDER2 = ["id", "description", "prompt", "tools", "model", "review"];
52326
+ });
52327
+
52079
52328
  // src/agent-tui/mcp-server-state.ts
52080
52329
  function createMcpServerWizard(server) {
52081
52330
  if (!server) {
@@ -52650,6 +52899,10 @@ function initialStore(workspace) {
52650
52899
  },
52651
52900
  sessionStats: {},
52652
52901
  agentThreads: [],
52902
+ lanes: [],
52903
+ toolNames: [],
52904
+ laneWizard: undefined,
52905
+ laneRemoval: undefined,
52653
52906
  lastError: undefined,
52654
52907
  requestUserInput: undefined,
52655
52908
  updateNotice: undefined
@@ -53408,6 +53661,56 @@ function createActions(store, setStore) {
53408
53661
  key: "id"
53409
53662
  }));
53410
53663
  },
53664
+ lanesSet(lanes) {
53665
+ setStore("ui", "lanes", reconcile(lanes, {
53666
+ key: "id"
53667
+ }));
53668
+ },
53669
+ toolNamesSet(names) {
53670
+ setStore("ui", "toolNames", names);
53671
+ },
53672
+ laneWizardOpen(lane) {
53673
+ setStore(produce((s) => {
53674
+ s.ui.lastError = undefined;
53675
+ s.ui.laneWizard = createLaneWizard(lane);
53676
+ }));
53677
+ },
53678
+ laneWizardPatch(patch) {
53679
+ setStore(produce((s) => {
53680
+ if (!s.ui.laneWizard)
53681
+ return;
53682
+ Object.assign(s.ui.laneWizard, patch);
53683
+ }));
53684
+ },
53685
+ laneWizardClose() {
53686
+ setStore(produce((s) => {
53687
+ s.ui.laneWizard = undefined;
53688
+ s.ui.lastError = undefined;
53689
+ }));
53690
+ },
53691
+ laneRemovalOpen(lane) {
53692
+ setStore(produce((s) => {
53693
+ s.ui.lastError = undefined;
53694
+ s.ui.laneRemoval = {
53695
+ lane,
53696
+ busy: false,
53697
+ error: undefined
53698
+ };
53699
+ }));
53700
+ },
53701
+ laneRemovalPatch(patch) {
53702
+ setStore(produce((s) => {
53703
+ if (!s.ui.laneRemoval)
53704
+ return;
53705
+ Object.assign(s.ui.laneRemoval, patch);
53706
+ }));
53707
+ },
53708
+ laneRemovalClose() {
53709
+ setStore(produce((s) => {
53710
+ s.ui.laneRemoval = undefined;
53711
+ s.ui.lastError = undefined;
53712
+ }));
53713
+ },
53411
53714
  messageNavigationRequested(direction) {
53412
53715
  setStore(produce((s) => {
53413
53716
  s.ui.messageNavigation.direction = direction;
@@ -53765,6 +54068,7 @@ function defaultOverlayFrame(kind) {
53765
54068
  case "evidence":
53766
54069
  case "findings":
53767
54070
  case "memory":
54071
+ case "subagents":
53768
54072
  case "agents":
53769
54073
  case "model":
53770
54074
  case "mcp":
@@ -53800,6 +54104,7 @@ function isSelectorOverlayKind(kind) {
53800
54104
  case "evidence":
53801
54105
  case "findings":
53802
54106
  case "memory":
54107
+ case "subagents":
53803
54108
  case "agents":
53804
54109
  case "model":
53805
54110
  case "mcp":
@@ -53825,6 +54130,7 @@ var init_store3 = __esm(() => {
53825
54130
  init_store2();
53826
54131
  init_runtime_port();
53827
54132
  init_model_provider_state();
54133
+ init_lane_state();
53828
54134
  init_mcp_server_state();
53829
54135
  init_email_account_state();
53830
54136
  PROXY_VIEW_FILTERS = ["all", "http", "websocket"];
@@ -55009,6 +55315,8 @@ var init_tool_presentation = __esm(() => {
55009
55315
  write: "writing"
55010
55316
  };
55011
55317
  TOOL_ACTIONS = {
55318
+ command_run: ["ran", "executing"],
55319
+ command_input: ["sent input", "sending input"],
55012
55320
  agent_manage: ["managed agents", "managing agents"],
55013
55321
  browser_manage: ["managed browser", "managing browser"],
55014
55322
  callback_manage: ["managed callbacks", "managing callbacks"],
@@ -55464,6 +55772,15 @@ var init_tool_lifecycle = __esm(() => {
55464
55772
  detail: "summary",
55465
55773
  showOutcome: false,
55466
55774
  groupItem: (args) => stringValue2(args.canonical)
55775
+ },
55776
+ command_poll: {
55777
+ groupKey: "background:poll",
55778
+ groupPast: "checked background work",
55779
+ groupActive: "checking background work",
55780
+ groupNoun: "check",
55781
+ groupMaxItems: 20,
55782
+ detail: "summary",
55783
+ showOutcome: false
55467
55784
  }
55468
55785
  };
55469
55786
  BROWSER_TOOLS = new Set(["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"]);
@@ -56726,9 +57043,7 @@ function activityLabel(rows, active) {
56726
57043
  const verb = active ? presentation.groupActive ?? "running" : presentation.groupPast ?? "ran";
56727
57044
  const [action = verb, qualifier] = verb.split(" \xB7 ", 2);
56728
57045
  if (presentation.groupNoun) {
56729
- const names = rows.flatMap((row) => row.presentation.groupItem ? [row.presentation.groupItem] : []).filter((name, index, values) => values.indexOf(name) === index);
56730
- const listed = names.slice(0, 5).join(", ");
56731
- return `${action} ${listed || `${count2} ${presentation.groupNoun}${count2 === 1 ? "" : "s"}`}${names.length > 5 ? `, +${names.length - 5}` : ""}`;
57046
+ return `${action} \xB7 ${count2}${qualifier ? ` \xB7 ${qualifier}` : ""}`;
56732
57047
  }
56733
57048
  return `${action} ${count2} ${presentation.noun}${count2 === 1 ? "" : "s"}${qualifier ? ` \xB7 ${qualifier}` : ""}`;
56734
57049
  }
@@ -57789,6 +58104,19 @@ function createStoreResourceController(input) {
57789
58104
  agentThreadRefreshes.clear();
57790
58105
  containerToggles.clear();
57791
58106
  }
58107
+ async function openSubagentsOverlay() {
58108
+ actions.overlayOpen("subagents");
58109
+ try {
58110
+ const [lanes, toolNames] = await Promise.all([port.listLanes(), port.listToolNames()]);
58111
+ if (store.ui.overlayStack.at(-1)?.kind === "subagents") {
58112
+ actions.lanesSet(lanes);
58113
+ actions.toolNamesSet(toolNames);
58114
+ }
58115
+ } catch (error) {
58116
+ if (store.ui.overlayStack.at(-1)?.kind === "subagents")
58117
+ actions.errorSet(error instanceof Error ? error.message : String(error));
58118
+ }
58119
+ }
57792
58120
  return {
57793
58121
  refreshSessionMcp,
57794
58122
  refreshContainerStatus,
@@ -57801,6 +58129,7 @@ function createStoreResourceController(input) {
57801
58129
  openAgentsOverlay,
57802
58130
  openMcpOverlay,
57803
58131
  openEmailOverlay,
58132
+ openSubagentsOverlay,
57804
58133
  toggleContainer,
57805
58134
  dispose: dispose2
57806
58135
  };
@@ -58289,6 +58618,7 @@ function TuiStoreProvider(props) {
58289
58618
  refreshAgentThreads: resources.refreshAgentThreads,
58290
58619
  openAgentsOverlay: resources.openAgentsOverlay,
58291
58620
  openMcpOverlay: resources.openMcpOverlay,
58621
+ openSubagentsOverlay: resources.openSubagentsOverlay,
58292
58622
  openEmailOverlay: resources.openEmailOverlay,
58293
58623
  toggleContainer: resources.toggleContainer,
58294
58624
  setStatusDetail: status.set
@@ -58653,7 +58983,7 @@ function defineDefaultCommands(openers = {}) {
58653
58983
  }) => {
58654
58984
  tui.toggleContainer();
58655
58985
  }
58656
- }, messageNavigation("next", undefined), messageNavigation("prev", undefined), slashPrompt("context", "inspect context", "show stored versus projected context and token budget"), slashLocal("model", "switch model", "choose a model or add a provider", () => openers.model?.(), "visible", ["models"]), slashLocal("mcp", "mcp servers", "show configured mcp server status", () => openers.mcp?.()), slashLocal("email", "email inboxes", "choose primary or secondary email and add imap accounts", () => openers.email?.()), slashPrompt("campaign", "campaign control", "start, pause, resume, inspect, or stop a durable campaign"), slashLocal("exit", "exit", "exit farai", ({
58986
+ }, messageNavigation("next", undefined), messageNavigation("prev", undefined), slashPrompt("context", "inspect context", "show stored versus projected context and token budget"), slashLocal("model", "switch model", "choose a model or add a provider", () => openers.model?.(), "visible", ["models"]), slashLocal("mcp", "mcp servers", "show configured mcp server status", () => openers.mcp?.()), slashLocal("subagents", "subagent lanes", "manage subagent roles (lanes): add, edit, or delete", () => openers.subagents?.(), "visible", ["lanes", "roles"]), slashLocal("email", "email inboxes", "choose primary or secondary email and add imap accounts", () => openers.email?.()), slashPrompt("campaign", "campaign control", "start, pause, resume, inspect, or stop a durable campaign"), slashLocal("exit", "exit", "exit farai", ({
58657
58987
  exit
58658
58988
  }) => {
58659
58989
  exit();
@@ -58897,6 +59227,10 @@ function routeKey(key, ctx) {
58897
59227
  return routeModelProviderRemoval(key, ctx.modelProviderRemoval);
58898
59228
  if (ctx.modelProviderWizard)
58899
59229
  return routeModelProviderWizard(key, ctx.modelProviderWizard);
59230
+ if (ctx.laneRemoval)
59231
+ return routeLaneRemoval(key, ctx.laneRemoval);
59232
+ if (ctx.laneWizard)
59233
+ return routeLaneWizard(key, ctx.laneWizard);
58900
59234
  if (ctx.pendingUserInput && key.ctrl && key.name === "q")
58901
59235
  return consumed({
58902
59236
  kind: "requestUserInput.show"
@@ -58904,7 +59238,7 @@ function routeKey(key, ctx) {
58904
59238
  if (ctx.requestUserInput)
58905
59239
  return routeRequestUserInput(key, ctx.requestUserInput);
58906
59240
  if (ctx.overlayKind)
58907
- return routeOverlay(key, ctx.overlayKind, ctx.modelOverlay, ctx.mcpOverlay, ctx.emailOverlay);
59241
+ return routeOverlay(key, ctx.overlayKind, ctx.modelOverlay, ctx.mcpOverlay, ctx.emailOverlay, ctx.subagentsOverlay);
58908
59242
  if (ctx.centerSurfaceKind)
58909
59243
  return routeCenterSurface(key, ctx.centerSurfaceKind, ctx.centerProxyFlowKind, ctx.centerSurfaceBusy);
58910
59244
  if (ctx.historySearchActive)
@@ -59289,6 +59623,80 @@ function routeModelProviderWizard(key, state) {
59289
59623
  });
59290
59624
  return PASSTHROUGH;
59291
59625
  }
59626
+ function routeLaneRemoval(key, state) {
59627
+ if (state.busy)
59628
+ return consumed();
59629
+ if (key.name === "return")
59630
+ return consumed({
59631
+ kind: "laneRemoval.confirm"
59632
+ });
59633
+ if (key.name === "escape" || key.ctrl && key.name === "c")
59634
+ return consumed({
59635
+ kind: "laneRemoval.cancel"
59636
+ });
59637
+ return consumed();
59638
+ }
59639
+ function routeLaneWizard(key, state) {
59640
+ if (state.busy)
59641
+ return consumed();
59642
+ if (key.name === "escape")
59643
+ return consumed({
59644
+ kind: "lane.back"
59645
+ });
59646
+ if (state.field === "tools") {
59647
+ if (key.ctrl && key.name === "a")
59648
+ return consumed({
59649
+ kind: "lane.toolSelectAll"
59650
+ });
59651
+ if (key.name === "up")
59652
+ return consumed({
59653
+ kind: "lane.toolMove",
59654
+ delta: -1
59655
+ });
59656
+ if (key.name === "down")
59657
+ return consumed({
59658
+ kind: "lane.toolMove",
59659
+ delta: 1
59660
+ });
59661
+ if (key.name === "space" || key.char === " ")
59662
+ return consumed({
59663
+ kind: "lane.toolToggle"
59664
+ });
59665
+ if (key.name === "return")
59666
+ return consumed({
59667
+ kind: "lane.next"
59668
+ });
59669
+ if (key.name === "backspace")
59670
+ return consumed({
59671
+ kind: "lane.toolFilterBackspace"
59672
+ });
59673
+ if (key.char && !key.ctrl && !key.meta)
59674
+ return consumed({
59675
+ kind: "lane.toolFilterAppend",
59676
+ char: key.char
59677
+ });
59678
+ return consumed();
59679
+ }
59680
+ if (state.field === "prompt") {
59681
+ if (key.ctrl && key.name === "s")
59682
+ return consumed({
59683
+ kind: "lane.next"
59684
+ });
59685
+ return PASSTHROUGH;
59686
+ }
59687
+ if (state.field === "review") {
59688
+ if (key.name === "return")
59689
+ return consumed({
59690
+ kind: "lane.next"
59691
+ });
59692
+ return consumed();
59693
+ }
59694
+ if (key.name === "return")
59695
+ return consumed({
59696
+ kind: "lane.next"
59697
+ });
59698
+ return PASSTHROUGH;
59699
+ }
59292
59700
  function routeRequestUserInput(key, state) {
59293
59701
  if (state.submitting)
59294
59702
  return consumed();
@@ -59372,7 +59780,7 @@ function routeRequestUserInput(key, state) {
59372
59780
  }
59373
59781
  }
59374
59782
  }
59375
- function routeOverlay(key, kind, model, mcp, email) {
59783
+ function routeOverlay(key, kind, model, mcp, email, subagents) {
59376
59784
  if (key.ctrl && key.name === "c")
59377
59785
  return consumed({
59378
59786
  kind: "overlay.pop"
@@ -59448,6 +59856,20 @@ function routeOverlay(key, kind, model, mcp, email) {
59448
59856
  kind: "mcp.removeServer"
59449
59857
  });
59450
59858
  }
59859
+ if (kind === "subagents" && key.ctrl) {
59860
+ if (key.name === "a")
59861
+ return consumed({
59862
+ kind: "subagents.add"
59863
+ });
59864
+ if (key.name === "e" && subagents?.editable)
59865
+ return consumed({
59866
+ kind: "subagents.edit"
59867
+ });
59868
+ if (key.name === "d" && subagents?.removable)
59869
+ return consumed({
59870
+ kind: "subagents.remove"
59871
+ });
59872
+ }
59451
59873
  if (kind === "agents" && (key.name === "space" || key.char === " ")) {
59452
59874
  return consumed({
59453
59875
  kind: "overlay.agentPreview"
@@ -59918,7 +60340,7 @@ function routeHistorySearch(key) {
59918
60340
  var LIST_OVERLAYS, NAME_ALIASES, PASSTHROUGH;
59919
60341
  var init_router = __esm(() => {
59920
60342
  init_slash_autocomplete();
59921
- LIST_OVERLAYS = new Set(["palette", "sessions", "evidence", "findings", "memory", "agents", "model", "mcp", "email"]);
60343
+ LIST_OVERLAYS = new Set(["palette", "sessions", "evidence", "findings", "memory", "agents", "model", "mcp", "email", "subagents"]);
59922
60344
  NAME_ALIASES = {
59923
60345
  enter: "return",
59924
60346
  esc: "escape",
@@ -60006,6 +60428,17 @@ function buildRouterContext(input) {
60006
60428
  busy: tui.store.ui.emailAccountRemoval.busy
60007
60429
  }
60008
60430
  } : {},
60431
+ ...tui.store.ui.laneWizard ? {
60432
+ laneWizard: {
60433
+ field: tui.store.ui.laneWizard.field,
60434
+ busy: tui.store.ui.laneWizard.busy
60435
+ }
60436
+ } : {},
60437
+ ...tui.store.ui.laneRemoval ? {
60438
+ laneRemoval: {
60439
+ busy: tui.store.ui.laneRemoval.busy
60440
+ }
60441
+ } : {},
60009
60442
  ...input.modelOverlay ? {
60010
60443
  modelOverlay: input.modelOverlay
60011
60444
  } : {},
@@ -60014,6 +60447,9 @@ function buildRouterContext(input) {
60014
60447
  } : {},
60015
60448
  ...input.emailOverlay ? {
60016
60449
  emailOverlay: input.emailOverlay
60450
+ } : {},
60451
+ ...input.subagentsOverlay ? {
60452
+ subagentsOverlay: input.subagentsOverlay
60017
60453
  } : {}
60018
60454
  };
60019
60455
  }
@@ -60083,6 +60519,30 @@ function overlayOptions(frame, tui, ctx) {
60083
60519
  footer: new Date(item.updatedAt).toLocaleString(),
60084
60520
  value: item.id
60085
60521
  }));
60522
+ case "subagents": {
60523
+ const laneRows = tui.store.ui.lanes.map((lane) => ({
60524
+ id: lane.id,
60525
+ title: lane.id,
60526
+ description: `${lane.description ?? "custom lane"}${lane.tools?.length ? ` \xB7 ${lane.tools.length} tools` : ""}${lane.model ? ` \xB7 ${lane.model}` : ""}`,
60527
+ badge: lane.source === "builtin" ? "builtin" : lane.source === "workspace" ? "project" : "custom",
60528
+ value: {
60529
+ kind: "lane",
60530
+ laneId: lane.id,
60531
+ editable: lane.editable
60532
+ }
60533
+ }));
60534
+ return [...laneRows, {
60535
+ id: "lane:add",
60536
+ title: "+ add lane",
60537
+ description: "create a new subagent role",
60538
+ numbered: false,
60539
+ separatorBefore: true,
60540
+ value: {
60541
+ kind: "lane_action",
60542
+ action: "add"
60543
+ }
60544
+ }];
60545
+ }
60086
60546
  case "model": {
60087
60547
  const session = tui.store.snapshot.session;
60088
60548
  if (tui.store.ui.availableModels.length === 0 && tui.store.ui.statusDetail === "loading models") {
@@ -60366,6 +60826,8 @@ function overlayTitle(frame) {
60366
60826
  return "mcp servers";
60367
60827
  case "email":
60368
60828
  return "email";
60829
+ case "subagents":
60830
+ return "subagent lanes";
60369
60831
  }
60370
60832
  }
60371
60833
  var init_overlay_options = __esm(() => {
@@ -60418,6 +60880,15 @@ function createOverlaySelection(tui, commandContext) {
60418
60880
  return;
60419
60881
  return tui.store.ui.emailAccounts.find((account) => account.id === choice.emailId);
60420
60882
  }
60883
+ function lane() {
60884
+ const frame = tui.store.ui.overlayStack.at(-1);
60885
+ if (frame?.kind !== "subagents")
60886
+ return;
60887
+ const choice = selectedOption()?.value;
60888
+ if (choice?.kind !== "lane")
60889
+ return;
60890
+ return tui.store.ui.lanes.find((item) => item.id === choice.laneId);
60891
+ }
60421
60892
  function selectedEmail() {
60422
60893
  const frame = tui.store.ui.overlayStack.at(-1);
60423
60894
  if (frame?.kind !== "email")
@@ -60451,6 +60922,18 @@ function createOverlaySelection(tui, commandContext) {
60451
60922
  removable: false
60452
60923
  };
60453
60924
  },
60925
+ lane,
60926
+ laneContext: () => {
60927
+ const item = lane();
60928
+ return item ? {
60929
+ laneId: item.id,
60930
+ editable: item.editable,
60931
+ removable: item.source === "global"
60932
+ } : {
60933
+ editable: false,
60934
+ removable: false
60935
+ };
60936
+ },
60454
60937
  emailAccount,
60455
60938
  selectedEmail,
60456
60939
  emailContext: () => {
@@ -61600,6 +62083,260 @@ var init_email_account_controller = __esm(() => {
61600
62083
  init_accounts();
61601
62084
  });
61602
62085
 
62086
+ // src/agent-tui/input/lane-controller.ts
62087
+ function createLaneController(input) {
62088
+ const {
62089
+ tui,
62090
+ port,
62091
+ selection
62092
+ } = input;
62093
+ const operations = createControllerOperations(tui);
62094
+ function overlayOpen() {
62095
+ return tui.store.ui.overlayStack.at(-1)?.kind === "subagents";
62096
+ }
62097
+ function reopen() {
62098
+ tui.actions.overlayClear();
62099
+ tui.actions.overlayPush({
62100
+ kind: "subagents",
62101
+ query: "",
62102
+ index: 0
62103
+ });
62104
+ }
62105
+ function openAdd() {
62106
+ operations.invalidate();
62107
+ tui.actions.laneWizardOpen();
62108
+ }
62109
+ function openEdit() {
62110
+ const lane = selection.lane();
62111
+ if (!lane?.editable)
62112
+ return;
62113
+ operations.invalidate();
62114
+ tui.actions.laneWizardOpen(lane);
62115
+ }
62116
+ function requestRemoval() {
62117
+ const lane = selection.lane();
62118
+ if (lane && lane.source === "global")
62119
+ tui.actions.laneRemovalOpen(lane);
62120
+ }
62121
+ async function confirmRemoval() {
62122
+ const removalState = tui.store.ui.laneRemoval;
62123
+ if (!removalState || removalState.busy)
62124
+ return;
62125
+ const operation = operations.begin();
62126
+ tui.actions.laneRemovalPatch({
62127
+ busy: true,
62128
+ error: undefined
62129
+ });
62130
+ let lanes;
62131
+ try {
62132
+ lanes = await port.removeLane(removalState.lane.id);
62133
+ } catch (error) {
62134
+ if (!operations.owns(operation) || input.isDisposed())
62135
+ return;
62136
+ if (tui.store.ui.laneRemoval?.lane.id !== removalState.lane.id)
62137
+ return;
62138
+ tui.actions.laneRemovalPatch({
62139
+ busy: false,
62140
+ error: error instanceof Error ? error.message : String(error)
62141
+ });
62142
+ return;
62143
+ }
62144
+ if (!operations.owns(operation) || input.isDisposed() || tui.store.ui.laneRemoval?.lane.id !== removalState.lane.id)
62145
+ return;
62146
+ tui.actions.laneRemovalClose();
62147
+ tui.actions.lanesSet(lanes);
62148
+ reopen();
62149
+ tui.setStatusDetail(`removed lane ${removalState.lane.id}`, 3000);
62150
+ }
62151
+ function cancelRemoval() {
62152
+ operations.invalidate();
62153
+ tui.actions.laneRemovalClose();
62154
+ }
62155
+ async function next() {
62156
+ const wizard = tui.store.ui.laneWizard;
62157
+ if (!wizard || wizard.busy)
62158
+ return;
62159
+ tui.actions.laneWizardPatch({
62160
+ error: undefined
62161
+ });
62162
+ if (wizard.field === "id") {
62163
+ const normalized = wizard.id.trim().toLowerCase();
62164
+ if (!LANE_ID2.test(normalized)) {
62165
+ tui.actions.laneWizardPatch({
62166
+ error: "lane id must match a-z 0-9 _ - and start alphanumeric"
62167
+ });
62168
+ return;
62169
+ }
62170
+ if (wizard.mode === "add" && tui.store.ui.lanes.some((lane) => lane.id === normalized && lane.source !== "builtin")) {
62171
+ tui.actions.laneWizardPatch({
62172
+ error: `${normalized} already exists \xB7 select it and press ctrl+e to edit`
62173
+ });
62174
+ return;
62175
+ }
62176
+ tui.actions.laneWizardPatch({
62177
+ id: normalized,
62178
+ field: "description"
62179
+ });
62180
+ return;
62181
+ }
62182
+ if (wizard.field === "description") {
62183
+ tui.actions.laneWizardPatch({
62184
+ field: "prompt"
62185
+ });
62186
+ return;
62187
+ }
62188
+ if (wizard.field === "prompt") {
62189
+ tui.actions.laneWizardPatch({
62190
+ field: "tools",
62191
+ toolFilter: "",
62192
+ toolCursor: 0
62193
+ });
62194
+ return;
62195
+ }
62196
+ if (wizard.field === "tools") {
62197
+ tui.actions.laneWizardPatch({
62198
+ field: "model"
62199
+ });
62200
+ return;
62201
+ }
62202
+ if (wizard.field === "model") {
62203
+ tui.actions.laneWizardPatch({
62204
+ field: "review"
62205
+ });
62206
+ return;
62207
+ }
62208
+ const operation = operations.begin();
62209
+ tui.actions.laneWizardPatch({
62210
+ busy: true,
62211
+ error: undefined
62212
+ });
62213
+ try {
62214
+ const lanes = await port.saveLane({
62215
+ id: wizard.id,
62216
+ ...wizard.description.trim() ? {
62217
+ description: wizard.description.trim()
62218
+ } : {},
62219
+ ...wizard.prompt.trim() ? {
62220
+ prompt: wizard.prompt.trim()
62221
+ } : {},
62222
+ ...wizard.tools.length ? {
62223
+ tools: wizard.tools
62224
+ } : {},
62225
+ ...wizard.model.trim() ? {
62226
+ model: wizard.model.trim()
62227
+ } : {}
62228
+ });
62229
+ if (!operations.owns(operation) || input.isDisposed() || !tui.store.ui.laneWizard)
62230
+ return;
62231
+ tui.actions.lanesSet(lanes);
62232
+ tui.actions.laneWizardClose();
62233
+ if (overlayOpen())
62234
+ reopen();
62235
+ tui.setStatusDetail(`lane ${wizard.id} saved`, 3000);
62236
+ } catch (error) {
62237
+ if (!operations.owns(operation) || input.isDisposed() || !tui.store.ui.laneWizard)
62238
+ return;
62239
+ tui.actions.laneWizardPatch({
62240
+ busy: false,
62241
+ error: error instanceof Error ? error.message : String(error)
62242
+ });
62243
+ }
62244
+ }
62245
+ function visibleTools() {
62246
+ const wizard = tui.store.ui.laneWizard;
62247
+ return wizard ? filteredToolNames(tui.store.ui.toolNames, wizard.toolFilter) : [];
62248
+ }
62249
+ function moveTool(delta) {
62250
+ const wizard = tui.store.ui.laneWizard;
62251
+ if (!wizard)
62252
+ return;
62253
+ const count2 = visibleTools().length;
62254
+ if (count2 === 0)
62255
+ return;
62256
+ tui.actions.laneWizardPatch({
62257
+ toolCursor: Math.max(0, Math.min(count2 - 1, wizard.toolCursor + delta))
62258
+ });
62259
+ }
62260
+ function toggleTool() {
62261
+ const wizard = tui.store.ui.laneWizard;
62262
+ if (!wizard)
62263
+ return;
62264
+ const tools = visibleTools();
62265
+ const name = tools[Math.max(0, Math.min(tools.length - 1, wizard.toolCursor))];
62266
+ if (!name)
62267
+ return;
62268
+ const next2 = wizard.tools.includes(name) ? wizard.tools.filter((tool) => tool !== name) : [...wizard.tools, name];
62269
+ tui.actions.laneWizardPatch({
62270
+ tools: next2
62271
+ });
62272
+ }
62273
+ function selectAllTools() {
62274
+ const wizard = tui.store.ui.laneWizard;
62275
+ if (!wizard)
62276
+ return;
62277
+ const visible = visibleTools();
62278
+ if (visible.length === 0)
62279
+ return;
62280
+ const allSelected = visible.every((name) => wizard.tools.includes(name));
62281
+ const next2 = allSelected ? wizard.tools.filter((name) => !visible.includes(name)) : [...new Set([...wizard.tools, ...visible])];
62282
+ tui.actions.laneWizardPatch({
62283
+ tools: next2
62284
+ });
62285
+ }
62286
+ function filterAppend(char) {
62287
+ const wizard = tui.store.ui.laneWizard;
62288
+ if (!wizard)
62289
+ return;
62290
+ tui.actions.laneWizardPatch({
62291
+ toolFilter: `${wizard.toolFilter}${char}`,
62292
+ toolCursor: 0
62293
+ });
62294
+ }
62295
+ function filterBackspace() {
62296
+ const wizard = tui.store.ui.laneWizard;
62297
+ if (!wizard?.toolFilter)
62298
+ return;
62299
+ tui.actions.laneWizardPatch({
62300
+ toolFilter: [...wizard.toolFilter].slice(0, -1).join(""),
62301
+ toolCursor: 0
62302
+ });
62303
+ }
62304
+ function back() {
62305
+ const wizard = tui.store.ui.laneWizard;
62306
+ if (!wizard || wizard.busy)
62307
+ return;
62308
+ if (wizard.mode === "add" && wizard.field === "id" || wizard.mode === "edit" && wizard.field === "description") {
62309
+ operations.invalidate();
62310
+ tui.actions.laneWizardClose();
62311
+ return;
62312
+ }
62313
+ tui.actions.laneWizardPatch({
62314
+ field: laneWizardFieldMove(wizard.field, -1),
62315
+ error: undefined
62316
+ });
62317
+ }
62318
+ return {
62319
+ dispose: () => operations.invalidate(),
62320
+ openAdd,
62321
+ openEdit,
62322
+ requestRemoval,
62323
+ confirmRemoval,
62324
+ cancelRemoval,
62325
+ next,
62326
+ back,
62327
+ moveTool,
62328
+ toggleTool,
62329
+ selectAllTools,
62330
+ filterAppend,
62331
+ filterBackspace
62332
+ };
62333
+ }
62334
+ var LANE_ID2;
62335
+ var init_lane_controller = __esm(() => {
62336
+ init_lane_state();
62337
+ LANE_ID2 = /^[a-z0-9][a-z0-9_-]*$/;
62338
+ });
62339
+
61603
62340
  // src/agent-tui/input/request-user-input-controller.ts
61604
62341
  function createRequestUserInputController(tui, focusComposer) {
61605
62342
  function current() {
@@ -61893,6 +62630,14 @@ function createOverlayController(input) {
61893
62630
  input.openEmailAccount?.();
61894
62631
  return;
61895
62632
  }
62633
+ case "subagents": {
62634
+ const choice = option.value;
62635
+ if (choice.kind === "lane_action")
62636
+ input.openLaneAdd?.();
62637
+ else if (choice.editable)
62638
+ input.openLaneEdit?.();
62639
+ return;
62640
+ }
61896
62641
  case "model": {
61897
62642
  const choice = option.value;
61898
62643
  if (choice.kind === "model_action") {
@@ -62862,6 +63607,12 @@ function KeyboardController() {
62862
63607
  selection: overlaySelection,
62863
63608
  isDisposed: () => disposed
62864
63609
  });
63610
+ const lane = createLaneController({
63611
+ tui,
63612
+ port,
63613
+ selection: overlaySelection,
63614
+ isDisposed: () => disposed
63615
+ });
62865
63616
  const requestUserInput = createRequestUserInputController(tui, () => composer.focus());
62866
63617
  const centerSurface = createCenterSurfaceController({
62867
63618
  tui,
@@ -62904,7 +63655,9 @@ function KeyboardController() {
62904
63655
  owns: ownsSession,
62905
63656
  openModelProvider: modelProvider.openAdd,
62906
63657
  openMcpServer: mcpServer.openAdd,
62907
- openEmailAccount: emailAccount.openAdd
63658
+ openEmailAccount: emailAccount.openAdd,
63659
+ openLaneAdd: lane.openAdd,
63660
+ openLaneEdit: lane.openEdit
62908
63661
  });
62909
63662
  let modelOverlayKey;
62910
63663
  let mcpOverlayKey;
@@ -62965,6 +63718,9 @@ function KeyboardController() {
62965
63718
  } : {},
62966
63719
  ...top?.kind === "email" ? {
62967
63720
  emailOverlay: overlaySelection.emailContext()
63721
+ } : {},
63722
+ ...top?.kind === "subagents" ? {
63723
+ subagentsOverlay: overlaySelection.laneContext()
62968
63724
  } : {}
62969
63725
  }));
62970
63726
  if (routed.type === "passthrough")
@@ -62993,6 +63749,7 @@ function KeyboardController() {
62993
63749
  modelProvider.dispose();
62994
63750
  mcpServer.dispose();
62995
63751
  emailAccount.dispose();
63752
+ lane.dispose();
62996
63753
  });
62997
63754
  function dispatchAction(action) {
62998
63755
  applyAction(action).catch((error) => {
@@ -63161,6 +63918,42 @@ function KeyboardController() {
63161
63918
  case "mcp.removeServer":
63162
63919
  mcpServer.requestRemoval();
63163
63920
  return;
63921
+ case "lane.next":
63922
+ await lane.next();
63923
+ return;
63924
+ case "lane.back":
63925
+ lane.back();
63926
+ return;
63927
+ case "lane.toolMove":
63928
+ lane.moveTool(action.delta);
63929
+ return;
63930
+ case "lane.toolToggle":
63931
+ lane.toggleTool();
63932
+ return;
63933
+ case "lane.toolSelectAll":
63934
+ lane.selectAllTools();
63935
+ return;
63936
+ case "lane.toolFilterAppend":
63937
+ lane.filterAppend(action.char);
63938
+ return;
63939
+ case "lane.toolFilterBackspace":
63940
+ lane.filterBackspace();
63941
+ return;
63942
+ case "laneRemoval.confirm":
63943
+ await lane.confirmRemoval();
63944
+ return;
63945
+ case "laneRemoval.cancel":
63946
+ lane.cancelRemoval();
63947
+ return;
63948
+ case "subagents.add":
63949
+ lane.openAdd();
63950
+ return;
63951
+ case "subagents.edit":
63952
+ lane.openEdit();
63953
+ return;
63954
+ case "subagents.remove":
63955
+ lane.requestRemoval();
63956
+ return;
63164
63957
  case "composer.submit":
63165
63958
  await composerActions.submit();
63166
63959
  return;
@@ -63343,6 +64136,7 @@ var init_keyboard_controller = __esm(() => {
63343
64136
  init_model_provider_controller();
63344
64137
  init_mcp_server_controller();
63345
64138
  init_email_account_controller();
64139
+ init_lane_controller();
63346
64140
  init_request_user_input_controller();
63347
64141
  init_center_surface_controller();
63348
64142
  init_overlay_controller();
@@ -63353,6 +64147,28 @@ var init_keyboard_controller = __esm(() => {
63353
64147
  });
63354
64148
 
63355
64149
  // src/agent-tui/theme.ts
64150
+ function toolFamilyColor(family) {
64151
+ switch (family) {
64152
+ case "workspace":
64153
+ return COLOR.success;
64154
+ case "recon":
64155
+ case "http":
64156
+ return COLOR.info;
64157
+ case "browser":
64158
+ return COLOR.accent;
64159
+ case "knowledge":
64160
+ return COLOR.agent;
64161
+ case "proxy":
64162
+ return COLOR.memory.endpoint;
64163
+ case "campaign":
64164
+ case "agent":
64165
+ return COLOR.memory.hypothesis;
64166
+ case "command":
64167
+ return COLOR.warning;
64168
+ default:
64169
+ return COLOR.text;
64170
+ }
64171
+ }
63356
64172
  var COLOR;
63357
64173
  var init_theme = __esm(() => {
63358
64174
  init_tool_presentation();
@@ -63361,6 +64177,8 @@ var init_theme = __esm(() => {
63361
64177
  muted: "#a8a8a8",
63362
64178
  text: "#d7d7d7",
63363
64179
  accent: "#5fd7ff",
64180
+ agent: "#b39ddb",
64181
+ info: "#7cb7ff",
63364
64182
  warning: "#d7af5f",
63365
64183
  error: "#ff5f5f",
63366
64184
  success: "#87d75f",
@@ -64789,7 +65607,7 @@ function AssistantMessage(props) {
64789
65607
  });
64790
65608
  insert(_el$2, createComponent2(TranscriptMarker, {
64791
65609
  get color() {
64792
- return COLOR.dim;
65610
+ return COLOR.agent;
64793
65611
  }
64794
65612
  }), _el$3);
64795
65613
  setProp(_el$3, "style", {
@@ -65084,7 +65902,7 @@ function StandardToolRow(props) {
65084
65902
  const right = [presentation().showOutcome !== false ? presentation().outcome : undefined, formatActivityDuration(props.row.durationMs)].filter(Boolean).join(" \xB7 ");
65085
65903
  return fitTerminalPair(title, right, Math.max(1, dims().width - 4), 8, 2);
65086
65904
  };
65087
- const headerColor = () => active() ? COLOR.accent : toolColor(props.row.status);
65905
+ const headerColor = () => active() ? COLOR.accent : toolColor(props.row.status, props.row.tool);
65088
65906
  const toggleClick = createPrimaryClickGesture(() => tui.actions.cellExpandedToggle(props.row.id));
65089
65907
  const previewClick = createPrimaryClickGesture(() => tui.actions.cellExpandedToggle(props.row.id));
65090
65908
  const semanticPreview = () => presentation().preview.filter((line) => line.trim() !== presentation().outcome?.trim());
@@ -65328,7 +66146,8 @@ function ActivityRow(props) {
65328
66146
  },
65329
66147
  children: (item, index) => {
65330
66148
  const presentation = () => item.presentation ?? presentToolActivity(item);
65331
- const pair = () => fitTerminalPair(presentation().compact, presentation().showOutcome !== false ? presentation().outcome ?? "" : "", width(), 8, 3);
66149
+ const compact = () => presentation().groupItem ?? presentation().compact;
66150
+ const pair = () => fitTerminalPair(compact(), presentation().showOutcome !== false ? presentation().outcome ?? "" : "", width(), 8, 3);
65332
66151
  return (() => {
65333
66152
  var _el$20 = createElement("box"), _el$21 = createElement("text"), _el$22 = createElement("text");
65334
66153
  insertNode(_el$20, _el$21);
@@ -66136,11 +66955,11 @@ function previewOutputLines(text2, limit) {
66136
66955
  const tail = Math.max(1, limit - head - 1);
66137
66956
  return [...lines.slice(0, head), `\u2026 +${lines.length - head - tail} lines (ctrl+t for full transcript)`, ...lines.slice(lines.length - tail)];
66138
66957
  }
66139
- function toolColor(status) {
66958
+ function toolColor(status, tool) {
66140
66959
  if (status === "failed" || status === "error" || status === "denied")
66141
66960
  return COLOR.error;
66142
66961
  if (status === "done")
66143
- return COLOR.text;
66962
+ return tool ? toolFamilyColor(toolLifecycleFamily(tool)) : COLOR.text;
66144
66963
  return COLOR.dim;
66145
66964
  }
66146
66965
  function looksLikeDiff(text2) {
@@ -66255,6 +67074,7 @@ var init_tool_cell = __esm(() => {
66255
67074
  init_filetype();
66256
67075
  init_syntax();
66257
67076
  init_theme();
67077
+ init_tool_lifecycle();
66258
67078
  init_terminal();
66259
67079
  init_store4();
66260
67080
  init_tool_presentation();
@@ -66283,13 +67103,14 @@ function NoticeRow(props) {
66283
67103
  };
66284
67104
  const label = () => {
66285
67105
  if (props.row.kind === "compaction")
66286
- return "compacted context";
67106
+ return "compacted";
66287
67107
  if (props.row.kind === "loop_stop")
66288
67108
  return props.row.reason;
66289
67109
  if (props.row.kind === "notice")
66290
67110
  return props.row.title;
66291
67111
  return props.row.title;
66292
67112
  };
67113
+ const references = () => props.row.kind === "compaction" ? props.row.references ?? [] : [];
66293
67114
  const detail = () => props.row.kind === "notice" ? props.row.detail : props.row.text;
66294
67115
  const body = () => {
66295
67116
  if (props.row.kind === "compaction")
@@ -66337,6 +67158,30 @@ function NoticeRow(props) {
66337
67158
  return _el$4;
66338
67159
  }
66339
67160
  }), null);
67161
+ insert(_el$, createComponent2(Show, {
67162
+ get when() {
67163
+ return references().length > 0;
67164
+ },
67165
+ get children() {
67166
+ var _el$6 = createElement("box");
67167
+ setProp(_el$6, "style", {
67168
+ flexDirection: "column",
67169
+ paddingLeft: 2
67170
+ });
67171
+ insert(_el$6, createComponent2(For, {
67172
+ get each() {
67173
+ return references();
67174
+ },
67175
+ children: (reference, index) => (() => {
67176
+ var _el$7 = createElement("text");
67177
+ insert(_el$7, () => `${index() === 0 ? "\u2514 " : " "}${reference}`);
67178
+ effect((_$p) => setProp(_el$7, "fg", COLOR.dim, _$p));
67179
+ return _el$7;
67180
+ })()
67181
+ }));
67182
+ return _el$6;
67183
+ }
67184
+ }), null);
66340
67185
  insert(_el$, createComponent2(Show, {
66341
67186
  get when() {
66342
67187
  return memo2(() => !!expanded())() && body();
@@ -66879,7 +67724,7 @@ function PhaseRow(props) {
66879
67724
  marginBottom: 1
66880
67725
  });
66881
67726
  insert(_el$4, () => `\u2022 phase changed \xB7 ${props.row.phase}${props.row.detail ? ` \xB7 ${props.row.detail}` : ""}`);
66882
- effect((_$p) => setProp(_el$4, "fg", COLOR.dim, _$p));
67727
+ effect((_$p) => setProp(_el$4, "fg", COLOR.accent, _$p));
66883
67728
  return _el$3;
66884
67729
  })();
66885
67730
  }
@@ -66997,23 +67842,32 @@ function ContextRow(props) {
66997
67842
  },
66998
67843
  get fallback() {
66999
67844
  return (() => {
67000
- var _el$11 = createElement("text");
67001
- insert(_el$11, () => " ".repeat(columns() * 2));
67002
- return _el$11;
67845
+ var _el$12 = createElement("text");
67846
+ insert(_el$12, () => " ".repeat(columns() * 2));
67847
+ return _el$12;
67003
67848
  })();
67004
67849
  },
67005
67850
  get children() {
67006
- return createComponent2(For, {
67851
+ return [createComponent2(For, {
67007
67852
  get each() {
67008
67853
  return grid();
67009
67854
  },
67010
67855
  children: (tone) => (() => {
67011
- var _el$12 = createElement("text");
67012
- insert(_el$12, tone === "free" ? "\u26F6 " : "\u26C1 ");
67013
- effect((_$p) => setProp(_el$12, "fg", toneColor(tone), _$p));
67014
- return _el$12;
67856
+ var _el$13 = createElement("text");
67857
+ insert(_el$13, tone === "free" ? "\u26F6 " : "\u26C1 ");
67858
+ effect((_$p) => setProp(_el$13, "fg", toneColor(tone), _$p));
67859
+ return _el$13;
67015
67860
  })()
67016
- });
67861
+ }), createComponent2(Show, {
67862
+ get when() {
67863
+ return grid().length < columns();
67864
+ },
67865
+ get children() {
67866
+ var _el$8 = createElement("text");
67867
+ insert(_el$8, () => " ".repeat((columns() - grid().length) * 2));
67868
+ return _el$8;
67869
+ }
67870
+ })];
67017
67871
  }
67018
67872
  }), null);
67019
67873
  insert(_el$7, createComponent2(Show, {
@@ -67022,24 +67876,24 @@ function ContextRow(props) {
67022
67876
  },
67023
67877
  get children() {
67024
67878
  return [(() => {
67025
- var _el$8 = createElement("text");
67026
- insertNode(_el$8, createTextNode(` `));
67027
- return _el$8;
67879
+ var _el$9 = createElement("text");
67880
+ insertNode(_el$9, createTextNode(` `));
67881
+ return _el$9;
67028
67882
  })(), createComponent2(Show, {
67029
67883
  get when() {
67030
67884
  return line().dot;
67031
67885
  },
67032
67886
  get children() {
67033
- var _el$0 = createElement("text");
67034
- insertNode(_el$0, createTextNode(`\u26C1 `));
67035
- effect((_$p) => setProp(_el$0, "fg", toneColor(line().dot), _$p));
67036
- return _el$0;
67887
+ var _el$1 = createElement("text");
67888
+ insertNode(_el$1, createTextNode(`\u26C1 `));
67889
+ effect((_$p) => setProp(_el$1, "fg", toneColor(line().dot), _$p));
67890
+ return _el$1;
67037
67891
  }
67038
67892
  }), (() => {
67039
- var _el$10 = createElement("text");
67040
- insert(_el$10, () => line().text);
67041
- effect((_$p) => setProp(_el$10, "fg", line().color, _$p));
67042
- return _el$10;
67893
+ var _el$11 = createElement("text");
67894
+ insert(_el$11, () => line().text);
67895
+ effect((_$p) => setProp(_el$11, "fg", line().color, _$p));
67896
+ return _el$11;
67043
67897
  })()];
67044
67898
  }
67045
67899
  }), null);
@@ -67210,6 +68064,32 @@ var init_cells = __esm(() => {
67210
68064
  });
67211
68065
 
67212
68066
  // src/agent-tui/transcript/transcript.tsx
68067
+ function compactReferences(toolCalls) {
68068
+ const verbs = {
68069
+ file_read: "read",
68070
+ file_edit: "edited",
68071
+ file_write: "wrote",
68072
+ patch_apply: "edited",
68073
+ notebook_edit: "edited"
68074
+ };
68075
+ const seen = new Set;
68076
+ const refs = [];
68077
+ for (const call2 of toolCalls) {
68078
+ const verb = verbs[canonicalToolName(call2.tool)];
68079
+ if (!verb)
68080
+ continue;
68081
+ const args2 = call2.args ?? {};
68082
+ const path = [args2.path, args2.file, args2.filePath, args2.notebookPath].find((value) => typeof value === "string" && value.trim());
68083
+ if (!path)
68084
+ continue;
68085
+ const line = `${verb} ${path.replace(/^\/workspace\//, "").replace(/^\/worktrees\/[^/]+\//, "")}`;
68086
+ if (!seen.has(line)) {
68087
+ seen.add(line);
68088
+ refs.push(line);
68089
+ }
68090
+ }
68091
+ return refs.slice(-12);
68092
+ }
67213
68093
  function Transcript(props = {}) {
67214
68094
  const tui = useTuiStore();
67215
68095
  const dims = useTuiDimensions();
@@ -67226,12 +68106,16 @@ function Transcript(props = {}) {
67226
68106
  return renderedTranscriptRows;
67227
68107
  const boundary = tui.store.snapshot.compactionBoundary;
67228
68108
  const summary = boundary ? formatCompactSummary(boundary.summary) : "";
68109
+ const references = boundary ? compactReferences(tui.store.snapshot.toolCalls) : [];
67229
68110
  const compactRow = boundary ? [{
67230
68111
  kind: "compaction",
67231
68112
  id: boundary.id,
67232
68113
  text: boundary.preCompactTokens !== undefined && boundary.postCompactTokens !== undefined ? `${boundary.preCompactTokens} \u2192 ${boundary.postCompactTokens} tokens` : "",
67233
68114
  ...summary ? {
67234
68115
  summary
68116
+ } : {},
68117
+ ...references.length ? {
68118
+ references
67235
68119
  } : {}
67236
68120
  }] : [];
67237
68121
  renderedTranscriptRows = [...compactRow, ...tui.timelineRows()];
@@ -67586,6 +68470,7 @@ var init_transcript = __esm(() => {
67586
68470
  init_branding();
67587
68471
  init_terminal();
67588
68472
  init_tool_activity();
68473
+ init_tool_names();
67589
68474
  });
67590
68475
 
67591
68476
  // src/agent-tui/surfaces/center-surface.tsx
@@ -70394,6 +71279,10 @@ function bottomPaneSurface(input) {
70394
71279
  return "email_account_removal";
70395
71280
  if (input.hasEmailAccountWizard)
70396
71281
  return "email_account_wizard";
71282
+ if (input.hasLaneRemoval)
71283
+ return "lane_removal";
71284
+ if (input.hasLaneWizard)
71285
+ return "lane_wizard";
70397
71286
  if (input.hasRequestUserInput)
70398
71287
  return "user_input";
70399
71288
  return bottomPaneSlot(input);
@@ -70839,22 +71728,33 @@ function StatusIndicator(props) {
70839
71728
  const glyph = () => SPINNER_FRAMES[(props.spinnerFrame ?? 0) % SPINNER_FRAMES.length];
70840
71729
  const text2 = () => {
70841
71730
  if (props.activity) {
70842
- const value2 = dims().width >= 56 ? `${glyph()} ${props.activity} (${fmtElapsed(props.elapsed)}${detail()} \u2022 esc to interrupt)` : `${glyph()} ${props.activity} ${fmtElapsed(props.elapsed)} \xB7 esc interrupt`;
70843
- return truncateLine2(value2.toLowerCase(), Math.max(1, dims().width));
71731
+ const value2 = dims().width >= 56 ? `${props.activity} (${fmtElapsed(props.elapsed)}${detail()} \u2022 esc to interrupt)` : `${props.activity} ${fmtElapsed(props.elapsed)} \xB7 esc interrupt`;
71732
+ return truncateLine2(value2.toLowerCase(), Math.max(1, dims().width - 2));
70844
71733
  }
70845
71734
  const value = tui.store.ui.statusDetail;
70846
71735
  return truncateLine2((value && value !== "working" && !isFooterStatusDetail(value) ? `\u2022 ${value}` : "").toLowerCase(), Math.max(1, dims().width));
70847
71736
  };
70848
71737
  return (() => {
70849
- var _el$ = createElement("box"), _el$2 = createElement("text");
70850
- insertNode(_el$, _el$2);
71738
+ var _el$ = createElement("box"), _el$3 = createElement("text");
71739
+ insertNode(_el$, _el$3);
70851
71740
  setProp(_el$, "style", {
70852
71741
  flexDirection: "row",
70853
71742
  flexShrink: 0,
70854
71743
  paddingTop: 1
70855
71744
  });
70856
- insert(_el$2, text2);
70857
- effect((_$p) => setProp(_el$2, "fg", COLOR.text, _$p));
71745
+ insert(_el$, createComponent2(Show, {
71746
+ get when() {
71747
+ return props.activity;
71748
+ },
71749
+ get children() {
71750
+ var _el$2 = createElement("text");
71751
+ insert(_el$2, () => `${glyph()} `);
71752
+ effect((_$p) => setProp(_el$2, "fg", COLOR.agent, _$p));
71753
+ return _el$2;
71754
+ }
71755
+ }), _el$3);
71756
+ insert(_el$3, text2);
71757
+ effect((_$p) => setProp(_el$3, "fg", COLOR.text, _$p));
70858
71758
  return _el$;
70859
71759
  })();
70860
71760
  }
@@ -70865,6 +71765,8 @@ var init_status_indicator = __esm(() => {
70865
71765
  init_solid2();
70866
71766
  init_solid2();
70867
71767
  init_solid2();
71768
+ init_solid2();
71769
+ init_solid();
70868
71770
  init_store4();
70869
71771
  init_renderers2();
70870
71772
  init_terminal();
@@ -71836,6 +72738,8 @@ function overlaySubtitle(frame) {
71836
72738
  return "review security findings";
71837
72739
  if (frame.kind === "memory")
71838
72740
  return "inspect durable memory";
72741
+ if (frame.kind === "subagents")
72742
+ return "roles farai can delegate to";
71839
72743
  return "";
71840
72744
  }
71841
72745
  function overlayHint(frame, matches, providers, mcpServers) {
@@ -71859,6 +72763,8 @@ function overlayHint(frame, matches, providers, mcpServers) {
71859
72763
  return "ctrl+a add email \xB7 esc back";
71860
72764
  return selected.persistent ? "ctrl+p primary \xB7 ctrl+s secondary \xB7 ctrl+e edit \xB7 ctrl+t test \xB7 ctrl+d remove \xB7 esc back" : "ctrl+p primary \xB7 ctrl+s secondary \xB7 esc back";
71861
72765
  }
72766
+ if (frame.kind === "subagents")
72767
+ return "ctrl+a add \xB7 ctrl+e edit \xB7 ctrl+d delete \xB7 esc close";
71862
72768
  if (frame.kind !== "model")
71863
72769
  return "press enter to confirm or esc to go back";
71864
72770
  if (selected?.kind === "model_action")
@@ -73911,6 +74817,595 @@ var init_email_account_wizard = __esm(() => {
73911
74817
  TEXT_FIELDS = ["label", "address", "username", "endpoint", "clientId", "clientSecret", "credential"];
73912
74818
  });
73913
74819
 
74820
+ // src/agent-tui/bottom-pane/multi-select-rows.tsx
74821
+ function MultiSelectRows(props) {
74822
+ const dims = useTuiDimensions();
74823
+ const limit = () => Math.max(1, Math.min(6, props.visibleLimit ?? 6));
74824
+ const window = createMemo(() => {
74825
+ const cursor = Math.max(0, Math.min(props.cursor, props.items.length - 1));
74826
+ const start = Math.max(0, Math.min(cursor - Math.floor(limit() / 2), Math.max(0, props.items.length - limit())));
74827
+ return props.items.slice(start, start + limit()).map((item, index) => ({
74828
+ item,
74829
+ index: start + index
74830
+ }));
74831
+ });
74832
+ return (() => {
74833
+ var _el$ = createElement("box");
74834
+ setProp(_el$, "style", {
74835
+ flexDirection: "column"
74836
+ });
74837
+ insert(_el$, createComponent2(For, {
74838
+ get each() {
74839
+ return window();
74840
+ },
74841
+ get fallback() {
74842
+ return (() => {
74843
+ var _el$2 = createElement("text");
74844
+ insertNode(_el$2, createTextNode(` no matching tools`));
74845
+ effect((_$p) => setProp(_el$2, "fg", COLOR.dim, _$p));
74846
+ return _el$2;
74847
+ })();
74848
+ },
74849
+ children: (entry) => {
74850
+ const active = () => entry.index === Math.max(0, Math.min(props.cursor, props.items.length - 1));
74851
+ const checked = () => props.selected(entry.item);
74852
+ return (() => {
74853
+ var _el$4 = createElement("box"), _el$5 = createElement("text"), _el$6 = createElement("text"), _el$7 = createElement("text");
74854
+ insertNode(_el$4, _el$5);
74855
+ insertNode(_el$4, _el$6);
74856
+ insertNode(_el$4, _el$7);
74857
+ setProp(_el$4, "style", {
74858
+ flexDirection: "row"
74859
+ });
74860
+ setProp(_el$5, "selectable", false);
74861
+ insert(_el$5, () => active() ? "\u203A " : " ");
74862
+ setProp(_el$6, "selectable", false);
74863
+ insert(_el$6, () => checked() ? "\u25C9 " : "\u25CB ");
74864
+ setProp(_el$7, "selectable", false);
74865
+ insert(_el$7, () => truncateLine2(entry.item, Math.max(1, dims().width - 8)));
74866
+ effect((_p$) => {
74867
+ var _v$ = active() ? COLOR.accent : COLOR.dim, _v$2 = checked() ? COLOR.success : COLOR.dim, _v$3 = active() ? COLOR.text : checked() ? COLOR.text : COLOR.dim;
74868
+ _v$ !== _p$.e && (_p$.e = setProp(_el$5, "fg", _v$, _p$.e));
74869
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$6, "fg", _v$2, _p$.t));
74870
+ _v$3 !== _p$.a && (_p$.a = setProp(_el$7, "fg", _v$3, _p$.a));
74871
+ return _p$;
74872
+ }, {
74873
+ e: undefined,
74874
+ t: undefined,
74875
+ a: undefined
74876
+ });
74877
+ return _el$4;
74878
+ })();
74879
+ }
74880
+ }));
74881
+ return _el$;
74882
+ })();
74883
+ }
74884
+ var init_multi_select_rows = __esm(() => {
74885
+ init_solid2();
74886
+ init_solid2();
74887
+ init_solid2();
74888
+ init_solid2();
74889
+ init_solid2();
74890
+ init_solid2();
74891
+ init_solid2();
74892
+ init_solid2();
74893
+ init_solid();
74894
+ init_theme();
74895
+ init_renderers2();
74896
+ init_terminal();
74897
+ });
74898
+
74899
+ // src/agent-tui/bottom-pane/lane-wizard.tsx
74900
+ function LaneWizard() {
74901
+ const tui = useTuiStore();
74902
+ const dims = useTuiDimensions();
74903
+ let inputRef;
74904
+ let inputField;
74905
+ let promptRef;
74906
+ const wizard = () => tui.store.ui.laneWizard;
74907
+ const value = () => {
74908
+ const field2 = wizard().field;
74909
+ if (field2 === "id")
74910
+ return wizard().id;
74911
+ if (field2 === "description")
74912
+ return wizard().description;
74913
+ if (field2 === "model")
74914
+ return wizard().model;
74915
+ return "";
74916
+ };
74917
+ const placeholder2 = () => {
74918
+ const field2 = wizard().field;
74919
+ if (field2 === "id")
74920
+ return "osint";
74921
+ if (field2 === "description")
74922
+ return "short role summary shown when picking a lane";
74923
+ if (field2 === "model")
74924
+ return "optional model id \xB7 empty inherits parent";
74925
+ return "";
74926
+ };
74927
+ const title = () => wizard().mode === "add" ? "add lane" : `edit ${wizard().id}`;
74928
+ const step = () => `${laneWizardStep(wizard().field)}/${laneWizardStepCount()}`;
74929
+ const header = () => fitTerminalPair(title(), step(), Math.max(1, dims().width - 4), 4, 1);
74930
+ const isTextField = () => TEXT_FIELDS2.includes(wizard().field);
74931
+ const visibleTools = () => filteredToolNames(tui.store.ui.toolNames, wizard().toolFilter);
74932
+ const bodyHeight = () => wizard().field === "prompt" ? inputFieldHeight(dims().height) + 6 : isTextField() ? inputFieldHeight(dims().height) + 4 : 9;
74933
+ const wizardHeight = () => bodyHeight() + 3;
74934
+ const status = () => wizard().error ?? tui.store.ui.lastError ?? (wizard().busy ? "saving lane\u2026" : "");
74935
+ const statusColor2 = () => wizard().error || tui.store.ui.lastError ? COLOR.error : COLOR.accent;
74936
+ createEffect(() => {
74937
+ const field2 = wizard().field;
74938
+ const activeText = isTextField() && !wizard().busy;
74939
+ if (activeText) {
74940
+ if (inputRef && inputField === field2 && inputRef.value !== value())
74941
+ inputRef.value = value();
74942
+ if (inputField === field2) {
74943
+ try {
74944
+ inputRef?.focus();
74945
+ } catch {}
74946
+ }
74947
+ } else {
74948
+ try {
74949
+ inputRef?.blur();
74950
+ } catch {}
74951
+ }
74952
+ if (field2 === "prompt" && !wizard().busy) {
74953
+ try {
74954
+ promptRef?.focus();
74955
+ } catch {}
74956
+ } else {
74957
+ try {
74958
+ promptRef?.blur();
74959
+ } catch {}
74960
+ }
74961
+ });
74962
+ onCleanup(() => {
74963
+ try {
74964
+ inputRef?.blur();
74965
+ } catch {}
74966
+ try {
74967
+ promptRef?.blur();
74968
+ } catch {}
74969
+ inputRef = undefined;
74970
+ inputField = undefined;
74971
+ promptRef = undefined;
74972
+ });
74973
+ return (() => {
74974
+ var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$4 = createElement("text"), _el$5 = createElement("box"), _el$9 = createElement("box"), _el$0 = createElement("text"), _el$1 = createElement("box"), _el$10 = createElement("text");
74975
+ insertNode(_el$, _el$2);
74976
+ insertNode(_el$, _el$5);
74977
+ insertNode(_el$, _el$9);
74978
+ insertNode(_el$, _el$1);
74979
+ setProp(_el$, "id", "lane-wizard");
74980
+ insertNode(_el$2, _el$3);
74981
+ insertNode(_el$2, _el$4);
74982
+ setProp(_el$2, "style", {
74983
+ height: 1,
74984
+ flexDirection: "row",
74985
+ justifyContent: "space-between",
74986
+ paddingLeft: 2,
74987
+ paddingRight: 2
74988
+ });
74989
+ insert(_el$3, () => header().left);
74990
+ insert(_el$4, () => header().right);
74991
+ insert(_el$5, createComponent2(Show, {
74992
+ get when() {
74993
+ return wizard().field === "tools";
74994
+ },
74995
+ get fallback() {
74996
+ return createComponent2(Show, {
74997
+ get when() {
74998
+ return wizard().field === "prompt";
74999
+ },
75000
+ get fallback() {
75001
+ return createComponent2(Show, {
75002
+ get when() {
75003
+ return wizard().field === "review";
75004
+ },
75005
+ get fallback() {
75006
+ return createComponent2(Show, {
75007
+ get when() {
75008
+ return wizard().field;
75009
+ },
75010
+ keyed: true,
75011
+ children: (field2) => (() => {
75012
+ var _el$16 = createElement("box"), _el$17 = createElement("text");
75013
+ insertNode(_el$16, _el$17);
75014
+ setProp(_el$16, "style", {
75015
+ flexDirection: "column",
75016
+ paddingTop: 1,
75017
+ paddingLeft: 2,
75018
+ paddingRight: 1
75019
+ });
75020
+ insert(_el$17, () => truncateLine2(fieldLabel3(field2), Math.max(1, dims().width - 3)));
75021
+ insert(_el$16, createComponent2(InputField, {
75022
+ marginTop: 1,
75023
+ get children() {
75024
+ return [createComponent2(InputFieldPrompt, {}), (() => {
75025
+ var _el$18 = createElement("input");
75026
+ use((node) => {
75027
+ inputRef = node;
75028
+ inputField = field2;
75029
+ if (node.value !== value())
75030
+ node.value = value();
75031
+ node.focus();
75032
+ }, _el$18);
75033
+ setProp(_el$18, "id", "lane-wizard-input");
75034
+ setProp(_el$18, "onInput", (next) => updateField2(tui, field2, next));
75035
+ effect((_p$) => {
75036
+ var _v$12 = value(), _v$13 = placeholder2(), _v$14 = COLOR.dim, _v$15 = COLOR.text, _v$16 = COLOR.text, _v$17 = COLOR.accent, _v$18 = {
75037
+ flexGrow: 1,
75038
+ backgroundColor: COLOR.panelActive
75039
+ };
75040
+ _v$12 !== _p$.e && (_p$.e = setProp(_el$18, "value", _v$12, _p$.e));
75041
+ _v$13 !== _p$.t && (_p$.t = setProp(_el$18, "placeholder", _v$13, _p$.t));
75042
+ _v$14 !== _p$.a && (_p$.a = setProp(_el$18, "placeholderColor", _v$14, _p$.a));
75043
+ _v$15 !== _p$.o && (_p$.o = setProp(_el$18, "textColor", _v$15, _p$.o));
75044
+ _v$16 !== _p$.i && (_p$.i = setProp(_el$18, "focusedTextColor", _v$16, _p$.i));
75045
+ _v$17 !== _p$.n && (_p$.n = setProp(_el$18, "cursorColor", _v$17, _p$.n));
75046
+ _v$18 !== _p$.s && (_p$.s = setProp(_el$18, "style", _v$18, _p$.s));
75047
+ return _p$;
75048
+ }, {
75049
+ e: undefined,
75050
+ t: undefined,
75051
+ a: undefined,
75052
+ o: undefined,
75053
+ i: undefined,
75054
+ n: undefined,
75055
+ s: undefined
75056
+ });
75057
+ return _el$18;
75058
+ })()];
75059
+ }
75060
+ }), null);
75061
+ effect((_$p) => setProp(_el$17, "fg", COLOR.dim, _$p));
75062
+ return _el$16;
75063
+ })()
75064
+ });
75065
+ },
75066
+ get children() {
75067
+ return createComponent2(LaneReview, {});
75068
+ }
75069
+ });
75070
+ },
75071
+ get children() {
75072
+ var _el$11 = createElement("box"), _el$12 = createElement("text"), _el$14 = createElement("box"), _el$15 = createElement("textarea");
75073
+ insertNode(_el$11, _el$12);
75074
+ insertNode(_el$11, _el$14);
75075
+ setProp(_el$11, "style", {
75076
+ flexDirection: "column",
75077
+ paddingTop: 1,
75078
+ paddingLeft: 2,
75079
+ paddingRight: 1
75080
+ });
75081
+ insertNode(_el$12, createTextNode(`role prompt \xB7 optional \xB7 injected into the subagent`));
75082
+ insertNode(_el$14, _el$15);
75083
+ use((node) => {
75084
+ promptRef = node;
75085
+ try {
75086
+ node.setText(wizard().prompt);
75087
+ } catch {}
75088
+ node.focus();
75089
+ }, _el$15);
75090
+ setProp(_el$15, "id", "lane-prompt-input");
75091
+ setProp(_el$15, "placeholder", "describe how this role should work");
75092
+ setProp(_el$15, "wrapMode", "word");
75093
+ setProp(_el$15, "onContentChange", () => {
75094
+ tui.actions.laneWizardPatch({
75095
+ prompt: promptRef?.plainText ?? "",
75096
+ error: undefined
75097
+ });
75098
+ });
75099
+ effect((_p$) => {
75100
+ var _v$7 = COLOR.dim, _v$8 = {
75101
+ flexDirection: "row",
75102
+ marginTop: 1,
75103
+ paddingTop: 1,
75104
+ paddingBottom: 1,
75105
+ paddingLeft: 1,
75106
+ paddingRight: 1,
75107
+ backgroundColor: COLOR.panelActive
75108
+ }, _v$9 = COLOR.dim, _v$0 = COLOR.text, _v$1 = COLOR.text, _v$10 = COLOR.accent, _v$11 = {
75109
+ height: Math.max(3, inputFieldHeight(dims().height)),
75110
+ flexGrow: 1,
75111
+ backgroundColor: "transparent"
75112
+ };
75113
+ _v$7 !== _p$.e && (_p$.e = setProp(_el$12, "fg", _v$7, _p$.e));
75114
+ _v$8 !== _p$.t && (_p$.t = setProp(_el$14, "style", _v$8, _p$.t));
75115
+ _v$9 !== _p$.a && (_p$.a = setProp(_el$15, "placeholderColor", _v$9, _p$.a));
75116
+ _v$0 !== _p$.o && (_p$.o = setProp(_el$15, "textColor", _v$0, _p$.o));
75117
+ _v$1 !== _p$.i && (_p$.i = setProp(_el$15, "focusedTextColor", _v$1, _p$.i));
75118
+ _v$10 !== _p$.n && (_p$.n = setProp(_el$15, "cursorColor", _v$10, _p$.n));
75119
+ _v$11 !== _p$.s && (_p$.s = setProp(_el$15, "style", _v$11, _p$.s));
75120
+ return _p$;
75121
+ }, {
75122
+ e: undefined,
75123
+ t: undefined,
75124
+ a: undefined,
75125
+ o: undefined,
75126
+ i: undefined,
75127
+ n: undefined,
75128
+ s: undefined
75129
+ });
75130
+ return _el$11;
75131
+ }
75132
+ });
75133
+ },
75134
+ get children() {
75135
+ var _el$6 = createElement("box"), _el$7 = createElement("text"), _el$8 = createElement("box");
75136
+ insertNode(_el$6, _el$7);
75137
+ insertNode(_el$6, _el$8);
75138
+ setProp(_el$6, "style", {
75139
+ flexDirection: "column",
75140
+ paddingTop: 1,
75141
+ paddingLeft: 2,
75142
+ paddingRight: 1
75143
+ });
75144
+ insert(_el$7, () => truncateLine2(`tools \xB7 ${wizard().tools.length} selected \xB7 omit to inherit${wizard().toolFilter ? ` \xB7 filter: ${wizard().toolFilter}` : ""}`, Math.max(1, dims().width - 3)));
75145
+ setProp(_el$8, "style", {
75146
+ marginTop: 1
75147
+ });
75148
+ insert(_el$8, createComponent2(MultiSelectRows, {
75149
+ get items() {
75150
+ return visibleTools();
75151
+ },
75152
+ selected: (name) => wizard().tools.includes(name),
75153
+ get cursor() {
75154
+ return wizard().toolCursor;
75155
+ }
75156
+ }));
75157
+ effect((_$p) => setProp(_el$7, "fg", COLOR.dim, _$p));
75158
+ return _el$6;
75159
+ }
75160
+ }));
75161
+ insertNode(_el$9, _el$0);
75162
+ setProp(_el$9, "style", {
75163
+ height: 1,
75164
+ paddingLeft: 2,
75165
+ paddingRight: 1
75166
+ });
75167
+ insert(_el$0, () => truncateLine2(status(), Math.max(1, dims().width - 3)));
75168
+ insertNode(_el$1, _el$10);
75169
+ setProp(_el$1, "style", {
75170
+ height: 1,
75171
+ paddingLeft: 2,
75172
+ paddingRight: 1
75173
+ });
75174
+ insert(_el$10, () => truncateLine2(wizardHint3(wizard().field), Math.max(1, dims().width - 3)));
75175
+ effect((_p$) => {
75176
+ var _v$ = {
75177
+ height: wizardHeight(),
75178
+ flexShrink: 0,
75179
+ flexDirection: "column",
75180
+ overflow: "hidden"
75181
+ }, _v$2 = COLOR.text, _v$3 = COLOR.dim, _v$4 = {
75182
+ height: bodyHeight(),
75183
+ flexShrink: 0,
75184
+ flexDirection: "column",
75185
+ overflow: "hidden"
75186
+ }, _v$5 = statusColor2(), _v$6 = COLOR.dim;
75187
+ _v$ !== _p$.e && (_p$.e = setProp(_el$, "style", _v$, _p$.e));
75188
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$3, "fg", _v$2, _p$.t));
75189
+ _v$3 !== _p$.a && (_p$.a = setProp(_el$4, "fg", _v$3, _p$.a));
75190
+ _v$4 !== _p$.o && (_p$.o = setProp(_el$5, "style", _v$4, _p$.o));
75191
+ _v$5 !== _p$.i && (_p$.i = setProp(_el$0, "fg", _v$5, _p$.i));
75192
+ _v$6 !== _p$.n && (_p$.n = setProp(_el$10, "fg", _v$6, _p$.n));
75193
+ return _p$;
75194
+ }, {
75195
+ e: undefined,
75196
+ t: undefined,
75197
+ a: undefined,
75198
+ o: undefined,
75199
+ i: undefined,
75200
+ n: undefined
75201
+ });
75202
+ return _el$;
75203
+ })();
75204
+ }
75205
+ function LaneReview() {
75206
+ const tui = useTuiStore();
75207
+ const dims = useTuiDimensions();
75208
+ const wizard = () => tui.store.ui.laneWizard;
75209
+ const width = () => Math.max(1, dims().width - 2);
75210
+ return (() => {
75211
+ var _el$19 = createElement("box"), _el$20 = createElement("text"), _el$21 = createElement("text"), _el$22 = createElement("text"), _el$23 = createElement("text"), _el$24 = createElement("text");
75212
+ insertNode(_el$19, _el$20);
75213
+ insertNode(_el$19, _el$21);
75214
+ insertNode(_el$19, _el$22);
75215
+ insertNode(_el$19, _el$23);
75216
+ insertNode(_el$19, _el$24);
75217
+ setProp(_el$19, "style", {
75218
+ flexDirection: "column",
75219
+ paddingTop: 1,
75220
+ paddingLeft: 2
75221
+ });
75222
+ insert(_el$20, () => truncateLine2(wizard().id || "unnamed lane", width()));
75223
+ insert(_el$21, () => truncateLine2(wizard().description || "no description", width()));
75224
+ insert(_el$22, () => truncateLine2(wizard().tools.length ? `tools: ${wizard().tools.join(", ")}` : "tools: inherit parent scope", width()));
75225
+ insert(_el$23, () => truncateLine2(wizard().model ? `model: ${wizard().model}` : "model: inherit parent", width()));
75226
+ insert(_el$24, () => truncateLine2(wizard().prompt ? `prompt: ${wizard().prompt.split(`
75227
+ `)[0]}` : "prompt: none", width()));
75228
+ effect((_p$) => {
75229
+ var _v$19 = COLOR.text, _v$20 = COLOR.dim, _v$21 = COLOR.dim, _v$22 = COLOR.dim, _v$23 = COLOR.dim;
75230
+ _v$19 !== _p$.e && (_p$.e = setProp(_el$20, "fg", _v$19, _p$.e));
75231
+ _v$20 !== _p$.t && (_p$.t = setProp(_el$21, "fg", _v$20, _p$.t));
75232
+ _v$21 !== _p$.a && (_p$.a = setProp(_el$22, "fg", _v$21, _p$.a));
75233
+ _v$22 !== _p$.o && (_p$.o = setProp(_el$23, "fg", _v$22, _p$.o));
75234
+ _v$23 !== _p$.i && (_p$.i = setProp(_el$24, "fg", _v$23, _p$.i));
75235
+ return _p$;
75236
+ }, {
75237
+ e: undefined,
75238
+ t: undefined,
75239
+ a: undefined,
75240
+ o: undefined,
75241
+ i: undefined
75242
+ });
75243
+ return _el$19;
75244
+ })();
75245
+ }
75246
+ function updateField2(tui, field2, value) {
75247
+ tui.actions.errorSet(undefined);
75248
+ if (field2 === "id")
75249
+ tui.actions.laneWizardPatch({
75250
+ id: value,
75251
+ error: undefined
75252
+ });
75253
+ if (field2 === "description")
75254
+ tui.actions.laneWizardPatch({
75255
+ description: value,
75256
+ error: undefined
75257
+ });
75258
+ if (field2 === "model")
75259
+ tui.actions.laneWizardPatch({
75260
+ model: value,
75261
+ error: undefined
75262
+ });
75263
+ }
75264
+ function fieldLabel3(field2) {
75265
+ if (field2 === "id")
75266
+ return "lane id";
75267
+ if (field2 === "description")
75268
+ return "description \xB7 optional";
75269
+ if (field2 === "model")
75270
+ return "model \xB7 optional \xB7 empty inherits parent";
75271
+ return field2;
75272
+ }
75273
+ function wizardHint3(field2) {
75274
+ if (field2 === "prompt")
75275
+ return "type \xB7 enter newline \xB7 ctrl+s continue \xB7 esc back";
75276
+ if (field2 === "tools")
75277
+ return "\u2191\u2193 move \xB7 space toggle \xB7 ctrl+a all \xB7 type filter \xB7 enter continue \xB7 esc back";
75278
+ if (field2 === "review")
75279
+ return "enter save \xB7 esc back";
75280
+ return "enter continue \xB7 esc back";
75281
+ }
75282
+ var TEXT_FIELDS2;
75283
+ var init_lane_wizard = __esm(() => {
75284
+ init_solid2();
75285
+ init_solid2();
75286
+ init_solid2();
75287
+ init_solid2();
75288
+ init_solid2();
75289
+ init_solid2();
75290
+ init_solid2();
75291
+ init_solid2();
75292
+ init_solid();
75293
+ init_store4();
75294
+ init_lane_state();
75295
+ init_theme();
75296
+ init_renderers2();
75297
+ init_terminal_text();
75298
+ init_terminal();
75299
+ init_input_field();
75300
+ init_multi_select_rows();
75301
+ TEXT_FIELDS2 = ["id", "description", "model"];
75302
+ });
75303
+
75304
+ // src/agent-tui/bottom-pane/lane-removal.tsx
75305
+ function LaneRemoval() {
75306
+ const tui = useTuiStore();
75307
+ const dims = useTuiDimensions();
75308
+ const state = () => tui.store.ui.laneRemoval;
75309
+ const lane = () => state().lane;
75310
+ const title = () => `remove ${lane().id}`;
75311
+ const descriptionColumn2 = () => selectionDescriptionColumn([{
75312
+ title: title()
75313
+ }], dims().width);
75314
+ const header = () => fitTerminalPair("remove lane", lane().source, Math.max(1, dims().width - 4), 8, 1);
75315
+ const hint = () => state().busy ? `removing ${lane().id}\u2026` : state().error ? `error \xB7 ${state().error}` : "enter remove \xB7 esc cancel";
75316
+ return (() => {
75317
+ var _el$ = createElement("box"), _el$2 = createElement("box"), _el$3 = createElement("text"), _el$4 = createElement("text"), _el$5 = createElement("box"), _el$6 = createElement("text"), _el$7 = createElement("box");
75318
+ insertNode(_el$, _el$2);
75319
+ insertNode(_el$, _el$5);
75320
+ setProp(_el$, "id", "lane-removal");
75321
+ setProp(_el$, "style", {
75322
+ height: 8,
75323
+ flexShrink: 0,
75324
+ flexDirection: "column",
75325
+ overflow: "hidden"
75326
+ });
75327
+ insertNode(_el$2, _el$3);
75328
+ insertNode(_el$2, _el$4);
75329
+ setProp(_el$2, "style", {
75330
+ height: 1,
75331
+ flexDirection: "row",
75332
+ justifyContent: "space-between",
75333
+ paddingLeft: 2,
75334
+ paddingRight: 2
75335
+ });
75336
+ insert(_el$3, () => header().left);
75337
+ insert(_el$4, () => header().right);
75338
+ insertNode(_el$5, _el$6);
75339
+ insertNode(_el$5, _el$7);
75340
+ setProp(_el$5, "style", {
75341
+ height: 5,
75342
+ flexShrink: 0,
75343
+ flexDirection: "column",
75344
+ paddingTop: 1,
75345
+ overflow: "hidden"
75346
+ });
75347
+ insert(_el$6, () => ` ${truncateLine2(`remove custom lane ${lane().id}?`, Math.max(1, dims().width - 2))}`);
75348
+ setProp(_el$7, "style", {
75349
+ flexDirection: "column",
75350
+ marginTop: 1
75351
+ });
75352
+ insert(_el$7, createComponent2(SelectionRow, {
75353
+ get title() {
75354
+ return title();
75355
+ },
75356
+ description: "reverts to the builtin definition if one exists",
75357
+ selected: true,
75358
+ get disabled() {
75359
+ return state().busy;
75360
+ },
75361
+ get width() {
75362
+ return dims().width;
75363
+ },
75364
+ get descriptionColumn() {
75365
+ return descriptionColumn2();
75366
+ },
75367
+ get titleColor() {
75368
+ return COLOR.error;
75369
+ }
75370
+ }));
75371
+ insert(_el$, createComponent2(SelectionMenuHint, {
75372
+ get text() {
75373
+ return hint();
75374
+ },
75375
+ get color() {
75376
+ return memo2(() => !!state().error)() ? COLOR.error : undefined;
75377
+ }
75378
+ }), null);
75379
+ effect((_p$) => {
75380
+ var _v$ = COLOR.text, _v$2 = COLOR.dim, _v$3 = COLOR.accent;
75381
+ _v$ !== _p$.e && (_p$.e = setProp(_el$3, "fg", _v$, _p$.e));
75382
+ _v$2 !== _p$.t && (_p$.t = setProp(_el$4, "fg", _v$2, _p$.t));
75383
+ _v$3 !== _p$.a && (_p$.a = setProp(_el$6, "fg", _v$3, _p$.a));
75384
+ return _p$;
75385
+ }, {
75386
+ e: undefined,
75387
+ t: undefined,
75388
+ a: undefined
75389
+ });
75390
+ return _el$;
75391
+ })();
75392
+ }
75393
+ var init_lane_removal = __esm(() => {
75394
+ init_solid2();
75395
+ init_solid2();
75396
+ init_solid2();
75397
+ init_solid2();
75398
+ init_solid2();
75399
+ init_solid2();
75400
+ init_solid2();
75401
+ init_store4();
75402
+ init_selection_row();
75403
+ init_theme();
75404
+ init_renderers2();
75405
+ init_terminal_text();
75406
+ init_terminal();
75407
+ });
75408
+
73914
75409
  // src/agent-tui/bottom-pane/email-account-removal.tsx
73915
75410
  function EmailAccountRemoval() {
73916
75411
  const tui = useTuiStore();
@@ -74066,6 +75561,8 @@ function BottomPane() {
74066
75561
  hasMcpServerWizard: mcpServerWizardActive(),
74067
75562
  hasEmailAccountRemoval: emailAccountRemovalActive(),
74068
75563
  hasEmailAccountWizard: emailAccountWizardActive(),
75564
+ hasLaneRemoval: laneRemovalActive(),
75565
+ hasLaneWizard: laneWizardActive(),
74069
75566
  hasRequestUserInput: inputRequestActive(),
74070
75567
  hasListFrame: Boolean(listFrame()),
74071
75568
  hasCenterFrame: Boolean(centerFrame()),
@@ -74077,6 +75574,8 @@ function BottomPane() {
74077
75574
  const mcpServerRemovalActive = () => Boolean(tui.store.ui.mcpServerRemoval);
74078
75575
  const emailAccountWizardActive = () => Boolean(tui.store.ui.emailAccountWizard);
74079
75576
  const emailAccountRemovalActive = () => Boolean(tui.store.ui.emailAccountRemoval);
75577
+ const laneWizardActive = () => Boolean(tui.store.ui.laneWizard);
75578
+ const laneRemovalActive = () => Boolean(tui.store.ui.laneRemoval);
74080
75579
  const inputRequestActive = () => Boolean(tui.store.snapshot.pendingUserInput && !tui.store.ui.requestUserInput?.dismissed);
74081
75580
  const inputRequestPending = () => Boolean(tui.store.snapshot.pendingUserInput);
74082
75581
  const composerSurfaceVisible = () => surface() === "composer";
@@ -74241,6 +75740,20 @@ function BottomPane() {
74241
75740
  get children() {
74242
75741
  return createComponent2(EmailAccountWizard, {});
74243
75742
  }
75743
+ }), createComponent2(Match, {
75744
+ get when() {
75745
+ return surface() === "lane_removal";
75746
+ },
75747
+ get children() {
75748
+ return createComponent2(LaneRemoval, {});
75749
+ }
75750
+ }), createComponent2(Match, {
75751
+ get when() {
75752
+ return surface() === "lane_wizard";
75753
+ },
75754
+ get children() {
75755
+ return createComponent2(LaneWizard, {});
75756
+ }
74244
75757
  }), createComponent2(Match, {
74245
75758
  get when() {
74246
75759
  return surface() === "user_input";
@@ -74488,6 +76001,8 @@ var init_bottom_pane = __esm(() => {
74488
76001
  init_mcp_server_wizard();
74489
76002
  init_mcp_server_removal();
74490
76003
  init_email_account_wizard();
76004
+ init_lane_wizard();
76005
+ init_lane_removal();
74491
76006
  init_email_account_removal();
74492
76007
  init_slash_autocomplete();
74493
76008
  init_renderers2();
@@ -74598,6 +76113,9 @@ ${note.text}`).join(`
74598
76113
  },
74599
76114
  email: () => {
74600
76115
  tui.openEmailOverlay();
76116
+ },
76117
+ subagents: () => {
76118
+ tui.openSubagentsOverlay();
74601
76119
  }
74602
76120
  }));
74603
76121
  onCleanup(disposeCommands);
@@ -79941,5 +81459,5 @@ Examples:
79941
81459
  `);
79942
81460
  }
79943
81461
 
79944
- //# debugId=A8584C8E669B34E864756E2164756E21
81462
+ //# debugId=96D533A4297076F264756E2164756E21
79945
81463
  //# sourceMappingURL=index.js.map