pentesting 0.73.2 → 0.73.3

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.
@@ -65,6 +65,8 @@ var PROCESS_EVENTS = {
65
65
  CONNECTION_DETECTED: "connection_detected",
66
66
  ROLE_CHANGED: "role_changed",
67
67
  COMMAND_SENT: "command_sent",
68
+ SHELL_UPGRADE_ATTEMPTED: "shell_upgrade_attempted",
69
+ SHELL_STABILIZED: "shell_stabilized",
68
70
  STOPPED: "stopped",
69
71
  DIED: "died",
70
72
  ZOMBIE_CLEANED: "zombie_cleaned"
@@ -18,7 +18,7 @@ import {
18
18
  getProcessEventLog,
19
19
  logEvent,
20
20
  setProcess
21
- } from "./chunk-YFDJI3GO.js";
21
+ } from "./chunk-GLO6TOJN.js";
22
22
 
23
23
  // src/shared/constants/time/conversions.ts
24
24
  var MS_PER_MINUTE = 6e4;
@@ -235,7 +235,7 @@ var INPUT_PROMPT_PATTERNS = [
235
235
 
236
236
  // src/shared/constants/agent.ts
237
237
  var APP_NAME = "Pentest AI";
238
- var APP_VERSION = "0.73.2";
238
+ var APP_VERSION = "0.73.3";
239
239
  var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
240
240
  var LLM_ROLES = {
241
241
  SYSTEM: "system",
@@ -509,6 +509,18 @@ var TOOL_NAMES = {
509
509
  READ_FILE: "read_file",
510
510
  WRITE_FILE: "write_file",
511
511
  BG_PROCESS: "bg_process",
512
+ LISTENER_START: "listener_start",
513
+ LISTENER_STATUS: "listener_status",
514
+ SHELL_PROMOTE: "shell_promote",
515
+ SHELL_EXEC: "shell_exec",
516
+ SHELL_CHECK: "shell_check",
517
+ SHELL_UPGRADE: "shell_upgrade",
518
+ EXPLOIT_CREDENTIAL_CHECK: "exploit_credential_check",
519
+ EXPLOIT_ARTIFACT_CHECK: "exploit_artifact_check",
520
+ EXPLOIT_FOOTHOLD_CHECK: "exploit_foothold_check",
521
+ PWN_CRASH_REPRO: "pwn_crash_repro",
522
+ PWN_OFFSET_CHECK: "pwn_offset_check",
523
+ PWN_PAYLOAD_SMOKE: "pwn_payload_smoke",
512
524
  // Intelligence & Research
513
525
  PARSE_NMAP: "parse_nmap",
514
526
  SEARCH_CVE: "search_cve",
@@ -1507,6 +1519,57 @@ function splitByPipe(command) {
1507
1519
  }
1508
1520
 
1509
1521
  // src/shared/utils/command-validator/redirect.ts
1522
+ function isAllowedRedirectPath(target) {
1523
+ return SAFE_REDIRECT_PATHS.some((pathPrefix) => target.startsWith(pathPrefix));
1524
+ }
1525
+ function updateQuoteState(ch, state3) {
1526
+ if (ch === "'" && !state3.inDouble) {
1527
+ state3.inSingle = !state3.inSingle;
1528
+ return true;
1529
+ }
1530
+ if (ch === '"' && !state3.inSingle) {
1531
+ state3.inDouble = !state3.inDouble;
1532
+ return true;
1533
+ }
1534
+ return false;
1535
+ }
1536
+ function extractFileDescriptor(command, operatorIndex) {
1537
+ let fd = "";
1538
+ let b = operatorIndex - 1;
1539
+ while (b >= 0 && /\d/.test(command[b])) {
1540
+ fd = command[b] + fd;
1541
+ b--;
1542
+ }
1543
+ return fd;
1544
+ }
1545
+ function skipSpaces(command, start) {
1546
+ let index = start;
1547
+ while (index < command.length && /\s/.test(command[index])) index++;
1548
+ return index;
1549
+ }
1550
+ function readRedirectTarget(command, start) {
1551
+ let i = start;
1552
+ let target = "";
1553
+ let targetInSingle = false;
1554
+ let targetInDouble = false;
1555
+ while (i < command.length) {
1556
+ const ch = command[i];
1557
+ if (ch === "'" && !targetInDouble) {
1558
+ targetInSingle = !targetInSingle;
1559
+ i++;
1560
+ continue;
1561
+ }
1562
+ if (ch === '"' && !targetInSingle) {
1563
+ targetInDouble = !targetInDouble;
1564
+ i++;
1565
+ continue;
1566
+ }
1567
+ if (!targetInSingle && !targetInDouble && /[\s|;&<>]/.test(ch)) break;
1568
+ target += ch;
1569
+ i++;
1570
+ }
1571
+ return { target, nextIndex: i };
1572
+ }
1510
1573
  function stripRedirects(segment) {
1511
1574
  return segment.replace(/\d*>\s*&\d+/g, "").replace(/\d*>>\s*\S+/g, "").replace(/\d*>\s*\S+/g, "").replace(/<\s*\S+/g, "").trim();
1512
1575
  }
@@ -1515,8 +1578,7 @@ function validateRedirects(command) {
1515
1578
  for (const { type, target } of redirects) {
1516
1579
  if (type === ">" || type === ">>") {
1517
1580
  if (target.startsWith("&") || target === "/dev/null") continue;
1518
- const isAllowed = SAFE_REDIRECT_PATHS.some((p) => target.startsWith(p));
1519
- if (!isAllowed) {
1581
+ if (!isAllowedRedirectPath(target)) {
1520
1582
  return {
1521
1583
  isSafe: false,
1522
1584
  error: `Redirect to '${target}' is not in allowed paths`,
@@ -1524,8 +1586,7 @@ function validateRedirects(command) {
1524
1586
  };
1525
1587
  }
1526
1588
  } else if (type === "<") {
1527
- const isAllowed = SAFE_REDIRECT_PATHS.some((p) => target.startsWith(p));
1528
- if (!isAllowed) {
1589
+ if (!isAllowedRedirectPath(target)) {
1529
1590
  return {
1530
1591
  isSafe: false,
1531
1592
  error: `Input redirect from '${target}' is not in allowed paths`,
@@ -1538,55 +1599,23 @@ function validateRedirects(command) {
1538
1599
  }
1539
1600
  function extractRedirectTargets(command) {
1540
1601
  const redirects = [];
1541
- let inSingle = false;
1542
- let inDouble = false;
1602
+ const quoteState = { inSingle: false, inDouble: false };
1543
1603
  let i = 0;
1544
1604
  while (i < command.length) {
1545
1605
  const ch = command[i];
1546
- if (ch === "'" && !inDouble) {
1547
- inSingle = !inSingle;
1606
+ if (updateQuoteState(ch, quoteState)) {
1548
1607
  i++;
1549
1608
  continue;
1550
1609
  }
1551
- if (ch === '"' && !inSingle) {
1552
- inDouble = !inDouble;
1553
- i++;
1554
- continue;
1555
- }
1556
- if (!inSingle && !inDouble) {
1610
+ if (!quoteState.inSingle && !quoteState.inDouble) {
1557
1611
  if (ch === ">" || ch === "<") {
1558
1612
  const isAppend = ch === ">" && command[i + 1] === ">";
1559
1613
  const type = isAppend ? ">>" : ch;
1560
1614
  const jump = isAppend ? 2 : 1;
1561
- let fd = "";
1562
- if (ch === ">") {
1563
- let b = i - 1;
1564
- while (b >= 0 && /\d/.test(command[b])) {
1565
- fd = command[b] + fd;
1566
- b--;
1567
- }
1568
- }
1569
- i += jump;
1570
- while (i < command.length && /\s/.test(command[i])) i++;
1571
- let target = "";
1572
- let targetInSingle = false;
1573
- let targetInDouble = false;
1574
- while (i < command.length) {
1575
- const tc = command[i];
1576
- if (tc === "'" && !targetInDouble) {
1577
- targetInSingle = !targetInSingle;
1578
- i++;
1579
- continue;
1580
- }
1581
- if (tc === '"' && !targetInSingle) {
1582
- targetInDouble = !targetInDouble;
1583
- i++;
1584
- continue;
1585
- }
1586
- if (!targetInSingle && !targetInDouble && /[\s|;&<>]/.test(tc)) break;
1587
- target += tc;
1588
- i++;
1589
- }
1615
+ const fd = ch === ">" ? extractFileDescriptor(command, i) : "";
1616
+ const targetStart = skipSpaces(command, i + jump);
1617
+ const { target, nextIndex } = readRedirectTarget(command, targetStart);
1618
+ i = nextIndex;
1590
1619
  if (target) {
1591
1620
  redirects.push({ type, fd, target });
1592
1621
  }
@@ -1946,76 +1975,115 @@ function getTorBrowserArgs() {
1946
1975
  }
1947
1976
 
1948
1977
  // src/shared/utils/config/tor/wrapper.ts
1978
+ function wrapCurl(command, socks) {
1979
+ if (!/\bcurl\b/.test(command)) return null;
1980
+ if (/--socks5|--proxy\b|-x\s/.test(command)) return command;
1981
+ return command.replace(/\bcurl\b/, `curl --socks5-hostname ${socks}`);
1982
+ }
1983
+ function wrapWget(command, socks) {
1984
+ if (!/\bwget\b/.test(command)) return null;
1985
+ if (/--execute.*proxy|http_proxy|https_proxy/i.test(command)) return command;
1986
+ return command.replace(
1987
+ /\bwget\b/,
1988
+ `wget -e use_proxy=yes -e http_proxy=socks5h://${socks} -e https_proxy=socks5h://${socks}`
1989
+ );
1990
+ }
1991
+ function wrapSqlmap(command) {
1992
+ if (!/\bsqlmap\b/.test(command)) return null;
1993
+ if (/--tor\b|--proxy\b/.test(command)) return command;
1994
+ return command.replace(
1995
+ /\bsqlmap\b/,
1996
+ `sqlmap --tor --tor-type=SOCKS5 --tor-port=${TOR_PROXY.SOCKS_PORT}`
1997
+ );
1998
+ }
1999
+ function wrapGobuster(command, socks) {
2000
+ if (!/\bgobuster\b/.test(command)) return null;
2001
+ if (/--proxy\b/.test(command)) return command;
2002
+ return command.replace(/\bgobuster\b/, `gobuster --proxy socks5://${socks}`);
2003
+ }
2004
+ function wrapFfuf(command, socks) {
2005
+ if (!/\bffuf\b/.test(command)) return null;
2006
+ if (/-x\s/.test(command)) return command;
2007
+ return command.replace(/\bffuf\b/, `ffuf -x socks5://${socks}`);
2008
+ }
2009
+ function wrapNmap(command, wrapperPrefix) {
2010
+ if (!/\bnmap\b/.test(command)) return null;
2011
+ let nmapCmd = command;
2012
+ nmapCmd = nmapCmd.replace(/\s-s[SAXFN]\b/g, " -sT");
2013
+ if (!/\s-Pn\b/.test(nmapCmd)) {
2014
+ nmapCmd = nmapCmd.replace(/\bnmap\b/, "nmap -Pn");
2015
+ }
2016
+ return `${wrapperPrefix} ${nmapCmd}`;
2017
+ }
1949
2018
  function wrapCommandForTor(command) {
1950
2019
  if (!isTorEnabled()) return command;
1951
- const SOCKS = `${TOR_PROXY.SOCKS_HOST}:${TOR_PROXY.SOCKS_PORT}`;
1952
- if (/\bcurl\b/.test(command)) {
1953
- if (/--socks5|--proxy\b|-x\s/.test(command)) return command;
1954
- return command.replace(/\bcurl\b/, `curl --socks5-hostname ${SOCKS}`);
1955
- }
1956
- if (/\bwget\b/.test(command)) {
1957
- if (/--execute.*proxy|http_proxy|https_proxy/i.test(command)) return command;
1958
- return command.replace(
1959
- /\bwget\b/,
1960
- `wget -e use_proxy=yes -e http_proxy=socks5h://${SOCKS} -e https_proxy=socks5h://${SOCKS}`
1961
- );
1962
- }
1963
- if (/\bsqlmap\b/.test(command)) {
1964
- if (/--tor\b|--proxy\b/.test(command)) return command;
1965
- return command.replace(
1966
- /\bsqlmap\b/,
1967
- `sqlmap --tor --tor-type=SOCKS5 --tor-port=${TOR_PROXY.SOCKS_PORT}`
1968
- );
1969
- }
1970
- if (/\bgobuster\b/.test(command)) {
1971
- if (/--proxy\b/.test(command)) return command;
1972
- return command.replace(/\bgobuster\b/, `gobuster --proxy socks5://${SOCKS}`);
1973
- }
1974
- if (/\bffuf\b/.test(command)) {
1975
- if (/-x\s/.test(command)) return command;
1976
- return command.replace(/\bffuf\b/, `ffuf -x socks5://${SOCKS}`);
1977
- }
1978
- if (/\bnmap\b/.test(command)) {
1979
- let nmapCmd = command;
1980
- nmapCmd = nmapCmd.replace(/\s-s[SAXFN]\b/g, " -sT");
1981
- if (!/\s-Pn\b/.test(nmapCmd)) {
1982
- nmapCmd = nmapCmd.replace(/\bnmap\b/, "nmap -Pn");
2020
+ const socks = `${TOR_PROXY.SOCKS_HOST}:${TOR_PROXY.SOCKS_PORT}`;
2021
+ const wrapperPrefix = `${TOR_PROXY.WRAPPER_CMD} ${TOR_PROXY.WRAPPER_FLAGS}`;
2022
+ const handlers = [
2023
+ () => wrapCurl(command, socks),
2024
+ () => wrapWget(command, socks),
2025
+ () => wrapSqlmap(command),
2026
+ () => wrapGobuster(command, socks),
2027
+ () => wrapFfuf(command, socks),
2028
+ () => wrapNmap(command, wrapperPrefix)
2029
+ ];
2030
+ for (const handler of handlers) {
2031
+ const wrapped = handler();
2032
+ if (wrapped !== null) {
2033
+ return wrapped;
1983
2034
  }
1984
- return `${TOR_PROXY.WRAPPER_CMD} ${TOR_PROXY.WRAPPER_FLAGS} ${nmapCmd}`;
1985
2035
  }
1986
- return `${TOR_PROXY.WRAPPER_CMD} ${TOR_PROXY.WRAPPER_FLAGS} ${command}`;
2036
+ return `${wrapperPrefix} ${command}`;
1987
2037
  }
1988
2038
 
1989
2039
  // src/shared/utils/config/tor/leak-detector.ts
2040
+ function checkPingLeakRisk(command) {
2041
+ if (!/\bping\b/.test(command)) return null;
2042
+ return {
2043
+ safe: false,
2044
+ reason: "ping uses ICMP \u2014 bypasses Tor, real IP exposed to target",
2045
+ suggestion: "Use TCP probe instead: curl --socks5-hostname 127.0.0.1:9050 -s --connect-timeout 5 http://<target>:<port>"
2046
+ };
2047
+ }
2048
+ function checkTracerouteLeakRisk(command) {
2049
+ if (!/\btraceroute\b|\btracepath\b|\bmtr\b/.test(command)) return null;
2050
+ return {
2051
+ safe: false,
2052
+ reason: "traceroute/tracepath/mtr uses ICMP/UDP \u2014 bypasses Tor, real IP exposed",
2053
+ suggestion: "Skip traceroute when Tor is enabled (reveals all hops including your IP)"
2054
+ };
2055
+ }
2056
+ function checkDnsLeakRisk(command) {
2057
+ if (!/\bdig\b/.test(command) && !/\bnslookup\b/.test(command) && !/(?:^|[;&|\s])host(?:\s|$)/.test(command)) {
2058
+ return null;
2059
+ }
2060
+ return {
2061
+ safe: false,
2062
+ reason: "dig/host/nslookup use UDP \u2014 DNS query bypasses Tor, real IP visible to DNS server",
2063
+ suggestion: `Use DNS-over-HTTPS: curl --socks5-hostname ${TOR_PROXY.SOCKS_HOST}:${TOR_PROXY.SOCKS_PORT} "https://dns.google/resolve?name=<host>&type=A"`
2064
+ };
2065
+ }
2066
+ function checkNmapUdpLeakRisk(command) {
2067
+ if (!/\bnmap\b/.test(command) || !/\s-sU\b/.test(command)) return null;
2068
+ return {
2069
+ safe: false,
2070
+ reason: "nmap -sU (UDP scan) bypasses Tor \u2014 UDP is not routed through SOCKS5",
2071
+ suggestion: `Skip UDP scan with Tor ON. Use TCP scan: ${TOR_PROXY.WRAPPER_CMD} ${TOR_PROXY.WRAPPER_FLAGS} nmap -sT -Pn`
2072
+ };
2073
+ }
1990
2074
  function checkTorLeakRisk(command) {
1991
2075
  if (!isTorEnabled()) return { safe: true };
1992
- if (/\bping\b/.test(command)) {
1993
- return {
1994
- safe: false,
1995
- reason: "ping uses ICMP \u2014 bypasses Tor, real IP exposed to target",
1996
- suggestion: "Use TCP probe instead: curl --socks5-hostname 127.0.0.1:9050 -s --connect-timeout 5 http://<target>:<port>"
1997
- };
1998
- }
1999
- if (/\btraceroute\b|\btracepath\b|\bmtr\b/.test(command)) {
2000
- return {
2001
- safe: false,
2002
- reason: "traceroute/tracepath/mtr uses ICMP/UDP \u2014 bypasses Tor, real IP exposed",
2003
- suggestion: "Skip traceroute when Tor is enabled (reveals all hops including your IP)"
2004
- };
2005
- }
2006
- if (/\bdig\b/.test(command) || /\bnslookup\b/.test(command) || /(?:^|[;&|\s])host(?:\s|$)/.test(command)) {
2007
- return {
2008
- safe: false,
2009
- reason: "dig/host/nslookup use UDP \u2014 DNS query bypasses Tor, real IP visible to DNS server",
2010
- suggestion: `Use DNS-over-HTTPS: curl --socks5-hostname ${TOR_PROXY.SOCKS_HOST}:${TOR_PROXY.SOCKS_PORT} "https://dns.google/resolve?name=<host>&type=A"`
2011
- };
2012
- }
2013
- if (/\bnmap\b/.test(command) && /\s-sU\b/.test(command)) {
2014
- return {
2015
- safe: false,
2016
- reason: "nmap -sU (UDP scan) bypasses Tor \u2014 UDP is not routed through SOCKS5",
2017
- suggestion: `Skip UDP scan with Tor ON. Use TCP scan: ${TOR_PROXY.WRAPPER_CMD} ${TOR_PROXY.WRAPPER_FLAGS} nmap -sT -Pn`
2018
- };
2076
+ const checks = [
2077
+ checkPingLeakRisk,
2078
+ checkTracerouteLeakRisk,
2079
+ checkDnsLeakRisk,
2080
+ checkNmapUdpLeakRisk
2081
+ ];
2082
+ for (const check of checks) {
2083
+ const risk = check(command);
2084
+ if (risk) {
2085
+ return risk;
2086
+ }
2019
2087
  }
2020
2088
  return { safe: true };
2021
2089
  }
@@ -2606,7 +2674,7 @@ async function cleanupAllProcesses() {
2606
2674
  cleanupDone = false;
2607
2675
  return;
2608
2676
  }
2609
- const { getBackgroundProcessesMap: getBackgroundProcessesMap2 } = await import("./process-registry-GSHEX2LT.js");
2677
+ const { getBackgroundProcessesMap: getBackgroundProcessesMap2 } = await import("./process-registry-QIW7ZIUT.js");
2610
2678
  const backgroundProcesses = getBackgroundProcessesMap2();
2611
2679
  terminateAllNatively(backgroundProcesses, "SIGTERM");
2612
2680
  await new Promise((r) => setTimeout(r, SYSTEM_LIMITS.CLEANUP_BATCH_WAIT_MS));
@@ -2674,43 +2742,35 @@ function cleanupOrphanProcesses() {
2674
2742
 
2675
2743
  // src/engine/process/resource-summary.ts
2676
2744
  import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
2677
- function getResourceSummary() {
2678
- const procs = listBackgroundProcesses();
2679
- const running = procs.filter((p) => p.isRunning);
2680
- const exited = procs.filter((p) => !p.isRunning);
2681
- const ports = getUsedPorts();
2682
- const recentEvents = getProcessEventLog().slice(-SYSTEM_LIMITS.RECENT_EVENTS_IN_SUMMARY);
2683
- if (running.length === 0 && exited.length === 0 && recentEvents.length === 0) return "";
2684
- const lines = [];
2685
- if (running.length > 0) {
2686
- lines.push(`Active Assets (${running.length}):`);
2687
- for (const p of running) {
2688
- const dur = Math.round(p.durationMs / 1e3);
2689
- const portInfo = p.listeningPort ? ` port:${p.listeningPort}` : "";
2690
- const childInfo = p.childPids.length > 0 ? ` children:${p.childPids.join(",")}` : "";
2691
- const interactiveFlag = p.isInteractive ? ` ${STATUS_MARKERS.INTERACTIVE}` : "";
2692
- const roleIcon = PROCESS_ICONS[p.role] || PROCESS_ICONS[PROCESS_ROLES.BACKGROUND];
2693
- let lastOutput = "";
2694
- try {
2695
- if (p.stdoutFile && existsSync5(p.stdoutFile)) {
2696
- const content = readFileSync5(p.stdoutFile, "utf-8");
2697
- const outputLines = content.trim().split("\n");
2698
- lastOutput = outputLines.slice(-SYSTEM_LIMITS.RECENT_OUTPUT_LINES).join(" | ").replace(/\n/g, " ");
2699
- }
2700
- } catch {
2745
+ function formatActiveProcesses(running, lines) {
2746
+ if (running.length === 0) return;
2747
+ lines.push(`Active Assets (${running.length}):`);
2748
+ for (const p of running) {
2749
+ const dur = Math.round(p.durationMs / 1e3);
2750
+ const portInfo = p.listeningPort ? ` port:${p.listeningPort}` : "";
2751
+ const childInfo = p.childPids.length > 0 ? ` children:${p.childPids.join(",")}` : "";
2752
+ const interactiveFlag = p.isInteractive ? ` ${STATUS_MARKERS.INTERACTIVE}` : "";
2753
+ const roleIcon = PROCESS_ICONS[p.role] || PROCESS_ICONS[PROCESS_ROLES.BACKGROUND];
2754
+ let lastOutput = "";
2755
+ try {
2756
+ if (p.stdoutFile && existsSync5(p.stdoutFile)) {
2757
+ const content = readFileSync5(p.stdoutFile, "utf-8");
2758
+ const outputLines = content.trim().split("\n");
2759
+ lastOutput = outputLines.slice(-SYSTEM_LIMITS.RECENT_OUTPUT_LINES).join(" | ").replace(/\n/g, " ");
2701
2760
  }
2702
- const outputSnippet = lastOutput ? `
2703
- > Log: ${lastOutput}` : "";
2704
- lines.push(` ${roleIcon} [${p.id}] PID:${p.pid}${portInfo}${childInfo}${interactiveFlag} [${p.role}] ${p.purpose} (${dur}s)${outputSnippet}`);
2761
+ } catch {
2705
2762
  }
2763
+ const outputSnippet = lastOutput ? `
2764
+ > Log: ${lastOutput}` : "";
2765
+ lines.push(` ${roleIcon} [${p.id}] PID:${p.pid}${portInfo}${childInfo}${interactiveFlag} [${p.role}] ${p.purpose} (${dur}s)${outputSnippet}`);
2706
2766
  }
2767
+ }
2768
+ function formatZombies(exited, lines) {
2707
2769
  const orphans = [];
2708
2770
  for (const p of exited) {
2709
2771
  if (p.childPids.length > 0) {
2710
2772
  const aliveChildren = p.childPids.filter((pid) => isPidAlive(pid));
2711
- if (aliveChildren.length > 0) {
2712
- orphans.push({ id: p.id, childPids: aliveChildren });
2713
- }
2773
+ if (aliveChildren.length > 0) orphans.push({ id: p.id, childPids: aliveChildren });
2714
2774
  }
2715
2775
  }
2716
2776
  if (orphans.length > 0) {
@@ -2721,10 +2781,8 @@ ${STATUS_MARKERS.WARNING} SUSPECTED ZOMBIES (Orphaned Children):`);
2721
2781
  }
2722
2782
  lines.push(` \u2192 Recommendation: Run bg_process({ action: "stop", process_id: "ID" }) to clean up.`);
2723
2783
  }
2724
- if (ports.length > 0) {
2725
- lines.push(`
2726
- Ports In Use: ${ports.join(", ")}`);
2727
- }
2784
+ }
2785
+ function formatActiveShells(running, lines) {
2728
2786
  const activeShells = running.filter((p) => p.role === PROCESS_ROLES.ACTIVE_SHELL);
2729
2787
  if (activeShells.length > 0) {
2730
2788
  lines.push(`
@@ -2733,19 +2791,39 @@ Ports In Use: ${ports.join(", ")}`);
2733
2791
  lines.push(` \u2192 [${s.id}] Use interact to execute commands. Upgrade if unstable.`);
2734
2792
  }
2735
2793
  }
2736
- if (exited.length > 0) {
2737
- lines.push(`Exited Processes (${exited.length}):`);
2738
- for (const p of exited) {
2739
- lines.push(` ${STATUS_MARKERS.EXITED} [${p.id}] exit:${p.exitCode} [${p.role}] ${p.description}`);
2740
- }
2794
+ }
2795
+ function formatExitedProcesses(exited, lines) {
2796
+ if (exited.length === 0) return;
2797
+ lines.push(`Exited Processes (${exited.length}):`);
2798
+ for (const p of exited) {
2799
+ lines.push(` ${STATUS_MARKERS.EXITED} [${p.id}] exit:${p.exitCode} [${p.role}] ${p.description}`);
2741
2800
  }
2742
- if (recentEvents.length > 0) {
2743
- lines.push(`Recent Process Events:`);
2744
- for (const e of recentEvents.slice(-SYSTEM_LIMITS.RECENT_EVENTS_DISPLAY)) {
2745
- const ago = Math.round((Date.now() - e.timestamp) / 1e3);
2746
- lines.push(` ${ago}s ago: [${e.event}] ${e.detail}`);
2747
- }
2801
+ }
2802
+ function formatRecentEvents(recentEvents, lines) {
2803
+ if (recentEvents.length === 0) return;
2804
+ lines.push(`Recent Process Events:`);
2805
+ for (const e of recentEvents.slice(-SYSTEM_LIMITS.RECENT_EVENTS_DISPLAY)) {
2806
+ const ago = Math.round((Date.now() - e.timestamp) / 1e3);
2807
+ lines.push(` ${ago}s ago: [${e.event}] ${e.detail}`);
2748
2808
  }
2809
+ }
2810
+ function getResourceSummary() {
2811
+ const procs = listBackgroundProcesses();
2812
+ const running = procs.filter((p) => p.isRunning);
2813
+ const exited = procs.filter((p) => !p.isRunning);
2814
+ const ports = getUsedPorts();
2815
+ const recentEvents = getProcessEventLog().slice(-SYSTEM_LIMITS.RECENT_EVENTS_IN_SUMMARY);
2816
+ if (running.length === 0 && exited.length === 0 && recentEvents.length === 0) return "";
2817
+ const lines = [];
2818
+ formatActiveProcesses(running, lines);
2819
+ formatZombies(exited, lines);
2820
+ if (ports.length > 0) {
2821
+ lines.push(`
2822
+ Ports In Use: ${ports.join(", ")}`);
2823
+ }
2824
+ formatActiveShells(running, lines);
2825
+ formatExitedProcesses(exited, lines);
2826
+ formatRecentEvents(recentEvents, lines);
2749
2827
  return lines.join("\n");
2750
2828
  }
2751
2829
 
@@ -3604,6 +3682,7 @@ var StateSerializer = class {
3604
3682
  const lines = [];
3605
3683
  this.formatContextAndMission(state3, lines);
3606
3684
  this.formatArtifactsAndObjectives(state3, lines);
3685
+ this.formatDelegatedTasks(state3, lines);
3607
3686
  this.formatTargets(state3, lines);
3608
3687
  this.formatFindings(state3, lines);
3609
3688
  this.formatLoot(state3, lines);
@@ -3625,6 +3704,34 @@ var StateSerializer = class {
3625
3704
  }
3626
3705
  }
3627
3706
  }
3707
+ static formatDelegatedTasks(state3, lines) {
3708
+ const activeTasks = state3.getActiveDelegatedTasks();
3709
+ if (activeTasks.length === 0) return;
3710
+ lines.push(`Delegated Tasks (${activeTasks.length} active):`);
3711
+ for (const task of activeTasks.slice(0, DISPLAY_LIMITS.COMPACT_LIST_ITEMS)) {
3712
+ const waiting = task.waitingOn ? ` waiting:${task.waitingOn}` : "";
3713
+ const workerType = task.nextWorkerType || task.workerType;
3714
+ const worker = workerType ? ` worker:${workerType}` : "";
3715
+ lines.push(` [${task.status}] ${task.task}${worker}${waiting}`);
3716
+ lines.push(` summary: ${task.summary}`);
3717
+ if (task.parentTaskId || task.rootTaskId) {
3718
+ const chainParts = [
3719
+ task.parentTaskId ? `parent:${task.parentTaskId}` : "",
3720
+ task.rootTaskId ? `root:${task.rootTaskId}` : ""
3721
+ ].filter(Boolean);
3722
+ lines.push(` chain: ${chainParts.join(" ")}`);
3723
+ }
3724
+ if (task.assets.length > 0) {
3725
+ lines.push(` assets: ${task.assets.join(", ")}`);
3726
+ }
3727
+ if (task.sessions.length > 0) {
3728
+ lines.push(` sessions: ${task.sessions.join(", ")}`);
3729
+ }
3730
+ if (task.resumeHint) {
3731
+ lines.push(` resume: ${task.resumeHint}`);
3732
+ }
3733
+ }
3734
+ }
3628
3735
  static formatContextAndMission(state3, lines) {
3629
3736
  const engagement = state3.getEngagement();
3630
3737
  const scope = state3.getScope();
@@ -3761,7 +3868,8 @@ function saveState(state3) {
3761
3868
  actionLog: state3.getRecentActions(AGENT_LIMITS.MAX_ACTION_LOG),
3762
3869
  currentPhase: state3.getPhase(),
3763
3870
  missionSummary: state3.getMissionSummary(),
3764
- missionChecklist: state3.getMissionChecklist()
3871
+ missionChecklist: state3.getMissionChecklist(),
3872
+ delegatedTasks: state3.getDelegatedTasks()
3765
3873
  };
3766
3874
  const sessionFile = join5(sessionsDir, FILE_PATTERNS.session());
3767
3875
  const json = JSON.stringify(snapshot, null, 2);
@@ -3832,6 +3940,11 @@ function loadState(state3) {
3832
3940
  if (snapshot.missionChecklist?.length > 0) {
3833
3941
  state3.restoreMissionChecklist(snapshot.missionChecklist);
3834
3942
  }
3943
+ if (Array.isArray(snapshot.delegatedTasks)) {
3944
+ for (const task of snapshot.delegatedTasks) {
3945
+ state3.restoreDelegatedTask(task);
3946
+ }
3947
+ }
3835
3948
  return true;
3836
3949
  } catch (err) {
3837
3950
  debugLog("general", `Failed to load state: ${err}`);
@@ -3922,6 +4035,7 @@ export {
3922
4035
  COMMAND_EVENT_TYPES,
3923
4036
  UI_COMMANDS,
3924
4037
  generateId,
4038
+ generatePrefixedId,
3925
4039
  WorkingMemory,
3926
4040
  EpisodicMemory,
3927
4041
  saveSessionSnapshot,