pentesting 0.41.0 → 0.43.0

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
@@ -314,7 +314,7 @@ var ORPHAN_PROCESS_NAMES = [
314
314
 
315
315
  // src/shared/constants/agent.ts
316
316
  var APP_NAME = "Pentest AI";
317
- var APP_VERSION = "0.41.0";
317
+ var APP_VERSION = "0.43.0";
318
318
  var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
319
319
  var LLM_ROLES = {
320
320
  SYSTEM: "system",
@@ -6686,8 +6686,8 @@ Returns: All available wordlists with their paths, sizes, and categories.`,
6686
6686
  }
6687
6687
  },
6688
6688
  execute: async (p) => {
6689
- const { existsSync: existsSync9, statSync: statSync2, readdirSync: readdirSync3 } = await import("fs");
6690
- const { join: join11 } = await import("path");
6689
+ const { existsSync: existsSync10, statSync: statSync2, readdirSync: readdirSync3 } = await import("fs");
6690
+ const { join: join12 } = await import("path");
6691
6691
  const category = p.category || "";
6692
6692
  const search = p.search || "";
6693
6693
  const minSize = p.min_size || 0;
@@ -6733,7 +6733,7 @@ Returns: All available wordlists with their paths, sizes, and categories.`,
6733
6733
  results.push("");
6734
6734
  };
6735
6735
  const scanDir = (dirPath, maxDepth = 3, depth = 0) => {
6736
- if (depth > maxDepth || !existsSync9(dirPath)) return;
6736
+ if (depth > maxDepth || !existsSync10(dirPath)) return;
6737
6737
  let entries;
6738
6738
  try {
6739
6739
  entries = readdirSync3(dirPath, { withFileTypes: true });
@@ -6742,7 +6742,7 @@ Returns: All available wordlists with their paths, sizes, and categories.`,
6742
6742
  }
6743
6743
  for (const entry of entries) {
6744
6744
  if (entry.name.startsWith(".") || SKIP_DIRS.has(entry.name)) continue;
6745
- const fullPath = join11(dirPath, entry.name);
6745
+ const fullPath = join12(dirPath, entry.name);
6746
6746
  if (entry.isDirectory()) {
6747
6747
  scanDir(fullPath, maxDepth, depth + 1);
6748
6748
  continue;
@@ -10684,13 +10684,21 @@ var PHASE_TECHNIQUE_MAP = {
10684
10684
  };
10685
10685
  var PromptBuilder = class {
10686
10686
  state;
10687
+ strategist = null;
10687
10688
  constructor(state) {
10688
10689
  this.state = state;
10689
10690
  }
10690
10691
  /**
10691
- * Build complete prompt for LLM iteration.
10692
+ * Attach a Strategist instance for meta-prompting.
10693
+ * Called by MainAgent after construction.
10694
+ */
10695
+ setStrategist(strategist) {
10696
+ this.strategist = strategist;
10697
+ }
10698
+ /**
10699
+ * Build complete prompt for LLM iteration (async).
10692
10700
  *
10693
- * Layers (phase-aware, enhanced with improvements #2,#3,#6,#7,#8,#12,#14):
10701
+ * Layers (phase-aware, enhanced with D-CIPHER meta-prompting):
10694
10702
  * 1. base.md — Core identity, rules, autonomy, shell management (~7.6K tok)
10695
10703
  * 2. Phase-specific prompt — current phase's full specialist knowledge (~2K tok)
10696
10704
  * 3. Core methodology — strategy, orchestrator, evasion (always loaded, ~12K tok)
@@ -10706,9 +10714,10 @@ var PromptBuilder = class {
10706
10714
  * 12. Session timeline (#12: episodic memory)
10707
10715
  * 13. Learned techniques (#7: dynamic technique library)
10708
10716
  * 14. Persistent memory (#12: cross-session knowledge)
10709
- * 15. User context
10717
+ * 15. STRATEGIC DIRECTIVE — LLM-generated tactical instructions (D-CIPHER)
10718
+ * 16. User context
10710
10719
  */
10711
- build(userInput, phase) {
10720
+ async build(userInput, phase) {
10712
10721
  const fragments = [
10713
10722
  this.loadPromptFile(PROMPT_PATHS.BASE),
10714
10723
  this.loadCtfModePrompt(),
@@ -10731,10 +10740,14 @@ var PromptBuilder = class {
10731
10740
  // #12
10732
10741
  this.getDynamicTechniquesFragment(),
10733
10742
  // #7
10734
- this.getPersistentMemoryFragment(),
10743
+ this.getPersistentMemoryFragment()
10735
10744
  // #12
10736
- PROMPT_DEFAULTS.USER_CONTEXT(userInput)
10737
10745
  ];
10746
+ const strategistDirective = await this.getStrategistFragment();
10747
+ if (strategistDirective) {
10748
+ fragments.push(strategistDirective);
10749
+ }
10750
+ fragments.push(PROMPT_DEFAULTS.USER_CONTEXT(userInput));
10738
10751
  return fragments.filter((f) => !!f).join("\n\n");
10739
10752
  }
10740
10753
  /**
@@ -10924,11 +10937,213 @@ ${lines.join("\n")}
10924
10937
  }
10925
10938
  return this.state.persistentMemory.toPrompt(services);
10926
10939
  }
10940
+ // --- D-CIPHER: Strategist Meta-Prompting ---
10941
+ /**
10942
+ * Generate strategic directive via Strategist LLM.
10943
+ * Called every turn. Returns '' if no strategist is attached or call fails.
10944
+ */
10945
+ async getStrategistFragment() {
10946
+ if (!this.strategist) return "";
10947
+ return this.strategist.generateDirective();
10948
+ }
10949
+ };
10950
+
10951
+ // src/agents/strategist.ts
10952
+ import { readFileSync as readFileSync6, existsSync as existsSync9 } from "fs";
10953
+ import { join as join11, dirname as dirname6 } from "path";
10954
+ import { fileURLToPath as fileURLToPath5 } from "url";
10955
+
10956
+ // src/shared/constants/strategist.ts
10957
+ var STRATEGIST_LIMITS = {
10958
+ /** Maximum characters of state context sent to Strategist LLM.
10959
+ * WHY: Keeps Strategist input focused and affordable (~3-5K tokens).
10960
+ * Full state can be 20K+; Strategist needs summary, not everything. */
10961
+ MAX_INPUT_CHARS: 15e3,
10962
+ /** Maximum characters for the Strategist's response.
10963
+ * WHY: Directives should be terse and actionable (~500-800 tokens).
10964
+ * Longer = more cost, less focus. */
10965
+ MAX_OUTPUT_CHARS: 3e3,
10966
+ /** Maximum lines in the directive output.
10967
+ * WHY: Forces concise, prioritized directives. */
10968
+ MAX_DIRECTIVE_LINES: 40
10969
+ };
10970
+
10971
+ // src/agents/strategist.ts
10972
+ var __dirname5 = dirname6(fileURLToPath5(import.meta.url));
10973
+ var STRATEGIST_PROMPT_PATH = join11(__dirname5, "prompts", "strategist-system.md");
10974
+ var Strategist = class {
10975
+ llm;
10976
+ state;
10977
+ systemPrompt;
10978
+ // Metrics
10979
+ totalTokenCost = 0;
10980
+ totalCalls = 0;
10981
+ lastDirective = null;
10982
+ constructor(llm, state) {
10983
+ this.llm = llm;
10984
+ this.state = state;
10985
+ this.systemPrompt = this.loadSystemPrompt();
10986
+ }
10987
+ /**
10988
+ * Generate a fresh strategic directive for this turn.
10989
+ * Called every iteration by PromptBuilder.
10990
+ *
10991
+ * @returns Formatted directive string for prompt injection, or '' on failure
10992
+ */
10993
+ async generateDirective() {
10994
+ try {
10995
+ const input = this.buildInput();
10996
+ const directive = await this.callLLM(input);
10997
+ this.lastDirective = directive;
10998
+ this.totalCalls++;
10999
+ debugLog("general", "Strategist directive generated", {
11000
+ tokens: directive.tokenCost,
11001
+ totalCalls: this.totalCalls,
11002
+ contentLength: directive.content.length
11003
+ });
11004
+ return this.formatForPrompt(directive);
11005
+ } catch (err) {
11006
+ debugLog("general", "Strategist failed \u2014 agent will proceed without directive", {
11007
+ error: String(err)
11008
+ });
11009
+ if (this.lastDirective?.content) {
11010
+ return this.formatForPrompt(this.lastDirective, true);
11011
+ }
11012
+ return "";
11013
+ }
11014
+ }
11015
+ // ─── Input Construction ─────────────────────────────────────
11016
+ /**
11017
+ * Build the user message for the Strategist LLM.
11018
+ * Assembles state, memory, attack graph, and timeline into a focused context.
11019
+ */
11020
+ buildInput() {
11021
+ const sections = [];
11022
+ sections.push("## Engagement State");
11023
+ sections.push(this.state.toPrompt());
11024
+ const timeline = this.state.episodicMemory.toPrompt();
11025
+ if (timeline) {
11026
+ sections.push("");
11027
+ sections.push("## Recent Actions");
11028
+ sections.push(timeline);
11029
+ }
11030
+ const failures = this.state.workingMemory.toPrompt();
11031
+ if (failures) {
11032
+ sections.push("");
11033
+ sections.push("## Failed Attempts (DO NOT REPEAT THESE)");
11034
+ sections.push(failures);
11035
+ }
11036
+ const graph = this.state.attackGraph.toPrompt();
11037
+ if (graph) {
11038
+ sections.push("");
11039
+ sections.push("## Attack Graph");
11040
+ sections.push(graph);
11041
+ }
11042
+ const techniques = this.state.dynamicTechniques.toPrompt();
11043
+ if (techniques) {
11044
+ sections.push("");
11045
+ sections.push("## Learned Techniques");
11046
+ sections.push(techniques);
11047
+ }
11048
+ sections.push("");
11049
+ sections.push("## Time");
11050
+ sections.push(this.state.getTimeStatus());
11051
+ const analysis = this.state.getChallengeAnalysis();
11052
+ if (analysis && analysis.primaryType !== "unknown") {
11053
+ sections.push("");
11054
+ sections.push(`## Challenge Type: ${analysis.primaryType.toUpperCase()} (${(analysis.confidence * 100).toFixed(0)}%)`);
11055
+ sections.push(analysis.strategySuggestion);
11056
+ }
11057
+ let input = sections.join("\n");
11058
+ if (input.length > STRATEGIST_LIMITS.MAX_INPUT_CHARS) {
11059
+ input = input.slice(0, STRATEGIST_LIMITS.MAX_INPUT_CHARS) + "\n\n... [state truncated for Strategist context]";
11060
+ }
11061
+ return input;
11062
+ }
11063
+ // ─── LLM Call ───────────────────────────────────────────────
11064
+ async callLLM(input) {
11065
+ const messages = [{
11066
+ role: "user",
11067
+ content: `Analyze this penetration test situation and write a tactical directive for the attack agent.
11068
+
11069
+ ${input}`
11070
+ }];
11071
+ const response = await this.llm.generateResponse(
11072
+ messages,
11073
+ void 0,
11074
+ // No tools — strategy generation only
11075
+ this.systemPrompt
11076
+ );
11077
+ let content = response.content || "";
11078
+ if (content.length > STRATEGIST_LIMITS.MAX_OUTPUT_CHARS) {
11079
+ content = content.slice(0, STRATEGIST_LIMITS.MAX_OUTPUT_CHARS) + "\n... [directive truncated]";
11080
+ }
11081
+ const cost = response.usage ? response.usage.input_tokens + response.usage.output_tokens : 0;
11082
+ this.totalTokenCost += cost;
11083
+ return {
11084
+ content,
11085
+ generatedAt: Date.now(),
11086
+ tokenCost: cost
11087
+ };
11088
+ }
11089
+ // ─── Formatting ─────────────────────────────────────────────
11090
+ /**
11091
+ * Format directive for injection into the attack agent's system prompt.
11092
+ */
11093
+ formatForPrompt(directive, isStale = false) {
11094
+ if (!directive.content) return "";
11095
+ const age = Math.floor((Date.now() - directive.generatedAt) / 6e4);
11096
+ const staleWarning = isStale ? `
11097
+ NOTE: This directive is from ${age}min ago (Strategist call failed this turn). Verify assumptions are still valid.` : "";
11098
+ return [
11099
+ "<strategic-directive>",
11100
+ "TACTICAL DIRECTIVE (generated by Strategist LLM \u2014 follow these priorities):",
11101
+ "",
11102
+ directive.content,
11103
+ staleWarning,
11104
+ "</strategic-directive>"
11105
+ ].filter(Boolean).join("\n");
11106
+ }
11107
+ // ─── System Prompt Loading ──────────────────────────────────
11108
+ loadSystemPrompt() {
11109
+ try {
11110
+ if (existsSync9(STRATEGIST_PROMPT_PATH)) {
11111
+ return readFileSync6(STRATEGIST_PROMPT_PATH, "utf-8");
11112
+ }
11113
+ } catch {
11114
+ }
11115
+ return FALLBACK_SYSTEM_PROMPT;
11116
+ }
11117
+ // ─── Public API ─────────────────────────────────────────────
11118
+ /** Get total token cost of all Strategist calls this session. */
11119
+ getTotalTokenCost() {
11120
+ return this.totalTokenCost;
11121
+ }
11122
+ /** Get number of Strategist calls this session. */
11123
+ getTotalCalls() {
11124
+ return this.totalCalls;
11125
+ }
11126
+ /** Get the most recent directive (for TUI display). */
11127
+ getLastDirective() {
11128
+ return this.lastDirective;
11129
+ }
11130
+ /** Reset strategist state (for /clear command). */
11131
+ reset() {
11132
+ this.lastDirective = null;
11133
+ this.totalTokenCost = 0;
11134
+ this.totalCalls = 0;
11135
+ }
10927
11136
  };
