pentesting 0.24.2 → 0.24.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -286,7 +286,7 @@ var ORPHAN_PROCESS_NAMES = [
286
286
  var ID_LENGTH = AGENT_LIMITS.ID_LENGTH;
287
287
  var ID_RADIX = AGENT_LIMITS.ID_RADIX;
288
288
  var APP_NAME = "Pentest AI";
289
- var APP_VERSION = "0.24.2";
289
+ var APP_VERSION = "0.24.4";
290
290
  var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
291
291
  var LLM_ROLES = {
292
292
  SYSTEM: "system",
@@ -560,11 +560,6 @@ var UI_COMMANDS = {
560
560
  };
561
561
  var PASSIVE_BINARIES = ["whois", "dig", "curl -i"];
562
562
  var EXPLOIT_BINARIES = ["impacket"];
563
- var MASK_PARTS = {
564
- C24: "24",
565
- C16: "16",
566
- C8: "8"
567
- };
568
563
  var COMMAND_EVENT_TYPES = {
569
564
  TOOL_MISSING: "tool_missing",
570
565
  TOOL_INSTALL: "tool_install",
@@ -870,6 +865,10 @@ var WORKSPACE = {
870
865
  /** Temporary files for active operations */
871
866
  get TEMP() {
872
867
  return path.join(getWorkspaceRoot(), "temp");
868
+ },
869
+ /** Full tool output files saved by Context Digest */
870
+ get OUTPUTS() {
871
+ return path.join(getWorkspaceRoot(), "outputs");
873
872
  }
874
873
  };
875
874
 
@@ -2164,7 +2163,9 @@ var SharedState = class {
2164
2163
  missionChecklist: [],
2165
2164
  ctfMode: true,
2166
2165
  // CTF mode ON by default
2167
- flags: []
2166
+ flags: [],
2167
+ startedAt: Date.now(),
2168
+ deadlineAt: 0
2168
2169
  };
2169
2170
  }
2170
2171
  // --- Mission & Persistent Context ---
@@ -2322,6 +2323,42 @@ var SharedState = class {
2322
2323
  getFlags() {
2323
2324
  return this.data.flags;
2324
2325
  }
2326
+ // --- Time Management ---
2327
+ /** Set a deadline for time-aware strategy (e.g., CTF competition end time) */
2328
+ setDeadline(deadlineMs) {
2329
+ this.data.deadlineAt = deadlineMs;
2330
+ }
2331
+ /** Get milliseconds since agent started */
2332
+ getElapsedMs() {
2333
+ return Date.now() - this.data.startedAt;
2334
+ }
2335
+ /** Get remaining milliseconds until deadline (0 if no deadline) */
2336
+ getRemainingMs() {
2337
+ if (this.data.deadlineAt === 0) return 0;
2338
+ return Math.max(0, this.data.deadlineAt - Date.now());
2339
+ }
2340
+ /** Get time status string for prompt injection */
2341
+ getTimeStatus() {
2342
+ const elapsed = this.getElapsedMs();
2343
+ const mins = Math.floor(elapsed / 6e4);
2344
+ let status = `\u23F1 Elapsed: ${mins}min`;
2345
+ if (this.data.deadlineAt > 0) {
2346
+ const remainingMs = this.getRemainingMs();
2347
+ const remainingMins = Math.floor(remainingMs / 6e4);
2348
+ if (remainingMins <= 0) {
2349
+ status += " | \u26A0\uFE0F TIME IS UP \u2014 submit any flags you have NOW";
2350
+ } else if (remainingMins <= 15) {
2351
+ status += ` | \u{1F534} ${remainingMins}min LEFT \u2014 FINAL PUSH: submit flags, check obvious locations, env vars, DBs`;
2352
+ } else if (remainingMins <= 30) {
2353
+ status += ` | \u{1F7E1} ${remainingMins}min LEFT \u2014 wrap up current vector, ensure all findings are captured`;
2354
+ } else if (remainingMins <= 60) {
2355
+ status += ` | \u{1F7E0} ${remainingMins}min LEFT \u2014 focus on highest-probability vectors only`;
2356
+ } else {
2357
+ status += ` | \u{1F7E2} ${remainingMins}min remaining`;
2358
+ }
2359
+ }
2360
+ return status;
2361
+ }
2325
2362
  /**
2326
2363
  * @remarks
2327
2364
  * WHY: Delegating complex string builder to specialized serializer.
@@ -2493,30 +2530,43 @@ var ScopeGuard = class {
2493
2530
  return false;
2494
2531
  }
2495
2532
  /**
2496
- * Simple CIDR matching (IP-only for now)
2533
+ * Accurate CIDR matching using bitwise arithmetic.
2534
+ * Supports any prefix length /0 through /32.
2535
+ * No external dependencies — pure math.
2497
2536
  */
2498
2537
  matchesCidr(target, cidr) {
2499
2538
  if (target === cidr) return true;
2500
2539
  if (cidr.includes("/")) {
2501
- const [network, mask] = cidr.split("/");
2502
- if (mask === MASK_PARTS.C24) {
2503
- const networkPrefix = network.split(".").slice(0, 3).join(".");
2504
- const targetPrefix = target.split(".").slice(0, 3).join(".");
2505
- return networkPrefix === targetPrefix;
2540
+ const [network, maskStr] = cidr.split("/");
2541
+ const maskBits = parseInt(maskStr, 10);
2542
+ if (isNaN(maskBits) || maskBits < 0 || maskBits > 32) {
2543
+ return target === cidr;
2506
2544
  }
2507
- if (mask === MASK_PARTS.C8) {
2508
- const networkPrefix = network.split(".")[0];
2509
- const targetPrefix = target.split(".")[0];
2510
- return networkPrefix === targetPrefix;
2511
- }
2512
- if (mask === MASK_PARTS.C16) {
2513
- const networkPrefix = network.split(".").slice(0, 2).join(".");
2514
- const targetPrefix = target.split(".").slice(0, 2).join(".");
2515
- return networkPrefix === targetPrefix;
2545
+ const targetInt = this.ipToUint32(target);
2546
+ const networkInt = this.ipToUint32(network);
2547
+ if (targetInt === null || networkInt === null) {
2548
+ return target === cidr;
2516
2549
  }
2550
+ const mask = maskBits === 0 ? 0 : ~0 << 32 - maskBits >>> 0;
2551
+ return (targetInt & mask) >>> 0 === (networkInt & mask) >>> 0;
2517
2552
  }
2518
2553
  return target === cidr;
2519
2554
  }
2555
+ /**
2556
+ * Convert IPv4 dotted-decimal string to unsigned 32-bit integer.
2557
+ * Returns null if the string is not a valid IPv4 address.
2558
+ */
2559
+ ipToUint32(ip) {
2560
+ const parts = ip.split(".");
2561
+ if (parts.length !== 4) return null;
2562
+ let result2 = 0;
2563
+ for (const part of parts) {
2564
+ const octet = parseInt(part, 10);
2565
+ if (isNaN(octet) || octet < 0 || octet > 255) return null;
2566
+ result2 = (result2 << 8 | octet) >>> 0;
2567
+ }
2568
+ return result2;
2569
+ }
2520
2570
  /**
2521
2571
  * Extract IPs and Domains from string
2522
2572
  */
@@ -4803,7 +4853,7 @@ Returns: All available wordlists with their paths, sizes, and categories.`,
4803
4853
  }
4804
4854
  },
4805
4855
  execute: async (p) => {
4806
- const { existsSync: existsSync7, statSync: statSync2, readdirSync: readdirSync3 } = await import("fs");
4856
+ const { existsSync: existsSync8, statSync: statSync2, readdirSync: readdirSync3 } = await import("fs");
4807
4857
  const { join: join10 } = await import("path");
4808
4858
  const category = p.category || "";
4809
4859
  const search = p.search || "";
@@ -4850,7 +4900,7 @@ Returns: All available wordlists with their paths, sizes, and categories.`,
4850
4900
  results.push("");
4851
4901
  };
4852
4902
  const scanDir = (dirPath, maxDepth = 3, depth = 0) => {
4853
- if (depth > maxDepth || !existsSync7(dirPath)) return;
4903
+ if (depth > maxDepth || !existsSync8(dirPath)) return;
4854
4904
  let entries;
4855
4905
  try {
4856
4906
  entries = readdirSync3(dirPath, { withFileTypes: true });
@@ -5233,8 +5283,8 @@ Requires root/sudo privileges.`,
5233
5283
  const iface = p.interface || "";
5234
5284
  const duration = p.duration || NETWORK_CONFIG.DEFAULT_SPOOF_DURATION;
5235
5285
  const hostsFile = createTempFile(".hosts");
5236
- const { writeFileSync: writeFileSync6 } = await import("fs");
5237
- writeFileSync6(hostsFile, `${spoofIp} ${domain}
5286
+ const { writeFileSync: writeFileSync7 } = await import("fs");
5287
+ writeFileSync7(hostsFile, `${spoofIp} ${domain}
5238
5288
  ${spoofIp} *.${domain}
5239
5289
  `);
5240
5290
  const ifaceFlag = iface ? `-i ${iface}` : "";
@@ -6651,7 +6701,7 @@ var FLAG_PATTERNS = {
6651
6701
  // Generic CTF flag formats
6652
6702
  generic: /flag\{[^}]+\}/gi,
6653
6703
  curly_upper: /FLAG\{[^}]+\}/g,
6654
- // Platform-specific (major competitions)
6704
+ // Platform-specific Major International Competitions
6655
6705
  htb: /HTB\{[^}]+\}/g,
6656
6706
  thm: /THM\{[^}]+\}/g,
6657
6707
  picoCTF: /picoCTF\{[^}]+\}/g,
@@ -6667,8 +6717,65 @@ var FLAG_PATTERNS = {
6667
6717
  // hxp CTF
6668
6718
  zer0pts: /zer0pts\{[^}]+\}/g,
6669
6719
  // zer0pts CTF
6720
+ // Asia-Pacific Major Competitions
6721
+ seccon: /SECCON\{[^}]+\}/g,
6722
+ // SECCON CTF (Japan)
6723
+ hitcon: /hitcon\{[^}]+\}/gi,
6724
+ // HITCON CTF (Taiwan)
6725
+ codegate: /codegate\{[^}]+\}/gi,
6726
+ // Codegate CTF (Korea)
6727
+ wacon: /WACON\{[^}]+\}/g,
6728
+ // WACON CTF (Korea)
6729
+ linectf: /LINECTF\{[^}]+\}/g,
6730
+ // LINE CTF (Korea/Japan)
6731
+ tsgctf: /TSGCTF\{[^}]+\}/g,
6732
+ // TSG CTF (Japan)
6733
+ kosenctf: /KosenCTF\{[^}]+\}/g,
6734
+ // Kosen CTF (Japan)
6735
+ asis: /ASIS\{[^}]+\}/g,
6736
+ // ASIS CTF (Iran)
6737
+ // Americas / Europe Major Competitions
6738
+ plaidctf: /PCTF\{[^}]+\}/g,
6739
+ // PlaidCTF (PPP)
6740
+ dicectf: /dice\{[^}]+\}/gi,
6741
+ // DiceCTF
6742
+ rwctf: /rwctf\{[^}]+\}/gi,
6743
+ // Real World CTF
6744
+ rctf: /RCTF\{[^}]+\}/g,
6745
+ // RCTF
6746
+ n1ctf: /N1CTF\{[^}]+\}/g,
6747
+ // N1CTF (Nu1L)
6748
+ bctf: /BCTF\{[^}]+\}/g,
6749
+ // BCTF
6750
+ _0ctf: /0CTF\{[^}]+\}/g,
6751
+ // 0CTF (Shanghai)
6752
+ defcamp: /CTF\{[^}]+\}/g,
6753
+ // DefCamp
6754
+ insomnihack: /INS\{[^}]+\}/g,
6755
+ // Insomni'hack
6756
+ justctf: /justCTF\{[^}]+\}/g,
6757
+ // justCTF
6758
+ lactf: /lactf\{[^}]+\}/gi,
6759
+ // LA CTF (UCLA)
6760
+ damctf: /dam\{[^}]+\}/gi,
6761
+ // DamCTF
6762
+ tjctf: /tjctf\{[^}]+\}/gi,
6763
+ // TJCTF
6764
+ buckeye: /buckeye\{[^}]+\}/gi,
6765
+ // BuckeyeCTF
6766
+ uiuctf: /uiuctf\{[^}]+\}/gi,
6767
+ // UIUCTF
6768
+ // Platform-specific
6769
+ cyber_apocalypse: /HTB\{[^}]+\}/g,
6770
+ // HTB Cyber Apocalypse (same as HTB)
6771
+ offshift: /oS\{[^}]+\}/g,
6772
+ // Offshift CTF
6773
+ imaginaryctf: /ictf\{[^}]+\}/gi,
6774
+ // ImaginaryCTF
6775
+ corctf: /corctf\{[^}]+\}/gi,
6776
+ // corCTF
6777
+ // Generic CTFd format — catches most custom competitions
6670
6778
  ctfd_generic: /[A-Z]{2,10}\{[^}]+\}/g,
