pentesting 0.73.2 → 0.73.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.
@@ -13,12 +13,13 @@ import {
13
13
  getAllProcesses,
14
14
  getBackgroundProcessesMap,
15
15
  getLimits,
16
- getPipelineConfig,
17
16
  getProcess,
18
17
  getProcessEventLog,
18
+ getRequiredRuntimeSection,
19
+ getRuntimeSectionOr,
19
20
  logEvent,
20
21
  setProcess
21
- } from "./chunk-YFDJI3GO.js";
22
+ } from "./chunk-I52SWXYV.js";
22
23
 
23
24
  // src/shared/constants/time/conversions.ts
24
25
  var MS_PER_MINUTE = 6e4;
@@ -138,6 +139,119 @@ var DISPLAY_LIMITS = {
138
139
  SESSION_VALUE_PREVIEW: 300
139
140
  };
140
141
 
142
+ // src/shared/utils/file-ops/runtime-asset-resolver.ts
143
+ import { existsSync, readFileSync } from "fs";
144
+ import path from "path";
145
+ import { fileURLToPath } from "url";
146
+ var RUNTIME_FILE_DIR = path.dirname(fileURLToPath(import.meta.url));
147
+ var DIST_DIR = path.basename(RUNTIME_FILE_DIR) === "dist" ? RUNTIME_FILE_DIR : path.resolve(process.cwd(), "dist");
148
+ function mapSourceAssetToDist(declaredPath) {
149
+ if (declaredPath.startsWith("src/agents/prompts/")) {
150
+ return path.join(DIST_DIR, "prompts", declaredPath.slice("src/agents/prompts/".length));
151
+ }
152
+ if (/^src\/domains\/[^/]+\/prompt\.md$/.test(declaredPath)) {
153
+ const [, domain] = declaredPath.split("/");
154
+ return path.join(DIST_DIR, domain, "prompt.md");
155
+ }
156
+ return null;
157
+ }
158
+ function getRuntimeAssetCandidates(declaredPath) {
159
+ const normalized = declaredPath.replace(/\\/g, "/").replace(/\/+$/, "");
160
+ const candidates = [path.resolve(process.cwd(), normalized)];
161
+ const distCandidate = mapSourceAssetToDist(normalized);
162
+ if (distCandidate) {
163
+ candidates.push(distCandidate);
164
+ }
165
+ return [...new Set(candidates)];
166
+ }
167
+ function resolveRuntimeAssetPath(declaredPath) {
168
+ for (const candidate of getRuntimeAssetCandidates(declaredPath)) {
169
+ if (existsSync(candidate)) {
170
+ return candidate;
171
+ }
172
+ }
173
+ return null;
174
+ }
175
+ function readRuntimeAssetFile(declaredPath, encoding = "utf-8") {
176
+ const resolved = resolveRuntimeAssetPath(declaredPath);
177
+ if (!resolved) {
178
+ return null;
179
+ }
180
+ return readFileSync(resolved, encoding);
181
+ }
182
+
183
+ // src/agents/llm-node-config.ts
184
+ var RUNTIME_CONFIG_PATH = "src/agents/runtime-config.ts";
185
+ function buildMissingNodeError(nodeName) {
186
+ return new Error(
187
+ `[runtime-config] llm_nodes.${nodeName} is not declared. Add it to ${RUNTIME_CONFIG_PATH}.`
188
+ );
189
+ }
190
+ function buildMissingPromptError(nodeName) {
191
+ return new Error(
192
+ `[runtime-config] llm_nodes.${nodeName} must declare system_prompt_file, fallback_system_prompt_file, or system_prompt. Add one to ${RUNTIME_CONFIG_PATH}.`
193
+ );
194
+ }
195
+ function buildMissingCooldownPolicyError(nodeName) {
196
+ return new Error(
197
+ `[runtime-config] llm_nodes.${nodeName}.cooldown_policy is required but not set. Add it to ${RUNTIME_CONFIG_PATH}.`
198
+ );
199
+ }
200
+ function resolvePromptFile(declaredPath, context) {
201
+ const content = readRuntimeAssetFile(declaredPath, "utf-8");
202
+ if (content === null) {
203
+ throw new Error(
204
+ `[runtime-config] ${context} points to "${declaredPath}" but no runtime asset was found. Tried: ${getRuntimeAssetCandidates(declaredPath).join(", ")}`
205
+ );
206
+ }
207
+ return content.trim();
208
+ }
209
+ function resolveInlinePrompt(prompt2) {
210
+ return prompt2 ? prompt2.trim() : void 0;
211
+ }
212
+ function resolveConfiguredPrompt(nodeName, nodeConfig) {
213
+ if (nodeConfig.system_prompt_file) {
214
+ return resolvePromptFile(
215
+ nodeConfig.system_prompt_file,
216
+ `llm_nodes.${nodeName}.system_prompt_file`
217
+ );
218
+ }
219
+ if (nodeConfig.fallback_system_prompt_file) {
220
+ return resolvePromptFile(
221
+ nodeConfig.fallback_system_prompt_file,
222
+ `llm_nodes.${nodeName}.fallback_system_prompt_file`
223
+ );
224
+ }
225
+ return resolveInlinePrompt(nodeConfig.system_prompt) ?? resolveInlinePrompt(nodeConfig.fallback_system_prompt);
226
+ }
227
+ function getLlmNodeConfig(nodeName) {
228
+ const nodeConfig = getRequiredRuntimeSection("llm_nodes")[nodeName];
229
+ if (!nodeConfig) {
230
+ throw buildMissingNodeError(nodeName);
231
+ }
232
+ return nodeConfig;
233
+ }
234
+ function llmNodeSystemPrompt(nodeName) {
235
+ const prompt2 = resolveConfiguredPrompt(nodeName, getLlmNodeConfig(nodeName));
236
+ if (!prompt2) {
237
+ throw buildMissingPromptError(nodeName);
238
+ }
239
+ return prompt2;
240
+ }
241
+ function llmNodeOutputParsing(nodeName) {
242
+ return getLlmNodeConfig(nodeName).output_parsing;
243
+ }
244
+ function llmNodeCooldownPolicy(nodeName) {
245
+ const policy = getLlmNodeConfig(nodeName).cooldown_policy;
246
+ if (!policy) {
247
+ throw buildMissingCooldownPolicyError(nodeName);
248
+ }
249
+ return policy;
250
+ }
251
+ function llmNodeMaxTokens(nodeName, fallback) {
252
+ return getLlmNodeConfig(nodeName).limits?.max_tokens ?? fallback;
253
+ }
254
+
141
255
  // src/shared/constants/llm/config.ts