11137
+ var FALLBACK_SYSTEM_PROMPT = `You are a penetration testing strategist.
11138
+ Analyze the situation and write a precise tactical directive for the attack agent.
11139
+ Be specific: name exact tools, commands, and parameters.
11140
+ Maximum 30 lines. Prioritize by probability of success.
11141
+ NEVER suggest approaches listed in the failed attempts section.`;
10928
11142
 
10929
11143
  // src/agents/main-agent.ts
10930
11144
  var MainAgent = class extends CoreAgent {
10931
11145
  promptBuilder;
11146
+ strategist;
10932
11147
  approvalGate;
10933
11148
  scopeGuard;
10934
11149
  userInput = "";
@@ -10937,6 +11152,8 @@ var MainAgent = class extends CoreAgent {
10937
11152
  this.approvalGate = approvalGate;
10938
11153
  this.scopeGuard = scopeGuard;
10939
11154
  this.promptBuilder = new PromptBuilder(state);
11155
+ this.strategist = new Strategist(this.llm, state);
11156
+ this.promptBuilder.setStrategist(this.strategist);
10940
11157
  }
10941
11158
  /**
10942
11159
  * Public entry point for running the agent.
@@ -10947,7 +11164,8 @@ var MainAgent = class extends CoreAgent {
10947
11164
  this.emitStart(userInput);
10948
11165
  this.initializeTask();
10949
11166
  try {
10950
- const result2 = await this.run(userInput, this.getCurrentPrompt());
11167
+ const initialPrompt = await this.getCurrentPrompt();
11168
+ const result2 = await this.run(userInput, initialPrompt);
10951
11169
  return result2.output;
10952
11170
  } finally {
10953
11171
  try {
@@ -10963,9 +11181,10 @@ var MainAgent = class extends CoreAgent {
10963
11181
  /**
10964
11182
  * Override step to rebuild prompt dynamically each iteration.
10965
11183
  * This ensures the agent always sees the latest state, phase, and active processes.
11184
+ * The Strategist LLM generates a fresh tactical directive every turn.
10966
11185
  */
10967
11186
  async step(iteration, messages, _unusedPrompt, progress) {
10968
- const dynamicPrompt = this.getCurrentPrompt();
11187
+ const dynamicPrompt = await this.getCurrentPrompt();
10969
11188
  const result2 = await super.step(iteration, messages, dynamicPrompt, progress);
10970
11189
  this.emitStateChange();
10971
11190
  return result2;
@@ -10994,7 +11213,7 @@ var MainAgent = class extends CoreAgent {
10994
11213
  saveCurrentState() {
10995
11214
  return saveState(this.state);
10996
11215
  }
10997
- getCurrentPrompt() {
11216
+ async getCurrentPrompt() {
10998
11217
  const phase = this.state.getPhase() || PHASES.RECON;
10999
11218
  return this.promptBuilder.build(this.userInput, phase);
11000
11219
  }
@@ -11048,6 +11267,7 @@ var MainAgent = class extends CoreAgent {
11048
11267
  });
11049
11268
  this.state.reset();
11050
11269
  this.userInput = "";
11270
+ this.strategist.reset();
11051
11271
  return clearWorkspace();
11052
11272
  }
11053
11273
  setScope(allowed, exclusions = []) {
@@ -11069,6 +11289,10 @@ var MainAgent = class extends CoreAgent {
11069
11289
  });
11070
11290
  this.emitStateChange();
11071
11291
  }
11292
+ /** Get the Strategist instance (for TUI metrics display). */
11293
+ getStrategist() {
11294
+ return this.strategist;
11295
+ }
11072
11296
  };