6671
- // Generic CTFd format
6672
6779
  // Hash-style flags (HTB user.txt / root.txt — 32-char hex)
6673
6780
  hash_flag: /\b[a-f0-9]{32}\b/g,
6674
6781
  // Base64-encoded flag detection (common in steganography/forensics)
@@ -6765,6 +6872,520 @@ function appendBlockedCommandHints(lines, errorLower) {
6765
6872
  }
6766
6873
  }
6767
6874
 
6875
+ // src/shared/utils/output-compressor.ts
6876
+ var MIN_COMPRESS_LENGTH = 3e3;
6877
+ var SUMMARY_HEADER = "\u2550\u2550\u2550 INTELLIGENCE SUMMARY (auto-extracted) \u2550\u2550\u2550";
6878
+ var SUMMARY_FOOTER = "\u2550\u2550\u2550 END SUMMARY \u2014 Full output follows \u2550\u2550\u2550";
6879
+ var TOOL_SIGNATURES = [
6880
+ {
6881
+ name: "nmap",
6882
+ signatures: [/Nmap scan report/i, /PORT\s+STATE\s+SERVICE/i, /Nmap done:/i],
6883
+ extract: extractNmapIntel
6884
+ },
6885
+ {
6886
+ name: "linpeas",
6887
+ signatures: [/linpeas/i, /╔══.*╗/, /Linux Privilege Escalation/i],
6888
+ extract: extractLinpeasIntel
6889
+ },
6890
+ {
6891
+ name: "enum4linux",
6892
+ signatures: [/enum4linux/i, /Starting enum4linux/i, /\|\s+Target\s+Information/i],
6893
+ extract: extractEnum4linuxIntel
6894
+ },
6895
+ {
6896
+ name: "gobuster/ffuf/feroxbuster",
6897
+ signatures: [/Gobuster/i, /FFUF/i, /feroxbuster/i, /Status:\s*\d{3}/],
6898
+ extract: extractDirBustIntel
6899
+ },
6900
+ {
6901
+ name: "sqlmap",
6902
+ signatures: [/sqlmap/i, /\[INFO\]\s+testing/i, /Parameter:\s+/i],
6903
+ extract: extractSqlmapIntel
6904
+ },
6905
+ {
6906
+ name: "hash_dump",
6907
+ signatures: [/\$[0-9]\$/, /\$2[aby]\$/, /:[0-9]+:[a-f0-9]{32}:/i],
6908
+ extract: extractHashIntel
6909
+ }
6910
+ ];
6911
+ function compressToolOutput(output, toolName) {
6912
+ if (!output || output.length < MIN_COMPRESS_LENGTH) {
6913
+ return output;
6914
+ }
6915
+ let intel = [];
6916
+ let detectedTool = "";
6917
+ for (const sig of TOOL_SIGNATURES) {
6918
+ const matched = sig.signatures.some((s) => s.test(output));
6919
+ if (matched) {
6920
+ detectedTool = sig.name;
6921
+ intel = sig.extract(output);
6922
+ break;
6923
+ }
6924
+ }
6925
+ if (intel.length === 0) {
6926
+ intel = extractGenericIntel(output);
6927
+ detectedTool = toolName || "unknown";
6928
+ }
6929
+ if (intel.length === 0) {
6930
+ return output;
6931
+ }
6932
+ const summary = [
6933
+ SUMMARY_HEADER,
6934
+ `Tool: ${detectedTool} | Output length: ${output.length} chars`,
6935
+ "",
6936
+ ...intel,
6937
+ "",
6938
+ SUMMARY_FOOTER,
6939
+ ""
6940
+ ].join("\n");
6941
+ return summary + output;
6942
+ }
6943
+ function extractNmapIntel(output) {
6944
+ const intel = [];
6945
+ const lines = output.split("\n");
6946
+ const hostMatches = output.match(/Nmap scan report for\s+(\S+)/gi);
6947
+ const openPorts = output.match(/^\d+\/\w+\s+open\s+/gm);
6948
+ if (hostMatches) intel.push(`Hosts scanned: ${hostMatches.length}`);
6949
+ if (openPorts) intel.push(`Open ports found: ${openPorts.length}`);
6950
+ const portLines = lines.filter((l) => /^\d+\/\w+\s+open\s+/.test(l.trim()));
6951
+ if (portLines.length > 0) {
6952
+ intel.push("Open ports:");
6953
+ for (const pl of portLines.slice(0, 30)) {
6954
+ intel.push(` ${pl.trim()}`);
6955
+ }
6956
+ }
6957
+ const vulnLines = lines.filter(
6958
+ (l) => /VULNERABLE|CVE-\d{4}-\d+|exploit|CRITICAL/i.test(l)
6959
+ );
6960
+ if (vulnLines.length > 0) {
6961
+ intel.push("\u26A0\uFE0F Vulnerability indicators:");
6962
+ for (const vl of vulnLines.slice(0, 10)) {
6963
+ intel.push(` ${vl.trim()}`);
6964
+ }
6965
+ }
6966
+ const osMatch = output.match(/OS details:\s*(.+)/i) || output.match(/Running:\s*(.+)/i);
6967
+ if (osMatch) intel.push(`OS: ${osMatch[1].trim()}`);
6968
+ return intel;
6969
+ }
6970
+ function extractLinpeasIntel(output) {
6971
+ const intel = [];
6972
+ const suidSection = output.match(/SUID[\s\S]*?(?=\n[═╔╗━]+|\n\n\n)/i);
6973
+ if (suidSection) {
6974
+ const suidBins = suidSection[0].match(/\/\S+/g);
6975
+ if (suidBins) {
6976
+ const interestingSuid = suidBins.filter(
6977
+ (b) => /python|perl|ruby|node|bash|vim|less|more|nano|find|nmap|awk|env|php|gcc|gdb|docker|strace|ltrace/i.test(b)
6978
+ );
6979
+ if (interestingSuid.length > 0) {
6980
+ intel.push(`\u{1F534} Exploitable SUID binaries: ${interestingSuid.join(", ")}`);
6981
+ }
6982
+ }
6983
+ }
6984
+ const sudoMatch = output.match(/User \S+ may run[\s\S]*?(?=\n\n|\n[═╔╗━])/i);
6985
+ if (sudoMatch) {
6986
+ intel.push(`\u{1F534} sudo -l: ${sudoMatch[0].trim().slice(0, 500)}`);
6987
+ }
6988
+ const writableMatch = output.match(/Interesting writable[\s\S]*?(?=\n\n|\n[═╔╗━])/i);
6989
+ if (writableMatch) {
6990
+ intel.push(`\u{1F4DD} Writable: ${writableMatch[0].trim().slice(0, 300)}`);
6991
+ }
6992
+ const cronMatch = output.match(/Cron[\s\S]*?(?=\n\n|\n[═╔╗━])/i);
6993
+ if (cronMatch) {
6994
+ const cronLines = cronMatch[0].split("\n").filter((l) => l.includes("*") || /\/(root|cron)/i.test(l));
6995
+ if (cronLines.length > 0) {
6996
+ intel.push("\u23F0 Cron entries:");
6997
+ cronLines.slice(0, 5).forEach((c) => intel.push(` ${c.trim()}`));
6998
+ }
6999
+ }
7000
+ const kernelMatch = output.match(/Linux version\s+(\S+)/i) || output.match(/Kernel:\s*(\S+)/i);
7001
+ if (kernelMatch) intel.push(`\u{1F427} Kernel: ${kernelMatch[1]}`);
7002
+ const passLines = output.split("\n").filter(
7003
+ (l) => /password\s*[=:]\s*\S+/i.test(l) && !/\*\*\*|example|sample/i.test(l)
7004
+ );
7005
+ if (passLines.length > 0) {
7006
+ intel.push("\u{1F511} Potential credentials found:");
7007
+ passLines.slice(0, 5).forEach((p) => intel.push(` ${p.trim()}`));
7008
+ }
7009
+ const cveMatches = output.match(/CVE-\d{4}-\d+/gi);
7010
+ if (cveMatches) {
7011
+ const uniqueCves = [...new Set(cveMatches)];
7012
+ intel.push(`\u26A0\uFE0F CVEs mentioned: ${uniqueCves.join(", ")}`);
7013
+ }
7014
+ return intel;
7015
+ }
7016
+ function extractEnum4linuxIntel(output) {
7017
+ const intel = [];
7018
+ const userMatches = output.match(/user:\[(\S+?)\]/gi);
7019
+ if (userMatches) {
7020
+ const users = userMatches.map((u) => u.replace(/user:\[|\]/gi, ""));
7021
+ intel.push(`\u{1F464} Users found: ${users.join(", ")}`);
7022
+ }
7023
+ const shareMatches = output.match(/Mapping: (\S+),\s*Listing: (\S+)/gi) || output.match(/\\\\\S+\\\\\S+/g);
7024
+ if (shareMatches) {
7025
+ intel.push(`\u{1F4C2} Shares: ${shareMatches.slice(0, 10).join(", ")}`);
7026
+ }
7027
+ const domainMatch = output.match(/Domain:\s*\[(\S+?)\]/i) || output.match(/Workgroup:\s*\[(\S+?)\]/i);
7028
+ if (domainMatch) intel.push(`\u{1F3E2} Domain: ${domainMatch[1]}`);
7029
+ const policyMatch = output.match(/Password Complexity|Account Lockout/i);
7030
+ if (policyMatch) intel.push("\u{1F510} Password policy information found");
7031
+ return intel;
7032
+ }
7033
+ function extractDirBustIntel(output) {
7034
+ const intel = [];
7035
+ const lines = output.split("\n");
7036
+ const interestingPaths = [];
7037
+ for (const line of lines) {
7038
+ const match = line.match(/(?:Status:\s*(\d{3})|(\d{3})\s+\d+\s+\d+).*?(\/\S+)/);
7039
+ if (match) {
7040
+ const status = match[1] || match[2];
7041
+ const path2 = match[3];
7042
+ if (["200", "301", "302", "403"].includes(status)) {
7043
+ interestingPaths.push(`[${status}] ${path2}`);
7044
+ }
7045
+ }
7046
+ const ffufMatch = line.match(/(\S+)\s+\[Status:\s*(\d+),?\s*Size:\s*(\d+)/);
7047
+ if (ffufMatch && ["200", "301", "302", "403"].includes(ffufMatch[2])) {
7048
+ interestingPaths.push(`[${ffufMatch[2]}] ${ffufMatch[1]} (${ffufMatch[3]}b)`);
7049
+ }
7050
+ }
7051
+ if (interestingPaths.length > 0) {
7052
+ intel.push(`\u{1F4C1} Discovered paths (${interestingPaths.length}):`);
7053
+ interestingPaths.slice(0, 20).forEach((p) => intel.push(` ${p}`));
7054
+ if (interestingPaths.length > 20) {
7055
+ intel.push(` ... and ${interestingPaths.length - 20} more`);
7056
+ }
7057
+ }
7058
+ return intel;
7059
+ }
7060
+ function extractSqlmapIntel(output) {
7061
+ const intel = [];
7062
+ const injectionTypes = output.match(/Type:\s*(\S.*?)(?:\n|$)/gi);
7063
+ if (injectionTypes) {
7064
+ intel.push("\u{1F489} SQL injection found:");
7065
+ injectionTypes.slice(0, 5).forEach((t) => intel.push(` ${t.trim()}`));
7066
+ }
7067
+ const dbMatch = output.match(/back-end DBMS:\s*(.+)/i);
7068
+ if (dbMatch) intel.push(`\u{1F5C4}\uFE0F DBMS: ${dbMatch[1].trim()}`);
7069
+ const dbListMatch = output.match(/available databases.*?:\s*([\s\S]*?)(?=\n\[|\n$)/i);
7070
+ if (dbListMatch) {
7071
+ intel.push(`\u{1F4CA} Databases: ${dbListMatch[1].trim().replace(/\n/g, ", ")}`);
7072
+ }
7073
+ const tableMatches = output.match(/Database:\s*\S+\s+Table:\s*\S+/gi);
7074
+ if (tableMatches) {
7075
+ intel.push(`\u{1F4CB} Tables dumped: ${tableMatches.length}`);
7076
+ }
7077
+ return intel;
7078
+ }
7079
+ function extractHashIntel(output) {
7080
+ const intel = [];
7081
+ const lines = output.split("\n");
7082
+ const md5Hashes = lines.filter((l) => /\b[a-f0-9]{32}\b/i.test(l) && !l.includes("{"));
7083
+ const sha256Hashes = lines.filter((l) => /\b[a-f0-9]{64}\b/i.test(l));
7084
+ const unixHashes = lines.filter((l) => /\$[0-9]\$|\$2[aby]\$|\$6\$|\$5\$/i.test(l));
7085
+ const ntlmHashes = lines.filter((l) => /:[0-9]+:[a-f0-9]{32}:[a-f0-9]{32}:::/i.test(l));
7086
+ if (md5Hashes.length > 0) intel.push(`#\uFE0F\u20E3 MD5 hashes: ${md5Hashes.length}`);
7087
+ if (sha256Hashes.length > 0) intel.push(`#\uFE0F\u20E3 SHA256 hashes: ${sha256Hashes.length}`);
7088
+ if (unixHashes.length > 0) intel.push(`#\uFE0F\u20E3 Unix crypt hashes: ${unixHashes.length}`);
7089
+ if (ntlmHashes.length > 0) {
7090
+ intel.push(`#\uFE0F\u20E3 NTLM hashes: ${ntlmHashes.length}`);
7091
+ ntlmHashes.slice(0, 5).forEach((h) => intel.push(` ${h.trim().slice(0, 100)}`));
7092
+ }
7093
+ return intel;
7094
+ }
7095
+ function extractGenericIntel(output) {
7096
+ const intel = [];
7097
+ const credPatterns = output.match(/(?:password|passwd|pwd|credentials?)\s*[=:]\s*\S+/gi);
7098
+ if (credPatterns) {
7099
+ intel.push("\u{1F511} Potential credentials detected:");
7100
+ credPatterns.slice(0, 5).forEach((c) => intel.push(` ${c.trim()}`));
7101
+ }
7102
+ const cves = output.match(/CVE-\d{4}-\d+/gi);
7103
+ if (cves) {
7104
+ const unique = [...new Set(cves)];
7105
+ intel.push(`\u26A0\uFE0F CVEs mentioned: ${unique.join(", ")}`);
7106
+ }
7107
+ const ips = output.match(/\b(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\b/g);
7108
+ if (ips) {
7109
+ const uniqueIps = [...new Set(ips)].filter(
7110
+ (ip) => !ip.startsWith("0.") && !ip.startsWith("255.") && ip !== "127.0.0.1"
7111
+ );
7112
+ if (uniqueIps.length > 0 && uniqueIps.length <= 20) {
7113
+ intel.push(`\u{1F310} IP addresses found: ${uniqueIps.join(", ")}`);
7114
+ }
7115
+ }
7116
+ const paths = output.match(/\/(?:etc\/(?:shadow|passwd|sudoers)|root\/|home\/\S+|var\/www\/\S+|opt\/\S+)\S*/g);
7117
+ if (paths) {
7118
+ const uniquePaths = [...new Set(paths)].slice(0, 10);
7119
+ intel.push(`\u{1F4C2} Interesting paths: ${uniquePaths.join(", ")}`);
7120
+ }
7121
+ const flagPatterns = output.match(/(?:flag|secret|key|token)\{[^}]+\}/gi);
7122
+ if (flagPatterns) {
7123
+ intel.push("\u{1F3F4} FLAG/SECRET patterns detected:");
7124
+ flagPatterns.forEach((f) => intel.push(` ${f}`));
7125
+ }
7126
+ return intel;
7127
+ }
7128
+
7129
+ // src/shared/utils/context-digest.ts
7130
+ import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync2, existsSync as existsSync6 } from "fs";
7131
+ var PASSTHROUGH_THRESHOLD = 3e3;
7132
+ var LAYER2_THRESHOLD = 8e3;
7133
+ var LAYER3_THRESHOLD = 5e4;
7134
+ var MAX_REDUCED_LINES = 500;
7135
+ var getOutputDir = () => WORKSPACE.OUTPUTS;
7136
+ var MAX_DUPLICATE_DISPLAY = 3;
7137
+ var LAYER3_MAX_INPUT_CHARS = 8e4;
7138
+ var FALLBACK_MAX_CHARS = 3e4;
7139
+ var SIGNAL_PATTERNS = [
7140
+ /error|fail|denied|refused|timeout|exception/i,
7141
+ /warning|warn|deprecated|insecure/i,
7142
+ /success|found|detected|discovered|vulnerable|VULNERABLE/i,
7143
+ /password|passwd|credential|secret|key|token|hash/i,
7144
+ /CVE-\d{4}-\d+/i,
7145
+ /\d+\/\w+\s+open\s+/,
7146
+ // nmap open port
7147
+ /flag\{|ctf\{|HTB\{|THM\{/i,
7148
+ // CTF flags
7149
+ /root:|admin:|www-data:|nobody:/,
7150
+ // /etc/passwd entries
7151
+ /\$[0-9]\$|\$2[aby]\$/,
7152
+ // password hashes
7153
+ /\b(?:192\.168|10\.|172\.(?:1[6-9]|2\d|3[01]))\.\d+\.\d+\b/,
7154
+ // internal IPs
7155
+ /BEGIN\s+(?:RSA|DSA|EC|OPENSSH)\s+PRIVATE\s+KEY/i,
7156
+ // private keys
7157
+ /Authorization:|Bearer\s+|Basic\s+/i
7158
+ // auth tokens
7159
+ ];
7160
+ var NOISE_PATTERNS = [
7161
+ /^\s*$/,
7162
+ // blank lines
7163
+ /^\[[\d:]+\]\s*$/,
7164
+ // timestamp-only lines
7165
+ /^#+\s*$/,
7166
+ // separator lines
7167
+ /^\s*Progress:\s*\[?[#=\-\s>]+\]?\s*\d+/i,
7168
+ // progress bars
7169
+ /\d+\s+requests?\s+(?:sent|made)/i,
7170
+ // request counters
7171
+ /^\s*(?:\.{3,}|={5,}|-{5,})\s*$/
7172
+ // decoration lines
7173
+ ];
7174
+ async function digestToolOutput(output, toolName, toolInput, llmDigestFn) {
7175
+ const originalLength = output.length;
7176
+ if (originalLength < PASSTHROUGH_THRESHOLD) {
7177
+ return {
7178
+ digestedOutput: output,
7179
+ fullOutputPath: null,
7180
+ layersApplied: [],
7181
+ originalLength,
7182
+ digestedLength: originalLength,
7183
+ compressionRatio: 1
7184
+ };
7185
+ }
7186
+ const layersApplied = [];
7187
+ let processed = output;
7188
+ let savedOutputPath = null;
7189
+ processed = compressToolOutput(processed, toolName);
7190
+ layersApplied.push(1);
7191
+ if (processed.length > LAYER2_THRESHOLD) {
7192
+ processed = structuralReduce(processed);
7193
+ layersApplied.push(2);
7194
+ }
7195
+ if (processed.length > LAYER3_THRESHOLD) {
7196
+ savedOutputPath = saveFullOutput(output, toolName);
7197
+ if (llmDigestFn) {
7198
+ try {
7199
+ const context = `Tool: ${toolName}${toolInput ? ` | Input: ${toolInput}` : ""}`;
7200
+ const digest = await llmDigestFn(processed, context);
7201
+ processed = formatLLMDigest(digest, savedOutputPath, originalLength);
7202
+ layersApplied.push(3);
7203
+ } catch (err) {
7204
+ debugLog("general", "Context Digest Layer 3 failed, falling back", { toolName, error: String(err) });
7205
+ processed = formatFallbackDigest(processed, savedOutputPath, originalLength);
7206
+ layersApplied.push(3);
7207
+ }
7208
+ } else {
7209
+ processed = formatFallbackDigest(processed, savedOutputPath, originalLength);
7210
+ }
7211
+ } else if (layersApplied.includes(2)) {
7212
+ savedOutputPath = saveFullOutput(output, toolName);
7213
+ }
7214
+ return {
7215
+ digestedOutput: processed,
7216
+ fullOutputPath: savedOutputPath,
7217
+ layersApplied,
7218
+ originalLength,
7219
+ digestedLength: processed.length,
7220
+ compressionRatio: processed.length / originalLength
7221
+ };
7222
+ }
7223
+ function structuralReduce(output) {
7224
+ let cleaned = stripAnsi(output);
7225
+ const lines = cleaned.split("\n");
7226
+ const result2 = [];
7227
+ const duplicateCounts = /* @__PURE__ */ new Map();
7228
+ let lastLine = "";
7229
+ let consecutiveDupes = 0;
7230
+ for (const line of lines) {
7231
+ const trimmed = line.trim();
7232
+ if (NOISE_PATTERNS.some((p) => p.test(trimmed))) {
7233
+ continue;
7234
+ }
7235
+ const isSignal = SIGNAL_PATTERNS.some((p) => p.test(trimmed));
7236
+ const normalized = normalizeLine(trimmed);
7237
+ if (normalized === normalizeLine(lastLine) && !isSignal) {
7238
+ consecutiveDupes++;
7239
+ duplicateCounts.set(normalized, (duplicateCounts.get(normalized) || 1) + 1);
7240
+ continue;
7241
+ }
7242
+ if (consecutiveDupes > 0) {
7243
+ if (consecutiveDupes <= MAX_DUPLICATE_DISPLAY) {
7244
+ for (let i = 0; i < consecutiveDupes; i++) {
7245
+ result2.push(lastLine);
7246
+ }
7247
+ } else {
7248
+ result2.push(` ... (${consecutiveDupes} similar lines collapsed)`);
7249
+ }
7250
+ consecutiveDupes = 0;
7251
+ }
7252
+ result2.push(line);
7253
+ lastLine = trimmed;
7254
+ }
7255
+ if (consecutiveDupes > MAX_DUPLICATE_DISPLAY) {
7256
+ result2.push(` ... (${consecutiveDupes} similar lines collapsed)`);
7257
+ } else {
7258
+ for (let i = 0; i < consecutiveDupes; i++) {
7259
+ result2.push(lastLine);
7260
+ }
7261
+ }
7262
+ if (result2.length > MAX_REDUCED_LINES) {
7263
+ const headSize = Math.floor(MAX_REDUCED_LINES * 0.4);
7264
+ const tailSize = Math.floor(MAX_REDUCED_LINES * 0.3);
7265
+ const signalBudget = MAX_REDUCED_LINES - headSize - tailSize;
7266
+ const head = result2.slice(0, headSize);
7267
+ const tail = result2.slice(-tailSize);
7268
+ const middle = result2.slice(headSize, -tailSize);
7269
+ const middleSignals = middle.filter((line) => SIGNAL_PATTERNS.some((p) => p.test(line))).slice(0, signalBudget);
7270
+ const skipped = middle.length - middleSignals.length;
7271
+ cleaned = [
7272
+ ...head,
7273
+ "",
7274
+ `... [${skipped} routine lines skipped \u2014 ${middleSignals.length} important lines preserved] ...`,
7275
+ "",
7276
+ ...middleSignals,
7277
+ "",
7278
+ `... [resuming last ${tailSize} lines] ...`,
7279
+ "",
7280
+ ...tail
7281
+ ].join("\n");
7282
+ } else {
7283
+ cleaned = result2.join("\n");
7284
+ }
7285
+ return cleaned;
7286
+ }
7287
+ var DIGEST_SYSTEM_PROMPT = `You are a pentesting output analyst. Given raw tool output, extract ONLY actionable intelligence. Be terse and structured.
7288
+
7289
+ FORMAT YOUR RESPONSE EXACTLY LIKE THIS:
7290
+ ## Key Findings
7291
+ - [finding 1]
7292
+ - [finding 2]
7293
+
7294
+ ## Credentials/Secrets
7295
+ - [any discovered credentials, hashes, tokens, keys]
7296
+
7297
+ ## Attack Vectors
7298
+ - [exploitable services, vulnerabilities, misconfigurations]
7299
+
7300
+ ## Next Steps
7301
+ - [recommended immediate actions based on findings]
7302
+
7303
+ RULES:
7304
+ - Be EXTREMELY concise \u2014 max 30 lines total
7305
+ - Only include ACTIONABLE findings \u2014 skip routine/expected results
7306
+ - If nothing interesting found, say "No actionable findings in this output"
7307
+ - Include exact values: port numbers, versions, usernames, file paths
7308
+ - Never include decorative output, banners, or progress information`;
7309
+ function formatLLMDigest(digest, filePath, originalChars) {
7310
+ return [
7311
+ "\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557",
7312
+ "\u2551 CONTEXT DIGEST (LLM-summarized) \u2551",
7313
+ "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D",
7314
+ "",
7315
+ digest,
7316
+ "",
7317
+ `\u{1F4C2} Full output saved: ${filePath} (${originalChars} chars)`,
7318
+ `\u{1F4A1} Use read_file("${filePath}") to see the complete raw output if needed.`
7319
+ ].join("\n");
7320
+ }
7321
+ function formatFallbackDigest(processed, filePath, originalChars) {
7322
+ const lines = processed.split("\n");
7323
+ const summaryEndIdx = lines.findIndex((l) => l.includes("END SUMMARY"));
7324
+ const summaryBlock = summaryEndIdx > 0 ? lines.slice(0, summaryEndIdx + 1).join("\n") : "";
7325
+ const remaining = summaryEndIdx > 0 ? lines.slice(summaryEndIdx + 1).join("\n") : processed;
7326
+ const maxChars = FALLBACK_MAX_CHARS;
7327
+ let truncatedRemaining = remaining;
7328
+ if (remaining.length > maxChars) {
7329
+ const headChars = Math.floor(maxChars * 0.6);
7330
+ const tailChars = Math.floor(maxChars * 0.4);
7331
+ const skipped = remaining.length - headChars - tailChars;
7332
+ truncatedRemaining = remaining.slice(0, headChars) + `
7333
+
7334
+ ... [${skipped} chars omitted \u2014 read full output from file] ...
7335
+
7336
+ ` + remaining.slice(-tailChars);
7337
+ }
7338
+ return [
7339
+ summaryBlock,
7340
+ truncatedRemaining,
7341
+ "",
7342
+ `\u{1F4C2} Full output saved: ${filePath} (${originalChars} chars)`,
7343
+ `\u{1F4A1} Use read_file("${filePath}") to see the complete raw output.`
7344
+ ].filter(Boolean).join("\n");
7345
+ }
7346
+ function stripAnsi(text) {
7347
+ return text.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "").replace(/\x1B\[[\d;]*m/g, "");
7348
+ }
7349
+ function normalizeLine(line) {
7350
+ return line.replace(/\d+/g, "N").replace(/0x[a-f0-9]+/gi, "H").replace(/\s+/g, " ").trim().toLowerCase();
7351
+ }
7352
+ function saveFullOutput(output, toolName) {
7353
+ try {
7354
+ const outputDir = getOutputDir();
7355
+ if (!existsSync6(outputDir)) {
7356
+ mkdirSync2(outputDir, { recursive: true });
7357
+ }
7358
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
7359
+ const safeName = toolName.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 30);
7360
+ const filePath = `${outputDir}/${timestamp}_${safeName}.txt`;
7361
+ writeFileSync6(filePath, output, "utf-8");
7362
+ return filePath;
7363
+ } catch (err) {
7364
+ debugLog("general", "Failed to save full output to file", { toolName, error: String(err) });
7365
+ return "(failed to save \u2014 output too large or disk full)";
7366
+ }
7367
+ }
7368
+ function createLLMDigestFn(llmClient) {
7369
+ return async (text, context) => {
7370
+ const truncatedText = text.length > LAYER3_MAX_INPUT_CHARS ? text.slice(0, LAYER3_MAX_INPUT_CHARS) + `
7371
+ ... [truncated for summarization, ${text.length - LAYER3_MAX_INPUT_CHARS} chars omitted]` : text;
7372
+ const messages = [{ role: "user", content: `Analyze this pentesting tool output and extract actionable intelligence.
7373
+
7374
+ Context: ${context}
7375
+
7376
+ --- OUTPUT START ---
7377
+ ${truncatedText}
7378
+ --- OUTPUT END ---` }];
7379
+ const response = await llmClient.generateResponse(
7380
+ messages,
7381
+ void 0,
7382
+ // no tools — summarization only
7383
+ DIGEST_SYSTEM_PROMPT
7384
+ );
7385
+ return response.content || "No actionable findings extracted.";
7386
+ };
7387
+ }
7388
+
6768
7389
  // src/agents/core-agent.ts
6769
7390
  var CoreAgent = class _CoreAgent {
6770
7391
  llm;
@@ -7154,14 +7775,7 @@ Please decide how to handle this error and continue.`;
7154
7775
  input: call.input
7155
7776
  });
7156
7777
  let outputText = result2.output ?? "";
7157
- if (outputText.length > AGENT_LIMITS.MAX_TOOL_OUTPUT_LENGTH) {
7158
- const truncated = outputText.slice(0, AGENT_LIMITS.MAX_TOOL_OUTPUT_LENGTH);
7159
- const remaining = outputText.length - AGENT_LIMITS.MAX_TOOL_OUTPUT_LENGTH;
7160
- outputText = `${truncated}
7161
-
7162
- ... [TRUNCATED ${remaining} characters for context hygiene] ...
7163
- \u{1F4A1} TIP: If you need to see the full output, use a tool to read the file directly or run the command with | head, | tail, or | grep.`;
7164
- }
7778
+ this.scanForFlags(outputText);
7165
7779
  if (result2.error) {
7166
7780
  outputText = this.enrichToolError({ toolName: call.name, input: call.input, error: result2.error, originalOutput: outputText, progress });
7167
7781
  if (progress) progress.toolErrors++;
@@ -7171,8 +7785,26 @@ Please decide how to handle this error and continue.`;
7171
7785
  progress.blockedCommandPatterns.clear();
7172
7786
  }
7173
7787
  }
7788
+ try {
7789
+ const llmDigestFn = createLLMDigestFn(this.llm);
7790
+ const digestResult = await digestToolOutput(
7791
+ outputText,
7792
+ call.name,
7793
+ JSON.stringify(call.input).slice(0, 200),
7794
+ llmDigestFn
7795
+ );
7796
+ outputText = digestResult.digestedOutput;
7797
+ } catch {
7798
+ if (outputText.length > AGENT_LIMITS.MAX_TOOL_OUTPUT_LENGTH) {
7799
+ const truncated = outputText.slice(0, AGENT_LIMITS.MAX_TOOL_OUTPUT_LENGTH);
7800
+ const remaining = outputText.length - AGENT_LIMITS.MAX_TOOL_OUTPUT_LENGTH;
7801
+ outputText = `${truncated}
7802
+
7803
+ ... [TRUNCATED ${remaining} characters for context hygiene] ...
7804
+ \u{1F4A1} TIP: If you need to see the full output, use a tool to read the file directly or run the command with | head, | tail, or | grep.`;
7805
+ }
7806
+ }
7174
7807
  this.emitToolResult(call.name, result2.success, outputText, result2.error, Date.now() - toolStartTime);
7175
- this.scanForFlags(outputText);
7176
7808
  return { toolCallId: call.id, output: outputText, error: result2.error };
7177
7809
  } catch (error) {
7178
7810
  const errorMsg = String(error);
@@ -7248,7 +7880,7 @@ Please decide how to handle this error and continue.`;
7248
7880
  };
7249
7881
 
7250
7882
  // src/agents/prompt-builder.ts
7251
- import { readFileSync as readFileSync4, existsSync as existsSync6, readdirSync as readdirSync2 } from "fs";
7883
+ import { readFileSync as readFileSync4, existsSync as existsSync7, readdirSync as readdirSync2 } from "fs";
7252
7884
  import { join as join9, dirname as dirname5 } from "path";
7253
7885
  import { fileURLToPath as fileURLToPath4 } from "url";
7254
7886
 
@@ -7363,7 +7995,8 @@ var PromptBuilder = class {
7363
7995
  * 5. Scope constraints
7364
7996
  * 6. Current state (targets, findings, loot, active processes)
7365
7997
  * 7. TODO list
7366
- * 8. User context
7998
+ * 8. Time awareness (elapsed + deadline)
7999
+ * 9. User context
7367
8000
  */
7368
8001
  build(userInput, phase) {
7369
8002
  const fragments = [
@@ -7375,6 +8008,7 @@ var PromptBuilder = class {
7375
8008
  this.getScopeFragment(),
7376
8009
  this.getStateFragment(),
7377
8010
  this.getTodoFragment(),
8011
+ this.getTimeFragment(),
7378
8012
  PROMPT_DEFAULTS.USER_CONTEXT(userInput)
7379
8013
  ];
7380
8014
  return fragments.filter((f) => !!f).join("\n\n");
@@ -7395,7 +8029,7 @@ ${content}
7395
8029
  */
7396
8030
  loadPromptFile(filename) {
7397
8031
  const path2 = join9(PROMPTS_DIR, filename);
7398
- return existsSync6(path2) ? readFileSync4(path2, PROMPT_CONFIG.ENCODING) : "";
8032
+ return existsSync7(path2) ? readFileSync4(path2, PROMPT_CONFIG.ENCODING) : "";
7399
8033
  }
7400
8034
  /**
7401
8035
  * Load phase-specific prompt.
@@ -7441,14 +8075,14 @@ ${content}
7441
8075
  * "마크다운 파일 하나를 폴더에 넣으면, PromptBuilder가 자동으로 발견하고 로드한다."
7442
8076
  */
7443
8077
  loadPhaseRelevantTechniques(phase) {
7444
- if (!existsSync6(TECHNIQUES_DIR)) return "";
8078
+ if (!existsSync7(TECHNIQUES_DIR)) return "";
7445
8079
  const priorityTechniques = PHASE_TECHNIQUE_MAP[phase] || [];
7446
8080
  const loadedSet = /* @__PURE__ */ new Set();
7447
8081
  const fragments = [];
7448
8082
  for (const technique of priorityTechniques) {
7449
8083
  const filePath = join9(TECHNIQUES_DIR, `${technique}.md`);
7450
8084
  try {
7451
- if (!existsSync6(filePath)) continue;
8085
+ if (!existsSync7(filePath)) continue;
7452
8086
  const content = readFileSync4(filePath, PROMPT_CONFIG.ENCODING);
7453
8087
  if (content) {
7454
8088
  fragments.push(`<technique-reference category="${technique}">
@@ -7494,6 +8128,11 @@ ${content}
7494
8128
  const list = todo.map((t) => `[${t.status === TODO_STATUSES.DONE ? "x" : " "}] ${t.content} (${t.priority})`).join("\n");
7495
8129
  return PROMPT_XML.TODO(list || PROMPT_DEFAULTS.EMPTY_TODO);
7496
8130
  }
8131
+ getTimeFragment() {
8132
+ return `<time-status>
8133
+ ${this.state.getTimeStatus()}
8134
+ </time-status>`;
8135
+ }
7497
8136
  };
7498
8137
 
7499
8138
  // src/agents/main-agent.ts
@@ -7731,16 +8370,16 @@ var THEME = {
7731
8370
  },
7732
8371
  // Status colors
7733
8372
  status: {
7734
- success: "#22c55e",
7735
- // Green
8373
+ success: "#10b981",
8374
+ // Emerald
7736
8375
  warning: "#f59e0b",
7737
8376
  // Amber
7738
8377
  error: "#ef4444",
7739
8378
  // Red
7740
- info: "#3b82f6",
7741
- // Blue
7742
- running: "#0ea5e9",
8379
+ info: "#0ea5e9",
7743
8380
  // Sky
8381
+ running: "#38bdf8",
8382
+ // Bright Sky
7744
8383
  pending: "#64748b"
7745
8384
  // Slate
7746
8385
  },
@@ -7795,7 +8434,23 @@ var THEME = {
7795
8434
  },
7796
8435
  // Gradients
7797
8436
  gradient: {
7798
- cyber: ["#00d4ff", "#00ff9f"],
8437
+ cyber: [
8438
+ "#38bdf8",
8439
+ // Sky 400
8440
+ "#34c6f4",
8441
+ "#30d0f0",
8442
+ "#2cd9ec",
8443
+ "#28e3e8",
8444
+ "#22d3ee",
8445
+ // Cyan 400
8446
+ "#25dbd6",
8447
+ "#28e4be",
8448
+ "#2dd4bf",
8449
+ // Teal 400
8450
+ "#31dbac",
8451
+ "#34d399"
8452
+ // Emerald 400
8453
+ ],
7799
8454
  danger: ["#ef4444", "#7f1d1d"],
7800
8455
  success: ["#22c55e", "#14532d"],
7801
8456
  gold: ["#f59e0b", "#78350f"],
@@ -7807,32 +8462,12 @@ var THEME = {
7807
8462
  identity: "#38bdf8"
7808
8463
  };
7809
8464
  var ASCII_BANNER = `
7810
- \u2571\u2594\u2594\u2572
7811
- \u2571\u2594 \u2594\u2572
7812
- \u2571\u2594 \u2694 \u2594\u2572
7813
- \u2571\u2594 \u2594\u2572
7814
- \u2571\u2594 \u2594\u2572
7815
- \u2571\u2594 \u2594\u2572
7816
- \u2571\u2594 \u2594\u2572
7817
- \u2571\u2594 \u2594\u2572
7818
- \u2572\u2581 \u2581\u2571
7819
- \u2572\u2581\u2581 \u2581\u2581\u2571
7820
- \u2572\u2581\u2581 \u2581\u2581\u2571
7821
- \u2500\u2500\u2500\u2500\u2500\u2573\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2573\u2500\u2500\u2500\u2500\u2500
7822
- \u2572 \u2571
7823
- \u2572 \u2571
7824
- \u2572 \u2571
7825
- \u2572 \u2571
7826
- \u2572 \u2571
7827
- \u25C9
7828
-
7829
- \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
7830
- \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
7831
- \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
7832
- \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
7833
- \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
7834
- \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
7835
- A U T O N O M O U S P E N T E S T A G E N T
8465
+ ____ __ __ _
8466
+ / __ \\___ ____ / /____ _______/ /_(_)___ ____ _
8467
+ / /_/ / _ \\/ __ \\/ __/ _ \\/ ___/ __/ / / __ \\/ __ \`/
8468
+ / ____/ __/ / / / /_/ __(__ ) /_/ / / / / / /_/ /
8469
+ /_/ \\___/_/ /_/\\__/\\___/____/\\__/_/_/_/ /_/\\__, /
8470
+ /____/
7836
8471
  `;
7837
8472
  var ICONS = {
7838
8473
  // Status