142
256
  var RETRY_CONFIG = {
143
257
  baseDelayMs: 2e3,
@@ -150,9 +264,8 @@ var RETRY_CONFIG = {
150
264
  autoRetryDelayMs: 1e4
151
265
  // 10s fixed interval for 429/network errors
152
266
  };
153
- var config = getPipelineConfig();
154
- var mainLlmTokens = config.llm_nodes?.["main_llm"]?.limits?.max_tokens ?? 128e3;
155
- var strategistTokens = config.llm_nodes?.["strategist"]?.limits?.max_tokens ?? 262144;
267
+ var mainLlmTokens = llmNodeMaxTokens("main_llm", 128e3);
268
+ var strategistTokens = llmNodeMaxTokens("strategist", 262144);
156
269
  var LLM_LIMITS = {
157
270
  /** WHY 64K: non-streaming calls (orchestrator, summaries) benefit from
158
271
  * generous output budgets. Don't force premature truncation. */
@@ -194,7 +307,7 @@ var AGENT_LIMITS = {
194
307
  MAX_FAILED_PATHS: state.max_failed_paths,
195
308
  MAX_ATTACK_NODES: state.max_attack_nodes,
196
309
  MAX_ATTACK_EDGES: state.max_attack_edges,
197
- // ─── Fixed Infrastructure Constants (not tunable via yaml) ───
310
+ // ─── Fixed Infrastructure Constants (not tunable via runtime config) ───
198
311
  /** Infinite retry on LLM errors — rate limit recovery */
199
312
  MAX_CONSECUTIVE_LLM_ERRORS: agent_loop.max_consecutive_llm_errors === "infinity" || agent_loop.max_consecutive_llm_errors === Infinity ? Infinity : Number(agent_loop.max_consecutive_llm_errors ?? Infinity),
200
313
  MAX_RETRIES: agent_loop.max_retries ?? 3,
@@ -218,7 +331,7 @@ var MEMORY_LIMITS = {
218
331
  TECHNIQUE_FAILURE_DECAY: state2.technique_failure_decay,
219
332
  TECHNIQUE_PRUNE_THRESHOLD: state2.technique_prune_threshold,
220
333
  MAX_TURN_ENTRIES: archive.max_turn_entries,
221
- /** Maximum unverified techniques to show in prompt (display-only, not in yaml) */
334
+ /** Maximum unverified techniques to show in prompt (display-only, not in runtime config) */
222
335
  PROMPT_UNVERIFIED_TECHNIQUES: 10
223
336
  };
224
337
 
@@ -235,7 +348,7 @@ var INPUT_PROMPT_PATTERNS = [
235
348
 
236
349
  // src/shared/constants/agent.ts
237
350
  var APP_NAME = "Pentest AI";
238
- var APP_VERSION = "0.73.2";
351
+ var APP_VERSION = "0.73.4";
239
352
  var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
240
353
  var LLM_ROLES = {
241
354
  SYSTEM: "system",
@@ -509,6 +622,19 @@ var TOOL_NAMES = {
509
622
  READ_FILE: "read_file",
510
623
  WRITE_FILE: "write_file",
511
624
  BG_PROCESS: "bg_process",
625
+ LISTENER_START: "listener_start",
626
+ LISTENER_STATUS: "listener_status",
627
+ SHELL_PROMOTE: "shell_promote",
628
+ SHELL_EXEC: "shell_exec",
629
+ SHELL_CHECK: "shell_check",
630
+ SHELL_UPGRADE: "shell_upgrade",
631
+ EXPLOIT_CREDENTIAL_CHECK: "exploit_credential_check",
632
+ EXPLOIT_ARTIFACT_CHECK: "exploit_artifact_check",
633
+ EXPLOIT_FOOTHOLD_CHECK: "exploit_foothold_check",
634
+ EXPLOIT_VECTOR_CHECK: "exploit_vector_check",
635
+ PWN_CRASH_REPRO: "pwn_crash_repro",
636
+ PWN_OFFSET_CHECK: "pwn_offset_check",
637
+ PWN_PAYLOAD_SMOKE: "pwn_payload_smoke",
512
638
  // Intelligence & Research
513
639
  PARSE_NMAP: "parse_nmap",
514
640
  SEARCH_CVE: "search_cve",
@@ -936,7 +1062,20 @@ function getInputHandler() {
936
1062
  }
937
1063
 
938
1064
  // src/shared/constants/paths.ts
939
- import path from "path";
1065
+ import path2 from "path";
1066
+
1067
+ // src/agents/workspace-config.ts
1068
+ var DEFAULT_WORKSPACE_ROOT = ".pentesting";
1069
+ var DEFAULT_WORKSPACE_DIRECTORIES = {};
1070
+ function getWorkspaceConfig() {
1071
+ return getRuntimeSectionOr("workspace", {});
1072
+ }
1073
+ function getWorkspaceRoot() {
1074
+ return getWorkspaceConfig().root ?? DEFAULT_WORKSPACE_ROOT;
1075
+ }
1076
+ function getWorkspaceDirectories() {
1077
+ return getWorkspaceConfig().directories ?? DEFAULT_WORKSPACE_DIRECTORIES;
1078
+ }
940
1079
 
941
1080
  // src/shared/constants/files/extensions.ts
942
1081
  var FILE_EXTENSIONS = {
@@ -983,9 +1122,9 @@ var SPECIAL_FILES = {
983
1122
  };
984
1123
 
985
1124
  // src/shared/utils/file-ops/file-utils.ts
986
- import { existsSync, mkdirSync } from "fs";
1125
+ import { existsSync as existsSync2, mkdirSync } from "fs";
987
1126
  function ensureDirExists(dirPath) {
988
- if (!existsSync(dirPath)) {
1127
+ if (!existsSync2(dirPath)) {
989
1128
  mkdirSync(dirPath, { recursive: true });
990
1129
  }
991
1130
  }
@@ -1013,15 +1152,13 @@ var _dirs = null;
1013
1152
  var _root = null;
1014
1153
  function getRoot() {
1015
1154
  if (_root === null) {
1016
- const ws = getPipelineConfig().workspace;
1017
- _root = ws?.root ?? ".pentesting";
1155
+ _root = getWorkspaceRoot();
1018
1156
  }
1019
1157
  return _root;
1020
1158
  }
1021
1159
  function getDirs() {
1022
1160
  if (_dirs === null) {
1023
- const ws = getPipelineConfig().workspace;
1024
- _dirs = ws?.directories ?? {};
1161
+ _dirs = getWorkspaceDirectories();
1025
1162
  }
1026
1163
  return _dirs;
1027
1164
  }
@@ -1033,39 +1170,39 @@ var WORK_DIR = dir("workspace", `${getRoot()}/workspace`);
1033
1170
  var MEMORY_DIR = dir("memory", `${getRoot()}/memory`);
1034
1171
  var WORKSPACE = {
1035
1172
  get ROOT() {
1036
- return path.resolve(getRoot());
1173
+ return path2.resolve(getRoot());
1037
1174
  },
1038
1175
  get TMP() {
1039
- return path.resolve(dir("workspace", `${getRoot()}/workspace`));
1176
+ return path2.resolve(dir("workspace", `${getRoot()}/workspace`));
1040
1177
  },
1041
1178
  get MEMORY() {
1042
- return path.resolve(dir("memory", `${getRoot()}/memory`));
1179
+ return path2.resolve(dir("memory", `${getRoot()}/memory`));
1043
1180
  },
1044
1181
  get POLICY() {
1045
- return path.resolve(dir("memory", `${getRoot()}/memory`), SPECIAL_FILES.POLICY);
1182
+ return path2.resolve(dir("memory", `${getRoot()}/memory`), SPECIAL_FILES.POLICY);
1046
1183
  },
1047
1184
  get REPORTS() {
1048
- return path.resolve(dir("reports", `${getRoot()}/reports`));
1185
+ return path2.resolve(dir("reports", `${getRoot()}/reports`));
1049
1186
  },
1050
1187
  get SESSIONS() {
1051
- return path.resolve(dir("sessions", `${getRoot()}/sessions`));
1188
+ return path2.resolve(dir("sessions", `${getRoot()}/sessions`));
1052
1189
  },
1053
1190
  get LOOT() {
1054
- return path.resolve(dir("loot", `${getRoot()}/loot`));
1191
+ return path2.resolve(dir("loot", `${getRoot()}/loot`));
1055
1192
  },
1056
1193
  get DEBUG() {
1057
- return path.resolve(dir("debug", `${getRoot()}/debug`));
1194
+ return path2.resolve(dir("debug", `${getRoot()}/debug`));
1058
1195
  },
1059
1196
  /** Turn insight files root: .pentesting/turns/ */
1060
1197
  get TURNS() {
1061
- return path.resolve(dir("turns", `${getRoot()}/turns`));
1198
+ return path2.resolve(dir("turns", `${getRoot()}/turns`));
1062
1199
  },
1063
1200
  /**
1064
1201
  * Resolve the turn memory file for a specific turn: .pentesting/turns/{N}-memory.md
1065
- * Pipeline.yaml: workspace.turn_structure → single file per turn
1202
+ * Runtime config turn storage policy → single file per turn
1066
1203
  */
1067
1204
  turnPath(turn) {
1068
- return path.resolve(dir("turns", `${getRoot()}/turns`), `${turn}-memory.md`);
1205
+ return path2.resolve(dir("turns", `${getRoot()}/turns`), `${turn}-memory.md`);
1069
1206
  }
1070
1207
  };
1071
1208
 
@@ -1298,7 +1435,7 @@ var BLOCKED_BINARIES = /* @__PURE__ */ new Set([
1298
1435
  ]);
1299
1436
 
1300
1437
  // src/shared/utils/debug/debug-logger.ts
1301
- import { appendFileSync, writeFileSync, statSync, readFileSync } from "fs";
1438
+ import { appendFileSync, writeFileSync, statSync, readFileSync as readFileSync2 } from "fs";
1302
1439
  import { join } from "path";
1303
1440
  var ROTATE_BYTES = 5 * 1024 * 1024;
1304
1441
  var WIPE_BYTES = 20 * 1024 * 1024;
@@ -1350,7 +1487,7 @@ var DebugLogger = class _DebugLogger {
1350
1487
  writeFileSync(this.logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [GENERAL] === LOG WIPED (exceeded ${Math.round(WIPE_BYTES / 1024 / 1024)}MB) ===
1351
1488
  `);
1352
1489
  } else if (size > ROTATE_BYTES) {
1353
- const content = readFileSync(this.logPath, "utf-8");
1490
+ const content = readFileSync2(this.logPath, "utf-8");
1354
1491
  const half = content.slice(Math.floor(content.length / 2));
1355
1492
  const firstNewline = half.indexOf("\n");
1356
1493
  const trimmed = firstNewline >= 0 ? half.slice(firstNewline + 1) : half;
@@ -1507,6 +1644,57 @@ function splitByPipe(command) {
1507
1644
  }
1508
1645
 
1509
1646
  // src/shared/utils/command-validator/redirect.ts
1647
+ function isAllowedRedirectPath(target) {
1648
+ return SAFE_REDIRECT_PATHS.some((pathPrefix) => target.startsWith(pathPrefix));
1649
+ }
1650
+ function updateQuoteState(ch, state3) {
1651
+ if (ch === "'" && !state3.inDouble) {
1652
+ state3.inSingle = !state3.inSingle;
1653
+ return true;
1654
+ }
1655
+ if (ch === '"' && !state3.inSingle) {
1656
+ state3.inDouble = !state3.inDouble;
1657
+ return true;
1658
+ }
1659
+ return false;
1660
+ }
1661
+ function extractFileDescriptor(command, operatorIndex) {
1662
+ let fd = "";
1663
+ let b = operatorIndex - 1;
1664
+ while (b >= 0 && /\d/.test(command[b])) {
1665
+ fd = command[b] + fd;
1666
+ b--;
1667
+ }
1668
+ return fd;
1669
+ }
1670
+ function skipSpaces(command, start) {
1671
+ let index = start;
1672
+ while (index < command.length && /\s/.test(command[index])) index++;
1673
+ return index;
1674
+ }
1675
+ function readRedirectTarget(command, start) {
1676
+ let i = start;
1677
+ let target = "";
1678
+ let targetInSingle = false;
1679
+ let targetInDouble = false;
1680
+ while (i < command.length) {
1681
+ const ch = command[i];
1682
+ if (ch === "'" && !targetInDouble) {
1683
+ targetInSingle = !targetInSingle;
1684
+ i++;
1685
+ continue;
1686
+ }
1687
+ if (ch === '"' && !targetInSingle) {
1688
+ targetInDouble = !targetInDouble;
1689
+ i++;
1690
+ continue;
1691
+ }
1692
+ if (!targetInSingle && !targetInDouble && /[\s|;&<>]/.test(ch)) break;
1693
+ target += ch;
1694
+ i++;
1695
+ }
1696
+ return { target, nextIndex: i };
1697
+ }
1510
1698
  function stripRedirects(segment) {
1511
1699
  return segment.replace(/\d*>\s*&\d+/g, "").replace(/\d*>>\s*\S+/g, "").replace(/\d*>\s*\S+/g, "").replace(/<\s*\S+/g, "").trim();
1512
1700
  }
@@ -1515,8 +1703,7 @@ function validateRedirects(command) {
1515
1703
  for (const { type, target } of redirects) {
1516
1704
  if (type === ">" || type === ">>") {
1517
1705
  if (target.startsWith("&") || target === "/dev/null") continue;
1518
- const isAllowed = SAFE_REDIRECT_PATHS.some((p) => target.startsWith(p));
1519
- if (!isAllowed) {
1706
+ if (!isAllowedRedirectPath(target)) {
1520
1707
  return {
1521
1708
  isSafe: false,
1522
1709
  error: `Redirect to '${target}' is not in allowed paths`,
@@ -1524,8 +1711,7 @@ function validateRedirects(command) {
1524
1711
  };
1525
1712
  }
1526
1713
  } else if (type === "<") {
1527
- const isAllowed = SAFE_REDIRECT_PATHS.some((p) => target.startsWith(p));
1528
- if (!isAllowed) {
1714
+ if (!isAllowedRedirectPath(target)) {
1529
1715
  return {
1530
1716
  isSafe: false,
1531
1717
  error: `Input redirect from '${target}' is not in allowed paths`,
@@ -1538,55 +1724,23 @@ function validateRedirects(command) {
1538
1724
  }
1539
1725
  function extractRedirectTargets(command) {
1540
1726
  const redirects = [];
1541
- let inSingle = false;
1542
- let inDouble = false;
1727
+ const quoteState = { inSingle: false, inDouble: false };
1543
1728
  let i = 0;
1544
1729
  while (i < command.length) {
1545
1730
  const ch = command[i];
1546
- if (ch === "'" && !inDouble) {
1547
- inSingle = !inSingle;
1548
- i++;
1549
- continue;
1550
- }
1551
- if (ch === '"' && !inSingle) {
1552
- inDouble = !inDouble;
1731
+ if (updateQuoteState(ch, quoteState)) {
1553
1732
  i++;
1554
1733
  continue;
1555
1734
  }
1556
- if (!inSingle && !inDouble) {
1735
+ if (!quoteState.inSingle && !quoteState.inDouble) {
1557
1736
  if (ch === ">" || ch === "<") {
1558
1737
  const isAppend = ch === ">" && command[i + 1] === ">";
1559
1738
  const type = isAppend ? ">>" : ch;
1560
1739
  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
- }
1740
+ const fd = ch === ">" ? extractFileDescriptor(command, i) : "";
1741
+ const targetStart = skipSpaces(command, i + jump);
1742
+ const { target, nextIndex } = readRedirectTarget(command, targetStart);
1743
+ i = nextIndex;
1590
1744
  if (target) {
1591
1745
  redirects.push({ type, fd, target });
1592
1746
  }
@@ -1946,76 +2100,115 @@ function getTorBrowserArgs() {
1946
2100
  }
1947
2101
 
1948
2102
  // src/shared/utils/config/tor/wrapper.ts
2103
+ function wrapCurl(command, socks) {
2104
+ if (!/\bcurl\b/.test(command)) return null;
2105
+ if (/--socks5|--proxy\b|-x\s/.test(command)) return command;
2106
+ return command.replace(/\bcurl\b/, `curl --socks5-hostname ${socks}`);
2107
+ }
2108
+ function wrapWget(command, socks) {
2109
+ if (!/\bwget\b/.test(command)) return null;
2110
+ if (/--execute.*proxy|http_proxy|https_proxy/i.test(command)) return command;
2111
+ return command.replace(
2112
+ /\bwget\b/,
2113
+ `wget -e use_proxy=yes -e http_proxy=socks5h://${socks} -e https_proxy=socks5h://${socks}`
2114
+ );
2115
+ }
2116
+ function wrapSqlmap(command) {
2117
+ if (!/\bsqlmap\b/.test(command)) return null;
2118
+ if (/--tor\b|--proxy\b/.test(command)) return command;
2119
+ return command.replace(
2120
+ /\bsqlmap\b/,
2121
+ `sqlmap --tor --tor-type=SOCKS5 --tor-port=${TOR_PROXY.SOCKS_PORT}`
2122
+ );
2123
+ }
2124
+ function wrapGobuster(command, socks) {
2125
+ if (!/\bgobuster\b/.test(command)) return null;
2126
+ if (/--proxy\b/.test(command)) return command;
2127
+ return command.replace(/\bgobuster\b/, `gobuster --proxy socks5://${socks}`);
2128
+ }
2129
+ function wrapFfuf(command, socks) {
2130
+ if (!/\bffuf\b/.test(command)) return null;
2131
+ if (/-x\s/.test(command)) return command;
2132
+ return command.replace(/\bffuf\b/, `ffuf -x socks5://${socks}`);
2133
+ }
2134
+ function wrapNmap(command, wrapperPrefix) {
2135
+ if (!/\bnmap\b/.test(command)) return null;
2136
+ let nmapCmd = command;
2137
+ nmapCmd = nmapCmd.replace(/\s-s[SAXFN]\b/g, " -sT");
2138
+ if (!/\s-Pn\b/.test(nmapCmd)) {
2139
+ nmapCmd = nmapCmd.replace(/\bnmap\b/, "nmap -Pn");
2140
+ }
2141
+ return `${wrapperPrefix} ${nmapCmd}`;
2142
+ }
1949
2143
  function wrapCommandForTor(command) {
1950
2144
  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");
2145
+ const socks = `${TOR_PROXY.SOCKS_HOST}:${TOR_PROXY.SOCKS_PORT}`;
2146
+ const wrapperPrefix = `${TOR_PROXY.WRAPPER_CMD} ${TOR_PROXY.WRAPPER_FLAGS}`;
2147
+ const handlers = [
2148
+ () => wrapCurl(command, socks),
2149
+ () => wrapWget(command, socks),
2150
+ () => wrapSqlmap(command),
2151
+ () => wrapGobuster(command, socks),
2152
+ () => wrapFfuf(command, socks),
2153
+ () => wrapNmap(command, wrapperPrefix)
2154
+ ];
2155
+ for (const handler of handlers) {
2156
+ const wrapped = handler();
2157
+ if (wrapped !== null) {
2158
+ return wrapped;
1983
2159
  }
1984
- return `${TOR_PROXY.WRAPPER_CMD} ${TOR_PROXY.WRAPPER_FLAGS} ${nmapCmd}`;
1985
2160
  }
1986
- return `${TOR_PROXY.WRAPPER_CMD} ${TOR_PROXY.WRAPPER_FLAGS} ${command}`;
2161
+ return `${wrapperPrefix} ${command}`;
1987
2162
  }
1988
2163
 
1989
2164
  // src/shared/utils/config/tor/leak-detector.ts
2165
+ function checkPingLeakRisk(command) {
2166
+ if (!/\bping\b/.test(command)) return null;
2167
+ return {
2168
+ safe: false,
2169
+ reason: "ping uses ICMP \u2014 bypasses Tor, real IP exposed to target",
2170
+ suggestion: "Use TCP probe instead: curl --socks5-hostname 127.0.0.1:9050 -s --connect-timeout 5 http://<target>:<port>"
2171
+ };
2172
+ }
2173
+ function checkTracerouteLeakRisk(command) {
2174
+ if (!/\btraceroute\b|\btracepath\b|\bmtr\b/.test(command)) return null;
2175
+ return {
2176
+ safe: false,
2177
+ reason: "traceroute/tracepath/mtr uses ICMP/UDP \u2014 bypasses Tor, real IP exposed",
2178
+ suggestion: "Skip traceroute when Tor is enabled (reveals all hops including your IP)"
2179
+ };
2180
+ }
2181
+ function checkDnsLeakRisk(command) {
2182
+ if (!/\bdig\b/.test(command) && !/\bnslookup\b/.test(command) && !/(?:^|[;&|\s])host(?:\s|$)/.test(command)) {
2183
+ return null;
2184
+ }
2185
+ return {
2186
+ safe: false,
2187
+ reason: "dig/host/nslookup use UDP \u2014 DNS query bypasses Tor, real IP visible to DNS server",
2188
+ suggestion: `Use DNS-over-HTTPS: curl --socks5-hostname ${TOR_PROXY.SOCKS_HOST}:${TOR_PROXY.SOCKS_PORT} "https://dns.google/resolve?name=<host>&type=A"`
2189
+ };
2190
+ }
2191
+ function checkNmapUdpLeakRisk(command) {
2192
+ if (!/\bnmap\b/.test(command) || !/\s-sU\b/.test(command)) return null;
2193
+ return {
2194
+ safe: false,
2195
+ reason: "nmap -sU (UDP scan) bypasses Tor \u2014 UDP is not routed through SOCKS5",
2196
+ suggestion: `Skip UDP scan with Tor ON. Use TCP scan: ${TOR_PROXY.WRAPPER_CMD} ${TOR_PROXY.WRAPPER_FLAGS} nmap -sT -Pn`
2197
+ };
2198
+ }
1990
2199
  function checkTorLeakRisk(command) {
1991
2200
  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
- };
2201
+ const checks = [
2202
+ checkPingLeakRisk,
2203
+ checkTracerouteLeakRisk,
2204
+ checkDnsLeakRisk,
2205
+ checkNmapUdpLeakRisk
2206
+ ];
2207
+ for (const check of checks) {
2208
+ const risk = check(command);
2209
+ if (risk) {
2210
+ return risk;
2211
+ }
2019
2212
  }
2020
2213
  return { safe: true };
2021
2214
  }
@@ -2276,7 +2469,7 @@ Suggestion: ${torLeak.suggestion}`
2276
2469
  }
2277
2470
 
2278
2471
  // src/engine/tools-base/file-operations.ts
2279
- import { readFileSync as readFileSync2, existsSync as existsSync2, writeFileSync as writeFileSync2 } from "fs";
2472
+ import { readFileSync as readFileSync3, existsSync as existsSync3, writeFileSync as writeFileSync2 } from "fs";
2280
2473
  import { dirname } from "path";
2281
2474
  import { join as join2 } from "path";
2282
2475
  import { tmpdir } from "os";
@@ -2296,14 +2489,14 @@ function getErrorMessage(error) {
2296
2489
  // src/engine/tools-base/file-operations.ts
2297
2490
  async function readFileContent(filePath) {
2298
2491
  try {
2299
- if (!existsSync2(filePath)) {
2492
+ if (!existsSync3(filePath)) {
2300
2493
  return {
2301
2494
  success: false,
2302
2495
  output: "",
2303
2496
  error: `File not found: ${filePath}`
2304
2497
  };
2305
2498
  }
2306
- const content = readFileSync2(filePath, "utf-8");
2499
+ const content = readFileSync3(filePath, "utf-8");
2307
2500
  return {
2308
2501
  success: true,
2309
2502
  output: content
@@ -2414,7 +2607,7 @@ function startBackgroundProcess(command, options = {}) {
2414
2607
  }
2415
2608
 
2416
2609
  // src/engine/process/process-interaction.ts
2417
- import { existsSync as existsSync3, readFileSync as readFileSync3, appendFileSync as appendFileSync2 } from "fs";
2610
+ import { existsSync as existsSync4, readFileSync as readFileSync4, appendFileSync as appendFileSync2 } from "fs";
2418
2611
  async function sendToProcess(processId, input, waitMs = SYSTEM_LIMITS.DEFAULT_WAIT_MS_INTERACT) {
2419
2612
  const proc = getProcess(processId);
2420
2613
  if (!proc) return { success: false, output: `Process ${processId} not found`, newOutput: "" };
@@ -2422,8 +2615,8 @@ async function sendToProcess(processId, input, waitMs = SYSTEM_LIMITS.DEFAULT_WA
2422
2615
  if (proc.hasExited) return { success: false, output: `Process ${processId} has exited`, newOutput: "" };
2423
2616
  let currentLen = 0;
2424
2617
  try {
2425
- if (existsSync3(proc.stdoutFile)) {
2426
- currentLen = readFileSync3(proc.stdoutFile, "utf-8").length;
2618
+ if (existsSync4(proc.stdoutFile)) {
2619
+ currentLen = readFileSync4(proc.stdoutFile, "utf-8").length;
2427
2620
  }
2428
2621
  } catch {
2429
2622
  }
@@ -2436,8 +2629,8 @@ async function sendToProcess(processId, input, waitMs = SYSTEM_LIMITS.DEFAULT_WA
2436
2629
  await new Promise((r) => setTimeout(r, waitMs));
2437
2630
  let fullStdout = "";
2438
2631
  try {
2439
- if (existsSync3(proc.stdoutFile)) {
2440
- fullStdout = readFileSync3(proc.stdoutFile, "utf-8");
2632
+ if (existsSync4(proc.stdoutFile)) {
2633
+ fullStdout = readFileSync4(proc.stdoutFile, "utf-8");
2441
2634
  }
2442
2635
  } catch {
2443
2636
  }
@@ -2459,7 +2652,7 @@ function promoteToShell(processId, description) {
2459
2652
  }
2460
2653
 
2461
2654
  // src/engine/process/process-monitor.ts
2462
- import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
2655
+ import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
2463
2656
  function isProcessRunning(processId) {
2464
2657
  const proc = getProcess(processId);
2465
2658
  if (!proc) return false;
@@ -2472,8 +2665,8 @@ function isProcessRunning(processId) {
2472
2665
  proc.childPids = discoverAllDescendants(proc.pid);
2473
2666
  if (proc.role === PROCESS_ROLES.LISTENER) {
2474
2667
  try {
2475
- if (existsSync4(proc.stdoutFile)) {
2476
- const stdout = readFileSync4(proc.stdoutFile, "utf-8");
2668
+ if (existsSync5(proc.stdoutFile)) {
2669
+ const stdout = readFileSync5(proc.stdoutFile, "utf-8");
2477
2670
  if (detectConnection(stdout)) {
2478
2671
  promoteToShell(processId, "Reverse shell connected (auto-detected)");
2479
2672
  logEvent(processId, PROCESS_EVENTS.CONNECTION_DETECTED, `Connection detected on port ${proc.listeningPort}`);
@@ -2491,16 +2684,16 @@ function getProcessOutput(processId) {
2491
2684
  let stdout = "";
2492
2685
  let stderr = "";
2493
2686
  try {
2494
- if (existsSync4(proc.stdoutFile)) {
2495
- const content = readFileSync4(proc.stdoutFile, "utf-8");
2687
+ if (existsSync5(proc.stdoutFile)) {
2688
+ const content = readFileSync5(proc.stdoutFile, "utf-8");
2496
2689
  stdout = content.length > SYSTEM_LIMITS.MAX_STDOUT_SLICE ? `... [truncated ${content.length - SYSTEM_LIMITS.MAX_STDOUT_SLICE} chars] ...
2497
2690
  ` + content.slice(-SYSTEM_LIMITS.MAX_STDOUT_SLICE) : content;
2498
2691
  }
2499
2692
  } catch {
2500
2693
  }
2501
2694
  try {
2502
- if (existsSync4(proc.stderrFile)) {
2503
- const content = readFileSync4(proc.stderrFile, "utf-8");
2695
+ if (existsSync5(proc.stderrFile)) {
2696
+ const content = readFileSync5(proc.stderrFile, "utf-8");
2504
2697
  stderr = content.length > SYSTEM_LIMITS.MAX_STDERR_SLICE ? `... [truncated ${content.length - SYSTEM_LIMITS.MAX_STDERR_SLICE} chars] ...
2505
2698
  ` + content.slice(-SYSTEM_LIMITS.MAX_STDERR_SLICE) : content;
2506
2699
  }
@@ -2606,7 +2799,7 @@ async function cleanupAllProcesses() {
2606
2799
  cleanupDone = false;
2607
2800
  return;
2608
2801
  }
2609
- const { getBackgroundProcessesMap: getBackgroundProcessesMap2 } = await import("./process-registry-GSHEX2LT.js");
2802
+ const { getBackgroundProcessesMap: getBackgroundProcessesMap2 } = await import("./process-registry-BI7BKPHN.js");
2610
2803
  const backgroundProcesses = getBackgroundProcessesMap2();
2611
2804
  terminateAllNatively(backgroundProcesses, "SIGTERM");
2612
2805
  await new Promise((r) => setTimeout(r, SYSTEM_LIMITS.CLEANUP_BATCH_WAIT_MS));
@@ -2673,44 +2866,36 @@ function cleanupOrphanProcesses() {
2673
2866
  }
2674
2867
 
2675
2868
  // src/engine/process/resource-summary.ts
2676
- 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 {
2869
+ import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
2870
+ function formatActiveProcesses(running, lines) {
2871
+ if (running.length === 0) return;
2872
+ lines.push(`Active Assets (${running.length}):`);
2873
+ for (const p of running) {
2874
+ const dur = Math.round(p.durationMs / 1e3);
2875
+ const portInfo = p.listeningPort ? ` port:${p.listeningPort}` : "";
2876
+ const childInfo = p.childPids.length > 0 ? ` children:${p.childPids.join(",")}` : "";
2877
+ const interactiveFlag = p.isInteractive ? ` ${STATUS_MARKERS.INTERACTIVE}` : "";
2878
+ const roleIcon = PROCESS_ICONS[p.role] || PROCESS_ICONS[PROCESS_ROLES.BACKGROUND];
2879
+ let lastOutput = "";
2880
+ try {
2881
+ if (p.stdoutFile && existsSync6(p.stdoutFile)) {
2882
+ const content = readFileSync6(p.stdoutFile, "utf-8");
2883
+ const outputLines = content.trim().split("\n");
2884
+ lastOutput = outputLines.slice(-SYSTEM_LIMITS.RECENT_OUTPUT_LINES).join(" | ").replace(/\n/g, " ");
2701
2885
  }
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}`);
2886
+ } catch {
2705
2887
  }
2888
+ const outputSnippet = lastOutput ? `
2889
+ > Log: ${lastOutput}` : "";
2890
+ lines.push(` ${roleIcon} [${p.id}] PID:${p.pid}${portInfo}${childInfo}${interactiveFlag} [${p.role}] ${p.purpose} (${dur}s)${outputSnippet}`);
2706
2891
  }
2892
+ }
2893
+ function formatZombies(exited, lines) {
2707
2894
  const orphans = [];
2708
2895
  for (const p of exited) {
2709
2896
  if (p.childPids.length > 0) {
2710
2897
  const aliveChildren = p.childPids.filter((pid) => isPidAlive(pid));
2711
- if (aliveChildren.length > 0) {
2712
- orphans.push({ id: p.id, childPids: aliveChildren });
2713
- }
2898
+ if (aliveChildren.length > 0) orphans.push({ id: p.id, childPids: aliveChildren });
2714
2899
  }
2715
2900
  }
2716
2901
  if (orphans.length > 0) {
@@ -2721,10 +2906,8 @@ ${STATUS_MARKERS.WARNING} SUSPECTED ZOMBIES (Orphaned Children):`);
2721
2906
  }
2722
2907
  lines.push(` \u2192 Recommendation: Run bg_process({ action: "stop", process_id: "ID" }) to clean up.`);
2723
2908
  }
2724
- if (ports.length > 0) {
2725
- lines.push(`
2726
- Ports In Use: ${ports.join(", ")}`);
2727
- }
2909
+ }
2910
+ function formatActiveShells(running, lines) {
2728
2911
  const activeShells = running.filter((p) => p.role === PROCESS_ROLES.ACTIVE_SHELL);
2729
2912
  if (activeShells.length > 0) {
2730
2913
  lines.push(`
@@ -2733,19 +2916,39 @@ Ports In Use: ${ports.join(", ")}`);
2733
2916
  lines.push(` \u2192 [${s.id}] Use interact to execute commands. Upgrade if unstable.`);
2734
2917
  }
2735
2918
  }
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
- }
2919
+ }
2920
+ function formatExitedProcesses(exited, lines) {
2921
+ if (exited.length === 0) return;
2922
+ lines.push(`Exited Processes (${exited.length}):`);
2923
+ for (const p of exited) {
2924
+ lines.push(` ${STATUS_MARKERS.EXITED} [${p.id}] exit:${p.exitCode} [${p.role}] ${p.description}`);
2741
2925
  }
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
- }
2926
+ }
2927
+ function formatRecentEvents(recentEvents, lines) {
2928
+ if (recentEvents.length === 0) return;
2929
+ lines.push(`Recent Process Events:`);
2930
+ for (const e of recentEvents.slice(-SYSTEM_LIMITS.RECENT_EVENTS_DISPLAY)) {
2931
+ const ago = Math.round((Date.now() - e.timestamp) / 1e3);
2932
+ lines.push(` ${ago}s ago: [${e.event}] ${e.detail}`);
2748
2933
  }
2934
+ }
2935
+ function getResourceSummary() {
2936
+ const procs = listBackgroundProcesses();
2937
+ const running = procs.filter((p) => p.isRunning);
2938
+ const exited = procs.filter((p) => !p.isRunning);
2939
+ const ports = getUsedPorts();
2940
+ const recentEvents = getProcessEventLog().slice(-SYSTEM_LIMITS.RECENT_EVENTS_IN_SUMMARY);
2941
+ if (running.length === 0 && exited.length === 0 && recentEvents.length === 0) return "";
2942
+ const lines = [];
2943
+ formatActiveProcesses(running, lines);
2944
+ formatZombies(exited, lines);
2945
+ if (ports.length > 0) {
2946
+ lines.push(`
2947
+ Ports In Use: ${ports.join(", ")}`);
2948
+ }
2949
+ formatActiveShells(running, lines);
2950
+ formatExitedProcesses(exited, lines);
2951
+ formatRecentEvents(recentEvents, lines);
2749
2952
  return lines.join("\n");
2750
2953
  }
2751
2954
 
@@ -3150,7 +3353,7 @@ var EpisodicMemory = class {
3150
3353
  };
3151
3354
 
3152
3355
  // src/shared/utils/agent-memory/persistent-memory.ts
3153
- import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
3356
+ import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
3154
3357
  import { join as join4 } from "path";
3155
3358
 
3156
3359
  // src/shared/utils/agent-memory/similarity.ts
@@ -3179,7 +3382,7 @@ function matchesServiceProfile(serviceProfile, services) {
3179
3382
  }
3180
3383
 
3181
3384
  // src/shared/utils/agent-memory/session-snapshot.ts
3182
- import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync4, unlinkSync as unlinkSync4 } from "fs";
3385
+ import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync4, unlinkSync as unlinkSync4 } from "fs";
3183
3386
  import { join as join3 } from "path";
3184
3387
  var SNAPSHOT_FILE = join3(WORKSPACE.MEMORY, SPECIAL_FILES.SESSION_SNAPSHOT);
3185
3388
  function saveSessionSnapshot(snapshot) {
@@ -3191,8 +3394,8 @@ function saveSessionSnapshot(snapshot) {
3191
3394
  }
3192
3395
  function loadSessionSnapshot() {
3193
3396
  try {
3194
- if (existsSync6(SNAPSHOT_FILE)) {
3195
- return JSON.parse(readFileSync6(SNAPSHOT_FILE, "utf-8"));
3397
+ if (existsSync7(SNAPSHOT_FILE)) {
3398
+ return JSON.parse(readFileSync7(SNAPSHOT_FILE, "utf-8"));
3196
3399
  }
3197
3400
  } catch {
3198
3401
  }
@@ -3223,7 +3426,7 @@ function snapshotToPrompt() {
3223
3426
  }
3224
3427
  function clearSessionSnapshot() {
3225
3428
  try {
3226
- if (existsSync6(SNAPSHOT_FILE)) {
3429
+ if (existsSync7(SNAPSHOT_FILE)) {
3227
3430
  unlinkSync4(SNAPSHOT_FILE);
3228
3431
  }
3229
3432
  } catch {
@@ -3373,8 +3576,8 @@ var PersistentMemory = class {
3373
3576
  }
3374
3577
  load() {
3375
3578
  try {
3376
- if (existsSync7(MEMORY_FILE)) {
3377
- const data = JSON.parse(readFileSync7(MEMORY_FILE, "utf-8"));
3579
+ if (existsSync8(MEMORY_FILE)) {
3580
+ const data = JSON.parse(readFileSync8(MEMORY_FILE, "utf-8"));
3378
3581
  return {
3379
3582
  ...data,
3380
3583
  exploitChains: data.exploitChains ?? []
@@ -3604,6 +3807,7 @@ var StateSerializer = class {
3604
3807
  const lines = [];
3605
3808
  this.formatContextAndMission(state3, lines);
3606
3809
  this.formatArtifactsAndObjectives(state3, lines);
3810
+ this.formatDelegatedTasks(state3, lines);
3607
3811
  this.formatTargets(state3, lines);
3608
3812
  this.formatFindings(state3, lines);
3609
3813
  this.formatLoot(state3, lines);
@@ -3625,6 +3829,34 @@ var StateSerializer = class {
3625
3829
  }
3626
3830
  }
3627
3831
  }
3832
+ static formatDelegatedTasks(state3, lines) {
3833
+ const activeTasks = state3.getActiveDelegatedTasks();
3834
+ if (activeTasks.length === 0) return;
3835
+ lines.push(`Delegated Tasks (${activeTasks.length} active):`);
3836
+ for (const task of activeTasks.slice(0, DISPLAY_LIMITS.COMPACT_LIST_ITEMS)) {
3837
+ const waiting = task.waitingOn ? ` waiting:${task.waitingOn}` : "";
3838
+ const workerType = task.nextWorkerType || task.workerType;
3839
+ const worker = workerType ? ` worker:${workerType}` : "";
3840
+ lines.push(` [${task.status}] ${task.task}${worker}${waiting}`);
3841
+ lines.push(` summary: ${task.summary}`);
3842
+ if (task.parentTaskId || task.rootTaskId) {
3843
+ const chainParts = [
3844
+ task.parentTaskId ? `parent:${task.parentTaskId}` : "",
3845
+ task.rootTaskId ? `root:${task.rootTaskId}` : ""
3846
+ ].filter(Boolean);
3847
+ lines.push(` chain: ${chainParts.join(" ")}`);
3848
+ }
3849
+ if (task.assets.length > 0) {
3850
+ lines.push(` assets: ${task.assets.join(", ")}`);
3851
+ }
3852
+ if (task.sessions.length > 0) {
3853
+ lines.push(` sessions: ${task.sessions.join(", ")}`);
3854
+ }
3855
+ if (task.resumeHint) {
3856
+ lines.push(` resume: ${task.resumeHint}`);
3857
+ }
3858
+ }
3859
+ }
3628
3860
  static formatContextAndMission(state3, lines) {
3629
3861
  const engagement = state3.getEngagement();
3630
3862
  const scope = state3.getScope();
@@ -3761,7 +3993,8 @@ function saveState(state3) {
3761
3993
  actionLog: state3.getRecentActions(AGENT_LIMITS.MAX_ACTION_LOG),
3762
3994
  currentPhase: state3.getPhase(),
3763
3995
  missionSummary: state3.getMissionSummary(),
3764
- missionChecklist: state3.getMissionChecklist()
3996
+ missionChecklist: state3.getMissionChecklist(),
3997
+ delegatedTasks: state3.getDelegatedTasks()
3765
3998
  };
3766
3999
  const sessionFile = join5(sessionsDir, FILE_PATTERNS.session());
3767
4000
  const json = JSON.stringify(snapshot, null, 2);
@@ -3790,15 +4023,15 @@ function pruneOldSessions(sessionsDir) {
3790
4023
  }
3791
4024
 
3792
4025
  // src/engine/state/persistence/loader.ts
3793
- import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
4026
+ import { readFileSync as readFileSync9, existsSync as existsSync9 } from "fs";
3794
4027
  import { join as join6 } from "path";
3795
4028
  function loadState(state3) {
3796
4029
  const latestFile = join6(WORKSPACE.SESSIONS, "latest.json");
3797
- if (!existsSync8(latestFile)) {
4030
+ if (!existsSync9(latestFile)) {
3798
4031
  return false;
3799
4032
  }
3800
4033
  try {
3801
- const raw = readFileSync8(latestFile, "utf-8");
4034
+ const raw = readFileSync9(latestFile, "utf-8");
3802
4035
  const snapshot = JSON.parse(raw);
3803
4036
  if (snapshot.version !== 1) {
3804
4037
  debugLog("general", `Unknown snapshot version: ${snapshot.version}`);
@@ -3832,6 +4065,11 @@ function loadState(state3) {
3832
4065
  if (snapshot.missionChecklist?.length > 0) {
3833
4066
  state3.restoreMissionChecklist(snapshot.missionChecklist);
3834
4067
  }
4068
+ if (Array.isArray(snapshot.delegatedTasks)) {
4069
+ for (const task of snapshot.delegatedTasks) {
4070
+ state3.restoreDelegatedTask(task);
4071
+ }
4072
+ }
3835
4073
  return true;
3836
4074
  } catch (err) {
3837
4075
  debugLog("general", `Failed to load state: ${err}`);
@@ -3840,8 +4078,8 @@ function loadState(state3) {
3840
4078
  }
3841
4079
 
3842
4080
  // src/engine/state/persistence/janitor.ts
3843
- import { existsSync as existsSync9, rmSync } from "fs";
3844
- import path2 from "path";
4081
+ import { existsSync as existsSync10, rmSync } from "fs";
4082
+ import path3 from "path";
3845
4083
  function clearWorkspace() {
3846
4084
  const cleared = [];
3847
4085
  const errors = [];
@@ -3852,13 +4090,13 @@ function clearWorkspace() {
3852
4090
  { path: WORKSPACE.TMP, label: "workspace/temp" },
3853
4091
  { path: WORKSPACE.LOOT, label: "loot" },
3854
4092
  { path: WORKSPACE.REPORTS, label: "reports" },
3855
- { path: path2.join(root, "archive"), label: "archive (historical cleanup)" },
3856
- { path: path2.join(root, "outputs"), label: "outputs (historical cleanup)" },
3857
- { path: path2.join(root, "journal"), label: "journal (historical cleanup)" }
4093
+ { path: path3.join(root, "archive"), label: "archive (historical cleanup)" },
4094
+ { path: path3.join(root, "outputs"), label: "outputs (historical cleanup)" },
4095
+ { path: path3.join(root, "journal"), label: "journal (historical cleanup)" }
3858
4096
  ];
3859
4097
  for (const dir2 of dirsToClean) {
3860
4098
  try {
3861
- if (existsSync9(dir2.path)) {
4099
+ if (existsSync10(dir2.path)) {
3862
4100
  rmSync(dir2.path, { recursive: true, force: true });
3863
4101
  ensureDirExists(dir2.path);
3864
4102
  cleared.push(dir2.label);
@@ -3872,6 +4110,7 @@ function clearWorkspace() {
3872
4110
 
3873
4111
  export {
3874
4112
  ensureDirExists,
4113
+ getWorkspaceConfig,
3875
4114
  FILE_EXTENSIONS,
3876
4115
  FILE_PATTERNS,
3877
4116
  WORK_DIR,
@@ -3883,6 +4122,11 @@ export {
3883
4122
  getTorBrowserArgs,
3884
4123
  MS_PER_MINUTE,
3885
4124
  DISPLAY_LIMITS,
4125
+ resolveRuntimeAssetPath,
4126
+ readRuntimeAssetFile,
4127
+ llmNodeSystemPrompt,
4128
+ llmNodeOutputParsing,
4129
+ llmNodeCooldownPolicy,
3886
4130
  RETRY_CONFIG,
3887
4131
  LLM_LIMITS,
3888
4132
  LLM_ERROR_TYPES,
@@ -3922,6 +4166,7 @@ export {
3922
4166
  COMMAND_EVENT_TYPES,
3923
4167
  UI_COMMANDS,
3924
4168
  generateId,
4169
+ generatePrefixedId,
3925
4170
  WorkingMemory,
3926
4171
  EpisodicMemory,
3927
4172
  saveSessionSnapshot,