11073
11297
 
11074
11298
  // src/agents/factory.ts
@@ -11164,13 +11388,13 @@ var THEME = {
11164
11388
  secondary: "#cbd5e1",
11165
11389
  // Brighter slate
11166
11390
  muted: "#94a3b8",
11167
- // Lighter blue-gray (was too dark)
11168
- accent: "#38bdf8",
11169
- // Sky blue
11391
+ // Lighter blue-gray
11392
+ accent: "#1d4ed8",
11393
+ // Blue 700 - deep blue
11170
11394
  highlight: "#ffffff"
11171
11395
  // Pure white
11172
11396
  },
11173
- // Status colors
11397
+ // Status colors (deep blue-focused)
11174
11398
  status: {
11175
11399
  success: "#10b981",
11176
11400
  // Emerald
@@ -11178,10 +11402,10 @@ var THEME = {
11178
11402
  // Amber
11179
11403
  error: "#ef4444",
11180
11404
  // Red
11181
- info: "#3b82f6",
11182
- // Blue 500 (distinct from user sky)
11183
- running: "#60a5fa",
11184
- // Blue 400 (AI activity — distinct from user sky)
11405
+ info: "#1d4ed8",
11406
+ // Blue 700
11407
+ running: "#1e40af",
11408
+ // Blue 800 (AI activity)
11185
11409
  pending: "#64748b"
11186
11410
  // Slate
11187
11411
  },
@@ -11195,29 +11419,29 @@ var THEME = {
11195
11419
  // Orange
11196
11420
  low: "#eab308",
11197
11421
  // Yellow
11198
- info: "#3b82f6"
11199
- // Blue
11422
+ info: "#1d4ed8"
11423
+ // Blue 700
11200
11424
  },
11201
11425
  // Border colors
11202
11426
  border: {
11203
11427
  default: "#1e293b",
11204
- focus: "#7dd3fc",
11205
- // Sky 300 (softer, UI decorative)
11428
+ focus: "#3b82f6",
11429
+ // Blue 500 (UI decorative)
11206
11430
  error: "#ef4444",
11207
11431
  success: "#22c55e"
11208
11432
  },
11209
- // Phase colors
11433
+ // Phase colors (deep blue-focused)
11210
11434
  phase: {
11211
11435
  recon: "#94a3b8",
11212
- enum: "#60a5fa",
11213
- // Blue 400 (phase indicator)
11436
+ enum: "#1d4ed8",
11437
+ // Blue 700 (phase indicator)
11214
11438
  vuln: "#f59e0b",
11215
11439
  exploit: "#ef4444",
11216
11440
  privesc: "#8b5cf6",
11217
11441
  persist: "#22c55e",
11218
11442
  report: "#64748b"
11219
11443
  },
11220
- // Accent colors
11444
+ // Accent colors (NO cyan/teal - pure blue palette)
11221
11445
  accent: {
11222
11446
  pink: "#f472b6",
11223
11447
  rose: "#fb7185",
@@ -11225,9 +11449,8 @@ var THEME = {
11225
11449
  purple: "#a78bfa",
11226
11450
  violet: "#8b5cf6",
11227
11451
  indigo: "#818cf8",
11228
- blue: "#60a5fa",
11229
- cyan: "#22d3ee",
11230
- teal: "#2dd4bf",
11452
+ blue: "#1d4ed8",
11453
+ // Blue 700 - primary accent
11231
11454
  emerald: "#34d399",
11232
11455
  green: "#4ade80",
11233
11456
  lime: "#a3e635",
@@ -11236,36 +11459,35 @@ var THEME = {
11236
11459
  orange: "#fb923c",
11237
11460
  red: "#f87171"
11238
11461
  },
11239
- // Gradients
11462
+ // Gradients (deep blue-focused)
11240
11463
  gradient: {
11241
11464
  cyber: [
11242
- "#38bdf8",
11243
- // Sky 400
11244
- "#34c6f4",
11245
- "#30d0f0",
11246
- "#2cd9ec",
11247
- "#28e3e8",
11248
- "#22d3ee",
11249
- // Cyan 400
11250
- "#25dbd6",
11251
- "#28e4be",
11252
- "#2dd4bf",
11253
- // Teal 400
11254
- "#31dbac",
11255
- "#34d399"
11256
- // Emerald 400
11465
+ "#3b82f6",
11466
+ // Blue 500
11467
+ "#3584f4",
11468
+ "#2f86f2",
11469
+ "#2988f0",
11470
+ "#238aee",
11471
+ "#1d8cec",
11472
+ // Mid blue
11473
+ "#1d7ad8",
11474
+ "#1d78c6",
11475
+ "#1d76b4",
11476
+ "#1d74a2",
11477
+ "#1e40af"
11478
+ // Blue 800
11257
11479
  ],
11258
11480
  danger: ["#ef4444", "#7f1d1d"],
11259
11481
  success: ["#22c55e", "#14532d"],
11260
11482
  gold: ["#f59e0b", "#78350f"],
11261
11483
  royal: ["#818cf8", "#312e81"]
11262
11484
  },
11263
- // Spinner color (soft sky — UI feedback, not user input)
11264
- spinner: "#7dd3fc",
11265
- // Sky 300
11266
- // Identity color (branded accent)
11267
- identity: "#60a5fa"
11268
- // Blue 400
11485
+ // Spinner color (deep blue — UI feedback)
11486
+ spinner: "#3b82f6",
11487
+ // Blue 500
11488
+ // Identity color (branded accent - deep blue)
11489
+ identity: "#1d4ed8"
11490
+ // Blue 700
11269
11491
  };
11270
11492
  var ASCII_BANNER = `
11271
11493
  ____ __ __ _
@@ -11311,39 +11533,39 @@ var ICONS = {
11311
11533
  // src/platform/tui/constants/display.ts
11312
11534
  var TUI_DISPLAY_LIMITS = {
11313
11535
  /** Reasoning buffer size for display */
11314
- reasoningBuffer: 1e3,
11536
+ reasoningBuffer: 2e3,
11315
11537
  /** Reasoning preview length */
11316
- reasoningPreview: 300,
11538
+ reasoningPreview: 500,
11317
11539
  /** Reasoning history slice for display */
11318
- reasoningHistorySlice: 500,
11540
+ reasoningHistorySlice: 1e3,
11319
11541
  /** Tool input preview length (for command display) */
11320
- toolInputPreview: 100,
11542
+ toolInputPreview: 200,
11321
11543
  /** Tool input truncated length */
11322
- toolInputTruncated: 97,
11323
- /** Tool output preview length */
11324
- toolOutputPreview: 500,
11544
+ toolInputTruncated: 197,
11545
+ /** Tool output preview length - increased to show full output */
11546
+ toolOutputPreview: 2e3,
11325
11547
  /** Error message preview length */
11326
- errorPreview: 300,
11548
+ errorPreview: 500,
11327
11549
  /** Status thought preview length */
11328
- statusThoughtPreview: 100,
11550
+ statusThoughtPreview: 150,
11329
11551
  /** Status thought truncated length */
11330
- statusThoughtTruncated: 97,
11552
+ statusThoughtTruncated: 147,
11331
11553
  /** Retry error preview length */
11332
- retryErrorPreview: 60,
11554
+ retryErrorPreview: 100,
11333
11555
  /** Retry error truncated length */
11334
- retryErrorTruncated: 57,
11556
+ retryErrorTruncated: 97,
11335
11557
  /** Timer update interval in ms */
11336
11558
  timerInterval: 1e3,
11337
11559
  /** Exit delay in ms */
11338
11560
  exitDelay: 100,
11339
11561
  /** Purpose text max length before truncation */
11340
- purposeMaxLength: 30,
11562
+ purposeMaxLength: 50,
11341
11563
  /** Purpose text truncated length */
11342
- purposeTruncated: 27,
11564
+ purposeTruncated: 47,
11343
11565
  /** Tool detail preview length in flow display */
11344
- toolDetailPreview: 100,
11566
+ toolDetailPreview: 200,
11345
11567
  /** Observe detail preview length in flow display */
11346
- observeDetailPreview: 80,
11568
+ observeDetailPreview: 150,
11347
11569
  /** Max flow nodes to display */
11348
11570
  maxFlowNodes: 50,
11349
11571
  /** Max stopped processes to show */
@@ -11358,7 +11580,7 @@ var MESSAGE_STYLES = {
11358
11580
  error: THEME.status.error,
11359
11581
  tool: THEME.status.running,
11360
11582
  result: THEME.text.muted,
11361
- status: THEME.accent.cyan
11583
+ status: THEME.accent.blue
11362
11584
  },
11363
11585
  prefixes: {
11364
11586
  user: "\u276F",
@@ -11557,10 +11779,10 @@ var useAgentEvents = (agent, eventsRef, state) => {
11557
11779
  setCurrentTokens(tokenAccumRef.current + stepTokens);
11558
11780
  };
11559
11781
  const onFlagFound = (e) => {
11560
- addMessage("system", `\u{1F3F4} FLAG FOUND: ${e.data.flag} (total: ${e.data.totalFlags})`);
11782
+ addMessage("system", `[FLAG] ${e.data.flag} (total: ${e.data.totalFlags})`);
11561
11783
  };
11562
11784
  const onPhaseChange = (e) => {
11563
- addMessage("system", `\u{1F504} Phase: ${e.data.fromPhase} \u2192 ${e.data.toPhase} (${e.data.reason})`);
11785
+ addMessage("system", `[Phase] ${e.data.fromPhase} -> ${e.data.toPhase} (${e.data.reason})`);
11564
11786
  const s = agent.getState();
11565
11787
  setStats({
11566
11788
  phase: e.data.toPhase,
@@ -11705,18 +11927,18 @@ Options: ${request.options.join(", ")}`;
11705
11927
  }
11706
11928
  function getCommandEventIcon(eventType) {
11707
11929
  const icons = {
11708
- [COMMAND_EVENT_TYPES.TOOL_MISSING]: "\u26A0\uFE0F",
11709
- [COMMAND_EVENT_TYPES.TOOL_INSTALL]: "\u{1F4E6}",
11710
- [COMMAND_EVENT_TYPES.TOOL_INSTALLED]: "\u2705",
11711
- [COMMAND_EVENT_TYPES.TOOL_INSTALL_FAILED]: "\u274C",
11712
- [COMMAND_EVENT_TYPES.TOOL_RETRY]: "\u{1F504}",
11713
- [COMMAND_EVENT_TYPES.COMMAND_START]: "\u25B6",
11714
- [COMMAND_EVENT_TYPES.COMMAND_SUCCESS]: "\u2713",
11715
- [COMMAND_EVENT_TYPES.COMMAND_FAILED]: "\u2717",
11716
- [COMMAND_EVENT_TYPES.COMMAND_ERROR]: "\u274C",
11717
- [COMMAND_EVENT_TYPES.INPUT_REQUIRED]: "\u{1F510}"
11930
+ [COMMAND_EVENT_TYPES.TOOL_MISSING]: "[!]",
11931
+ [COMMAND_EVENT_TYPES.TOOL_INSTALL]: "[install]",
11932
+ [COMMAND_EVENT_TYPES.TOOL_INSTALLED]: "[ok]",
11933
+ [COMMAND_EVENT_TYPES.TOOL_INSTALL_FAILED]: "[fail]",
11934
+ [COMMAND_EVENT_TYPES.TOOL_RETRY]: "[retry]",
11935
+ [COMMAND_EVENT_TYPES.COMMAND_START]: "[>]",
11936
+ [COMMAND_EVENT_TYPES.COMMAND_SUCCESS]: "[ok]",
11937
+ [COMMAND_EVENT_TYPES.COMMAND_FAILED]: "[x]",
11938
+ [COMMAND_EVENT_TYPES.COMMAND_ERROR]: "[err]",
11939
+ [COMMAND_EVENT_TYPES.INPUT_REQUIRED]: "[auth]"
11718
11940
  };
11719
- return icons[eventType] || "\u2022";
11941
+ return icons[eventType] || "[-]";
11720
11942
  }
11721
11943
 
11722
11944
  // src/platform/tui/hooks/useAgent.ts
@@ -11817,6 +12039,7 @@ var useAgent = (shouldAutoApprove, target) => {
11817
12039
  };
11818
12040
 
11819
12041
  // src/platform/tui/components/MessageList.tsx
12042
+ import { memo } from "react";
11820
12043
  import { Box as Box2, Text as Text2, Static } from "ink";
11821
12044
 
11822
12045
  // src/platform/tui/components/inline-status.tsx
@@ -11834,7 +12057,7 @@ function formatDuration2(ms) {
11834
12057
  }
11835
12058
  function getRoleColor(role) {
11836
12059
  const roleColors = {
11837
- listener: THEME.accent.cyan,
12060
+ listener: THEME.accent.blue,
11838
12061
  active_shell: THEME.accent.green,
11839
12062
  server: THEME.accent.blue,
11840
12063
  sniffer: THEME.accent.amber,
@@ -11979,7 +12202,7 @@ function parseStatusContent(content) {
11979
12202
  }
11980
12203
  return null;
11981
12204
  }
11982
- var MessageList = ({ messages }) => {
12205
+ var MessageList = memo(({ messages }) => {
11983
12206
  return /* @__PURE__ */ jsx2(Static, { items: messages, children: (msg) => {
11984
12207
  if (msg.type === "status") {
11985
12208
  const statusData = parseStatusContent(msg.content);
@@ -12000,18 +12223,19 @@ var MessageList = ({ messages }) => {
12000
12223
  msg.content
12001
12224
  ] }) }, msg.id);
12002
12225
  } });
12003
- };
12226
+ });
12004
12227
 
