pentesting 0.70.5 → 0.70.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/main.js +512 -545
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -82,12 +82,9 @@ var THEME = {
82
82
  spinner: COLORS.primary
83
83
  };
84
84
  var ASCII_BANNER = `
85
- \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557
86
- \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D
87
- \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2557
88
- \u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551
89
- \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
90
- \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D
85
+ \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
86
+ \u2502 P E N T E S T I N G A G E N T \u2502
87
+ \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
91
88
  `;
92
89
  var ICONS = {
93
90
  // Status
@@ -762,7 +759,7 @@ var INPUT_PROMPT_PATTERNS = [
762
759
 
763
760
  // src/shared/constants/agent.ts
764
761
  var APP_NAME = "Pentest AI";
765
- var APP_VERSION = "0.70.5";
762
+ var APP_VERSION = "0.70.7";
766
763
  var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
767
764
  var LLM_ROLES = {
768
765
  SYSTEM: "system",
@@ -817,7 +814,7 @@ import { render } from "ink";
817
814
  import chalk from "chalk";
818
815
 
819
816
  // src/platform/tui/app.tsx
820
- import { useState as useState7, useCallback as useCallback11, useRef as useRef10 } from "react";
817
+ import { useState as useState6, useCallback as useCallback9, useRef as useRef8 } from "react";
821
818
  import { Box as Box19, useApp, useStdout as useStdout4 } from "ink";
822
819
 
823
820
  // src/platform/tui/hooks/useAgent.ts
@@ -1115,7 +1112,8 @@ var TOOL_NAMES = {
1115
1112
  MITM_PROXY: "mitm_proxy",
1116
1113
  PACKET_SNIFF: "packet_sniff",
1117
1114
  DNS_SPOOF: "dns_spoof",
1118
- TRAFFIC_INTERCEPT: "traffic_intercept"
1115
+ TRAFFIC_INTERCEPT: "traffic_intercept",
1116
+ ANALYZE_PCAP: "analyze_pcap"
1119
1117
  };
1120
1118
 
1121
1119
  // src/shared/constants/attack.ts
@@ -1174,7 +1172,8 @@ var EVENT_TYPES = {
1174
1172
  RETRY: "retry",
1175
1173
  USAGE_UPDATE: "usage_update",
1176
1174
  INPUT_REQUEST: "input_request",
1177
- FLAG_FOUND: "flag_found"
1175
+ FLAG_FOUND: "flag_found",
1176
+ NOTIFICATION: "notification"
1178
1177
  };
1179
1178
  var COMMAND_EVENT_TYPES = {
1180
1179
  TOOL_MISSING: "tool_missing",
@@ -1186,7 +1185,9 @@ var COMMAND_EVENT_TYPES = {
1186
1185
  COMMAND_SUCCESS: "command_success",
1187
1186
  COMMAND_FAILED: "command_failed",
1188
1187
  COMMAND_ERROR: "command_error",
1189
- INPUT_REQUIRED: "input_required"
1188
+ INPUT_REQUIRED: "input_required",
1189
+ COMMAND_STDOUT: "command_stdout",
1190
+ PROCESS_NOTIFICATION: "process_notification"
1190
1191
  };
1191
1192
 
1192
1193
  // src/shared/constants/ui-commands.ts
@@ -1268,13 +1269,6 @@ var EDGE_STATUS = {
1268
1269
  SUCCEEDED: "succeeded",
1269
1270
  FAILED: "failed"
1270
1271
  };
1271
- var SEVERITY = {
1272
- CRITICAL: "critical",
1273
- HIGH: "high",
1274
- MEDIUM: "medium",
1275
- LOW: "low",
1276
- INFO: "info"
1277
- };
1278
1272
  var IMPACT_WEIGHT = {
1279
1273
  critical: 4,
1280
1274
  high: 3,
@@ -1523,21 +1517,21 @@ function dfsChains(nodeId, path2, pathEdges, visited, ctx) {
1523
1517
  visited.delete(nodeId);
1524
1518
  }
1525
1519
  function estimateImpact(node) {
1526
- if (node.type === NODE_TYPE.LOOT) return SEVERITY.CRITICAL;
1520
+ if (node.type === NODE_TYPE.LOOT) return SEVERITIES.CRITICAL;
1527
1521
  if (node.type === NODE_TYPE.ACCESS) {
1528
1522
  const level = String(node.data.level || "");
1529
- if (["root", "SYSTEM", "Administrator", "admin"].includes(level)) return SEVERITY.CRITICAL;
1530
- return SEVERITY.HIGH;
1523
+ if (["root", "SYSTEM", "Administrator", "admin"].includes(level)) return SEVERITIES.CRITICAL;
1524
+ return SEVERITIES.HIGH;
1531
1525
  }
1532
1526
  if (node.type === NODE_TYPE.VULNERABILITY) {
1533
1527
  const sev = String(node.data.severity || "");
1534
- if (sev === SEVERITY.CRITICAL) return SEVERITY.CRITICAL;
1535
- if (sev === SEVERITY.HIGH) return SEVERITY.HIGH;
1536
- if (sev === SEVERITY.MEDIUM) return SEVERITY.MEDIUM;
1528
+ if (sev === SEVERITIES.CRITICAL) return SEVERITIES.CRITICAL;
1529
+ if (sev === SEVERITIES.HIGH) return SEVERITIES.HIGH;
1530
+ if (sev === SEVERITIES.MEDIUM) return SEVERITIES.MEDIUM;
1537
1531
  }
1538
- if (node.type === NODE_TYPE.DOMAIN) return SEVERITY.CRITICAL;
1539
- if (node.type === NODE_TYPE.CERTIFICATE_TEMPLATE) return SEVERITY.HIGH;
1540
- return SEVERITY.LOW;
1532
+ if (node.type === NODE_TYPE.DOMAIN) return SEVERITIES.CRITICAL;
1533
+ if (node.type === NODE_TYPE.CERTIFICATE_TEMPLATE) return SEVERITIES.HIGH;
1534
+ return SEVERITIES.LOW;
1541
1535
  }
1542
1536
  function extractSuccessfulChain(nodes, edges) {
1543
1537
  const MIN_CHAIN_LENGTH = 2;
@@ -1753,10 +1747,10 @@ function formatPathsList(nodes, edges, failedPaths) {
1753
1747
  const c = chains[i];
1754
1748
  const prob = (c.probability * 100).toFixed(0);
1755
1749
  const impactColors = {
1756
- [SEVERITY.CRITICAL]: "\u{1F534}",
1757
- [SEVERITY.HIGH]: "\u{1F7E0}",
1758
- [SEVERITY.MEDIUM]: "\u{1F7E1}",
1759
- [SEVERITY.LOW]: "\u{1F7E2}"
1750
+ [SEVERITIES.CRITICAL]: "\u{1F534}",
1751
+ [SEVERITIES.HIGH]: "\u{1F7E0}",
1752
+ [SEVERITIES.MEDIUM]: "\u{1F7E1}",
1753
+ [SEVERITIES.LOW]: "\u{1F7E2}"
1760
1754
  };
1761
1755
  const impactIcon = impactColors[c.impact] || "\u26AA";
1762
1756
  lines.push(`${impactIcon} PATH ${i + 1} [${c.impact.toUpperCase()} | prob: ${prob}% | steps: ${c.length}]`);
@@ -4397,133 +4391,133 @@ function injectCurlMaxTime(command, timeoutSec) {
4397
4391
  if (/--max-time\b|-m\s+\d/.test(command)) return command;
4398
4392
  return command.replace(/\bcurl\b/, `curl --max-time ${timeoutSec}`);
4399
4393
  }
4400
- async function executeCommandOnce(command, options = {}) {
4401
- return new Promise((resolve) => {
4402
- const eventEmitter = getEventEmitter();
4403
- const inputHandler = getInputHandler();
4404
- const timeout = options.timeout ?? TOOL_TIMEOUTS.DEFAULT_COMMAND;
4405
- const safeCommand = injectCurlMaxTime(command, CURL_MAX_TIME_SEC);
4406
- const torLeak = checkTorLeakRisk(safeCommand);
4407
- if (!torLeak.safe) {
4408
- resolve({
4409
- success: false,
4410
- output: "",
4411
- error: `\u{1F6D1} TOR IP LEAK BLOCKED
4394
+ function prepareCommand(command) {
4395
+ const safeCommand = injectCurlMaxTime(command, CURL_MAX_TIME_SEC);
4396
+ const torLeak = checkTorLeakRisk(safeCommand);
4397
+ if (!torLeak.safe) {
4398
+ return {
4399
+ safeCommand,
4400
+ execCommand: "",
4401
+ error: `\u{1F6D1} TOR IP LEAK BLOCKED
4412
4402
  Reason: ${torLeak.reason}
4413
4403
  Suggestion: ${torLeak.suggestion}`
4404
+ };
4405
+ }
4406
+ return { safeCommand, execCommand: wrapCommandForTor(safeCommand) };
4407
+ }
4408
+ function setupTimeout(child, timeoutMs, onTimeout) {
4409
+ return setTimeout(() => {
4410
+ onTimeout();
4411
+ try {
4412
+ process.kill(-child.pid, "SIGKILL");
4413
+ } catch {
4414
+ try {
4415
+ child.kill("SIGKILL");
4416
+ } catch {
4417
+ }
4418
+ }
4419
+ }, timeoutMs);
4420
+ }
4421
+ var ProcessIOHandler = class {
4422
+ constructor(child, processState, eventEmitter, inputHandler) {
4423
+ this.child = child;
4424
+ this.processState = processState;
4425
+ this.eventEmitter = eventEmitter;
4426
+ this.inputHandler = inputHandler;
4427
+ }
4428
+ stdout = "";
4429
+ stderr = "";
4430
+ inputHandled = false;
4431
+ async checkForInputPrompt(data) {
4432
+ if (this.inputHandled || !this.inputHandler || this.processState.terminated) return;
4433
+ for (const pattern of INPUT_PROMPT_PATTERNS) {
4434
+ if (pattern.test(data)) {
4435
+ this.inputHandled = true;
4436
+ this.eventEmitter?.({
4437
+ type: COMMAND_EVENT_TYPES.INPUT_REQUIRED,
4438
+ message: `Input required: ${data.trim().slice(0, SYSTEM_LIMITS.MAX_PROMPT_PREVIEW)}`,
4439
+ detail: "Waiting for user input..."
4440
+ });
4441
+ try {
4442
+ const userInput = await this.inputHandler(data.trim());
4443
+ if (userInput !== null && !this.processState.terminated && this.child.stdin?.writable) {
4444
+ this.child.stdin.write(userInput + "\n");
4445
+ }
4446
+ } catch {
4447
+ }
4448
+ return;
4449
+ }
4450
+ }
4451
+ }
4452
+ handleStdout(data) {
4453
+ const text = data.toString();
4454
+ this.stdout += text;
4455
+ const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
4456
+ if (lines.length > 0) {
4457
+ this.eventEmitter?.({
4458
+ type: COMMAND_EVENT_TYPES.COMMAND_STDOUT,
4459
+ message: lines[lines.length - 1]
4414
4460
  });
4415
- return;
4461
+ }
4462
+ this.checkForInputPrompt(text);
4463
+ }
4464
+ handleStderr(data) {
4465
+ const text = data.toString();
4466
+ this.stderr += text;
4467
+ this.checkForInputPrompt(text);
4468
+ }
4469
+ };
4470
+ async function executeCommandOnce(command, options = {}) {
4471
+ return new Promise((resolve) => {
4472
+ const eventEmitter = getEventEmitter();
4473
+ const { safeCommand, execCommand, error } = prepareCommand(command);
4474
+ if (error) {
4475
+ return resolve({ success: false, output: "", error });
4416
4476
  }
4417
4477
  eventEmitter?.({
4418
4478
  type: COMMAND_EVENT_TYPES.COMMAND_START,
4419
4479
  message: `Executing: ${safeCommand.slice(0, DISPLAY_LIMITS.COMMAND_PREVIEW)}${safeCommand.length > DISPLAY_LIMITS.COMMAND_PREVIEW ? "..." : ""}`
4420
4480
  });
4421
- const execCommand = wrapCommandForTor(safeCommand);
4422
4481
  const child = spawn3("sh", ["-c", execCommand], {
4423
4482
  detached: true,
4424
4483
  env: { ...process.env, ...options.env },
4425
4484
  cwd: options.cwd
4426
4485
  });
4427
- let stdout = "";
4428
- let stderr = "";
4429
- let inputHandled = false;
4430
- let processTerminated = false;
4431
- let timedOut = false;
4432
- const killTimer = setTimeout(() => {
4433
- if (processTerminated) return;
4434
- timedOut = true;
4435
- processTerminated = true;
4436
- try {
4437
- process.kill(-child.pid, "SIGKILL");
4438
- } catch {
4439
- try {
4440
- child.kill("SIGKILL");
4441
- } catch {
4442
- }
4443
- }
4444
- }, timeout);
4445
- const checkForInputPrompt = async (data) => {
4446
- if (inputHandled || !inputHandler || processTerminated) return;
4447
- for (const pattern of INPUT_PROMPT_PATTERNS) {
4448
- if (pattern.test(data)) {
4449
- inputHandled = true;
4450
- eventEmitter?.({
4451
- type: COMMAND_EVENT_TYPES.INPUT_REQUIRED,
4452
- message: `Input required: ${data.trim().slice(0, SYSTEM_LIMITS.MAX_PROMPT_PREVIEW)}`,
4453
- detail: "Waiting for user input..."
4454
- });
4455
- const promptText = data.trim();
4456
- try {
4457
- const userInput = await inputHandler(promptText);
4458
- if (userInput !== null && !processTerminated && child.stdin?.writable) {
4459
- child.stdin.write(userInput + "\n");
4460
- }
4461
- } catch {
4462
- }
4463
- return;
4464
- }
4465
- }
4466
- };
4467
- child.stdout.on("data", (data) => {
4468
- const text = data.toString();
4469
- stdout += text;
4470
- checkForInputPrompt(text);
4471
- });
4472
- child.stderr.on("data", (data) => {
4473
- const text = data.toString();
4474
- stderr += text;
4475
- checkForInputPrompt(text);
4486
+ const processState = { terminated: false, timedOut: false };
4487
+ const timeoutMs = options.timeout ?? TOOL_TIMEOUTS.DEFAULT_COMMAND;
4488
+ const killTimer = setupTimeout(child, timeoutMs, () => {
4489
+ processState.timedOut = true;
4490
+ processState.terminated = true;
4476
4491
  });
4492
+ const ioHandler = new ProcessIOHandler(child, processState, eventEmitter, getInputHandler());
4493
+ child.stdout.on("data", (d) => ioHandler.handleStdout(d));
4494
+ child.stderr.on("data", (d) => ioHandler.handleStderr(d));
4477
4495
  child.on("close", (code) => {
4478
4496
  clearTimeout(killTimer);
4479
- processTerminated = true;
4480
- if (timedOut) {
4481
- eventEmitter?.({
4482
- type: COMMAND_EVENT_TYPES.COMMAND_FAILED,
4483
- message: `Command timed out after ${Math.round(timeout / 1e3)}s`
4484
- });
4485
- resolve({
4497
+ processState.terminated = true;
4498
+ const outTrimmed = ioHandler.stdout.trim();
4499
+ const errTrimmed = ioHandler.stderr.trim();
4500
+ if (processState.timedOut) {
4501
+ eventEmitter?.({ type: COMMAND_EVENT_TYPES.COMMAND_FAILED, message: `Command timed out after ${Math.round(timeoutMs / 1e3)}s` });
4502
+ return resolve({
4486
4503
  success: false,
4487
- output: stdout.trim(),
4488
- error: `Command timed out after ${Math.round(timeout / 1e3)}s. Output so far:
4489
- ${(stdout + stderr).trim().slice(-500)}`
4504
+ output: outTrimmed,
4505
+ error: `Command timed out after ${Math.round(timeoutMs / 1e3)}s. Output so far:
4506
+ ${(ioHandler.stdout + ioHandler.stderr).trim().slice(-500)}`
4490
4507
  });
4491
- return;
4492
4508
  }
4493
4509
  if (code === 0) {
4494
- eventEmitter?.({
4495
- type: COMMAND_EVENT_TYPES.COMMAND_SUCCESS,
4496
- message: `Command completed`
4497
- });
4498
- resolve({
4499
- success: true,
4500
- output: stdout.trim() || stderr.trim()
4501
- });
4502
- } else {
4503
- eventEmitter?.({
4504
- type: COMMAND_EVENT_TYPES.COMMAND_FAILED,
4505
- message: `Command failed (exit ${code})`,
4506
- detail: stderr.slice(0, SYSTEM_LIMITS.MAX_INPUT_SLICE)
4507
- });
4508
- resolve({
4509
- success: false,
4510
- output: stdout.trim(),
4511
- error: stderr.trim() || `Process exited with code ${code}`
4512
- });
4510
+ eventEmitter?.({ type: COMMAND_EVENT_TYPES.COMMAND_SUCCESS, message: `Command completed` });
4511
+ return resolve({ success: true, output: outTrimmed || errTrimmed });
4513
4512
  }
4513
+ eventEmitter?.({ type: COMMAND_EVENT_TYPES.COMMAND_FAILED, message: `Command failed (exit ${code})`, detail: errTrimmed.slice(0, SYSTEM_LIMITS.MAX_INPUT_SLICE) });
4514
+ return resolve({ success: false, output: outTrimmed, error: errTrimmed || `Process exited with code ${code}` });
4514
4515
  });
4515
4516
  child.on("error", (err) => {
4516
4517
  clearTimeout(killTimer);
4517
- processTerminated = true;
4518
- eventEmitter?.({
4519
- type: COMMAND_EVENT_TYPES.COMMAND_ERROR,
4520
- message: `Command error: ${err.message}`
4521
- });
4522
- resolve({
4523
- success: false,
4524
- output: "",
4525
- error: err.message || String(err)
4526
- });
4518
+ processState.terminated = true;
4519
+ eventEmitter?.({ type: COMMAND_EVENT_TYPES.COMMAND_ERROR, message: `Command error: ${err.message}` });
4520
+ resolve({ success: false, output: "", error: err.message || String(err) });
4527
4521
  });
4528
4522
  });
4529
4523
  }
@@ -4702,6 +4696,13 @@ function startBackgroundProcess(command, options = {}) {
4702
4696
  bgProcess.hasExited = true;
4703
4697
  bgProcess.exitCode = code;
4704
4698
  logEvent(processId, PROCESS_EVENTS.DIED, `Process exited with code ${code}`);
4699
+ const emitter = getEventEmitter();
4700
+ if (emitter) {
4701
+ emitter({
4702
+ type: COMMAND_EVENT_TYPES.PROCESS_NOTIFICATION,
4703
+ message: `Process finished: ${bgProcess.description} (exit ${code}). Ready for analysis.`
4704
+ });
4705
+ }
4705
4706
  });
4706
4707
  setProcess(processId, bgProcess);
4707
4708
  logEvent(processId, PROCESS_EVENTS.STARTED, `${role} started: ${command.slice(0, SYSTEM_LIMITS.MAX_DESCRIPTION_LENGTH)} (PID:${child.pid})`);
@@ -6363,10 +6364,6 @@ Returns recommendations on process status, port conflicts, long-running tasks, e
6363
6364
  }
6364
6365
  ];
6365
6366
 
6366
- // src/domains/registry.ts
6367
- import { join as join11, dirname as dirname3 } from "path";
6368
- import { fileURLToPath } from "url";
6369
-
6370
6367
  // src/shared/constants/service-ports.ts
6371
6368
  var SERVICE_PORTS = {
6372
6369
  SSH: 22,
@@ -6779,7 +6776,8 @@ Requires root/sudo privileges.`,
6779
6776
  duration: { type: "number", description: "Capture duration in seconds. Default: 30" },
6780
6777
  output_file: { type: "string", description: "Path to save pcap file. Auto-generated if not specified." },
6781
6778
  count: { type: "number", description: "Number of packets to capture. Default: 1000" },
6782
- extract_creds: { type: "boolean", description: "Try to extract credentials from captured traffic. Default: true" }
6779
+ extract_creds: { type: "boolean", description: "Try to extract credentials from captured traffic. Default: true" },
6780
+ background: { type: "boolean", description: "Run the capture in the background and return immediately. Default: false" }
6783
6781
  },
6784
6782
  execute: async (p) => {
6785
6783
  const iface = p.interface || "any";
@@ -6788,11 +6786,23 @@ Requires root/sudo privileges.`,
6788
6786
  const outputFile = p.output_file || createTempFile(FILE_EXTENSIONS.PCAP);
6789
6787
  const count = p.count || NETWORK_CONFIG.DEFAULT_PACKET_COUNT;
6790
6788
  const extractCreds = p.extract_creds !== false;
6789
+ const isBackground = p.background === true;
6791
6790
  const filterFlag = filter ? `"${filter}"` : "";
6792
6791
  const captureCmd = NETWORK_COMMANDS.TCPDUMP_CAPTURE.replace("${duration}", duration.toString()).replace("${iface}", iface).replace("${count}", count.toString()).replace("${outputFile}", outputFile).replace("${filterFlag}", filterFlag);
6793
6792
  const proc = startBackgroundProcess(captureCmd, {
6794
6793
  description: `Packet capture on ${iface}`
6795
6794
  });
6795
+ if (isBackground) {
6796
+ return {
6797
+ success: true,
6798
+ output: `Packet capture started in background.
6799
+ Process ID: ${proc.id}
6800
+ Duration: ${duration}s
6801
+ Output File: ${outputFile}
6802
+
6803
+ You can continue with other tasks. To analyze the capture later, use run_cmd with tshark or tcpdump against the output file.`
6804
+ };
6805
+ }
6796
6806
  await new Promise((r) => setTimeout(r, (duration + NETWORK_CONFIG.WAIT_BUFFER_SECONDS) * 1e3));
6797
6807
  const captureOutput = getProcessOutput(proc.id);
6798
6808
  await stopBackgroundProcess(proc.id);
@@ -6958,6 +6968,56 @@ ${dnsQueries.output}
6958
6968
  }
6959
6969
  });
6960
6970
 
6971
+ // src/domains/network/analyze-pcap.ts
6972
+ var createAnalyzePcapTool = () => ({
6973
+ name: TOOL_NAMES.ANALYZE_PCAP,
6974
+ description: `Perform deep microscopic analysis of a PCAP file using tshark filters.
6975
+ Use this to:
6976
+ - Extract credentials from specific protocols
6977
+ - Reconstruct TCP streams
6978
+ - Analyze specific communication patterns (e.g. HTTP, SMB, DNS)
6979
+ - Identify potential attack vectors from intercepted traffic`,
6980
+ parameters: {
6981
+ file_path: { type: "string", description: "Path to the PCAP file to analyze." },
6982
+ filter: { type: "string", description: 'tshark display filter (e.g., "http.request.method == POST", "tcp.port == 445")' },
6983
+ fields: { type: "string", description: 'Comma-separated list of fields to extract (e.g., "http.host,http.user_agent,http.auth_basic")' },
6984
+ limit: { type: "number", description: "Maximum number of packets to process. Default: 100" }
6985
+ },
6986
+ required: ["file_path"],
6987
+ execute: async (p) => {
6988
+ const filePath = p.file_path;
6989
+ const filter = p.filter || "";
6990
+ const fields = p.fields || "";
6991
+ const limit = p.limit || 100;
6992
+ let cmd = `tshark -r ${filePath}`;
6993
+ if (filter) {
6994
+ cmd += ` -Y "${filter}"`;
6995
+ }
6996
+ if (fields) {
6997
+ const fieldArgs = fields.split(",").map((f) => `-e ${f.trim()}`).join(" ");
6998
+ cmd += ` -T fields ${fieldArgs} -E header=y -E separator=/t`;
6999
+ } else {
7000
+ cmd += ` -c ${limit}`;
7001
+ }
7002
+ const result = await runCommand(cmd);
7003
+ if (!result.success) {
7004
+ return {
7005
+ success: false,
7006
+ output: `Failed to analyze PCAP: ${result.error}`
7007
+ };
7008
+ }
7009
+ let output = `PCAP Analysis Results for ${filePath}:
7010
+ `;
7011
+ if (filter) output += `Filter: ${filter}
7012
+ `;
7013
+ if (fields) output += `Fields: ${fields}
7014
+ `;
7015
+ output += `
7016
+ ${result.output}`;
7017
+ return { success: true, output };
7018
+ }
7019
+ });
7020
+
6961
7021
  // src/engine/tools/intel-utils/types.ts
6962
7022
  var PORT_STATE = {
6963
7023
  OPEN: "open",
@@ -8057,13 +8117,12 @@ var RETRY_CONFIG = {
8057
8117
  baseDelayMs: 2e3,
8058
8118
  maxDelayMs: 6e4,
8059
8119
  jitterMs: 1e3,
8060
- // Auto-retry only for transient errors (rate limits)
8061
- // Other errors are returned to agent for autonomous decision
8062
- // Set high to handle extended rate limiting during peak usage
8063
- autoRetryMaxAttempts: 10,
8064
- // Auto-retry for rate limits
8065
- autoRetryDelayMs: 5e3
8066
- // Initial delay for rate limit retry (exponential backoff)
8120
+ /** WHY Infinity: Rate limit (429) errors are transient infrastructure delays.
8121
+ * Infinite fixed-interval retries allow the agent to eventually recover
8122
+ * without failing the entire engagement. */
8123
+ autoRetryMaxAttempts: Infinity,
8124
+ autoRetryDelayMs: 1e4
8125
+ // 10s fixed interval for 429/network errors
8067
8126
  };
8068
8127
  var LLM_LIMITS = {
8069
8128
  /** WHY 64K: non-streaming calls (orchestrator, summaries) benefit from
@@ -8320,7 +8379,8 @@ var NETWORK_TOOLS = [
8320
8379
  createDnsSpoofTool(),
8321
8380
  createMitmProxyTool(),
8322
8381
  createPacketSniffTool(),
8323
- createTrafficInterceptTool()
8382
+ createTrafficInterceptTool(),
8383
+ createAnalyzePcapTool()
8324
8384
  ];
8325
8385
 
8326
8386
  // src/domains/web/intel.ts
@@ -10739,98 +10799,82 @@ var INTEL_TOOLS = [
10739
10799
  ];
10740
10800
 
10741
10801
  // src/domains/registry.ts
10742
- var __dirname = dirname3(fileURLToPath(import.meta.url));
10743
10802
  var DOMAINS = {
10744
10803
  [SERVICE_CATEGORIES.NETWORK]: {
10745
10804
  id: SERVICE_CATEGORIES.NETWORK,
10746
10805
  name: "Network Infrastructure",
10747
- description: "Vulnerability scanning, port mapping, and network service exploitation.",
10748
- promptPath: join11(__dirname, "network/prompt.md")
10806
+ description: "Vulnerability scanning, port mapping, and network service exploitation."
10749
10807
  },
10750
10808
  [SERVICE_CATEGORIES.WEB]: {
10751
10809
  id: SERVICE_CATEGORIES.WEB,
10752
10810
  name: "Web Application",
10753
- description: "Web app security testing, injection attacks, and auth bypass.",
10754
- promptPath: join11(__dirname, "web/prompt.md")
10811
+ description: "Web app security testing, injection attacks, and auth bypass."
10755
10812
  },
10756
10813
  // Phase 2 domains (using internal categories for now)
10757
10814
  "engagement": {
10758
10815
  id: "engagement",
10759
10816
  name: "Engagement Management",
10760
- description: "Mission planning, state tracking, and finding management.",
10761
- promptPath: join11(__dirname, "engagement/prompt.md")
10817
+ description: "Mission planning, state tracking, and finding management."
10762
10818
  },
10763
10819
  "exploit": {
10764
10820
  id: "exploit",
10765
10821
  name: "Exploitation Utilities",
10766
- description: "Payload mutation, hash cracking, and wordlists.",
10767
- promptPath: join11(__dirname, "exploit/prompt.md")
10822
+ description: "Payload mutation, hash cracking, and wordlists."
10768
10823
  },
10769
10824
  "intel": {
10770
10825
  id: "intel",
10771
10826
  name: "Vulnerability Intelligence",
10772
- description: "OWASP knowledge, CVE research, and web attack surface.",
10773
- promptPath: join11(__dirname, "intel/prompt.md")
10827
+ description: "OWASP knowledge, CVE research, and web attack surface."
10774
10828
  },
10775
10829
  [SERVICE_CATEGORIES.DATABASE]: {
10776
10830
  id: SERVICE_CATEGORIES.DATABASE,
10777
10831
  name: "Database Security",
10778
- description: "SQL injection, database enumeration, and data extraction.",
10779
- promptPath: join11(__dirname, "database/prompt.md")
10832
+ description: "SQL injection, database enumeration, and data extraction."
10780
10833
  },
10781
10834
  [SERVICE_CATEGORIES.AD]: {
10782
10835
  id: SERVICE_CATEGORIES.AD,
10783
10836
  name: "Active Directory",
10784
- description: "Kerberos, LDAP, and Windows domain privilege escalation.",
10785
- promptPath: join11(__dirname, "ad/prompt.md")
10837
+ description: "Kerberos, LDAP, and Windows domain privilege escalation."
10786
10838
  },
10787
10839
  [SERVICE_CATEGORIES.EMAIL]: {
10788
10840
  id: SERVICE_CATEGORIES.EMAIL,
10789
10841
  name: "Email Services",
10790
- description: "SMTP, IMAP, POP3 security and user enumeration.",
10791
- promptPath: join11(__dirname, "email/prompt.md")
10842
+ description: "SMTP, IMAP, POP3 security and user enumeration."
10792
10843
  },
10793
10844
  [SERVICE_CATEGORIES.REMOTE_ACCESS]: {
10794
10845
  id: SERVICE_CATEGORIES.REMOTE_ACCESS,
10795
10846
  name: "Remote Access",
10796
- description: "SSH, RDP, VNC and other remote control protocols.",
10797
- promptPath: join11(__dirname, "remote-access/prompt.md")
10847
+ description: "SSH, RDP, VNC and other remote control protocols."
10798
10848
  },
10799
10849
  [SERVICE_CATEGORIES.FILE_SHARING]: {
10800
10850
  id: SERVICE_CATEGORIES.FILE_SHARING,
10801
10851
  name: "File Sharing",
10802
- description: "SMB, NFS, FTP and shared resource security.",
10803
- promptPath: join11(__dirname, "file-sharing/prompt.md")
10852
+ description: "SMB, NFS, FTP and shared resource security."
10804
10853
  },
10805
10854
  [SERVICE_CATEGORIES.CLOUD]: {
10806
10855
  id: SERVICE_CATEGORIES.CLOUD,
10807
10856
  name: "Cloud Infrastructure",
10808
- description: "AWS, Azure, and GCP security and misconfiguration.",
10809
- promptPath: join11(__dirname, "cloud/prompt.md")
10857
+ description: "AWS, Azure, and GCP security and misconfiguration."
10810
10858
  },
10811
10859
  [SERVICE_CATEGORIES.CONTAINER]: {
10812
10860
  id: SERVICE_CATEGORIES.CONTAINER,
10813
10861
  name: "Container Systems",
10814
- description: "Docker and Kubernetes security testing.",
10815
- promptPath: join11(__dirname, "container/prompt.md")
10862
+ description: "Docker and Kubernetes security testing."
10816
10863
  },
10817
10864
  [SERVICE_CATEGORIES.API]: {
10818
10865
  id: SERVICE_CATEGORIES.API,
10819
10866
  name: "API Security",
10820
- description: "REST, GraphQL, and SOAP API security testing.",
10821
- promptPath: join11(__dirname, "api/prompt.md")
10867
+ description: "REST, GraphQL, and SOAP API security testing."
10822
10868
  },
10823
10869
  [SERVICE_CATEGORIES.WIRELESS]: {
10824
10870
  id: SERVICE_CATEGORIES.WIRELESS,
10825
10871
  name: "Wireless Networks",
10826
- description: "WiFi and Bluetooth security testing.",
10827
- promptPath: join11(__dirname, "wireless/prompt.md")
10872
+ description: "WiFi and Bluetooth security testing."
10828
10873
  },
10829
10874
  [SERVICE_CATEGORIES.ICS]: {
10830
10875
  id: SERVICE_CATEGORIES.ICS,
10831
10876
  name: "Industrial Systems",
10832
- description: "Critical infrastructure - Modbus, DNP3, ENIP.",
10833
- promptPath: join11(__dirname, "ics/prompt.md")
10877
+ description: "Critical infrastructure - Modbus, DNP3, ENIP."
10834
10878
  }
10835
10879
  };
10836
10880
  function loadAllDomainTools(state, events) {
@@ -11503,20 +11547,38 @@ function classifyError(error) {
11503
11547
  }
11504
11548
 
11505
11549
  // src/engine/llm-client/retry-handler.ts
11550
+ var retryUtils = {
11551
+ sleep: (ms, signal) => {
11552
+ return new Promise((resolve, reject) => {
11553
+ if (signal?.aborted) {
11554
+ reject(new DOMException("Aborted", "AbortError"));
11555
+ return;
11556
+ }
11557
+ const timer = setTimeout(resolve, ms);
11558
+ signal?.addEventListener("abort", () => {
11559
+ clearTimeout(timer);
11560
+ reject(new DOMException("Aborted", "AbortError"));
11561
+ }, { once: true });
11562
+ });
11563
+ }
11564
+ };
11506
11565
  async function executeWithRetry(operation, callbacks) {
11507
11566
  const signal = callbacks?.abortSignal;
11508
11567
  const maxAttempts = RETRY_CONFIG.autoRetryMaxAttempts;
11509
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
11568
+ const delayMs = RETRY_CONFIG.autoRetryDelayMs;
11569
+ let attempt = 0;
11570
+ while (attempt < maxAttempts) {
11510
11571
  if (signal?.aborted) throwAbort();
11511
11572
  try {
11512
11573
  return await operation();
11513
11574
  } catch (error) {
11514
11575
  if (isAbortError(error, signal)) throwAbort();
11515
11576
  const errorInfo = classifyError(error);
11516
- if (errorInfo.type === LLM_ERROR_TYPES.RATE_LIMIT && attempt < maxAttempts - 1) {
11517
- const delayMs = RETRY_CONFIG.autoRetryDelayMs * Math.pow(2, attempt);
11518
- callbacks?.onRetry?.(attempt + 1, maxAttempts, delayMs, errorInfo.message);
11519
- await sleep(delayMs, signal);
11577
+ const isRetryableType = errorInfo.type === LLM_ERROR_TYPES.RATE_LIMIT || errorInfo.type === LLM_ERROR_TYPES.NETWORK_ERROR || errorInfo.type === LLM_ERROR_TYPES.TIMEOUT;
11578
+ if (isRetryableType) {
11579
+ attempt++;
11580
+ callbacks?.onRetry?.(attempt, maxAttempts, delayMs, errorInfo.message);
11581
+ await retryUtils.sleep(delayMs, signal);
11520
11582
  continue;
11521
11583
  }
11522
11584
  throw new LLMError(errorInfo);
@@ -11524,19 +11586,6 @@ async function executeWithRetry(operation, callbacks) {
11524
11586
  }
11525
11587
  throw new LLMError({ type: LLM_ERROR_TYPES.UNKNOWN, message: "Max retry exceeded", isRetryable: false });
11526
11588
  }
11527
- function sleep(ms, signal) {
11528
- return new Promise((resolve, reject) => {
11529
- if (signal?.aborted) {
11530
- reject(new DOMException("Aborted", "AbortError"));
11531
- return;
11532
- }
11533
- const timer = setTimeout(resolve, ms);
11534
- signal?.addEventListener("abort", () => {
11535
- clearTimeout(timer);
11536
- reject(new DOMException("Aborted", "AbortError"));
11537
- }, { once: true });
11538
- });
11539
- }
11540
11589
  function isAbortError(error, signal) {
11541
11590
  return error?.name === "AbortError" || !!signal?.aborted;
11542
11591
  }
@@ -12518,6 +12567,18 @@ function extractHypothesizedReason(failureLine) {
12518
12567
  }
12519
12568
 
12520
12569
  // src/shared/utils/context-digest/formatters.ts
12570
+ function formatContextForLLM(memo14) {
12571
+ const compact = {};
12572
+ if (memo14.keyFindings.length > 0) compact.findings = memo14.keyFindings;
12573
+ if (memo14.credentials.length > 0) compact.credentials = memo14.credentials;
12574
+ if (memo14.attackVectors.length > 0) compact.attackVectors = memo14.attackVectors;
12575
+ if (memo14.failures.length > 0) compact.failures = memo14.failures;
12576
+ if (memo14.suspicions.length > 0) compact.suspicions = memo14.suspicions;
12577
+ if (memo14.nextSteps.length > 0) compact.nextSteps = memo14.nextSteps;
12578
+ compact.attackValue = memo14.attackValue;
12579
+ if (memo14.reflection) compact.reflection = memo14.reflection;
12580
+ return JSON.stringify(compact);
12581
+ }
12521
12582
  function formatAnalystDigest(digest, filePath, originalChars) {
12522
12583
  return [
12523
12584
  digest,
@@ -12555,6 +12616,7 @@ async function digestToolOutput(output, toolName, toolInput, analystFn) {
12555
12616
  if (originalLength < PASSTHROUGH_THRESHOLD) {
12556
12617
  return {
12557
12618
  digestedOutput: output,
12619
+ contextForLLM: output,
12558
12620
  fullOutputPath: null,
12559
12621
  analystUsed: false,
12560
12622
  memo: null,
@@ -12574,8 +12636,10 @@ async function digestToolOutput(output, toolName, toolInput, analystFn) {
12574
12636
  const rawAnalystResponse = await analystFn(preprocessed, context);
12575
12637
  const memo14 = parseAnalystMemo(rawAnalystResponse);
12576
12638
  const formatted = formatAnalystDigest(rawAnalystResponse, savedOutputPath, originalLength);
12639
+ const contextForLLM = formatContextForLLM(memo14);
12577
12640
  return {
12578
12641
  digestedOutput: formatted,
12642
+ contextForLLM,
12579
12643
  fullOutputPath: savedOutputPath,
12580
12644
  analystUsed: true,
12581
12645
  memo: memo14,
@@ -12593,6 +12657,7 @@ async function digestToolOutput(output, toolName, toolInput, analystFn) {
12593
12657
  const fallback = formatFallbackDigest(preprocessed, savedOutputPath, originalLength);
12594
12658
  return {
12595
12659
  digestedOutput: fallback,
12660
+ contextForLLM: fallback,
12596
12661
  fullOutputPath: savedOutputPath,
12597
12662
  analystUsed: false,
12598
12663
  memo: null,
@@ -12696,7 +12761,7 @@ ${text}
12696
12761
 
12697
12762
  // src/agents/tool-executor/result-handler/digest.ts
12698
12763
  async function digestAndEmit(call, outputText, result, toolStartTime, llm, events) {
12699
- let digestedOutputForLLM = outputText;
12764
+ let contextOutputForLLM = outputText;
12700
12765
  let digestResult = null;
12701
12766
  try {
12702
12767
  const llmDigestFn = createLLMDigestFn(llm);
@@ -12706,12 +12771,12 @@ async function digestAndEmit(call, outputText, result, toolStartTime, llm, event
12706
12771
  JSON.stringify(call.input).slice(0, DISPLAY_LIMITS.OUTPUT_SUMMARY),
12707
12772
  llmDigestFn
12708
12773
  );
12709
- digestedOutputForLLM = digestResult.digestedOutput;
12774
+ contextOutputForLLM = digestResult.contextForLLM;
12710
12775
  } catch {
12711
- if (digestedOutputForLLM.length > AGENT_LIMITS.MAX_TOOL_OUTPUT_LENGTH) {
12712
- const truncated = digestedOutputForLLM.slice(0, AGENT_LIMITS.MAX_TOOL_OUTPUT_LENGTH);
12713
- const remaining = digestedOutputForLLM.length - AGENT_LIMITS.MAX_TOOL_OUTPUT_LENGTH;
12714
- digestedOutputForLLM = `${truncated}
12776
+ if (contextOutputForLLM.length > AGENT_LIMITS.MAX_TOOL_OUTPUT_LENGTH) {
12777
+ const truncated = contextOutputForLLM.slice(0, AGENT_LIMITS.MAX_TOOL_OUTPUT_LENGTH);
12778
+ const remaining = contextOutputForLLM.length - AGENT_LIMITS.MAX_TOOL_OUTPUT_LENGTH;
12779
+ contextOutputForLLM = `${truncated}
12715
12780
 
12716
12781
  ... [TRUNCATED ${remaining} chars] ...`;
12717
12782
  }
@@ -12720,16 +12785,16 @@ async function digestAndEmit(call, outputText, result, toolStartTime, llm, event
12720
12785
  const tuiOutput = digestResult?.digestedOutput ? `${digestResult.digestedOutput}${outputFilePath ? `
12721
12786
  \u{1F4C4} Full output: ${outputFilePath}` : ""}` : outputText.slice(0, DISPLAY_LIMITS.OUTPUT_SUMMARY);
12722
12787
  emitToolResult(events, call.name, result.success, tuiOutput, result.error, Date.now() - toolStartTime);
12723
- return { digestedOutputForLLM, digestResult };
12788
+ return { contextOutputForLLM, digestResult };
12724
12789
  }
12725
12790
 
12726
12791
  // src/agents/tool-executor/result-handler/journal.ts
12727
- function recordJournalMemo(call, result, digestedOutputForLLM, digestResult, turnState, state) {
12792
+ function recordJournalMemo(call, result, contextOutputForLLM, digestResult, turnState, state) {
12728
12793
  turnState.toolJournal.push({
12729
12794
  name: call.name,
12730
12795
  inputSummary: JSON.stringify(call.input),
12731
12796
  success: result.success,
12732
- analystSummary: digestResult?.memo ? digestResult.memo.keyFindings.join("; ") || "No key findings" : digestedOutputForLLM,
12797
+ analystSummary: digestResult?.memo ? digestResult.memo.keyFindings.join("; ") || "No key findings" : contextOutputForLLM,
12733
12798
  outputFile: digestResult?.fullOutputPath ?? null
12734
12799
  });
12735
12800
  if (digestResult?.memo) {
@@ -12843,7 +12908,7 @@ async function executeSingle(call, deps, progress) {
12843
12908
  let outputText = result.output ?? "";
12844
12909
  scanForFlags(outputText, deps.state, deps.events);
12845
12910
  outputText = handleToolResult(result, call, outputText, progress);
12846
- const { digestedOutputForLLM, digestResult } = await digestAndEmit(
12911
+ const { contextOutputForLLM, digestResult } = await digestAndEmit(
12847
12912
  call,
12848
12913
  outputText,
12849
12914
  result,
@@ -12851,8 +12916,8 @@ async function executeSingle(call, deps, progress) {
12851
12916
  deps.llm,
12852
12917
  deps.events
12853
12918
  );
12854
- recordJournalMemo(call, result, digestedOutputForLLM, digestResult, deps.turnState, deps.state);
12855
- return { toolCallId: call.id, output: digestedOutputForLLM, error: result.error };
12919
+ recordJournalMemo(call, result, contextOutputForLLM, digestResult, deps.turnState, deps.state);
12920
+ return { toolCallId: call.id, output: contextOutputForLLM, error: result.error };
12856
12921
  } catch (error) {
12857
12922
  const errorMsg = String(error);
12858
12923
  const enrichedError = enrichToolErrorContext({
@@ -13171,17 +13236,17 @@ var PHASE_TECHNIQUE_MAP = {
13171
13236
 
13172
13237
  // src/agents/prompt-builder/prompt-loader.ts
13173
13238
  import { readFileSync as readFileSync8, existsSync as existsSync11 } from "fs";
13174
- import { join as join12, dirname as dirname4 } from "path";
13175
- import { fileURLToPath as fileURLToPath2 } from "url";
13176
- var __dirname2 = dirname4(fileURLToPath2(import.meta.url));
13177
- var PROMPTS_DIR = join12(__dirname2, "../prompts");
13178
- var TECHNIQUES_DIR = join12(PROMPTS_DIR, PROMPT_PATHS.TECHNIQUES_DIR);
13239
+ import { join as join11, dirname as dirname3 } from "path";
13240
+ import { fileURLToPath } from "url";
13241
+ var __dirname = dirname3(fileURLToPath(import.meta.url));
13242
+ var PROMPTS_DIR = join11(__dirname, "../prompts");
13243
+ var TECHNIQUES_DIR = join11(PROMPTS_DIR, PROMPT_PATHS.TECHNIQUES_DIR);
13179
13244
  function loadPromptFile(filename) {
13180
- const path2 = join12(PROMPTS_DIR, filename);
13245
+ const path2 = join11(PROMPTS_DIR, filename);
13181
13246
  return existsSync11(path2) ? readFileSync8(path2, PROMPT_CONFIG.ENCODING) : "";
13182
13247
  }
13183
13248
  function loadTechniqueFile(techniqueName) {
13184
- const filePath = join12(TECHNIQUES_DIR, `${techniqueName}.md`);
13249
+ const filePath = join11(TECHNIQUES_DIR, `${techniqueName}.md`);
13185
13250
  try {
13186
13251
  if (!existsSync11(filePath)) return "";
13187
13252
  return readFileSync8(filePath, PROMPT_CONFIG.ENCODING);
@@ -13330,11 +13395,11 @@ ${lines.join("\n")}
13330
13395
 
13331
13396
  // src/shared/utils/journal/reader.ts
13332
13397
  import { readFileSync as readFileSync9, existsSync as existsSync13 } from "fs";
13333
- import { join as join14 } from "path";
13398
+ import { join as join13 } from "path";
13334
13399
 
13335
13400
  // src/shared/utils/journal/rotation.ts
13336
13401
  import { existsSync as existsSync12, readdirSync as readdirSync3, statSync as statSync4, rmSync as rmSync2 } from "fs";
13337
- import { join as join13 } from "path";
13402
+ import { join as join12 } from "path";
13338
13403
  function parseTurnNumbers(turnsDir) {
13339
13404
  if (!existsSync12(turnsDir)) return [];
13340
13405
  return readdirSync3(turnsDir).filter((e) => e.startsWith(TURN_FOLDER_PREFIX) && /^\d+$/.test(e.slice(TURN_FOLDER_PREFIX.length))).map((e) => Number(e.slice(TURN_FOLDER_PREFIX.length)));
@@ -13343,12 +13408,12 @@ function rotateTurnRecords() {
13343
13408
  try {
13344
13409
  const turnsDir = WORKSPACE.TURNS;
13345
13410
  if (!existsSync12(turnsDir)) return;
13346
- const turnDirs = parseTurnNumbers(turnsDir).map((n) => `${TURN_FOLDER_PREFIX}${n}`).filter((e) => statSync4(join13(turnsDir, e)).isDirectory()).sort((a, b) => Number(a.slice(TURN_FOLDER_PREFIX.length)) - Number(b.slice(TURN_FOLDER_PREFIX.length)));
13411
+ const turnDirs = parseTurnNumbers(turnsDir).map((n) => `${TURN_FOLDER_PREFIX}${n}`).filter((e) => statSync4(join12(turnsDir, e)).isDirectory()).sort((a, b) => Number(a.slice(TURN_FOLDER_PREFIX.length)) - Number(b.slice(TURN_FOLDER_PREFIX.length)));
13347
13412
  if (turnDirs.length > MEMORY_LIMITS.MAX_TURN_ENTRIES) {
13348
13413
  const dirsToDel = turnDirs.slice(0, turnDirs.length - MEMORY_LIMITS.MAX_TURN_ENTRIES);
13349
13414
  for (const dir of dirsToDel) {
13350
13415
  try {
13351
- rmSync2(join13(turnsDir, dir), { recursive: true, force: true });
13416
+ rmSync2(join12(turnsDir, dir), { recursive: true, force: true });
13352
13417
  } catch {
13353
13418
  }
13354
13419
  }
@@ -13367,7 +13432,7 @@ function readJournalSummary() {
13367
13432
  const turnsDir = WORKSPACE.TURNS;
13368
13433
  const turnDirs = parseTurnNumbers(turnsDir).sort((a, b) => b - a);
13369
13434
  for (const turn of turnDirs) {
13370
- const summaryPath = join14(WORKSPACE.turnPath(turn), TURN_FILES.SUMMARY);
13435
+ const summaryPath = join13(WORKSPACE.turnPath(turn), TURN_FILES.SUMMARY);
13371
13436
  if (existsSync13(summaryPath)) {
13372
13437
  return readFileSync9(summaryPath, "utf-8");
13373
13438
  }
@@ -13384,7 +13449,7 @@ function getRecentEntries(count = MEMORY_LIMITS.MAX_TURN_ENTRIES) {
13384
13449
  const entries = [];
13385
13450
  for (const turn of turnDirs) {
13386
13451
  try {
13387
- const filePath = join14(WORKSPACE.turnPath(turn), TURN_FILES.STRUCTURED);
13452
+ const filePath = join13(WORKSPACE.turnPath(turn), TURN_FILES.STRUCTURED);
13388
13453
  if (existsSync13(filePath)) {
13389
13454
  const raw = readFileSync9(filePath, "utf-8");
13390
13455
  entries.push(JSON.parse(raw));
@@ -13410,7 +13475,7 @@ function getNextTurnNumber() {
13410
13475
 
13411
13476
  // src/shared/utils/journal/summary.ts
13412
13477
  import { writeFileSync as writeFileSync9 } from "fs";
13413
- import { join as join15 } from "path";
13478
+ import { join as join14 } from "path";
13414
13479
 
13415
13480
  // src/shared/utils/journal/summary-collector.ts
13416
13481
  function collectSummaryBuckets(entries) {
@@ -13515,7 +13580,7 @@ function regenerateJournalSummary() {
13515
13580
  const turnDir = WORKSPACE.turnPath(latestTurn);
13516
13581
  ensureDirExists(turnDir);
13517
13582
  const summary = buildSummaryFromEntries(entries);
13518
- const summaryPath = join15(turnDir, TURN_FILES.SUMMARY);
13583
+ const summaryPath = join14(turnDir, TURN_FILES.SUMMARY);
13519
13584
  writeFileSync9(summaryPath, summary, "utf-8");
13520
13585
  debugLog("general", "Journal summary regenerated", {
13521
13586
  entries: entries.length,
@@ -14109,10 +14174,10 @@ function formatForPrompt(directive, isStale = false) {
14109
14174
 
14110
14175
  // src/agents/strategist/prompt-loader.ts
14111
14176
  import { readFileSync as readFileSync10, existsSync as existsSync14 } from "fs";
14112
- import { join as join16, dirname as dirname5 } from "path";
14113
- import { fileURLToPath as fileURLToPath3 } from "url";
14114
- var __dirname3 = dirname5(fileURLToPath3(import.meta.url));
14115
- var STRATEGIST_PROMPT_PATH = join16(__dirname3, "../prompts", "strategist-system.md");
14177
+ import { join as join15, dirname as dirname4 } from "path";
14178
+ import { fileURLToPath as fileURLToPath2 } from "url";
14179
+ var __dirname2 = dirname4(fileURLToPath2(import.meta.url));
14180
+ var STRATEGIST_PROMPT_PATH = join15(__dirname2, "../prompts", "strategist-system.md");
14116
14181
  function loadSystemPrompt() {
14117
14182
  try {
14118
14183
  if (existsSync14(STRATEGIST_PROMPT_PATH)) {
@@ -14513,7 +14578,7 @@ async function processReflection(toolJournal, memo14, phase, reflector) {
14513
14578
 
14514
14579
  // src/agents/main-agent/turn-recorder.ts
14515
14580
  import { writeFileSync as writeFileSync10, existsSync as existsSync15, readFileSync as readFileSync11 } from "fs";
14516
- import { join as join17 } from "path";
14581
+ import { join as join16 } from "path";
14517
14582
  async function recordTurn(context) {
14518
14583
  const { turnCounter, phase, toolJournal, memo: memo14, reflections, summaryRegenerator } = context;
14519
14584
  if (toolJournal.length === 0) {
@@ -14551,8 +14616,8 @@ async function createTurnArchive(turnCounter, ctx) {
14551
14616
  memo: memo14,
14552
14617
  reflection: entry.reflection
14553
14618
  });
14554
- writeFileSync10(join17(turnDir, TURN_FILES.RECORD), turnContent, "utf-8");
14555
- writeFileSync10(join17(turnDir, TURN_FILES.STRUCTURED), JSON.stringify(entry, null, 2), "utf-8");
14619
+ writeFileSync10(join16(turnDir, TURN_FILES.RECORD), turnContent, "utf-8");
14620
+ writeFileSync10(join16(turnDir, TURN_FILES.STRUCTURED), JSON.stringify(entry, null, 2), "utf-8");
14556
14621
  writeAnalystFile(turnDir, memo14);
14557
14622
  } catch {
14558
14623
  }
@@ -14566,7 +14631,7 @@ function writeAnalystFile(turnDir, memo14) {
14566
14631
  if (memo14.suspicions.length > 0) memoLines.push("## Suspicious", ...memo14.suspicions.map((s) => `- ${s}`), "");
14567
14632
  if (memo14.nextSteps.length > 0) memoLines.push("## Next Steps", ...memo14.nextSteps.map((n) => `- ${n}`), "");
14568
14633
  if (memoLines.length > 0) {
14569
- writeFileSync10(join17(turnDir, TURN_FILES.ANALYST), memoLines.join("\n"), "utf-8");
14634
+ writeFileSync10(join16(turnDir, TURN_FILES.ANALYST), memoLines.join("\n"), "utf-8");
14570
14635
  flowLog(
14571
14636
  "\u2462Analyst",
14572
14637
  "\u2192",
@@ -14579,11 +14644,11 @@ async function regenerateSummary(turnCounter, summaryRegenerator, ctx) {
14579
14644
  const { entry, journalTools, memo: memo14, phase } = ctx;
14580
14645
  try {
14581
14646
  const turnDir = WORKSPACE.turnPath(turnCounter);
14582
- const summaryPath = join17(turnDir, TURN_FILES.SUMMARY);
14647
+ const summaryPath = join16(turnDir, TURN_FILES.SUMMARY);
14583
14648
  const prevTurn = turnCounter - 1;
14584
14649
  let existingSummary = null;
14585
14650
  if (prevTurn >= 1) {
14586
- const prevSummaryPath = join17(WORKSPACE.turnPath(prevTurn), TURN_FILES.SUMMARY);
14651
+ const prevSummaryPath = join16(WORKSPACE.turnPath(prevTurn), TURN_FILES.SUMMARY);
14587
14652
  if (existsSync15(prevSummaryPath)) {
14588
14653
  existingSummary = readFileSync11(prevSummaryPath, "utf-8");
14589
14654
  }
@@ -15198,6 +15263,10 @@ function createLifecycleHandlers(agent, state, _reasoningBufferRef) {
15198
15263
  todo: s.getTodo().length
15199
15264
  });
15200
15265
  };
15266
+ const onNotification = (e) => {
15267
+ const prefix = e.data.level === "error" ? "\u274C" : e.data.level === "warning" ? "\u26A0\uFE0F" : e.data.level === "success" ? "\u2705" : "\u{1F514}";
15268
+ addMessage("system", `${prefix} [${e.data.title}] ${e.data.message}`);
15269
+ };
15201
15270
  return {
15202
15271
  onComplete,
15203
15272
  onRetry,
@@ -15206,7 +15275,8 @@ function createLifecycleHandlers(agent, state, _reasoningBufferRef) {
15206
15275
  onAIResponse,
15207
15276
  onUsageUpdate,
15208
15277
  onFlagFound,
15209
- onPhaseChange
15278
+ onPhaseChange,
15279
+ onNotification
15210
15280
  };
15211
15281
  }
15212
15282
 
@@ -15276,9 +15346,26 @@ function setupInputHandlers(setInputRequest, addMessage) {
15276
15346
  }
15277
15347
 
15278
15348
  // src/platform/tui/hooks/useAgentEvents/handlers/command.ts
15279
- function setupCommandHandlers(addMessage) {
15349
+ function setupCommandHandlers(addMessage, setCurrentStatus) {
15350
+ let lastStatusBase = "";
15280
15351
  setCommandEventEmitter((event) => {
15281
- if (event.type === COMMAND_EVENT_TYPES.COMMAND_START || event.type === COMMAND_EVENT_TYPES.COMMAND_SUCCESS) {
15352
+ if (event.type === COMMAND_EVENT_TYPES.COMMAND_START) {
15353
+ lastStatusBase = event.message;
15354
+ return;
15355
+ }
15356
+ if (event.type === COMMAND_EVENT_TYPES.COMMAND_SUCCESS) {
15357
+ return;
15358
+ }
15359
+ if (event.type === COMMAND_EVENT_TYPES.COMMAND_STDOUT) {
15360
+ if (lastStatusBase) {
15361
+ const cleanLine = event.message.replace(/\s+/g, " ").slice(0, 100);
15362
+ setCurrentStatus(`${lastStatusBase}
15363
+ [live] ${cleanLine}`);
15364
+ }
15365
+ return;
15366
+ }
15367
+ if (event.type === COMMAND_EVENT_TYPES.PROCESS_NOTIFICATION) {
15368
+ addMessage("system", `\u{1F514} [Background Alert] ${event.message}`);
15282
15369
  return;
15283
15370
  }
15284
15371
  const icon = getCommandEventIcon(event.type);
@@ -15325,7 +15412,7 @@ var useAgentEvents = (agent, eventsRef, state) => {
15325
15412
  }, reasoningBufferRef);
15326
15413
  const reasoningHandlers = createReasoningHandlers({ addMessage, setCurrentStatus }, reasoningBufferRef);
15327
15414
  const cleanupInput = setupInputHandlers(setInputRequest, addMessage);
15328
- const cleanupCommand = setupCommandHandlers(addMessage);
15415
+ const cleanupCommand = setupCommandHandlers(addMessage, setCurrentStatus);
15329
15416
  const updateStats = () => {
15330
15417
  const s = agent.getState();
15331
15418
  setStats({
@@ -15344,6 +15431,7 @@ var useAgentEvents = (agent, eventsRef, state) => {
15344
15431
  events.on(EVENT_TYPES.USAGE_UPDATE, lifecycleHandlers.onUsageUpdate);
15345
15432
  events.on(EVENT_TYPES.FLAG_FOUND, lifecycleHandlers.onFlagFound);
15346
15433
  events.on(EVENT_TYPES.PHASE_CHANGE, lifecycleHandlers.onPhaseChange);
15434
+ events.on(EVENT_TYPES.NOTIFICATION, lifecycleHandlers.onNotification);
15347
15435
  events.on(EVENT_TYPES.STATE_CHANGE, updateStats);
15348
15436
  events.on(EVENT_TYPES.START, updateStats);
15349
15437
  events.on(EVENT_TYPES.REASONING_START, reasoningHandlers.onStart);
@@ -16003,7 +16091,7 @@ var useCommands = (props) => {
16003
16091
  };
16004
16092
 
16005
16093
  // src/platform/tui/hooks/useKeyboardShortcuts.ts
16006
- import { useCallback as useCallback7, useEffect as useEffect4 } from "react";
16094
+ import { useCallback as useCallback6, useEffect as useEffect4 } from "react";
16007
16095
  import { useInput } from "ink";
16008
16096
 
16009
16097
  // src/platform/tui/hooks/keyboard/useDoubleTap.ts
@@ -16093,17 +16181,6 @@ var useEscHandler = ({
16093
16181
  return { handleEsc };
16094
16182
  };
16095
16183
 
16096
- // src/platform/tui/hooks/keyboard/useScrollHandler.ts
16097
- import { useRef as useRef5, useCallback as useCallback6 } from "react";
16098
- var useScrollHandler = ({ onScroll }) => {
16099
- const onScrollRef = useRef5(onScroll);
16100
- onScrollRef.current = onScroll;
16101
- const handleScroll = useCallback6((delta) => {
16102
- onScrollRef.current?.(delta);
16103
- }, []);
16104
- return { handleScroll };
16105
- };
16106
-
16107
16184
  // src/platform/tui/hooks/useKeyboardShortcuts.ts
16108
16185
  var DOUBLE_TAP_WINDOW = 3e3;
16109
16186
  var useKeyboardShortcuts = ({
@@ -16115,8 +16192,7 @@ var useKeyboardShortcuts = ({
16115
16192
  inputRequestRef,
16116
16193
  isModalOpenRef,
16117
16194
  inputRef,
16118
- clearInput,
16119
- onScroll
16195
+ clearInput
16120
16196
  }) => {
16121
16197
  const { handleCtrlC } = useExitHandler({
16122
16198
  handleExit,
@@ -16135,18 +16211,11 @@ var useKeyboardShortcuts = ({
16135
16211
  inputRef,
16136
16212
  windowMs: DOUBLE_TAP_WINDOW
16137
16213
  });
16138
- const { handleScroll } = useScrollHandler({ onScroll });
16139
- useInput(useCallback7((ch, key) => {
16214
+ useInput(useCallback6((ch, key) => {
16140
16215
  if (isModalOpenRef.current) return;
16141
16216
  if (key.escape) handleEsc();
16142
16217
  if (key.ctrl && ch === "c") handleCtrlC();
16143
- if (key.pageUp) {
16144
- handleScroll(-10);
16145
- }
16146
- if (key.pageDown) {
16147
- handleScroll(10);
16148
- }
16149
- }, [handleEsc, handleCtrlC, handleScroll, isModalOpenRef]));
16218
+ }, [handleEsc, handleCtrlC, isModalOpenRef]));
16150
16219
  useEffect4(() => {
16151
16220
  const onSignal = () => handleCtrlC();
16152
16221
  process.on("SIGINT", onSignal);
@@ -16177,33 +16246,32 @@ var AnimationProvider = ({ children }) => {
16177
16246
  var useAnimationTick = () => useContext(AnimationContext);
16178
16247
 
16179
16248
  // src/platform/tui/components/MessageList.tsx
16180
- import { memo as memo7, useState as useState4, useCallback as useCallback8, useRef as useRef6 } from "react";
16181
- import { Box as Box8, Text as Text8, useInput as useInput2 } from "ink";
16249
+ import { memo as memo7 } from "react";
16250
+ import { Box as Box8, Static } from "ink";
16182
16251
 
16183
16252
  // src/platform/tui/components/messages/ThinkingBlock.tsx
16184
16253
  import { memo } from "react";
16185
16254
  import { Box, Text } from "ink";
16186
16255
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
16187
16256
  var THINKING_PREVIEW_LINES = 3;
16188
- var ThinkingBlock = memo(({ msg, isExpanded }) => {
16257
+ var ThinkingBlock = memo(({ msg }) => {
16189
16258
  const lines = msg.content.split("\n");
16190
16259
  const charCount = msg.content.length;
16191
16260
  const estTokens = Math.round(charCount / LLM_LIMITS.charsPerTokenEstimate);
16192
16261
  const hiddenCount = lines.length - THINKING_PREVIEW_LINES;
16193
- const visibleLines = isExpanded ? lines : lines.slice(0, THINKING_PREVIEW_LINES);
16262
+ const visibleLines = lines.slice(0, THINKING_PREVIEW_LINES);
16194
16263
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 0, children: [
16195
16264
  /* @__PURE__ */ jsxs(Box, { children: [
16196
16265
  /* @__PURE__ */ jsx2(Text, { color: THEME.dimGray, children: " \u25C6 " }),
16197
16266
  /* @__PURE__ */ jsx2(Text, { color: THEME.gray, children: "Reasoning" }),
16198
16267
  /* @__PURE__ */ jsx2(Text, { color: THEME.dimGray, children: ` (~${estTokens} tokens)` }),
16199
- !isExpanded && hiddenCount > 0 && /* @__PURE__ */ jsx2(Text, { color: THEME.dimGray, children: ` [+${hiddenCount} lines \xB7 R]` }),
16200
- isExpanded && /* @__PURE__ */ jsx2(Text, { color: THEME.dimGray, children: " [R to collapse]" })
16268
+ hiddenCount > 0 && /* @__PURE__ */ jsx2(Text, { color: THEME.dimGray, children: ` [+${hiddenCount} lines]` })
16201
16269
  ] }),
16202
16270
  visibleLines.map((line, i) => /* @__PURE__ */ jsxs(Box, { children: [
16203
16271
  /* @__PURE__ */ jsx2(Text, { dimColor: true, children: " \u2502 " }),
16204
16272
  /* @__PURE__ */ jsx2(Text, { dimColor: true, children: line })
16205
16273
  ] }, i)),
16206
- !isExpanded && hiddenCount > 0 && /* @__PURE__ */ jsxs(Box, { children: [
16274
+ hiddenCount > 0 && /* @__PURE__ */ jsxs(Box, { children: [
16207
16275
  /* @__PURE__ */ jsx2(Text, { dimColor: true, children: " \u2502 " }),
16208
16276
  /* @__PURE__ */ jsx2(Text, { dimColor: true, children: "..." })
16209
16277
  ] })
@@ -16611,23 +16679,22 @@ function classifyResult(content) {
16611
16679
  if (content.startsWith("\u26A0") || content.startsWith("!")) return RESULT_KINDS.WARNING;
16612
16680
  return RESULT_KINDS.NEUTRAL;
16613
16681
  }
16614
- function computeResultDisplay(content, isExpanded) {
16682
+ function computeResultDisplay(content) {
16615
16683
  const kind = classifyResult(content);
16616
16684
  const isFailure = kind === RESULT_KINDS.FAILURE;
16617
16685
  const lines = content.split("\n");
16618
16686
  const firstLine = lines[0] || "";
16619
16687
  const restLines = lines.slice(1);
16620
16688
  const hiddenCount = restLines.length;
16621
- const effectiveExpanded = isFailure ? true : isExpanded;
16622
- const showCount = effectiveExpanded ? hiddenCount : Math.min(hiddenCount, RESULT_PREVIEW_LINES);
16623
- const visibleRest = restLines.slice(0, showCount);
16624
- const hasMore = !effectiveExpanded && hiddenCount > RESULT_PREVIEW_LINES;
16625
- return { firstLine, visibleRest, hasMore, effectiveExpanded };
16689
+ const previewLines = isFailure ? RESULT_PREVIEW_LINES * 2 : RESULT_PREVIEW_LINES;
16690
+ const visibleRest = restLines.slice(0, previewLines);
16691
+ const hasMore = hiddenCount > previewLines;
16692
+ return { firstLine, visibleRest, hasMore };
16626
16693
  }
16627
16694
 
16628
16695
  // src/platform/tui/components/messages/MessageRow.tsx
16629
16696
  import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
16630
- var MessageRow = memo4(({ msg, isExpanded }) => {
16697
+ var MessageRow = memo4(({ msg }) => {
16631
16698
  if (msg.type === "status") {
16632
16699
  const statusData = parseStatusContent(msg.content);
16633
16700
  if (statusData) {
@@ -16646,7 +16713,7 @@ var MessageRow = memo4(({ msg, isExpanded }) => {
16646
16713
  ] }, msg.id);
16647
16714
  }
16648
16715
  if (msg.type === "thinking") {
16649
- return /* @__PURE__ */ jsx6(ThinkingBlock, { msg, isExpanded });
16716
+ return /* @__PURE__ */ jsx6(ThinkingBlock, { msg });
16650
16717
  }
16651
16718
  if (msg.type === "tool") {
16652
16719
  return /* @__PURE__ */ jsx6(Box5, { children: /* @__PURE__ */ jsx6(ToolCard, { content: msg.content }) }, msg.id);
@@ -16654,15 +16721,13 @@ var MessageRow = memo4(({ msg, isExpanded }) => {
16654
16721
  if (msg.type === "result") {
16655
16722
  const kind = classifyResult(msg.content);
16656
16723
  const color = kind === "success" ? THEME.primary : kind === "failure" ? THEME.red : kind === "warning" ? THEME.yellow : THEME.gray;
16657
- const { firstLine, visibleRest, hasMore, effectiveExpanded } = computeResultDisplay(msg.content, isExpanded);
16724
+ const { firstLine, visibleRest, hasMore } = computeResultDisplay(msg.content);
16658
16725
  const hiddenCount = msg.content.split("\n").length - 1;
16659
- const isFailure = kind === "failure";
16660
16726
  return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
16661
16727
  /* @__PURE__ */ jsxs5(Box5, { children: [
16662
16728
  /* @__PURE__ */ jsx6(Text5, { color: THEME.dimGray, children: " \u2514 " }),
16663
16729
  /* @__PURE__ */ jsx6(Text5, { color, children: firstLine }),
16664
- hasMore && /* @__PURE__ */ jsx6(Text5, { dimColor: true, color: THEME.dimGray, children: ` [+${hiddenCount - RESULT_PREVIEW_LINES} \xB7 T]` }),
16665
- effectiveExpanded && hiddenCount > RESULT_PREVIEW_LINES && !isFailure && /* @__PURE__ */ jsx6(Text5, { dimColor: true, color: THEME.dimGray, children: " [T \u2193]" })
16730
+ hasMore && /* @__PURE__ */ jsx6(Text5, { dimColor: true, color: THEME.dimGray, children: ` [+${hiddenCount - RESULT_PREVIEW_LINES}]` })
16666
16731
  ] }),
16667
16732
  visibleRest.map((line, i) => /* @__PURE__ */ jsxs5(Box5, { children: [
16668
16733
  /* @__PURE__ */ jsx6(Text5, { color: THEME.dimGray, children: " " }),
@@ -16677,18 +16742,11 @@ var MessageRow = memo4(({ msg, isExpanded }) => {
16677
16742
  const eLines = msg.content.split("\n");
16678
16743
  const eFirst = eLines[0] || msg.content;
16679
16744
  const eHidden = eLines.length - 1;
16680
- return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
16681
- /* @__PURE__ */ jsxs5(Box5, { children: [
16682
- /* @__PURE__ */ jsx6(Text5, { color: THEME.red, children: " \u2717 " }),
16683
- /* @__PURE__ */ jsx6(Text5, { color: THEME.red, children: eFirst }),
16684
- !isExpanded && eHidden > 0 && /* @__PURE__ */ jsx6(Text5, { dimColor: true, color: THEME.dimGray, children: ` [+${eHidden} \xB7 E]` }),
16685
- isExpanded && eHidden > 0 && /* @__PURE__ */ jsx6(Text5, { dimColor: true, color: THEME.dimGray, children: " [E \u2191]" })
16686
- ] }),
16687
- isExpanded && eLines.slice(1).map((line, i) => /* @__PURE__ */ jsxs5(Box5, { children: [
16688
- /* @__PURE__ */ jsx6(Text5, { color: THEME.dimGray, children: " " }),
16689
- /* @__PURE__ */ jsx6(Text5, { dimColor: true, color: THEME.red, children: line })
16690
- ] }, i))
16691
- ] }, msg.id);
16745
+ return /* @__PURE__ */ jsx6(Box5, { flexDirection: "column", children: /* @__PURE__ */ jsxs5(Box5, { children: [
16746
+ /* @__PURE__ */ jsx6(Text5, { color: THEME.red, children: " \u2717 " }),
16747
+ /* @__PURE__ */ jsx6(Text5, { color: THEME.red, children: eFirst }),
16748
+ eHidden > 0 && /* @__PURE__ */ jsx6(Text5, { dimColor: true, color: THEME.dimGray, children: ` [+${eHidden}]` })
16749
+ ] }) }, msg.id);
16692
16750
  }
16693
16751
  if (msg.type === "user") {
16694
16752
  return /* @__PURE__ */ jsxs5(Box5, { children: [
@@ -16726,11 +16784,9 @@ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
16726
16784
  var EmptyState = memo6(({ modelName, autoApproveMode, version }) => {
16727
16785
  const autoLabel = autoApproveMode ? "ON" : "OFF";
16728
16786
  const autoColor = autoApproveMode ? THEME.primary : THEME.gray;
16729
- return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", paddingX: 1, paddingY: 1, children: [
16787
+ return /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [
16730
16788
  /* @__PURE__ */ jsx8(ShimmerBanner, { banner: ASCII_BANNER }),
16731
- /* @__PURE__ */ jsx8(Text7, { children: " " }),
16732
- /* @__PURE__ */ jsxs6(Box7, { gap: 2, children: [
16733
- /* @__PURE__ */ jsx8(Text7, { color: THEME.primary, bold: true, children: "\u25C8 Pentesting" }),
16789
+ /* @__PURE__ */ jsxs6(Box7, { paddingLeft: 2, gap: 2, children: [
16734
16790
  version && /* @__PURE__ */ jsx8(Text7, { color: THEME.dimGray, children: `v${version}` }),
16735
16791
  modelName && /* @__PURE__ */ jsxs6(Text7, { color: THEME.dimGray, children: [
16736
16792
  "\xB7",
@@ -16744,99 +16800,35 @@ var EmptyState = memo6(({ modelName, autoApproveMode, version }) => {
16744
16800
  ] })
16745
16801
  ] }),
16746
16802
  /* @__PURE__ */ jsx8(Text7, { children: " " }),
16747
- /* @__PURE__ */ jsx8(Text7, { color: THEME.gray, children: " Get started:" }),
16748
- /* @__PURE__ */ jsxs6(Text7, { color: THEME.gray, children: [
16749
- " ",
16750
- " ",
16751
- /* @__PURE__ */ jsxs6(Text7, { color: THEME.white, children: [
16752
- "/target ",
16753
- "<ip>"
16803
+ /* @__PURE__ */ jsxs6(Box7, { flexDirection: "column", paddingLeft: 2, children: [
16804
+ /* @__PURE__ */ jsx8(Text7, { color: THEME.gray, children: "Get started:" }),
16805
+ /* @__PURE__ */ jsxs6(Text7, { color: THEME.gray, children: [
16806
+ " ",
16807
+ /* @__PURE__ */ jsxs6(Text7, { color: THEME.white, children: [
16808
+ "/target ",
16809
+ "<ip>"
16810
+ ] }),
16811
+ " \u2014 Set target IP or domain"
16754
16812
  ] }),
16755
- " \u2014 Set target IP or domain"
16756
- ] }),
16757
- /* @__PURE__ */ jsxs6(Text7, { color: THEME.gray, children: [
16758
- " ",
16759
- " ",
16760
- /* @__PURE__ */ jsx8(Text7, { color: THEME.white, children: "/start" }),
16761
- " \u2014 Begin autonomous pentest"
16762
- ] }),
16763
- /* @__PURE__ */ jsxs6(Text7, { color: THEME.gray, children: [
16764
- " ",
16765
- " ",
16766
- /* @__PURE__ */ jsx8(Text7, { color: THEME.white, children: "/help" }),
16767
- " \u2014 Show all commands"
16768
- ] }),
16769
- /* @__PURE__ */ jsx8(Text7, { children: " " }),
16770
- /* @__PURE__ */ jsx8(Text7, { color: THEME.dimGray, children: " Or just type a task and press Enter." })
16813
+ /* @__PURE__ */ jsxs6(Text7, { color: THEME.gray, children: [
16814
+ " ",
16815
+ /* @__PURE__ */ jsx8(Text7, { color: THEME.white, children: "/start" }),
16816
+ " \u2014 Begin autonomous pentest"
16817
+ ] }),
16818
+ /* @__PURE__ */ jsxs6(Text7, { color: THEME.gray, children: [
16819
+ " ",
16820
+ /* @__PURE__ */ jsx8(Text7, { color: THEME.white, children: "/help" }),
16821
+ " \u2014 Show all commands"
16822
+ ] }),
16823
+ /* @__PURE__ */ jsx8(Text7, { children: " " }),
16824
+ /* @__PURE__ */ jsx8(Text7, { color: THEME.dimGray, children: "Or just type a task and press Enter." })
16825
+ ] })
16771
16826
  ] });
16772
16827
  });
16773
16828
 
16774
- // src/platform/tui/components/messages/sliding-window.ts
16775
- var MAX_VISIBLE_MESSAGES = 200;
16776
- function computeSlidingWindow(messages, scrollOffset) {
16777
- const allVisible = messages.length > MAX_VISIBLE_MESSAGES ? messages.slice(-MAX_VISIBLE_MESSAGES) : messages;
16778
- const clampedOffset = Math.min(scrollOffset, Math.max(0, allVisible.length - 1));
16779
- const visibleMessages = clampedOffset === 0 ? allVisible : allVisible.slice(0, allVisible.length - clampedOffset);
16780
- const hiddenAbove = allVisible.length - visibleMessages.length;
16781
- return { visibleMessages, hiddenAbove, clampedOffset };
16782
- }
16783
-
16784
16829
  // src/platform/tui/components/MessageList.tsx
16785
- import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
16786
- var MessageList = memo7(({ messages, isModalOpen, modelName, autoApproveMode, version, scrollOffset = 0 }) => {
16787
- const [expandedIds, setExpandedIds] = useState4(/* @__PURE__ */ new Set());
16788
- const messagesRef = useRef6(messages);
16789
- messagesRef.current = messages;
16790
- const isModalOpenRef = useRef6(isModalOpen);
16791
- isModalOpenRef.current = isModalOpen;
16792
- useInput2(useCallback8((_ch, key) => {
16793
- if (isModalOpenRef.current || key.ctrl) return;
16794
- if (_ch === "r") {
16795
- const msgs = messagesRef.current;
16796
- for (let i = msgs.length - 1; i >= 0; i--) {
16797
- if (msgs[i].type === "thinking") {
16798
- const id = msgs[i].id;
16799
- setExpandedIds((prev) => {
16800
- const next = new Set(prev);
16801
- if (next.has(id)) next.delete(id);
16802
- else next.add(id);
16803
- return next;
16804
- });
16805
- break;
16806
- }
16807
- }
16808
- }
16809
- if (_ch === "e") {
16810
- const msgs = messagesRef.current;
16811
- for (let i = msgs.length - 1; i >= 0; i--) {
16812
- if (msgs[i].type === "error") {
16813
- const id = msgs[i].id;
16814
- setExpandedIds((prev) => {
16815
- const next = new Set(prev);
16816
- if (next.has(id)) next.delete(id);
16817
- else next.add(id);
16818
- return next;
16819
- });
16820
- break;
16821
- }
16822
- }
16823
- }
16824
- if (_ch === "t") {
16825
- const msgs = messagesRef.current;
16826
- for (let i = msgs.length - 1; i >= 0; i--) {
16827
- if (msgs[i].type === "result") {
16828
- const id = msgs[i].id;
16829
- setExpandedIds((prev) => {
16830
- const next = new Set(prev);
16831
- if (next.has(id)) next.delete(id);
16832
- else next.add(id);
16833
- return next;
16834
- });
16835
- break;
16836
- }
16837
- }
16838
- }
16839
- }, []));
16830
+ import { jsx as jsx9 } from "react/jsx-runtime";
16831
+ var MessageList = memo7(({ messages, modelName, autoApproveMode, version }) => {
16840
16832
  if (messages.length === 0) {
16841
16833
  return /* @__PURE__ */ jsx9(
16842
16834
  EmptyState,
@@ -16847,30 +16839,19 @@ var MessageList = memo7(({ messages, isModalOpen, modelName, autoApproveMode, ve
16847
16839
  }
16848
16840
  );
16849
16841
  }
16850
- const { visibleMessages, hiddenAbove, clampedOffset } = computeSlidingWindow(messages, scrollOffset);
16851
- return /* @__PURE__ */ jsxs7(Box8, { flexDirection: "column", children: [
16852
- clampedOffset > 0 && /* @__PURE__ */ jsx9(Box8, { paddingX: 1, children: /* @__PURE__ */ jsx9(Text8, { dimColor: true, color: THEME.dimGray, children: `\u2191 ${hiddenAbove} message${hiddenAbove !== 1 ? "s" : ""} above \xB7 PgDn to scroll down` }) }),
16853
- visibleMessages.map((msg) => /* @__PURE__ */ jsx9(
16854
- MessageRow,
16855
- {
16856
- msg,
16857
- isExpanded: expandedIds.has(msg.id)
16858
- },
16859
- msg.id
16860
- ))
16861
- ] });
16842
+ return /* @__PURE__ */ jsx9(Box8, { flexDirection: "column", children: /* @__PURE__ */ jsx9(Static, { items: messages, children: (msg) => /* @__PURE__ */ jsx9(MessageRow, { msg }, msg.id) }) });
16862
16843
  });
16863
16844
 
16864
16845
  // src/platform/tui/components/StatusDisplay.tsx
16865
16846
  import { memo as memo10 } from "react";
16866
- import { Box as Box11, Text as Text13 } from "ink";
16847
+ import { Box as Box11, Text as Text12 } from "ink";
16867
16848
 
16868
16849
  // src/platform/tui/hooks/useStatusTimer.ts
16869
- import { useState as useState5, useEffect as useEffect6, useRef as useRef7 } from "react";
16850
+ import { useState as useState4, useEffect as useEffect6, useRef as useRef5 } from "react";
16870
16851
  var useStatusTimer = (currentStatus, isProcessing) => {
16871
- const [statusElapsed, setStatusElapsed] = useState5(0);
16872
- const statusTimerRef = useRef7(null);
16873
- const statusStartRef = useRef7(Date.now());
16852
+ const [statusElapsed, setStatusElapsed] = useState4(0);
16853
+ const statusTimerRef = useRef5(null);
16854
+ const statusStartRef = useRef5(Date.now());
16874
16855
  useEffect6(() => {
16875
16856
  if (statusTimerRef.current) clearInterval(statusTimerRef.current);
16876
16857
  if (isProcessing && currentStatus) {
@@ -16891,11 +16872,11 @@ var useStatusTimer = (currentStatus, isProcessing) => {
16891
16872
  };
16892
16873
 
16893
16874
  // src/platform/tui/components/status/RetryView.tsx
16894
- import { Box as Box9, Text as Text10 } from "ink";
16875
+ import { Box as Box9, Text as Text9 } from "ink";
16895
16876
 
16896
16877
  // src/platform/tui/components/StarSpinner.tsx
16897
16878
  import { memo as memo8 } from "react";
16898
- import { Text as Text9 } from "ink";
16879
+ import { Text as Text8 } from "ink";
16899
16880
  import { jsx as jsx10 } from "react/jsx-runtime";
16900
16881
  var FRAMES = [
16901
16882
  "\xB7",
@@ -16916,31 +16897,31 @@ var FRAMES = [
16916
16897
  var StarSpinner = memo8(({ color }) => {
16917
16898
  const tick = useAnimationTick();
16918
16899
  const index = tick % FRAMES.length;
16919
- return /* @__PURE__ */ jsx10(Text9, { color: color || "yellow", children: FRAMES[index] });
16900
+ return /* @__PURE__ */ jsx10(Text8, { color: color || "yellow", children: FRAMES[index] });
16920
16901
  });
16921
16902
 
16922
16903
  // src/platform/tui/components/status/RetryView.tsx
16923
- import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
16904
+ import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
16924
16905
  var RetryView = ({ retryState }) => {
16925
16906
  const truncateError = (err) => {
16926
16907
  return err.length > DISPLAY_LIMITS.RETRY_ERROR_PREVIEW ? err.substring(0, DISPLAY_LIMITS.RETRY_ERROR_TRUNCATED) + "..." : err;
16927
16908
  };
16928
- return /* @__PURE__ */ jsxs8(Box9, { flexDirection: "column", height: 2, children: [
16929
- /* @__PURE__ */ jsxs8(Box9, { children: [
16930
- /* @__PURE__ */ jsx11(Text10, { color: THEME.yellow, wrap: "truncate", children: /* @__PURE__ */ jsx11(StarSpinner, { color: THEME.yellow }) }),
16931
- /* @__PURE__ */ jsxs8(Text10, { color: THEME.yellow, bold: true, wrap: "truncate", children: [
16909
+ return /* @__PURE__ */ jsxs7(Box9, { flexDirection: "column", height: 2, children: [
16910
+ /* @__PURE__ */ jsxs7(Box9, { children: [
16911
+ /* @__PURE__ */ jsx11(Text9, { color: THEME.yellow, wrap: "truncate", children: /* @__PURE__ */ jsx11(StarSpinner, { color: THEME.yellow }) }),
16912
+ /* @__PURE__ */ jsxs7(Text9, { color: THEME.yellow, bold: true, wrap: "truncate", children: [
16932
16913
  " \u29F3 Retry #",
16933
16914
  retryState.attempt,
16934
16915
  "/",
16935
16916
  retryState.maxRetries
16936
16917
  ] }),
16937
- /* @__PURE__ */ jsxs8(Text10, { color: THEME.gray, wrap: "truncate", children: [
16918
+ /* @__PURE__ */ jsxs7(Text9, { color: THEME.gray, wrap: "truncate", children: [
16938
16919
  " \u2014 ",
16939
16920
  retryState.countdown,
16940
16921
  "s"
16941
16922
  ] })
16942
16923
  ] }),
16943
- /* @__PURE__ */ jsx11(Box9, { children: /* @__PURE__ */ jsxs8(Text10, { color: THEME.gray, wrap: "truncate", children: [
16924
+ /* @__PURE__ */ jsx11(Box9, { children: /* @__PURE__ */ jsxs7(Text9, { color: THEME.gray, wrap: "truncate", children: [
16944
16925
  " ",
16945
16926
  truncateError(retryState.error)
16946
16927
  ] }) })
@@ -16948,11 +16929,11 @@ var RetryView = ({ retryState }) => {
16948
16929
  };
16949
16930
 
16950
16931
  // src/platform/tui/components/status/ProcessingView.tsx
16951
- import { Box as Box10, Text as Text12 } from "ink";
16932
+ import { Box as Box10, Text as Text11 } from "ink";
16952
16933
 
16953
16934
  // src/platform/tui/components/ShimmerText.tsx
16954
16935
  import { memo as memo9 } from "react";
16955
- import { Text as Text11 } from "ink";
16936
+ import { Text as Text10 } from "ink";
16956
16937
  import { jsx as jsx12 } from "react/jsx-runtime";
16957
16938
  var WAVE_SPEED = 0.25 * (120 / ANIM_TICK_MS);
16958
16939
  var CHAR_PHASE_GAP = 0.55;
@@ -16965,16 +16946,16 @@ function sinToColor(sin) {
16965
16946
  var ShimmerText = memo9(({ children, bold, phase = 0 }) => {
16966
16947
  const tick = useAnimationTick();
16967
16948
  const globalPhase = tick * WAVE_SPEED + phase;
16968
- return /* @__PURE__ */ jsx12(Text11, { bold, children: Array.from(children).map((char, i) => {
16949
+ return /* @__PURE__ */ jsx12(Text10, { bold, children: Array.from(children).map((char, i) => {
16969
16950
  const charPhase = globalPhase - i * CHAR_PHASE_GAP;
16970
16951
  const sin = Math.sin(charPhase);
16971
16952
  const color = sinToColor(sin);
16972
- return /* @__PURE__ */ jsx12(Text11, { color, children: char }, i);
16953
+ return /* @__PURE__ */ jsx12(Text10, { color, children: char }, i);
16973
16954
  }) });
16974
16955
  });
16975
16956
 
16976
16957
  // src/platform/tui/components/status/ProcessingView.tsx
16977
- import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
16958
+ import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
16978
16959
  var ProcessingView = ({
16979
16960
  statusMain,
16980
16961
  detailText,
@@ -16993,26 +16974,26 @@ var ProcessingView = ({
16993
16974
  const parenIdx = statusMain.indexOf("(");
16994
16975
  const shimmerPart = parenIdx > -1 ? statusMain.slice(0, parenIdx).trimEnd() : statusMain;
16995
16976
  const staticSuffix = parenIdx > -1 ? " " + statusMain.slice(parenIdx) : "";
16996
- return /* @__PURE__ */ jsxs9(Box10, { flexDirection: "column", height: 2, children: [
16997
- /* @__PURE__ */ jsxs9(Box10, { children: [
16998
- /* @__PURE__ */ jsx13(Text12, { color, wrap: "truncate", children: /* @__PURE__ */ jsx13(StarSpinner, { color }) }),
16999
- /* @__PURE__ */ jsx13(Text12, { children: " " }),
16977
+ return /* @__PURE__ */ jsxs8(Box10, { flexDirection: "column", height: 2, children: [
16978
+ /* @__PURE__ */ jsxs8(Box10, { children: [
16979
+ /* @__PURE__ */ jsx13(Text11, { color, wrap: "truncate", children: /* @__PURE__ */ jsx13(StarSpinner, { color }) }),
16980
+ /* @__PURE__ */ jsx13(Text11, { children: " " }),
17000
16981
  /* @__PURE__ */ jsx13(ShimmerText, { bold: true, phase: 0, children: shimmerPart }),
17001
- staticSuffix ? /* @__PURE__ */ jsx13(Text12, { color: THEME.dimGray, wrap: "truncate", children: staticSuffix }) : null,
17002
- /* @__PURE__ */ jsxs9(Text12, { color: THEME.dimGray, wrap: "truncate", children: [
16982
+ staticSuffix ? /* @__PURE__ */ jsx13(Text11, { color: THEME.dimGray, wrap: "truncate", children: staticSuffix }) : null,
16983
+ /* @__PURE__ */ jsxs8(Text11, { color: THEME.dimGray, wrap: "truncate", children: [
17003
16984
  " ",
17004
16985
  meta
17005
16986
  ] })
17006
16987
  ] }),
17007
- /* @__PURE__ */ jsx13(Box10, { children: detailText ? /* @__PURE__ */ jsxs9(Text12, { color: THEME.dimGray, wrap: "truncate", children: [
16988
+ /* @__PURE__ */ jsx13(Box10, { children: detailText ? /* @__PURE__ */ jsxs8(Text11, { color: THEME.dimGray, wrap: "truncate", children: [
17008
16989
  " ",
17009
16990
  detailText
17010
- ] }) : /* @__PURE__ */ jsx13(Text12, { children: " " }) })
16991
+ ] }) : /* @__PURE__ */ jsx13(Text11, { children: " " }) })
17011
16992
  ] });
17012
16993
  };
17013
16994
 
17014
16995
  // src/platform/tui/components/StatusDisplay.tsx
17015
- import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
16996
+ import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
17016
16997
  var StatusDisplay = memo10(({
17017
16998
  retryState,
17018
16999
  isProcessing,
@@ -17026,9 +17007,9 @@ var StatusDisplay = memo10(({
17026
17007
  return /* @__PURE__ */ jsx14(RetryView, { retryState });
17027
17008
  }
17028
17009
  if (isProcessing && isWaitingForInput) {
17029
- return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", height: 2, children: [
17030
- /* @__PURE__ */ jsx14(Text13, { children: " " }),
17031
- /* @__PURE__ */ jsx14(Text13, { children: " " })
17010
+ return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", height: 2, children: [
17011
+ /* @__PURE__ */ jsx14(Text12, { children: " " }),
17012
+ /* @__PURE__ */ jsx14(Text12, { children: " " })
17032
17013
  ] });
17033
17014
  }
17034
17015
  if (isProcessing) {
@@ -17047,19 +17028,19 @@ var StatusDisplay = memo10(({
17047
17028
  }
17048
17029
  );
17049
17030
  }
17050
- return /* @__PURE__ */ jsxs10(Box11, { flexDirection: "column", height: 2, children: [
17051
- /* @__PURE__ */ jsx14(Text13, { children: " " }),
17052
- /* @__PURE__ */ jsx14(Text13, { children: " " })
17031
+ return /* @__PURE__ */ jsxs9(Box11, { flexDirection: "column", height: 2, children: [
17032
+ /* @__PURE__ */ jsx14(Text12, { children: " " }),
17033
+ /* @__PURE__ */ jsx14(Text12, { children: " " })
17053
17034
  ] });
17054
17035
  });
17055
17036
 
17056
17037
  // src/platform/tui/components/ChatInput.tsx
17057
- import { useMemo, useCallback as useCallback9, useRef as useRef8, memo as memo11, useState as useState6, useEffect as useEffect7 } from "react";
17058
- import { Box as Box15, Text as Text17, useInput as useInput3 } from "ink";
17038
+ import { useMemo, useCallback as useCallback7, useRef as useRef6, memo as memo11, useState as useState5, useEffect as useEffect7 } from "react";
17039
+ import { Box as Box15, Text as Text16, useInput as useInput2 } from "ink";
17059
17040
 
17060
17041
  // src/platform/tui/components/input/AutocompletePreview.tsx
17061
- import { Box as Box12, Text as Text14 } from "ink";
17062
- import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
17042
+ import { Box as Box12, Text as Text13 } from "ink";
17043
+ import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
17063
17044
  var AutocompletePreview = ({
17064
17045
  suggestions,
17065
17046
  clampedIdx
@@ -17067,26 +17048,26 @@ var AutocompletePreview = ({
17067
17048
  return /* @__PURE__ */ jsx15(Box12, { flexDirection: "column", paddingX: 1, children: suggestions.map((cmd, i) => {
17068
17049
  const isSelected = i === clampedIdx;
17069
17050
  const argsText = cmd.args ? ` ${cmd.args}` : "";
17070
- return /* @__PURE__ */ jsxs11(Box12, { children: [
17071
- /* @__PURE__ */ jsx15(Text14, { color: isSelected ? THEME.primary : THEME.dimGray, children: isSelected ? " \u276F " : " " }),
17072
- /* @__PURE__ */ jsxs11(Text14, { color: isSelected ? THEME.white : THEME.gray, bold: isSelected, children: [
17051
+ return /* @__PURE__ */ jsxs10(Box12, { children: [
17052
+ /* @__PURE__ */ jsx15(Text13, { color: isSelected ? THEME.primary : THEME.dimGray, children: isSelected ? " \u276F " : " " }),
17053
+ /* @__PURE__ */ jsxs10(Text13, { color: isSelected ? THEME.white : THEME.gray, bold: isSelected, children: [
17073
17054
  "/",
17074
17055
  cmd.name
17075
17056
  ] }),
17076
- /* @__PURE__ */ jsx15(Text14, { dimColor: true, color: THEME.gray, children: argsText }),
17077
- /* @__PURE__ */ jsxs11(Text14, { dimColor: true, color: THEME.dimGray, children: [
17057
+ /* @__PURE__ */ jsx15(Text13, { dimColor: true, color: THEME.gray, children: argsText }),
17058
+ /* @__PURE__ */ jsxs10(Text13, { dimColor: true, color: THEME.dimGray, children: [
17078
17059
  " \u2014 ",
17079
17060
  cmd.description
17080
17061
  ] }),
17081
- isSelected && cmd.alias && /* @__PURE__ */ jsx15(Text14, { dimColor: true, color: THEME.dimGray, children: ` (/${cmd.alias})` })
17062
+ isSelected && cmd.alias && /* @__PURE__ */ jsx15(Text13, { dimColor: true, color: THEME.dimGray, children: ` (/${cmd.alias})` })
17082
17063
  ] }, cmd.name);
17083
17064
  }) });
17084
17065
  };
17085
17066
 
17086
17067
  // src/platform/tui/components/input/SecretInputArea.tsx
17087
- import { Box as Box13, Text as Text15, useStdout } from "ink";
17068
+ import { Box as Box13, Text as Text14, useStdout } from "ink";
17088
17069
  import TextInput from "ink-text-input";
17089
- import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
17070
+ import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
17090
17071
  var OUTER_PADDING = 2;
17091
17072
  var SecretInputArea = ({
17092
17073
  inputRequest,
@@ -17097,10 +17078,10 @@ var SecretInputArea = ({
17097
17078
  const { stdout } = useStdout();
17098
17079
  const borderWidth = Math.max(10, (stdout?.columns ?? 80) - OUTER_PADDING);
17099
17080
  const borderLine = "\u2501".repeat(borderWidth);
17100
- return /* @__PURE__ */ jsxs12(Box13, { flexDirection: "column", children: [
17101
- /* @__PURE__ */ jsx16(Box13, { children: /* @__PURE__ */ jsx16(Text15, { color: THEME.yellow, children: borderLine }) }),
17102
- /* @__PURE__ */ jsxs12(Box13, { paddingX: 1, children: [
17103
- /* @__PURE__ */ jsx16(Text15, { color: THEME.yellow, bold: true, children: "\u25B8 " }),
17081
+ return /* @__PURE__ */ jsxs11(Box13, { flexDirection: "column", children: [
17082
+ /* @__PURE__ */ jsx16(Box13, { children: /* @__PURE__ */ jsx16(Text14, { color: THEME.yellow, children: borderLine }) }),
17083
+ /* @__PURE__ */ jsxs11(Box13, { paddingX: 1, children: [
17084
+ /* @__PURE__ */ jsx16(Text14, { color: THEME.yellow, bold: true, children: "\u25B8 " }),
17104
17085
  /* @__PURE__ */ jsx16(
17105
17086
  TextInput,
17106
17087
  {
@@ -17112,14 +17093,14 @@ var SecretInputArea = ({
17112
17093
  }
17113
17094
  )
17114
17095
  ] }),
17115
- /* @__PURE__ */ jsx16(Box13, { children: /* @__PURE__ */ jsx16(Text15, { color: THEME.yellow, children: borderLine }) })
17096
+ /* @__PURE__ */ jsx16(Box13, { children: /* @__PURE__ */ jsx16(Text14, { color: THEME.yellow, children: borderLine }) })
17116
17097
  ] });
17117
17098
  };
17118
17099
 
17119
17100
  // src/platform/tui/components/input/NormalInputArea.tsx
17120
- import { Box as Box14, Text as Text16, useStdout as useStdout2 } from "ink";
17101
+ import { Box as Box14, Text as Text15, useStdout as useStdout2 } from "ink";
17121
17102
  import TextInput2 from "ink-text-input";
17122
- import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
17103
+ import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
17123
17104
  var OUTER_PADDING2 = 2;
17124
17105
  var NormalInputArea = ({
17125
17106
  inputKey,
@@ -17131,10 +17112,10 @@ var NormalInputArea = ({
17131
17112
  const { stdout } = useStdout2();
17132
17113
  const borderWidth = Math.max(10, (stdout?.columns ?? 80) - OUTER_PADDING2);
17133
17114
  const borderLine = "\u2500".repeat(borderWidth);
17134
- return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", children: [
17135
- /* @__PURE__ */ jsx17(Box14, { children: /* @__PURE__ */ jsx17(Text16, { dimColor: true, color: THEME.dimGray, children: borderLine }) }),
17136
- /* @__PURE__ */ jsxs13(Box14, { paddingX: 1, children: [
17137
- /* @__PURE__ */ jsx17(Text16, { color: THEME.primary, children: "\u276F " }),
17115
+ return /* @__PURE__ */ jsxs12(Box14, { flexDirection: "column", children: [
17116
+ /* @__PURE__ */ jsx17(Box14, { children: /* @__PURE__ */ jsx17(Text15, { dimColor: true, color: THEME.dimGray, children: borderLine }) }),
17117
+ /* @__PURE__ */ jsxs12(Box14, { paddingX: 1, children: [
17118
+ /* @__PURE__ */ jsx17(Text15, { color: THEME.primary, children: "\u276F " }),
17138
17119
  /* @__PURE__ */ jsx17(
17139
17120
  TextInput2,
17140
17121
  {
@@ -17146,12 +17127,12 @@ var NormalInputArea = ({
17146
17127
  inputKey
17147
17128
  )
17148
17129
  ] }),
17149
- /* @__PURE__ */ jsx17(Box14, { children: /* @__PURE__ */ jsx17(Text16, { dimColor: true, color: THEME.dimGray, children: borderLine }) })
17130
+ /* @__PURE__ */ jsx17(Box14, { children: /* @__PURE__ */ jsx17(Text15, { dimColor: true, color: THEME.dimGray, children: borderLine }) })
17150
17131
  ] });
17151
17132
  };
17152
17133
 
17153
17134
  // src/platform/tui/components/ChatInput.tsx
17154
- import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
17135
+ import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
17155
17136
  var MAX_SUGGESTIONS = 6;
17156
17137
  var ChatInput = memo11(({
17157
17138
  value,
@@ -17171,25 +17152,25 @@ var ChatInput = memo11(({
17171
17152
  return getMatchingCommands(partialCmd).slice(0, MAX_SUGGESTIONS);
17172
17153
  }, [isSlashMode, partialCmd, hasArgs]);
17173
17154
  const showPreview = isSlashMode && !hasArgs && suggestions.length > 0;
17174
- const [selectedIdx, setSelectedIdx] = useState6(0);
17155
+ const [selectedIdx, setSelectedIdx] = useState5(0);
17175
17156
  const clampedIdx = Math.min(selectedIdx, Math.max(0, suggestions.length - 1));
17176
- const selectedIdxRef = useRef8(clampedIdx);
17157
+ const selectedIdxRef = useRef6(clampedIdx);
17177
17158
  selectedIdxRef.current = clampedIdx;
17178
- const suggestionsRef = useRef8(suggestions);
17159
+ const suggestionsRef = useRef6(suggestions);
17179
17160
  suggestionsRef.current = suggestions;
17180
- const isSlashModeRef = useRef8(isSlashMode);
17161
+ const isSlashModeRef = useRef6(isSlashMode);
17181
17162
  isSlashModeRef.current = isSlashMode;
17182
- const hasArgsRef = useRef8(hasArgs);
17163
+ const hasArgsRef = useRef6(hasArgs);
17183
17164
  hasArgsRef.current = hasArgs;
17184
- const showPreviewRef = useRef8(showPreview);
17165
+ const showPreviewRef = useRef6(showPreview);
17185
17166
  showPreviewRef.current = showPreview;
17186
- const inputRequestRef = useRef8(inputRequest);
17167
+ const inputRequestRef = useRef6(inputRequest);
17187
17168
  inputRequestRef.current = inputRequest;
17188
- const onChangeRef = useRef8(onChange);
17169
+ const onChangeRef = useRef6(onChange);
17189
17170
  onChangeRef.current = onChange;
17190
- const [pastedHint, setPastedHint] = useState6(null);
17191
- const prevValueRef = useRef8(value);
17192
- const pasteTimerRef = useRef8(null);
17171
+ const [pastedHint, setPastedHint] = useState5(null);
17172
+ const prevValueRef = useRef6(value);
17173
+ const pasteTimerRef = useRef6(null);
17193
17174
  useEffect7(() => {
17194
17175
  const diff = value.length - prevValueRef.current.length;
17195
17176
  if (diff > 20) {
@@ -17202,8 +17183,8 @@ var ChatInput = memo11(({
17202
17183
  if (pasteTimerRef.current) clearTimeout(pasteTimerRef.current);
17203
17184
  };
17204
17185
  }, [value]);
17205
- const [inputKey, setInputKey] = useState6(0);
17206
- const completeCommand = useCallback9((idx) => {
17186
+ const [inputKey, setInputKey] = useState5(0);
17187
+ const completeCommand = useCallback7((idx) => {
17207
17188
  const sug = suggestionsRef.current;
17208
17189
  if (!sug.length) return;
17209
17190
  const best = sug[Math.min(idx, sug.length - 1)];
@@ -17213,9 +17194,9 @@ var ChatInput = memo11(({
17213
17194
  setSelectedIdx(0);
17214
17195
  setInputKey((k) => k + 1);
17215
17196
  }, []);
17216
- const onSubmitRef = useRef8(onSubmit);
17197
+ const onSubmitRef = useRef6(onSubmit);
17217
17198
  onSubmitRef.current = onSubmit;
17218
- const wrappedOnSubmit = useCallback9((val) => {
17199
+ const wrappedOnSubmit = useCallback7((val) => {
17219
17200
  if (showPreviewRef.current) {
17220
17201
  const sug = suggestionsRef.current;
17221
17202
  if (!sug.length) {
@@ -17234,7 +17215,7 @@ var ChatInput = memo11(({
17234
17215
  }
17235
17216
  onSubmitRef.current(val);
17236
17217
  }, [completeCommand]);
17237
- useInput3(useCallback9((_input, key) => {
17218
+ useInput2(useCallback7((_input, key) => {
17238
17219
  if (inputRequestRef.current.status === "active") return;
17239
17220
  const sug = suggestionsRef.current;
17240
17221
  const visible = showPreviewRef.current;
@@ -17250,7 +17231,7 @@ var ChatInput = memo11(({
17250
17231
  completeCommand(selectedIdxRef.current);
17251
17232
  }
17252
17233
  }, [completeCommand]));
17253
- return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", children: [
17234
+ return /* @__PURE__ */ jsxs13(Box15, { flexDirection: "column", children: [
17254
17235
  showPreview && /* @__PURE__ */ jsx18(
17255
17236
  AutocompletePreview,
17256
17237
  {
@@ -17282,14 +17263,14 @@ var ChatInput = memo11(({
17282
17263
  }
17283
17264
  )
17284
17265
  ),
17285
- pastedHint && /* @__PURE__ */ jsx18(Box15, { paddingX: 2, children: /* @__PURE__ */ jsx18(Text17, { dimColor: true, color: THEME.dimGray, children: pastedHint }) })
17266
+ pastedHint && /* @__PURE__ */ jsx18(Box15, { paddingX: 2, children: /* @__PURE__ */ jsx18(Text16, { dimColor: true, color: THEME.dimGray, children: pastedHint }) })
17286
17267
  ] });
17287
17268
  });
17288
17269
 
17289
17270
  // src/platform/tui/components/footer.tsx
17290
17271
  import { memo as memo12 } from "react";
17291
- import { Box as Box16, Text as Text18 } from "ink";
17292
- import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
17272
+ import { Box as Box16, Text as Text17 } from "ink";
17273
+ import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
17293
17274
  var CTX_WARN_THRESHOLD = 0.8;
17294
17275
  var MAX_CONTEXT_TOKENS = LLM_LIMITS.streamMaxTokens;
17295
17276
  var formatElapsed = (totalSeconds) => {
@@ -17305,7 +17286,7 @@ var formatElapsed = (totalSeconds) => {
17305
17286
  var Footer = memo12(({ phase, targets, findings, todo, elapsedTime, isProcessing, totalTokens, turnCount }) => {
17306
17287
  const ctxPct = totalTokens > 0 ? Math.round(totalTokens / MAX_CONTEXT_TOKENS * 100) : 0;
17307
17288
  const ctxColor = ctxPct >= CTX_WARN_THRESHOLD * 100 ? THEME.yellow : THEME.dimGray;
17308
- return /* @__PURE__ */ jsxs15(
17289
+ return /* @__PURE__ */ jsxs14(
17309
17290
  Box16,
17310
17291
  {
17311
17292
  width: "100%",
@@ -17313,44 +17294,44 @@ var Footer = memo12(({ phase, targets, findings, todo, elapsedTime, isProcessing
17313
17294
  justifyContent: "space-between",
17314
17295
  overflow: "hidden",
17315
17296
  children: [
17316
- /* @__PURE__ */ jsxs15(Box16, { gap: 2, children: [
17317
- /* @__PURE__ */ jsxs15(Text18, { color: THEME.gray, children: [
17297
+ /* @__PURE__ */ jsxs14(Box16, { gap: 2, children: [
17298
+ /* @__PURE__ */ jsxs14(Text17, { color: THEME.gray, children: [
17318
17299
  "Phase: ",
17319
- /* @__PURE__ */ jsx19(Text18, { color: THEME.white, children: phase })
17300
+ /* @__PURE__ */ jsx19(Text17, { color: THEME.white, children: phase })
17320
17301
  ] }),
17321
- /* @__PURE__ */ jsxs15(Text18, { color: THEME.gray, children: [
17302
+ /* @__PURE__ */ jsxs14(Text17, { color: THEME.gray, children: [
17322
17303
  "Targets: ",
17323
- /* @__PURE__ */ jsx19(Text18, { color: THEME.white, children: targets })
17304
+ /* @__PURE__ */ jsx19(Text17, { color: THEME.white, children: targets })
17324
17305
  ] }),
17325
- /* @__PURE__ */ jsxs15(Text18, { color: THEME.gray, children: [
17306
+ /* @__PURE__ */ jsxs14(Text17, { color: THEME.gray, children: [
17326
17307
  "Findings: ",
17327
- /* @__PURE__ */ jsx19(Text18, { color: THEME.white, children: findings })
17308
+ /* @__PURE__ */ jsx19(Text17, { color: THEME.white, children: findings })
17328
17309
  ] }),
17329
- /* @__PURE__ */ jsxs15(Text18, { color: THEME.gray, children: [
17310
+ /* @__PURE__ */ jsxs14(Text17, { color: THEME.gray, children: [
17330
17311
  "Tasks: ",
17331
- /* @__PURE__ */ jsx19(Text18, { color: THEME.white, children: todo })
17312
+ /* @__PURE__ */ jsx19(Text17, { color: THEME.white, children: todo })
17332
17313
  ] })
17333
17314
  ] }),
17334
- /* @__PURE__ */ jsxs15(Box16, { gap: 2, children: [
17335
- isProcessing ? /* @__PURE__ */ jsxs15(Box16, { gap: 1, children: [
17336
- /* @__PURE__ */ jsx19(Text18, { dimColor: true, color: THEME.dimGray, children: "[ESC] abort" }),
17337
- /* @__PURE__ */ jsx19(Text18, { dimColor: true, color: THEME.dimGray, children: "[^C\xD72] exit" })
17338
- ] }) : /* @__PURE__ */ jsx19(Text18, { dimColor: true, color: THEME.dimGray, children: "[/help] commands" }),
17339
- turnCount > 0 && /* @__PURE__ */ jsxs15(Text18, { dimColor: true, color: THEME.dimGray, children: [
17315
+ /* @__PURE__ */ jsxs14(Box16, { gap: 2, children: [
17316
+ isProcessing ? /* @__PURE__ */ jsxs14(Box16, { gap: 1, children: [
17317
+ /* @__PURE__ */ jsx19(Text17, { dimColor: true, color: THEME.dimGray, children: "[ESC] abort" }),
17318
+ /* @__PURE__ */ jsx19(Text17, { dimColor: true, color: THEME.dimGray, children: "[^C\xD72] exit" })
17319
+ ] }) : /* @__PURE__ */ jsx19(Text17, { dimColor: true, color: THEME.dimGray, children: "[/help] commands" }),
17320
+ turnCount > 0 && /* @__PURE__ */ jsxs14(Text17, { dimColor: true, color: THEME.dimGray, children: [
17340
17321
  "turn:",
17341
17322
  turnCount
17342
17323
  ] }),
17343
- totalTokens > 0 && /* @__PURE__ */ jsxs15(Text18, { dimColor: true, color: ctxColor, children: [
17324
+ totalTokens > 0 && /* @__PURE__ */ jsxs14(Text17, { dimColor: true, color: ctxColor, children: [
17344
17325
  "ctx:",
17345
17326
  ctxPct,
17346
17327
  "%"
17347
17328
  ] }),
17348
- totalTokens > 0 && /* @__PURE__ */ jsxs15(Text18, { dimColor: true, color: THEME.dimGray, children: [
17329
+ totalTokens > 0 && /* @__PURE__ */ jsxs14(Text17, { dimColor: true, color: THEME.dimGray, children: [
17349
17330
  "\u2191",
17350
17331
  formatTokens(totalTokens)
17351
17332
  ] }),
17352
- /* @__PURE__ */ jsx19(Text18, { color: isProcessing ? THEME.primary : THEME.gray, children: isProcessing ? "Running " : "Idle " }),
17353
- /* @__PURE__ */ jsx19(Text18, { color: THEME.white, children: formatElapsed(elapsedTime) })
17333
+ /* @__PURE__ */ jsx19(Text17, { color: isProcessing ? THEME.primary : THEME.gray, children: isProcessing ? "Running " : "Idle " }),
17334
+ /* @__PURE__ */ jsx19(Text17, { color: THEME.white, children: formatElapsed(elapsedTime) })
17354
17335
  ] })
17355
17336
  ]
17356
17337
  }
@@ -17359,9 +17340,9 @@ var Footer = memo12(({ phase, targets, findings, todo, elapsedTime, isProcessing
17359
17340
  var footer_default = Footer;
17360
17341
 
17361
17342
  // src/platform/tui/components/Modal.tsx
17362
- import { useMemo as useMemo2, memo as memo13, useCallback as useCallback10, useRef as useRef9 } from "react";
17363
- import { Box as Box17, Text as Text19, useStdout as useStdout3, useInput as useInput4 } from "ink";
17364
- import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
17343
+ import { useMemo as useMemo2, memo as memo13, useCallback as useCallback8, useRef as useRef7 } from "react";
17344
+ import { Box as Box17, Text as Text18, useStdout as useStdout3, useInput as useInput3 } from "ink";
17345
+ import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
17365
17346
  var MODAL_TITLES = {
17366
17347
  findings: "\u25C8 FINDINGS \u25C8",
17367
17348
  graph: "\u25C8 ATTACK GRAPH \u25C8",
@@ -17374,9 +17355,9 @@ var Modal = memo13(({
17374
17355
  onScroll,
17375
17356
  onClose
17376
17357
  }) => {
17377
- const onScrollRef = useRef9(onScroll);
17358
+ const onScrollRef = useRef7(onScroll);
17378
17359
  onScrollRef.current = onScroll;
17379
- const onCloseRef = useRef9(onClose);
17360
+ const onCloseRef = useRef7(onClose);
17380
17361
  onCloseRef.current = onClose;
17381
17362
  const { stdout } = useStdout3();
17382
17363
  const terminalHeight = stdout?.rows ?? 24;
@@ -17388,7 +17369,7 @@ var Modal = memo13(({
17388
17369
  () => lines.slice(scrollOffset, scrollOffset + maxHeight),
17389
17370
  [lines, scrollOffset, maxHeight]
17390
17371
  );
17391
- useInput4(useCallback10((input, key) => {
17372
+ useInput3(useCallback8((input, key) => {
17392
17373
  if (key.escape || input === "q") {
17393
17374
  onCloseRef.current();
17394
17375
  } else if (key.upArrow || input === "k") {
@@ -17405,14 +17386,14 @@ var Modal = memo13(({
17405
17386
  const endLine = Math.min(scrollOffset + maxHeight, totalLines);
17406
17387
  const scrollbarHeight = Math.max(1, Math.floor(maxHeight * (maxHeight / Math.max(totalLines, 1))));
17407
17388
  const scrollbarPosition = totalLines > maxHeight ? Math.floor(scrollOffset / (totalLines - maxHeight) * (maxHeight - scrollbarHeight)) : 0;
17408
- return /* @__PURE__ */ jsxs16(
17389
+ return /* @__PURE__ */ jsxs15(
17409
17390
  Box17,
17410
17391
  {
17411
17392
  flexDirection: "column",
17412
17393
  width: terminalWidth,
17413
17394
  height: terminalHeight,
17414
17395
  children: [
17415
- /* @__PURE__ */ jsx20(Box17, { justifyContent: "center", marginBottom: 0, children: /* @__PURE__ */ jsx20(Text19, { color: THEME.cyan, bold: true, children: (() => {
17396
+ /* @__PURE__ */ jsx20(Box17, { justifyContent: "center", marginBottom: 0, children: /* @__PURE__ */ jsx20(Text18, { color: THEME.cyan, bold: true, children: (() => {
17416
17397
  const title = MODAL_TITLES[type];
17417
17398
  const sideWidth = Math.max(3, Math.floor((terminalWidth - title.length - 2) / 2));
17418
17399
  return `${"\u2500".repeat(sideWidth)} ${title} ${"\u2500".repeat(sideWidth)}`;
@@ -17427,17 +17408,17 @@ var Modal = memo13(({
17427
17408
  flexGrow: 1,
17428
17409
  children: visibleLines.map((line, i) => {
17429
17410
  const showScrollbar = totalLines > maxHeight && i >= scrollbarPosition && i < scrollbarPosition + scrollbarHeight;
17430
- return /* @__PURE__ */ jsxs16(Box17, { children: [
17431
- /* @__PURE__ */ jsx20(Text19, { color: THEME.white, wrap: "truncate", children: line }),
17411
+ return /* @__PURE__ */ jsxs15(Box17, { children: [
17412
+ /* @__PURE__ */ jsx20(Text18, { color: THEME.white, wrap: "truncate", children: line }),
17432
17413
  /* @__PURE__ */ jsx20(Box17, { flexGrow: 1 }),
17433
- totalLines > maxHeight && /* @__PURE__ */ jsx20(Text19, { color: showScrollbar ? THEME.cyan : THEME.dimGray, children: showScrollbar ? "\u2588" : "\u2502" })
17414
+ totalLines > maxHeight && /* @__PURE__ */ jsx20(Text18, { color: showScrollbar ? THEME.cyan : THEME.dimGray, children: showScrollbar ? "\u2588" : "\u2502" })
17434
17415
  ] }, i);
17435
17416
  })
17436
17417
  }
17437
17418
  ),
17438
- /* @__PURE__ */ jsxs16(Box17, { justifyContent: "space-between", paddingX: 1, children: [
17439
- /* @__PURE__ */ jsx20(Text19, { dimColor: true, color: THEME.gray, children: "\u2191\u2193/jk: scroll | g/G: top/bottom | ESC/q: close" }),
17440
- /* @__PURE__ */ jsxs16(Text19, { dimColor: true, color: THEME.cyan, children: [
17419
+ /* @__PURE__ */ jsxs15(Box17, { justifyContent: "space-between", paddingX: 1, children: [
17420
+ /* @__PURE__ */ jsx20(Text18, { dimColor: true, color: THEME.gray, children: "\u2191\u2193/jk: scroll | g/G: top/bottom | ESC/q: close" }),
17421
+ /* @__PURE__ */ jsxs15(Text18, { dimColor: true, color: THEME.cyan, children: [
17441
17422
  startLine,
17442
17423
  "-",
17443
17424
  endLine,
@@ -17452,7 +17433,7 @@ var Modal = memo13(({
17452
17433
 
17453
17434
  // src/platform/tui/components/app/bottom-region.tsx
17454
17435
  import { Box as Box18 } from "ink";
17455
- import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
17436
+ import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
17456
17437
  var BottomRegion = ({
17457
17438
  input,
17458
17439
  setInput,
@@ -17476,7 +17457,7 @@ var BottomRegion = ({
17476
17457
  const suggestionCount = isSlashMode && !hasArgs && inputRequest.status !== "active" ? Math.min(getMatchingCommands(partialCmd).length, MAX_SUGGESTIONS) : 0;
17477
17458
  const previewHeight = suggestionCount;
17478
17459
  const bottomMinHeight = 7;
17479
- return /* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", minHeight: bottomMinHeight, children: [
17460
+ return /* @__PURE__ */ jsxs16(Box18, { flexDirection: "column", minHeight: bottomMinHeight, children: [
17480
17461
  /* @__PURE__ */ jsx21(
17481
17462
  StatusDisplay,
17482
17463
  {
@@ -17517,16 +17498,16 @@ var BottomRegion = ({
17517
17498
  };
17518
17499
 
17519
17500
  // src/platform/tui/app.tsx
17520
- import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
17501
+ import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
17521
17502
  var MODEL_NAME = getModel() || DEFAULT_MODEL;
17522
17503
  var App = ({ autoApprove = false, target }) => {
17523
17504
  const { exit } = useApp();
17524
17505
  const { stdout } = useStdout4();
17525
17506
  const terminalWidth = stdout?.columns ?? 80;
17526
- const [input, setInput] = useState7("");
17527
- const [secretInput, setSecretInput] = useState7("");
17528
- const [autoApproveMode, setAutoApproveMode] = useState7(autoApprove);
17529
- const [modal, setModal] = useState7({ type: null, content: "", scrollOffset: 0 });
17507
+ const [input, setInput] = useState6("");
17508
+ const [secretInput, setSecretInput] = useState6("");
17509
+ const [autoApproveMode, setAutoApproveMode] = useState6(autoApprove);
17510
+ const [modal, setModal] = useState6({ type: null, content: "", scrollOffset: 0 });
17530
17511
  const {
17531
17512
  agent,
17532
17513
  messages,
@@ -17546,37 +17527,26 @@ var App = ({ autoApprove = false, target }) => {
17546
17527
  addMessage,
17547
17528
  refreshStats
17548
17529
  } = useAgent(autoApproveMode, target);
17549
- const isProcessingRef = useRef10(isProcessing);
17530
+ const isProcessingRef = useRef8(isProcessing);
17550
17531
  isProcessingRef.current = isProcessing;
17551
- const autoApproveModeRef = useRef10(autoApproveMode);
17532
+ const autoApproveModeRef = useRef8(autoApproveMode);
17552
17533
  autoApproveModeRef.current = autoApproveMode;
17553
- const inputRequestRef = useRef10(inputRequest);
17534
+ const inputRequestRef = useRef8(inputRequest);
17554
17535
  inputRequestRef.current = inputRequest;
17555
- const isModalOpenRef = useRef10(!!modal.type);
17536
+ const isModalOpenRef = useRef8(!!modal.type);
17556
17537
  isModalOpenRef.current = !!modal.type;
17557
- const inputRef = useRef10(input);
17538
+ const inputRef = useRef8(input);
17558
17539
  inputRef.current = input;
17559
- const clearInput = useCallback11(() => {
17540
+ const clearInput = useCallback9(() => {
17560
17541
  setInput("");
17561
17542
  }, []);
17562
- const [historyScrollOffset, setHistoryScrollOffset] = useState7(0);
17563
- const handleScroll = useCallback11((delta) => {
17564
- setHistoryScrollOffset((prev) => Math.max(0, prev - delta));
17565
- }, []);
17566
- const messageCountRef = useRef10(messages.length);
17567
- if (messages.length !== messageCountRef.current) {
17568
- messageCountRef.current = messages.length;
17569
- if (historyScrollOffset > 0) {
17570
- setHistoryScrollOffset(0);
17571
- }
17572
- }
17573
- const showModal = useCallback11((type, content) => {
17543
+ const showModal = useCallback9((type, content) => {
17574
17544
  setModal({ type, content, scrollOffset: 0 });
17575
17545
  }, []);
17576
- const closeModal = useCallback11(() => {
17546
+ const closeModal = useCallback9(() => {
17577
17547
  setModal({ type: null, content: "", scrollOffset: 0 });
17578
17548
  }, []);
17579
- const handleModalScroll = useCallback11((delta) => {
17549
+ const handleModalScroll = useCallback9((delta) => {
17580
17550
  setModal((prev) => {
17581
17551
  const lines = prev.content.split("\n");
17582
17552
  const maxHeight = (stdout?.rows ?? 24) - 6;
@@ -17585,7 +17555,7 @@ var App = ({ autoApprove = false, target }) => {
17585
17555
  return { ...prev, scrollOffset: newOffset };
17586
17556
  });
17587
17557
  }, [stdout?.rows]);
17588
- const handleExit = useCallback11(() => {
17558
+ const handleExit = useCallback9(() => {
17589
17559
  const ir = inputRequestRef.current;
17590
17560
  if (ir.status === "active") {
17591
17561
  ir.resolve(null);
@@ -17608,7 +17578,7 @@ var App = ({ autoApprove = false, target }) => {
17608
17578
  isProcessingRef,
17609
17579
  autoApproveModeRef
17610
17580
  });
17611
- const handleSubmit = useCallback11(async (value) => {
17581
+ const handleSubmit = useCallback9(async (value) => {
17612
17582
  const trimmed = value.trim();
17613
17583
  if (!trimmed) return;
17614
17584
  setInput("");
@@ -17623,7 +17593,7 @@ var App = ({ autoApprove = false, target }) => {
17623
17593
  await executeTask(trimmed);
17624
17594
  }
17625
17595
  }, [agent, addMessage, executeTask, handleCommand]);
17626
- const handleSecretSubmit = useCallback11((value) => {
17596
+ const handleSecretSubmit = useCallback9((value) => {
17627
17597
  const ir = inputRequestRef.current;
17628
17598
  if (ir.status !== "active") return;
17629
17599
  const displayText = ir.isPassword ? "\u2022".repeat(Math.min(value.length, 20)) : value;
@@ -17641,8 +17611,7 @@ var App = ({ autoApprove = false, target }) => {
17641
17611
  inputRequestRef,
17642
17612
  isModalOpenRef,
17643
17613
  inputRef,
17644
- clearInput,
17645
- onScroll: handleScroll
17614
+ clearInput
17646
17615
  });
17647
17616
  if (modal.type) {
17648
17617
  return /* @__PURE__ */ jsx22(Box19, { flexDirection: "column", paddingX: 1, width: terminalWidth, children: /* @__PURE__ */ jsx22(
@@ -17656,16 +17625,14 @@ var App = ({ autoApprove = false, target }) => {
17656
17625
  }
17657
17626
  ) });
17658
17627
  }
17659
- return /* @__PURE__ */ jsxs18(Box19, { flexDirection: "column", paddingX: 1, width: terminalWidth, children: [
17628
+ return /* @__PURE__ */ jsxs17(Box19, { flexDirection: "column", paddingX: 1, width: terminalWidth, children: [
17660
17629
  /* @__PURE__ */ jsx22(Box19, { flexDirection: "column", children: /* @__PURE__ */ jsx22(
17661
17630
  MessageList,
17662
17631
  {
17663
17632
  messages,
17664
- isModalOpen: !!modal.type,
17665
17633
  modelName: MODEL_NAME,
17666
17634
  autoApproveMode,
17667
- version: APP_VERSION,
17668
- scrollOffset: historyScrollOffset
17635
+ version: APP_VERSION
17669
17636
  }
17670
17637
  ) }),
17671
17638
  /* @__PURE__ */ jsx22(