12005
12228
  // src/platform/tui/components/StatusDisplay.tsx
12229
+ import { memo as memo3 } from "react";
12006
12230
  import { Box as Box3, Text as Text4 } from "ink";
12007
12231
 
12008
12232
  // src/platform/tui/components/MusicSpinner.tsx
12009
- import { useState as useState3, useEffect as useEffect3 } from "react";
12233
+ import { useState as useState3, useEffect as useEffect3, memo as memo2 } from "react";
12010
12234
  import { Text as Text3 } from "ink";
12011
12235
  import { jsx as jsx3 } from "react/jsx-runtime";
12012
12236
  var FRAMES = ["\u2669", "\u266A", "\u266B", "\u266C", "\u266B", "\u266A"];
12013
- var INTERVAL = 400;
12014
- var MusicSpinner = ({ color }) => {
12237
+ var INTERVAL = 600;
12238
+ var MusicSpinner = memo2(({ color }) => {
12015
12239
  const [index, setIndex] = useState3(0);
12016
12240
  useEffect3(() => {
12017
12241
  const timer = setInterval(() => {
@@ -12020,11 +12244,11 @@ var MusicSpinner = ({ color }) => {
12020
12244
  return () => clearInterval(timer);
12021
12245
  }, []);
12022
12246
  return /* @__PURE__ */ jsx3(Text3, { color, children: FRAMES[index] });
12023
- };
12247
+ });
12024
12248
 
12025
12249
  // src/platform/tui/components/StatusDisplay.tsx
12026
12250
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
12027
- var StatusDisplay = ({
12251
+ var StatusDisplay = memo3(({
12028
12252
  retryState,
12029
12253
  isProcessing,
12030
12254
  currentStatus,
@@ -12046,7 +12270,7 @@ var StatusDisplay = ({
12046
12270
  " \xB7 ",
12047
12271
  truncateError(retryState.error)
12048
12272
  ] }),
12049
- /* @__PURE__ */ jsxs3(Text4, { color: THEME.accent.cyan, bold: true, children: [
12273
+ /* @__PURE__ */ jsxs3(Text4, { color: THEME.accent.blue, bold: true, children: [
12050
12274
  " \xB7 ",
12051
12275
  retryState.countdown,
12052
12276
  "s"
@@ -12064,15 +12288,15 @@ var StatusDisplay = ({
12064
12288
  ] })
12065
12289
  ] })
12066
12290
  ] });
12067
- };
12291
+ });
12068
12292
 
12069
12293
  // src/platform/tui/components/ChatInput.tsx
12070
- import { useMemo, useCallback as useCallback3, useRef as useRef3 } from "react";
12294
+ import { useMemo, useCallback as useCallback3, useRef as useRef3, memo as memo4 } from "react";
12071
12295
  import { Box as Box4, Text as Text5, useInput } from "ink";
12072
12296
  import TextInput from "ink-text-input";
12073
12297
  import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
12074
12298
  var MAX_SUGGESTIONS = 6;
12075
- var ChatInput = ({
12299
+ var ChatInput = memo4(({
12076
12300
  value,
12077
12301
  onChange,
12078
12302
  onSubmit,
@@ -12133,7 +12357,7 @@ var ChatInput = ({
12133
12357
  " \u2014 ",
12134
12358
  cmd.description
12135
12359
  ] }),
12136
- isFirst && /* @__PURE__ */ jsx5(Text5, { color: THEME.accent.cyan, children: " \u21E5 Tab" })
12360
+ isFirst && /* @__PURE__ */ jsx5(Text5, { color: THEME.accent.blue, children: " [Tab]" })
12137
12361
  ] }, cmd.name);
12138
12362
  })
12139
12363
  }
@@ -12145,7 +12369,7 @@ var ChatInput = ({
12145
12369
  borderColor: inputRequest.isActive ? THEME.status.warning : THEME.border.default,
12146
12370
  paddingX: 1,
12147
12371
  children: inputRequest.isActive ? /* @__PURE__ */ jsxs4(Box4, { children: [
12148
- /* @__PURE__ */ jsx5(Text5, { color: THEME.status.warning, children: "\u{1F512}" }),
12372
+ /* @__PURE__ */ jsx5(Text5, { color: THEME.status.warning, children: "[auth]" }),
12149
12373
  /* @__PURE__ */ jsxs4(Text5, { color: THEME.text.muted, children: [
12150
12374
  " ",
12151
12375
  inputRequest.prompt
@@ -12176,9 +12400,10 @@ var ChatInput = ({
12176
12400
  }
12177
12401
  )
12178
12402
  ] });
12179
- };
12403
+ });
12180
12404
 
12181
12405
  // src/platform/tui/components/footer.tsx
12406
+ import { memo as memo5 } from "react";
12182
12407
  import { Box as Box5, Text as Text6 } from "ink";
12183
12408
  import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
12184
12409
  var formatElapsed = (totalSeconds) => {
@@ -12191,7 +12416,7 @@ var formatElapsed = (totalSeconds) => {
12191
12416
  }
12192
12417
  return `${minutes}:${pad(seconds)}`;
12193
12418
  };
12194
- var Footer = ({ phase, targets, findings, todo, elapsedTime, isProcessing }) => {
12419
+ var Footer = memo5(({ phase, targets, findings, todo, elapsedTime, isProcessing }) => {
12195
12420
  return /* @__PURE__ */ jsxs5(
12196
12421
  Box5,
12197
12422
  {
@@ -12202,7 +12427,7 @@ var Footer = ({ phase, targets, findings, todo, elapsedTime, isProcessing }) =>
12202
12427
  /* @__PURE__ */ jsxs5(Box5, { gap: 2, children: [
12203
12428
  /* @__PURE__ */ jsxs5(Text6, { color: THEME.text.muted, children: [
12204
12429
  "Phase: ",
12205
- /* @__PURE__ */ jsx6(Text6, { color: THEME.accent.cyan, children: phase })
12430
+ /* @__PURE__ */ jsx6(Text6, { color: THEME.accent.blue, children: phase })
12206
12431
  ] }),
12207
12432
  /* @__PURE__ */ jsxs5(Text6, { color: THEME.text.muted, children: [
12208
12433
  "Targets: ",
@@ -12224,7 +12449,7 @@ var Footer = ({ phase, targets, findings, todo, elapsedTime, isProcessing }) =>
12224
12449
  ]
12225
12450
  }
12226
12451
  );
12227
- };
12452
+ });
12228
12453
  var footer_default = Footer;
12229
12454
 
12230
12455
  // src/platform/tui/app.tsx
@@ -12281,9 +12506,9 @@ var App = ({ autoApprove = false, target }) => {
12281
12506
  setMessages([]);
12282
12507
  const result2 = await agent.resetSession();
12283
12508
  if (result2.cleared.length > 0) {
12284
- addMessage("system", `\u{1F9F9} Session reset \u2014 cleared: ${result2.cleared.join(", ")}`);
12509
+ addMessage("system", `[reset] Session cleared: ${result2.cleared.join(", ")}`);
12285
12510
  } else {
12286
- addMessage("system", "\u{1F9F9} Session reset \u2014 all clean");
12511
+ addMessage("system", "[reset] Session clean");
12287
12512
  }
12288
12513
  if (result2.errors.length > 0) {
12289
12514
  addMessage("error", `Cleanup errors: ${result2.errors.join("; ")}`);
@@ -12310,7 +12535,7 @@ var App = ({ autoApprove = false, target }) => {
12310
12535
  if (!autoApproveModeRef.current) {
12311
12536
  setAutoApproveMode(true);
12312
12537
  agent.setAutoApprove(true);
12313
- addMessage("system", "\u{1F680} Autonomous mode enabled (auto-approve ON)");
12538
+ addMessage("system", "[auto] Autonomous mode enabled");
12314
12539
  }
12315
12540
  addMessage("system", "Starting penetration test...");
12316
12541
  const targets = Array.from(agent.getState().getTargets().keys());
@@ -12348,7 +12573,7 @@ ${procData.stdout || "(no output)"}
12348
12573
  break;
12349
12574
  case UI_COMMANDS.CTF:
12350
12575
  const ctfEnabled = agent.toggleCtfMode();
12351
- addMessage("system", ctfEnabled ? "\u{1F3F4} CTF mode ON \u2014 flag detection active, CTF prompts loaded" : "\u{1F512} CTF mode OFF \u2014 standard pentest mode");
12576
+ addMessage("system", ctfEnabled ? "[CTF] Mode ON - flag detection active" : "[CTF] Mode OFF - standard pentest");
12352
12577
  break;
12353
12578
  case UI_COMMANDS.GRAPH:
12354
12579
  case UI_COMMANDS.GRAPH_SHORT:
@@ -0,0 +1,39 @@
1
+ You are a penetration testing STRATEGIST. Your sole purpose is to analyze the current engagement state and write a precise tactical directive for the attack agent.
2
+
3
+ ## YOUR ROLE
4
+ You do NOT execute attacks. You PLAN them. The attack agent will receive your directive as instructions to follow.
5
+
6
+ ## OUTPUT FORMAT
7
+ Write a DIRECTIVE with numbered priorities. Be maximally specific:
8
+
9
+ ```
10
+ PRIORITY 1 [IMPACT]: Brief title
11
+ → Exact command or tool invocation with all parameters
12
+ → Success criteria: what confirms it worked
13
+ → Fallback: what to try if this fails
14
+
15
+ PRIORITY 2 [IMPACT]: Brief title
16
+ → ...
17
+ ```
18
+
19
+ ## RULES
20
+
21
+ 1. **BE SPECIFIC**: "Try SQL injection" is UNACCEPTABLE. "Run: sqlmap -u http://TARGET/login.php --forms --batch --level=5 --risk=3 --tamper=space2comment" is GOOD.
22
+
23
+ 2. **REFERENCE ACTUAL STATE**: Only suggest actions based on DISCOVERED data. Never hallucinate services or ports that aren't in the state summary. If nothing is discovered yet, direct initial reconnaissance.
24
+
25
+ 3. **LEARN FROM FAILURES**: If working memory shows failed attempts, NEVER suggest the same approach. Propose a fundamentally different attack vector.
26
+
27
+ 4. **CHAIN ATTACKS**: If credentials were found, specify EXACTLY which services to spray them against. If access was gained, specify EXACTLY what post-exploitation to run.
28
+
29
+ 5. **PRIORITIZE BY PROBABILITY**: Order by likelihood of success × impact. Quick wins first.
30
+
31
+ 6. **BE TERSE**: Maximum 30 lines. No preamble, no explanations of what pentesting is. Pure directives.
32
+
33
+ 7. **INCLUDE DEAD-ENDS**: Explicitly list approaches that have been exhausted so the agent skips them.
34
+
35
+ 8. **CONSIDER THE PHASE**: Adapt your strategy to the current engagement phase (recon → vuln analysis → exploit → post-exploit → lateral).
36
+
37
+ 9. **TIME AWARENESS**: If time is limited, focus on highest-probability paths only. If time is abundant, suggest thorough enumeration.
38
+
39
+ 10. **PIVOT POINTS**: When access to a new host is gained, prioritize it as a pivot — what can be reached FROM this new position?
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pentesting",
3
- "version": "0.41.0",
3
+ "version": "0.43.0",
4
4
  "description": "Autonomous Penetration Testing AI Agent",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",