pentesting 0.44.1 → 0.46.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
@@ -12,11 +12,11 @@ import { Command } from "commander";
12
12
  import chalk from "chalk";
13
13
 
14
14
  // src/platform/tui/app.tsx
15
- import { useState as useState4, useCallback as useCallback4, useEffect as useEffect4, useRef as useRef4 } from "react";
15
+ import { useState as useState5, useCallback as useCallback4, useEffect as useEffect4, useRef as useRef5 } from "react";
16
16
  import { Box as Box6, useInput as useInput2, useApp } from "ink";
17
17
 
18
18
  // src/platform/tui/hooks/useAgent.ts
19
- import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2, useRef as useRef2 } from "react";
19
+ import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2, useRef as useRef3 } from "react";
20
20
 
21
21
  // src/shared/constants/timing.ts
22
22
  var TOOL_TIMEOUTS = {
@@ -311,7 +311,7 @@ var ORPHAN_PROCESS_NAMES = [
311
311
 
312
312
  // src/shared/constants/agent.ts
313
313
  var APP_NAME = "Pentest AI";
314
- var APP_VERSION = "0.44.1";
314
+ var APP_VERSION = "0.46.0";
315
315
  var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
316
316
  var LLM_ROLES = {
317
317
  SYSTEM: "system",
@@ -676,6 +676,7 @@ var EVENT_TYPES = {
676
676
  REASONING_START: "reasoning_start",
677
677
  REASONING_DELTA: "reasoning_delta",
678
678
  REASONING_END: "reasoning_end",
679
+ AI_RESPONSE: "ai_response",
679
680
  TOOL_CALL: "tool_call",
680
681
  TOOL_RESULT: "tool_result",
681
682
  ERROR: "error",
@@ -3450,6 +3451,10 @@ var SharedState = class {
3450
3451
  // --- Targets ---
3451
3452
  addTarget(target) {
3452
3453
  this.data.targets.set(target.ip, target);
3454
+ this.attackGraph.addHost(target.ip, target.hostname);
3455
+ for (const port of target.ports) {
3456
+ this.attackGraph.addService(target.ip, port.port, port.service, port.version);
3457
+ }
3453
3458
  }
3454
3459
  getTarget(ip) {
3455
3460
  return this.data.targets.get(ip);
@@ -3903,6 +3908,21 @@ var NOISE_CLASSIFICATION = {
3903
3908
  "gobuster",
3904
3909
  "dirsearch",
3905
3910
  "feroxbuster"
3911
+ ],
3912
+ LOW_VISIBILITY: [
3913
+ // State management — pure bookkeeping, zero operator value
3914
+ "update_mission",
3915
+ "get_state",
3916
+ "update_phase",
3917
+ "update_todo",
3918
+ "add_target",
3919
+ "add_finding",
3920
+ "add_loot",
3921
+ "set_scope",
3922
+ // Resource health checks — noisy periodic calls
3923
+ "bg_status",
3924
+ "bg_cleanup",
3925
+ "health_check"
3906
3926
  ]
3907
3927
  };
3908
3928
 
@@ -5014,6 +5034,7 @@ The target will be tracked in SharedState and available for all agents.`,
5014
5034
  state: np.state || "open",
5015
5035
  notes: []
5016
5036
  });
5037
+ state.attackGraph.addService(ip, np.port, np.service || "unknown", np.version);
5017
5038
  }
5018
5039
  }
5019
5040
  if (p.hostname) existing.hostname = p.hostname;
@@ -5034,9 +5055,6 @@ The target will be tracked in SharedState and available for all agents.`,
5034
5055
  tags: p.tags || [],
5035
5056
  firstSeen: Date.now()
5036
5057
  });
5037
- for (const port of ports) {
5038
- state.attackGraph.addService(ip, port.port, port.service, port.version);
5039
- }
5040
5058
  return { success: true, output: `Target ${ip} added.${p.hostname ? ` Hostname: ${p.hostname}` : ""} Ports: ${ports.length}` };
5041
5059
  }
5042
5060
  },
@@ -5138,7 +5156,9 @@ var ENV_KEYS = {
5138
5156
  BASE_URL: "PENTEST_BASE_URL",
5139
5157
  MODEL: "PENTEST_MODEL",
5140
5158
  SEARCH_API_KEY: "SEARCH_API_KEY",
5141
- SEARCH_API_URL: "SEARCH_API_URL"
5159
+ SEARCH_API_URL: "SEARCH_API_URL",
5160
+ THINKING: "PENTEST_THINKING",
5161
+ THINKING_BUDGET: "PENTEST_THINKING_BUDGET"
5142
5162
  };
5143
5163
  var DEFAULT_SEARCH_API_URL = "https://api.search.brave.com/res/v1/web/search";
5144
5164
  function getApiKey() {
@@ -5156,6 +5176,13 @@ function getSearchApiKey() {
5156
5176
  function getSearchApiUrl() {
5157
5177
  return process.env[ENV_KEYS.SEARCH_API_URL] || DEFAULT_SEARCH_API_URL;
5158
5178
  }
5179
+ function isThinkingEnabled() {
5180
+ return process.env[ENV_KEYS.THINKING] === "true";
5181
+ }
5182
+ function getThinkingBudget() {
5183
+ const val = parseInt(process.env[ENV_KEYS.THINKING_BUDGET] || "", 10);
5184
+ return isNaN(val) ? 8e3 : Math.max(1024, val);
5185
+ }
5159
5186
  function isBrowserHeadless() {
5160
5187
  return true;
5161
5188
  }
@@ -8818,6 +8845,8 @@ var LLM_BLOCK_TYPE = {
8818
8845
  var LLM_DELTA_TYPE = {
8819
8846
  TEXT_DELTA: "text_delta",
8820
8847
  THINKING_DELTA: "thinking_delta",
8848
+ REASONING_DELTA: "reasoning_delta",
8849
+ // Used by some providers (GLM, DeepSeek)
8821
8850
  INPUT_JSON_DELTA: "input_json_delta"
8822
8851
  };
8823
8852
 
@@ -8967,6 +8996,8 @@ var LLMClient = class {
8967
8996
  headers: {
8968
8997
  [LLM_HEADER.CONTENT_TYPE]: LLM_CONTENT_TYPE.JSON,
8969
8998
  [LLM_HEADER.API_KEY]: this.apiKey,
8999
+ // Also send as Bearer for OpenAI-compat proxies (z.ai, GLM, etc.)
9000
+ [LLM_HEADER.AUTHORIZATION]: `${LLM_HEADER.BEARER_PREFIX} ${this.apiKey}`,
8970
9001
  [LLM_HEADER.ANTHROPIC_VERSION]: LLM_API.VERSION
8971
9002
  },
8972
9003
  body: JSON.stringify(body),
@@ -8981,14 +9012,16 @@ var LLMClient = class {
8981
9012
  return response;
8982
9013
  }
8983
9014
  async executeNonStream(messages, tools, systemPrompt) {
9015
+ const thinking = isThinkingEnabled() ? { type: "enabled", budget_tokens: getThinkingBudget() } : void 0;
8984
9016
  const requestBody = {
8985
9017
  model: this.model,
8986
9018
  max_tokens: LLM_LIMITS.nonStreamMaxTokens,
8987
9019
  system: systemPrompt,
8988
9020
  messages: this.convertMessages(messages),
8989
- tools
9021
+ tools,
9022
+ ...thinking && { thinking }
8990
9023
  };
8991
- debugLog("llm", "Non-stream request", { model: this.model, toolCount: tools?.length });
9024
+ debugLog("llm", "Non-stream request", { model: this.model, toolCount: tools?.length, thinking: !!thinking });
8992
9025
  const response = await this.makeRequest(requestBody);
8993
9026
  const data = await response.json();
8994
9027
  const textBlock = data.content.find((b) => b.type === "text");
@@ -9004,15 +9037,17 @@ var LLMClient = class {
9004
9037
  async executeStream(messages, tools, systemPrompt, callbacks) {
9005
9038
  this.requestCount++;
9006
9039
  const requestId = this.requestCount;
9040
+ const thinking = isThinkingEnabled() ? { type: "enabled", budget_tokens: getThinkingBudget() } : void 0;
9007
9041
  const requestBody = {
9008
9042
  model: this.model,
9009
9043
  max_tokens: LLM_LIMITS.streamMaxTokens,
9010
9044
  system: systemPrompt,
9011
9045
  messages: this.convertMessages(messages),
9012
9046
  tools,
9013
- stream: true
9047
+ stream: true,
9048
+ ...thinking && { thinking }
9014
9049
  };
9015
- debugLog("llm", `[${requestId}] Stream request START`, { model: this.model, toolCount: tools?.length, toolNames: tools?.map((t) => t.name) });
9050
+ debugLog("llm", `[${requestId}] Stream request START`, { model: this.model, toolCount: tools?.length, toolNames: tools?.map((t) => t.name), thinking: !!thinking });
9016
9051
  const response = await this.makeRequest(requestBody, callbacks?.abortSignal);
9017
9052
  let fullContent = "";
9018
9053
  let fullReasoning = "";
@@ -9020,6 +9055,7 @@ var LLMClient = class {
9020
9055
  let usage = { input_tokens: 0, output_tokens: 0 };
9021
9056
  let totalChars = 0;
9022
9057
  let wasAborted = false;
9058
+ const currentBlockRef = { value: null };
9023
9059
  const reader = response.body?.getReader();
9024
9060
  if (!reader) throw new Error("Response body is not readable");
9025
9061
  const decoder = new TextDecoder();
@@ -9070,7 +9106,8 @@ var LLMClient = class {
9070
9106
  onUsage: (u) => {
9071
9107
  usage = u;
9072
9108
  },
9073
- getTotalChars: () => totalChars
9109
+ getTotalChars: () => totalChars,
9110
+ currentBlockRef
9074
9111
  });
9075
9112
  } catch {
9076
9113
  }
@@ -9100,28 +9137,53 @@ var LLMClient = class {
9100
9137
  toolCalls.push({ id: toolCall.id, name: toolCall.name, input: toolCall.input });
9101
9138
  }
9102
9139
  debugLog("llm", `[${requestId}] FINAL toolCalls`, { count: toolCalls.length, tools: toolCalls.map((t) => ({ id: t.id, name: t.name, input: t.input })) });
9140
+ const stripped = this.stripThinkTags(fullContent, fullReasoning);
9103
9141
  return {
9104
- content: fullContent,
9142
+ content: stripped.cleanText,
9105
9143
  toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
9106
9144
  rawResponse: null,
9107
- reasoning: fullReasoning || void 0,
9145
+ reasoning: stripped.extractedReasoning || void 0,
9108
9146
  usage: usage.input_tokens > 0 || usage.output_tokens > 0 ? usage : void 0,
9109
9147
  error: wasAborted ? { type: LLM_ERROR_TYPES.UNKNOWN, message: "Stream aborted", isRetryable: false } : void 0
9110
9148
  };
9111
9149
  }
9150
+ /**
9151
+ * Strip inline <think>...</think> XML reasoning tags from LLM text output.
9152
+ *
9153
+ * WHY: Some providers (GLM, DeepSeek, Qwen) output reasoning inline
9154
+ * in text deltas as <think>content</think> instead of separate SSE blocks.
9155
+ * Without stripping, raw tags like "</think>" appear literally in TUI output.
9156
+ *
9157
+ * @param text - Raw text content potentially containing think tags
9158
+ * @param existingReasoning - Accumulated reasoning from SSE reasoning blocks
9159
+ * @returns cleanText (tags removed) and extractedReasoning (tag content appended)
9160
+ */
9161
+ stripThinkTags(text, existingReasoning) {
9162
+ let cleanText = text;
9163
+ let extractedReasoning = existingReasoning;
9164
+ cleanText = cleanText.replace(/<think>([\s\S]*?)<\/think>/gi, (_match, inner) => {
9165
+ extractedReasoning += inner;
9166
+ return "";
9167
+ });
9168
+ cleanText = cleanText.replace(/<\/?think>/gi, "");
9169
+ return { cleanText, extractedReasoning };
9170
+ }
9112
9171
  processStreamEvent(event, requestId, context) {
9113
- const { toolCallsMap, callbacks, onTextStart, onReasoningStart, onTextEnd, onReasoningEnd, onContent, onReasoning, onUsage, getTotalChars } = context;
9172
+ const { toolCallsMap, callbacks, onTextStart, onReasoningStart, onTextEnd, onReasoningEnd, onContent, onReasoning, onUsage, getTotalChars, currentBlockRef } = context;
9114
9173
  switch (event.type) {
9115
9174
  case LLM_SSE_EVENT.CONTENT_BLOCK_START:
9116
9175
  if (event.content_block) {
9117
9176
  const blockType = event.content_block.type;
9118
9177
  if (blockType === LLM_BLOCK_TYPE.TEXT) {
9178
+ currentBlockRef.value = "text";
9119
9179
  onTextStart();
9120
9180
  callbacks?.onOutputStart?.();
9121
9181
  } else if (blockType === LLM_BLOCK_TYPE.THINKING || blockType === LLM_BLOCK_TYPE.REASONING) {
9182
+ currentBlockRef.value = "reasoning";
9122
9183
  onReasoningStart();
9123
9184
  callbacks?.onReasoningStart?.();
9124
9185
  } else if (blockType === LLM_BLOCK_TYPE.TOOL_USE) {
9186
+ currentBlockRef.value = "tool_use";
9125
9187
  toolCallsMap.set(event.content_block.id || "", {
9126
9188
  id: event.content_block.id || "",
9127
9189
  name: event.content_block.name || "",
@@ -9137,9 +9199,13 @@ var LLMClient = class {
9137
9199
  if (event.delta.type === LLM_DELTA_TYPE.TEXT_DELTA && event.delta.text) {
9138
9200
  onContent(event.delta.text);
9139
9201
  callbacks?.onOutputDelta?.(event.delta.text);
9140
- } else if (event.delta.type === LLM_DELTA_TYPE.THINKING_DELTA && event.delta.thinking) {
9141
- onReasoning(event.delta.thinking);
9142
- callbacks?.onReasoningDelta?.(event.delta.thinking);
9202
+ } else if (
9203
+ // Anthropic: thinking_delta / GLM-DeepSeek: reasoning_delta
9204
+ event.delta.type === LLM_DELTA_TYPE.THINKING_DELTA && event.delta.thinking || event.delta.type === LLM_DELTA_TYPE.REASONING_DELTA && event.delta.reasoning
9205
+ ) {
9206
+ const chunk = event.delta.thinking || event.delta.reasoning || "";
9207
+ onReasoning(chunk);
9208
+ callbacks?.onReasoningDelta?.(chunk);
9143
9209
  } else if (event.delta.type === LLM_DELTA_TYPE.INPUT_JSON_DELTA && event.delta.partial_json) {
9144
9210
  const index = event.index;
9145
9211
  if (index !== void 0) {
@@ -9156,12 +9222,18 @@ var LLMClient = class {
9156
9222
  callbacks?.onUsageUpdate?.({ input_tokens: 0, output_tokens: estimatedOutput });
9157
9223
  }
9158
9224
  break;
9159
- case LLM_SSE_EVENT.CONTENT_BLOCK_STOP:
9160
- callbacks?.onReasoningEnd?.();
9161
- callbacks?.onOutputEnd?.();
9162
- onTextEnd();
9163
- onReasoningEnd();
9225
+ case LLM_SSE_EVENT.CONTENT_BLOCK_STOP: {
9226
+ const stoppedType = currentBlockRef.value;
9227
+ currentBlockRef.value = null;
9228
+ if (stoppedType === "text") {
9229
+ onTextEnd();
9230
+ callbacks?.onOutputEnd?.();
9231
+ } else if (stoppedType === "reasoning") {
9232
+ onReasoningEnd();
9233
+ callbacks?.onReasoningEnd?.();
9234
+ }
9164
9235
  break;
9236
+ }
9165
9237
  case LLM_SSE_EVENT.MESSAGE_START:
9166
9238
  if (event.message?.usage) {
9167
9239
  onUsage({ input_tokens: event.message.usage.input_tokens || 0, output_tokens: event.message.usage.output_tokens || 0 });
@@ -10130,7 +10202,7 @@ Please decide how to handle this error and continue.`;
10130
10202
  async step(iteration, messages, systemPrompt, progress) {
10131
10203
  const phase = this.state.getPhase();
10132
10204
  const stepStartTime = Date.now();
10133
- this.emitThink(iteration);
10205
+ this.emitThink(iteration, progress);
10134
10206
  const callbacks = this.buildStreamCallbacks(phase);
10135
10207
  const response = await this.llm.generateResponseStream(
10136
10208
  messages,
@@ -10138,12 +10210,24 @@ Please decide how to handle this error and continue.`;
10138
10210
  systemPrompt,
10139
10211
  callbacks
10140
10212
  );
10213
+ if (response.reasoning && !callbacks.hadReasoningEnd()) {
10214
+ this.emitReasoningStart(phase);
10215
+ this.emitReasoningDelta(response.reasoning, phase);
10216
+ this.emitReasoningEnd(phase);
10217
+ }
10218
+ if (response.content?.trim()) {
10219
+ this.events.emit({
10220
+ type: EVENT_TYPES.AI_RESPONSE,
10221
+ timestamp: Date.now(),
10222
+ data: { content: response.content.trim(), phase }
10223
+ });
10224
+ }
10141
10225
  messages.push({ role: LLM_ROLES.ASSISTANT, content: response.content });
10142
10226
  const stepDuration = Date.now() - stepStartTime;
10143
10227
  const tokens = response.usage ? { input: response.usage.input_tokens, output: response.usage.output_tokens } : void 0;
10144
10228
  if (!response.toolCalls?.length) {
10145
- const hasDonemeaningfulWork = (progress?.totalToolsExecuted ?? 0) > 0;
10146
- if (hasDonemeaningfulWork) {
10229
+ const hasDoneMeaningfulWork = (progress?.totalToolsExecuted ?? 0) > 0;
10230
+ if (hasDoneMeaningfulWork) {
10147
10231
  this.emitComplete(response.content, iteration, 0, stepDuration, tokens);
10148
10232
  return { output: response.content, toolsExecuted: 0, isCompleted: true };
10149
10233
  }
@@ -10157,11 +10241,26 @@ Please decide how to handle this error and continue.`;
10157
10241
  // SUBSECTION: Callback Builder
10158
10242
  // ─────────────────────────────────────────────────────────────────
10159
10243
  buildStreamCallbacks(phase) {
10160
- return {
10244
+ let _reasoningEndFired = false;
10245
+ let _outputBuffer = "";
10246
+ const callbacks = {
10161
10247
  onReasoningStart: () => this.emitReasoningStart(phase),
10162
10248
  onReasoningDelta: (content) => this.emitReasoningDelta(content, phase),
10163
- onReasoningEnd: () => this.emitReasoningEnd(phase),
10164
- onOutputDelta: () => {
10249
+ onReasoningEnd: () => {
10250
+ _reasoningEndFired = true;
10251
+ this.emitReasoningEnd(phase);
10252
+ },
10253
+ // WHY: Show AI text as it streams, not after completion.
10254
+ // The user sees what the AI is writing in real-time via the status bar.
10255
+ onOutputDelta: (text) => {
10256
+ _outputBuffer += text;
10257
+ const firstLine = _outputBuffer.split("\n")[0]?.slice(0, 120) || "";
10258
+ this.events.emit({
10259
+ type: EVENT_TYPES.THINK,
10260
+ timestamp: Date.now(),
10261
+ data: { thought: `Writing\u2026 ${_outputBuffer.length} chars
10262
+ ${firstLine}`, phase }
10263
+ });
10165
10264
  },
10166
10265
  onRetry: (attempt, maxRetries, delayMs, error) => {
10167
10266
  this.events.emit({
@@ -10177,19 +10276,40 @@ Please decide how to handle this error and continue.`;
10177
10276
  data: { inputTokens: usage.input_tokens, outputTokens: usage.output_tokens }
10178
10277
  });
10179
10278
  },
10180
- abortSignal: this.abortController?.signal
10279
+ abortSignal: this.abortController?.signal,
10280
+ // WHY: Used by step() to detect if SSE reasoning blocks fired.
10281
+ // If not, we know it was an inline <think> model and must emit post-stream.
10282
+ hadReasoningEnd: () => _reasoningEndFired
10181
10283
  };
10284
+ return callbacks;
10182
10285
  }
10183
10286
  // ─────────────────────────────────────────────────────────────────
10184
10287
  // SUBSECTION: Event Emitters
10185
10288
  // ─────────────────════════════════════════════════════════════
10186
- emitThink(iteration) {
10289
+ emitThink(iteration, progress) {
10290
+ const phase = this.state.getPhase();
10291
+ const targets = this.state.getTargets().size;
10292
+ const findings = this.state.getFindings().length;
10293
+ const toolsUsed = progress?.totalToolsExecuted ?? 0;
10294
+ const hasErrors = (progress?.toolErrors ?? 0) > 0;
10295
+ let thought;
10296
+ if (iteration === 0) {
10297
+ thought = targets > 0 ? `Analyzing ${targets} target${targets > 1 ? "s" : ""} \xB7 Planning ${phase} approach` : `Reviewing task \xB7 Building ${phase} strategy`;
10298
+ } else if (toolsUsed === 0) {
10299
+ thought = `Iteration ${iteration + 1} \xB7 No actions yet \xB7 Reconsidering approach`;
10300
+ } else if (hasErrors) {
10301
+ thought = `Iteration ${iteration + 1} \xB7 [${phase}] ${toolsUsed} tools \xB7 ${progress?.toolErrors} error${(progress?.toolErrors ?? 0) > 1 ? "s" : ""} \xB7 Adapting strategy`;
10302
+ } else if (findings > 0) {
10303
+ thought = `Iteration ${iteration + 1} \xB7 [${phase}] ${findings} finding${findings > 1 ? "s" : ""} \xB7 ${toolsUsed} tools \xB7 Continuing`;
10304
+ } else {
10305
+ thought = `Iteration ${iteration + 1} \xB7 [${phase}] ${toolsUsed} tool${toolsUsed !== 1 ? "s" : ""} executed \xB7 Evaluating results`;
10306
+ }
10187
10307
  this.events.emit({
10188
10308
  type: EVENT_TYPES.THINK,
10189
10309
  timestamp: Date.now(),
10190
10310
  data: {
10191
- thought: `${this.agentType} agent iteration ${iteration + 1}: Decision making`,
10192
- phase: this.state.getPhase()
10311
+ thought,
10312
+ phase
10193
10313
  }
10194
10314
  });
10195
10315
  }
@@ -11461,63 +11581,56 @@ import { useState, useRef, useCallback } from "react";
11461
11581
 
11462
11582
  // src/shared/constants/theme.ts
11463
11583
  var HEX = {
11464
- primary: "#FF968A",
11465
- pink: "#FFAEAE",
11466
- peach: "#FFC5BF"
11584
+ primary: "#2496ED",
11585
+ // Docker blue
11586
+ cyan: "#58C4F0",
11587
+ // bright sky blue
11588
+ teal: "#4EC9B0",
11589
+ // mint-teal (success states)
11590
+ yellow: "#E0AF68",
11591
+ // warm amber (warning)
11592
+ gray: "#C8C8C8",
11593
+ // secondary text
11594
+ white: "#FFFFFF",
11595
+ red: "#F7768E"
11596
+ // pastel red (error)
11467
11597
  };
11468
11598
  var COLORS = {
11469
- // Primary accent - coral/salmon (ANSI 210 is light coral)
11470
- primary: "ansi256(210)",
11471
- // Pastel pink (ANSI 217 is light pink)
11472
- pink: "ansi256(217)",
11473
- // Pastel peach (ANSI 216 is light salmon)
11474
- peach: "ansi256(216)",
11475
- // Warning yellow (ANSI 222 is light goldenrod)
11476
- yellow: "ansi256(222)",
11477
- // Orange (ANSI 215)
11478
- orange: "ansi256(215)",
11479
- // Bright white for main text
11599
+ primary: "ansi256(33)",
11600
+ // Docker blue (#0087ff) — closest to #2496ED
11601
+ cyan: "ansi256(74)",
11602
+ // bright sky blue (#5fafd7)
11603
+ teal: "ansi256(79)",
11604
+ // mint-teal (#5fd7af) — success states
11605
+ yellow: "ansi256(179)",
11606
+ // warm amber (warning)
11480
11607
  white: "white",
11481
- // Light gray for secondary text (ANSI 252 is light gray)
11482
- gray: "ansi256(252)",
11483
- // Bright red for errors
11484
- red: "red"
11608
+ // main text unchanged
11609
+ gray: "ansi256(250)",
11610
+ // secondary text bright enough to read
11611
+ dimGray: "ansi256(240)",
11612
+ // dimmer text for sub-lines
11613
+ red: "ansi256(204)"
11614
+ // pastel red (error)
11485
11615
  };
11486
11616
  var THEME = {
11487
11617
  ...COLORS,
11488
- bg: {
11489
- primary: "#050505",
11490
- input: "#020617"
11491
- },
11492
- text: {
11493
- primary: COLORS.white,
11494
- // AI responses - main text
11495
- secondary: COLORS.gray,
11496
- // Secondary info
11497
- muted: COLORS.gray,
11498
- // Metadata, hints
11499
- accent: COLORS.gray
11500
- // Very subtle
11501
- },
11502
11618
  status: {
11503
- success: COLORS.gray,
11504
- // Keep it subtle
11619
+ success: COLORS.teal,
11620
+ // teal for success
11505
11621
  warning: COLORS.yellow,
11506
- // Pastel yellow
11507
- error: COLORS.red,
11508
- // Bright red for errors
11509
- running: COLORS.gray
11510
- // Processing indicator
11622
+ // amber for warnings
11623
+ error: COLORS.red
11624
+ // red for errors
11511
11625
  },
11512
11626
  border: {
11513
- // ANSI 241 is a medium slate gray for borders
11514
- default: "ansi256(241)",
11627
+ default: "ansi256(25)",
11628
+ // muted Docker blue border (#005faf)
11515
11629
  focus: COLORS.primary,
11516
11630
  error: COLORS.red
11517
11631
  },
11518
11632
  gradient: {
11519
- // Hex colors required for gradient-string
11520
- cyber: [HEX.primary, HEX.pink, HEX.peach]
11633
+ cyber: [HEX.primary, HEX.cyan, HEX.teal]
11521
11634
  },
11522
11635
  spinner: COLORS.primary
11523
11636
  };
@@ -11537,9 +11650,8 @@ var ICONS = {
11537
11650
  info: "\u2139",
11538
11651
  running: "\u25C9",
11539
11652
  thinking: "\u25D0",
11540
- // Actions (pentesting-specific)
11653
+ // Actions
11541
11654
  target: "\u25C8",
11542
- // Diamond for target
11543
11655
  scan: "\u25CE",
11544
11656
  exploit: "\u2607",
11545
11657
  shell: "\u276F",
@@ -11557,9 +11669,7 @@ var ICONS = {
11557
11669
  unlock: "\u2B1A",
11558
11670
  key: "\u26B7",
11559
11671
  flag: "\u2691",
11560
- // Simple flag
11561
11672
  pwned: "\u25C8"
11562
- // Compromised
11563
11673
  };
11564
11674
 
11565
11675
  // src/platform/tui/constants/display.ts
@@ -11570,7 +11680,11 @@ var TUI_DISPLAY_LIMITS = {
11570
11680
  reasoningPreview: 500,
11571
11681
  /** Reasoning history slice for display */
11572
11682
  reasoningHistorySlice: 1e3,
11573
- /** Tool input preview length (for command display) */
11683
+ /** Max number of output lines shown from a tool result (success) */
11684
+ toolOutputLines: 20,
11685
+ /** Max number of output lines shown from a tool result (error — show more) */
11686
+ errorOutputLines: 25,
11687
+ /** Tool output preview length (for command display) */
11574
11688
  toolInputPreview: 200,
11575
11689
  /** Tool input truncated length */
11576
11690
  toolInputTruncated: 197,
@@ -11601,7 +11715,11 @@ var TUI_DISPLAY_LIMITS = {
11601
11715
  /** Max flow nodes to display */
11602
11716
  maxFlowNodes: 50,
11603
11717
  /** Max stopped processes to show */
11604
- maxStoppedProcesses: 3
11718
+ maxStoppedProcesses: 3,
11719
+ /** Max chars for live reasoning preview in status sub-line */
11720
+ reasoningPreviewChars: 100,
11721
+ /** Max chars for thinking block first-line summary */
11722
+ thinkingSummaryChars: 72
11605
11723
  };
11606
11724
  var MESSAGE_STYLES = {
11607
11725
  colors: {
@@ -11619,8 +11737,10 @@ var MESSAGE_STYLES = {
11619
11737
  // Tool commands - light gray
11620
11738
  result: THEME.gray,
11621
11739
  // Tool results - light gray
11622
- status: THEME.primary
11623
- // Status - pastel red accent
11740
+ status: THEME.primary,
11741
+ // Status - teal accent
11742
+ thinking: THEME.cyan
11743
+ // Extended reasoning — sky blue (distinct from primary)
11624
11744
  },
11625
11745
  prefixes: {
11626
11746
  user: "\u276F",
@@ -11630,7 +11750,9 @@ var MESSAGE_STYLES = {
11630
11750
  error: "\u2717",
11631
11751
  tool: "\u23BF",
11632
11752
  result: "\u21B3",
11633
- status: "\u25C8"
11753
+ status: "\u25C8",
11754
+ thinking: "\u25D0"
11755
+ // Thinking/reasoning icon
11634
11756
  }
11635
11757
  };
11636
11758
  var COMMAND_DEFINITIONS = [
@@ -11753,7 +11875,7 @@ var useAgentState = () => {
11753
11875
  };
11754
11876
 
11755
11877
  // src/platform/tui/hooks/useAgentEvents.ts
11756
- import { useEffect } from "react";
11878
+ import { useEffect, useRef as useRef2 } from "react";
11757
11879
  var useAgentEvents = (agent, eventsRef, state) => {
11758
11880
  const {
11759
11881
  addMessage,
@@ -11769,35 +11891,51 @@ var useAgentEvents = (agent, eventsRef, state) => {
11769
11891
  lastStepTokensRef,
11770
11892
  clearAllTimers
11771
11893
  } = state;
11894
+ const reasoningBufferRef = useRef2("");
11772
11895
  useEffect(() => {
11773
11896
  const events = eventsRef.current;
11774
11897
  const onToolCall = (e) => {
11775
- setCurrentStatus(`Executing: ${e.data.toolName}`);
11898
+ if (NOISE_CLASSIFICATION.LOW_VISIBILITY.includes(e.data.toolName)) return;
11899
+ setCurrentStatus(`${e.data.toolName}\u2026`);
11776
11900
  const inputStr = formatToolInput(e.data.toolName, e.data.input);
11777
- addMessage("tool", inputStr ? `${e.data.toolName} ${inputStr}` : e.data.toolName);
11901
+ const label = inputStr ? `${toDisplayName(e.data.toolName)}(${inputStr})` : `${toDisplayName(e.data.toolName)}`;
11902
+ addMessage("tool", label);
11778
11903
  };
11779
11904
  const onToolResult = (e) => {
11905
+ if (NOISE_CLASSIFICATION.LOW_VISIBILITY.includes(e.data.toolName)) {
11906
+ return;
11907
+ }
11780
11908
  const icon = e.data.success ? "\u2713" : "\u2717";
11781
- const content = e.data.success ? e.data.outputSummary || e.data.output || "" : e.data.error || e.data.output || "Unknown error";
11782
- const maxLen = e.data.success ? TUI_DISPLAY_LIMITS.toolOutputPreview : TUI_DISPLAY_LIMITS.errorPreview;
11783
- const preview = content.slice(0, maxLen).replace(/\n/g, " ");
11784
- const more = content.length > maxLen ? "..." : "";
11785
- addMessage("result", `${icon} ${preview}${more}`);
11909
+ const rawContent = e.data.success ? e.data.outputSummary || e.data.output || "" : e.data.error || e.data.output || "Unknown error";
11910
+ if (!rawContent.trim()) {
11911
+ addMessage("result", `${icon}`);
11912
+ return;
11913
+ }
11914
+ const lines = rawContent.replace(/^\s*-n\s+/gm, "").split("\n").map((l) => l.trimEnd()).filter((l, i, arr) => {
11915
+ if (l === "" && arr[i - 1] === "") return false;
11916
+ return true;
11917
+ });
11918
+ addMessage("result", `${icon} ${lines.join("\n")}`);
11786
11919
  };
11787
11920
  const onComplete = (e) => {
11788
- addMessage("system", `\u2713 Complete ${formatMeta(e.data.durationMs || 0, (e.data.tokens?.input || 0) + (e.data.tokens?.output || 0))}`);
11921
+ const meta = formatMeta(e.data.durationMs || 0, (e.data.tokens?.input || 0) + (e.data.tokens?.output || 0));
11922
+ addMessage("system", `\u2713 Done ${meta}`);
11789
11923
  lastResponseMetaRef.current = { durationMs: e.data.durationMs, tokens: e.data.tokens };
11790
11924
  };
11791
11925
  const onRetry = (e) => {
11792
11926
  handleRetry(e, addMessage, setRetryState, retryCountdownRef, retryCountRef);
11793
11927
  };
11794
11928
  const onThink = (e) => {
11795
- const t = e.data.thought;
11796
- setCurrentStatus(t.length > TUI_DISPLAY_LIMITS.statusThoughtPreview ? t.substring(0, TUI_DISPLAY_LIMITS.statusThoughtTruncated) + "..." : t);
11929
+ setCurrentStatus(e.data.thought);
11797
11930
  };
11798
11931
  const onError = (e) => {
11799
11932
  addMessage("error", e.data.message || "An error occurred");
11800
11933
  };
11934
+ const onAIResponse = (e) => {
11935
+ if (e.data.content?.trim()) {
11936
+ addMessage("ai", e.data.content.trim());
11937
+ }
11938
+ };
11801
11939
  const onUsageUpdate = (e) => {
11802
11940
  const stepTokens = e.data.inputTokens + e.data.outputTokens;
11803
11941
  if (stepTokens < lastStepTokensRef.current) {
@@ -11810,7 +11948,7 @@ var useAgentEvents = (agent, eventsRef, state) => {
11810
11948
  addMessage("system", `[FLAG] ${e.data.flag} (total: ${e.data.totalFlags})`);
11811
11949
  };
11812
11950
  const onPhaseChange = (e) => {
11813
- addMessage("system", `[Phase] ${e.data.fromPhase} -> ${e.data.toPhase} (${e.data.reason})`);
11951
+ addMessage("system", `Phase ${e.data.fromPhase} \u2192 ${e.data.toPhase} (${e.data.reason})`);
11814
11952
  const s = agent.getState();
11815
11953
  setStats({
11816
11954
  phase: e.data.toPhase,
@@ -11819,6 +11957,25 @@ var useAgentEvents = (agent, eventsRef, state) => {
11819
11957
  todo: s.getTodo().length
11820
11958
  });
11821
11959
  };
11960
+ const onReasoningStart = () => {
11961
+ reasoningBufferRef.current = "";
11962
+ setCurrentStatus("Thinking\u2026");
11963
+ };
11964
+ const onReasoningDelta = (e) => {
11965
+ reasoningBufferRef.current += e.data.content;
11966
+ const chars = reasoningBufferRef.current.length;
11967
+ const firstLine = reasoningBufferRef.current.split("\n")[0]?.slice(0, TUI_DISPLAY_LIMITS.reasoningPreviewChars) || "";
11968
+ setCurrentStatus(`Thinking\u2026 ${chars} chars
11969
+ ${firstLine}`);
11970
+ };
11971
+ const onReasoningEnd = () => {
11972
+ const text = reasoningBufferRef.current.trim();
11973
+ reasoningBufferRef.current = "";
11974
+ setCurrentStatus("");
11975
+ if (text) {
11976
+ addMessage("thinking", text);
11977
+ }
11978
+ };
11822
11979
  setInputHandler((p) => {
11823
11980
  return new Promise((resolve) => {
11824
11981
  const isPassword = /password|passphrase/i.test(p);
@@ -11850,9 +12007,11 @@ var useAgentEvents = (agent, eventsRef, state) => {
11850
12007
  });
11851
12008
  });
11852
12009
  setCommandEventEmitter((event) => {
12010
+ if (event.type === COMMAND_EVENT_TYPES.COMMAND_START || event.type === COMMAND_EVENT_TYPES.COMMAND_SUCCESS) {
12011
+ return;
12012
+ }
11853
12013
  const icon = getCommandEventIcon(event.type);
11854
- const msg = event.detail ? `${icon} ${event.message}
11855
- ${event.detail}` : `${icon} ${event.message}`;
12014
+ const msg = event.detail ? `${icon} ${event.message} ${event.detail}` : `${icon} ${event.message}`;
11856
12015
  addMessage("system", msg);
11857
12016
  });
11858
12017
  const updateStats = () => {
@@ -11875,6 +12034,10 @@ var useAgentEvents = (agent, eventsRef, state) => {
11875
12034
  events.on(EVENT_TYPES.PHASE_CHANGE, onPhaseChange);
11876
12035
  events.on(EVENT_TYPES.STATE_CHANGE, updateStats);
11877
12036
  events.on(EVENT_TYPES.START, updateStats);
12037
+ events.on(EVENT_TYPES.REASONING_START, onReasoningStart);
12038
+ events.on(EVENT_TYPES.REASONING_DELTA, onReasoningDelta);
12039
+ events.on(EVENT_TYPES.REASONING_END, onReasoningEnd);
12040
+ events.on(EVENT_TYPES.AI_RESPONSE, onAIResponse);
11878
12041
  updateStats();
11879
12042
  return () => {
11880
12043
  events.removeAllListeners();
@@ -11900,6 +12063,16 @@ var useAgentEvents = (agent, eventsRef, state) => {
11900
12063
  eventsRef
11901
12064
  ]);
11902
12065
  };
12066
+ function toDisplayName(toolName) {
12067
+ const MAP = {
12068
+ [TOOL_NAMES.RUN_CMD]: "Bash",
12069
+ [TOOL_NAMES.BG_PROCESS]: "Process",
12070
+ [TOOL_NAMES.READ_FILE]: "Read",
12071
+ [TOOL_NAMES.WRITE_FILE]: "Write",
12072
+ [TOOL_NAMES.ASK_USER]: "Input"
12073
+ };
12074
+ return MAP[toolName] ?? toolName.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
12075
+ }
11903
12076
  function formatToolInput(toolName, input) {
11904
12077
  if (!input || Object.keys(input).length === 0) return "";
11905
12078
  try {
@@ -11955,24 +12128,24 @@ Options: ${request.options.join(", ")}`;
11955
12128
  }
11956
12129
  function getCommandEventIcon(eventType) {
11957
12130
  const icons = {
11958
- [COMMAND_EVENT_TYPES.TOOL_MISSING]: "[!]",
11959
- [COMMAND_EVENT_TYPES.TOOL_INSTALL]: "[install]",
11960
- [COMMAND_EVENT_TYPES.TOOL_INSTALLED]: "[ok]",
11961
- [COMMAND_EVENT_TYPES.TOOL_INSTALL_FAILED]: "[fail]",
11962
- [COMMAND_EVENT_TYPES.TOOL_RETRY]: "[retry]",
11963
- [COMMAND_EVENT_TYPES.COMMAND_START]: "[>]",
11964
- [COMMAND_EVENT_TYPES.COMMAND_SUCCESS]: "[ok]",
11965
- [COMMAND_EVENT_TYPES.COMMAND_FAILED]: "[x]",
11966
- [COMMAND_EVENT_TYPES.COMMAND_ERROR]: "[err]",
11967
- [COMMAND_EVENT_TYPES.INPUT_REQUIRED]: "[auth]"
12131
+ [COMMAND_EVENT_TYPES.TOOL_MISSING]: "!",
12132
+ [COMMAND_EVENT_TYPES.TOOL_INSTALL]: "\u2193",
12133
+ [COMMAND_EVENT_TYPES.TOOL_INSTALLED]: "\u2713",
12134
+ [COMMAND_EVENT_TYPES.TOOL_INSTALL_FAILED]: "\u2717",
12135
+ [COMMAND_EVENT_TYPES.TOOL_RETRY]: "\u21BA",
12136
+ [COMMAND_EVENT_TYPES.COMMAND_START]: "\u25B6",
12137
+ [COMMAND_EVENT_TYPES.COMMAND_SUCCESS]: "\u2713",
12138
+ [COMMAND_EVENT_TYPES.COMMAND_FAILED]: "\u2717",
12139
+ [COMMAND_EVENT_TYPES.COMMAND_ERROR]: "\u26A0",
12140
+ [COMMAND_EVENT_TYPES.INPUT_REQUIRED]: "\u{1F512}"
11968
12141
  };
11969
- return icons[eventType] || "[-]";
12142
+ return icons[eventType] || "\u2022";
11970
12143
  }
11971
12144
 
11972
12145
  // src/platform/tui/hooks/useAgent.ts
11973
12146
  var useAgent = (shouldAutoApprove, target) => {
11974
12147
  const [agent] = useState2(() => AgentFactory.createMainAgent(shouldAutoApprove));
11975
- const eventsRef = useRef2(agent.getEventEmitter());
12148
+ const eventsRef = useRef3(agent.getEventEmitter());
11976
12149
  const state = useAgentState();
11977
12150
  const {
11978
12151
  messages,
@@ -12025,7 +12198,7 @@ var useAgent = (shouldAutoApprove, target) => {
12025
12198
  setCurrentStatus("");
12026
12199
  addMessage("system", "Interrupted");
12027
12200
  }, [agent, addMessage, manageTimer, setIsProcessing, setCurrentStatus]);
12028
- const inputRequestRef = useRef2(inputRequest);
12201
+ const inputRequestRef = useRef3(inputRequest);
12029
12202
  inputRequestRef.current = inputRequest;
12030
12203
  const cancelInputRequest = useCallback2(() => {
12031
12204
  const ir = inputRequestRef.current;
@@ -12244,9 +12417,76 @@ var MessageList = memo(({ messages }) => {
12244
12417
  }
12245
12418
  ) }, msg.id);
12246
12419
  }
12247
- }
12248
- return /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsxs2(Text2, { color: MESSAGE_STYLES.colors[msg.type], dimColor: msg.type === "result", children: [
12249
- MESSAGE_STYLES.prefixes[msg.type],
12420
+ return /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsxs2(Text2, { color: THEME.gray, children: [
12421
+ "\u2022 ",
12422
+ msg.content
12423
+ ] }) }, msg.id);
12424
+ }
12425
+ if (msg.type === "thinking") {
12426
+ const lines = msg.content.split("\n");
12427
+ const charCount = msg.content.length;
12428
+ const firstLine = lines[0]?.slice(0, TUI_DISPLAY_LIMITS.thinkingSummaryChars) || "";
12429
+ const isMultiLine = lines.length > 1 || charCount > TUI_DISPLAY_LIMITS.thinkingSummaryChars;
12430
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginTop: 0, marginBottom: 1, children: [
12431
+ /* @__PURE__ */ jsxs2(Box2, { children: [
12432
+ /* @__PURE__ */ jsx2(Text2, { color: THEME.cyan, children: "\u25D0 " }),
12433
+ /* @__PURE__ */ jsx2(Text2, { color: THEME.cyan, bold: true, children: "Thinking" }),
12434
+ /* @__PURE__ */ jsx2(Text2, { color: THEME.gray, children: ` (${lines.length} line${lines.length !== 1 ? "s" : ""}, ${charCount} chars)` })
12435
+ ] }),
12436
+ lines.map((line, i) => /* @__PURE__ */ jsxs2(Box2, { children: [
12437
+ /* @__PURE__ */ jsx2(Text2, { color: THEME.cyan, children: "\u2502 " }),
12438
+ /* @__PURE__ */ jsx2(Text2, { color: THEME.gray, children: line })
12439
+ ] }, i)),
12440
+ /* @__PURE__ */ jsxs2(Box2, { children: [
12441
+ /* @__PURE__ */ jsx2(Text2, { color: THEME.cyan, children: "\u2570\u2500" }),
12442
+ isMultiLine && /* @__PURE__ */ jsx2(Text2, { color: THEME.gray, children: ` ${firstLine}\u2026` })
12443
+ ] })
12444
+ ] }, msg.id);
12445
+ }
12446
+ if (msg.type === "tool") {
12447
+ return /* @__PURE__ */ jsxs2(Box2, { children: [
12448
+ /* @__PURE__ */ jsx2(Text2, { color: THEME.gray, children: "\u23BF " }),
12449
+ /* @__PURE__ */ jsx2(Text2, { color: THEME.primary, children: msg.content })
12450
+ ] }, msg.id);
12451
+ }
12452
+ if (msg.type === "result") {
12453
+ const isSuccess = msg.content.startsWith("\u2713");
12454
+ const isFailure = msg.content.startsWith("\u2717");
12455
+ const color = isSuccess ? THEME.primary : isFailure ? THEME.red : THEME.gray;
12456
+ const lines = msg.content.split("\n");
12457
+ const firstLine = lines[0] || "";
12458
+ const restLines = lines.slice(1);
12459
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", children: [
12460
+ /* @__PURE__ */ jsxs2(Box2, { children: [
12461
+ /* @__PURE__ */ jsx2(Text2, { color: THEME.gray, children: "\u21B3 " }),
12462
+ /* @__PURE__ */ jsx2(Text2, { color, children: firstLine })
12463
+ ] }),
12464
+ restLines.map((line, i) => /* @__PURE__ */ jsx2(Box2, { paddingLeft: 2, children: /* @__PURE__ */ jsx2(Text2, { color: THEME.gray, children: line }) }, i))
12465
+ ] }, msg.id);
12466
+ }
12467
+ if (msg.type === "ai" || msg.type === "assistant") {
12468
+ return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", marginBottom: 0, children: /* @__PURE__ */ jsx2(Text2, { color: THEME.white, children: msg.content }) }, msg.id);
12469
+ }
12470
+ if (msg.type === "error") {
12471
+ return /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsxs2(Text2, { color: THEME.red, children: [
12472
+ "\u2717 ",
12473
+ msg.content
12474
+ ] }) }, msg.id);
12475
+ }
12476
+ if (msg.type === "user") {
12477
+ return /* @__PURE__ */ jsxs2(Box2, { children: [
12478
+ /* @__PURE__ */ jsx2(Text2, { color: THEME.primary, children: "\u276F " }),
12479
+ /* @__PURE__ */ jsx2(Text2, { color: THEME.white, children: msg.content })
12480
+ ] }, msg.id);
12481
+ }
12482
+ if (msg.type === "system") {
12483
+ return /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsxs2(Text2, { color: THEME.gray, children: [
12484
+ "\u2022 ",
12485
+ msg.content
12486
+ ] }) }, msg.id);
12487
+ }
12488
+ return /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsxs2(Text2, { color: MESSAGE_STYLES.colors[msg.type] ?? THEME.gray, children: [
12489
+ MESSAGE_STYLES.prefixes[msg.type] ?? "\u2022",
12250
12490
  " ",
12251
12491
  msg.content
12252
12492
  ] }) }, msg.id);
@@ -12287,39 +12527,48 @@ var StatusDisplay = memo3(({
12287
12527
  return err.length > DISPLAY_LIMITS.RETRY_ERROR_PREVIEW ? err.substring(0, DISPLAY_LIMITS.RETRY_ERROR_TRUNCATED) + "..." : err;
12288
12528
  };
12289
12529
  const meta = formatMeta(elapsedTime * 1e3, currentTokens);
12530
+ const statusLines = currentStatus ? currentStatus.split("\n").filter(Boolean) : [];
12531
+ const statusMain = statusLines[0] || "Processing...";
12532
+ const statusSub = statusLines.slice(1).join(" ");
12533
+ const isThinkingStatus = statusMain.startsWith("Thinking");
12290
12534
  return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginTop: 0, children: [
12291
12535
  retryState.status === "retrying" && /* @__PURE__ */ jsxs3(Box3, { marginBottom: 1, children: [
12292
12536
  /* @__PURE__ */ jsx4(Text4, { color: THEME.yellow, children: /* @__PURE__ */ jsx4(MusicSpinner, { color: THEME.yellow }) }),
12293
- /* @__PURE__ */ jsxs3(Text4, { color: THEME.yellow, children: [
12537
+ /* @__PURE__ */ jsxs3(Text4, { color: THEME.yellow, bold: true, children: [
12294
12538
  " \u27F3 Retry #",
12295
- retryState.attempt
12539
+ retryState.attempt,
12540
+ "/",
12541
+ retryState.maxRetries
12296
12542
  ] }),
12297
12543
  /* @__PURE__ */ jsxs3(Text4, { color: THEME.gray, children: [
12298
- " \xB7 ",
12299
- truncateError(retryState.error)
12300
- ] }),
12301
- /* @__PURE__ */ jsxs3(Text4, { color: THEME.primary, bold: true, children: [
12302
- " \xB7 ",
12544
+ " \u2014 ",
12303
12545
  retryState.countdown,
12304
- "s"
12546
+ "s \xB7 ",
12547
+ truncateError(retryState.error)
12305
12548
  ] })
12306
12549
  ] }),
12307
- isProcessing && retryState.status !== "retrying" && /* @__PURE__ */ jsxs3(Box3, { marginBottom: 1, children: [
12308
- /* @__PURE__ */ jsx4(Text4, { color: THEME.primary, children: /* @__PURE__ */ jsx4(MusicSpinner, { color: THEME.primary }) }),
12309
- /* @__PURE__ */ jsxs3(Text4, { color: THEME.gray, children: [
12310
- " ",
12311
- currentStatus || "Processing"
12550
+ isProcessing && retryState.status !== "retrying" && /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginBottom: 1, children: [
12551
+ /* @__PURE__ */ jsxs3(Box3, { children: [
12552
+ /* @__PURE__ */ jsx4(Text4, { color: isThinkingStatus ? THEME.cyan : THEME.primary, children: /* @__PURE__ */ jsx4(MusicSpinner, { color: isThinkingStatus ? THEME.cyan : THEME.primary }) }),
12553
+ /* @__PURE__ */ jsxs3(Text4, { color: isThinkingStatus ? THEME.cyan : THEME.primary, bold: true, children: [
12554
+ " ",
12555
+ statusMain
12556
+ ] }),
12557
+ /* @__PURE__ */ jsxs3(Text4, { color: THEME.gray, children: [
12558
+ " ",
12559
+ meta
12560
+ ] })
12312
12561
  ] }),
12313
- /* @__PURE__ */ jsxs3(Text4, { color: THEME.gray, children: [
12314
- " ",
12315
- meta
12316
- ] })
12562
+ statusSub ? /* @__PURE__ */ jsx4(Box3, { paddingLeft: 2, children: /* @__PURE__ */ jsxs3(Text4, { color: isThinkingStatus ? THEME.cyan : THEME.gray, children: [
12563
+ "\u2502 ",
12564
+ statusSub
12565
+ ] }) }) : null
12317
12566
  ] })
12318
12567
  ] });
12319
12568
  });
12320
12569
 
12321
12570
  // src/platform/tui/components/ChatInput.tsx
12322
- import { useMemo, useCallback as useCallback3, useRef as useRef3, memo as memo4 } from "react";
12571
+ import { useMemo, useCallback as useCallback3, useRef as useRef4, memo as memo4, useState as useState4 } from "react";
12323
12572
  import { Box as Box4, Text as Text5, useInput } from "ink";
12324
12573
  import TextInput from "ink-text-input";
12325
12574
  import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
@@ -12342,24 +12591,25 @@ var ChatInput = memo4(({
12342
12591
  return getMatchingCommands(partialCmd).slice(0, MAX_SUGGESTIONS);
12343
12592
  }, [isSlashMode, partialCmd, hasArgs]);
12344
12593
  const showPreview = isSlashMode && !hasArgs && suggestions.length > 0;
12345
- const suggestionsRef = useRef3(suggestions);
12594
+ const suggestionsRef = useRef4(suggestions);
12346
12595
  suggestionsRef.current = suggestions;
12347
- const isSlashModeRef = useRef3(isSlashMode);
12596
+ const isSlashModeRef = useRef4(isSlashMode);
12348
12597
  isSlashModeRef.current = isSlashMode;
12349
- const hasArgsRef = useRef3(hasArgs);
12598
+ const hasArgsRef = useRef4(hasArgs);
12350
12599
  hasArgsRef.current = hasArgs;
12351
- const inputRequestRef = useRef3(inputRequest);
12600
+ const inputRequestRef = useRef4(inputRequest);
12352
12601
  inputRequestRef.current = inputRequest;
12353
- const onChangeRef = useRef3(onChange);
12602
+ const onChangeRef = useRef4(onChange);
12354
12603
  onChangeRef.current = onChange;
12604
+ const [inputKey, setInputKey] = useState4(0);
12355
12605
  useInput(useCallback3((_input, key) => {
12356
12606
  if (inputRequestRef.current.status === "active") return;
12357
12607
  if (key.tab && isSlashModeRef.current && !hasArgsRef.current && suggestionsRef.current.length > 0) {
12358
12608
  const best = suggestionsRef.current[0];
12359
12609
  const argsHint = best.args ? " " : "";
12360
12610
  const completed = `/${best.name}${argsHint}`;
12361
- onChangeRef.current("");
12362
- setTimeout(() => onChangeRef.current(completed), 0);
12611
+ onChangeRef.current(completed);
12612
+ setInputKey((k) => k + 1);
12363
12613
  }
12364
12614
  }, []));
12365
12615
  return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
@@ -12425,7 +12675,8 @@ var ChatInput = memo4(({
12425
12675
  onChange,
12426
12676
  onSubmit,
12427
12677
  placeholder
12428
- }
12678
+ },
12679
+ inputKey
12429
12680
  )
12430
12681
  ] })
12431
12682
  }
@@ -12487,9 +12738,9 @@ var footer_default = Footer;
12487
12738
  import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
12488
12739
  var App = ({ autoApprove = false, target }) => {
12489
12740
  const { exit } = useApp();
12490
- const [input, setInput] = useState4("");
12491
- const [secretInput, setSecretInput] = useState4("");
12492
- const [autoApproveMode, setAutoApproveMode] = useState4(autoApprove);
12741
+ const [input, setInput] = useState5("");
12742
+ const [secretInput, setSecretInput] = useState5("");
12743
+ const [autoApproveMode, setAutoApproveMode] = useState5(autoApprove);
12493
12744
  const {
12494
12745
  agent,
12495
12746
  messages,
@@ -12508,11 +12759,11 @@ var App = ({ autoApprove = false, target }) => {
12508
12759
  addMessage,
12509
12760
  refreshStats
12510
12761
  } = useAgent(autoApproveMode, target);
12511
- const isProcessingRef = useRef4(isProcessing);
12762
+ const isProcessingRef = useRef5(isProcessing);
12512
12763
  isProcessingRef.current = isProcessing;
12513
- const autoApproveModeRef = useRef4(autoApproveMode);
12764
+ const autoApproveModeRef = useRef5(autoApproveMode);
12514
12765
  autoApproveModeRef.current = autoApproveMode;
12515
- const inputRequestRef = useRef4(inputRequest);
12766
+ const inputRequestRef = useRef5(inputRequest);
12516
12767
  inputRequestRef.current = inputRequest;
12517
12768
  const handleExit = useCallback4(() => {
12518
12769
  const ir = inputRequestRef.current;
@@ -12755,6 +13006,7 @@ var CLI_SCAN_TYPES = Object.freeze([
12755
13006
  ]);
12756
13007
 
12757
13008
  // src/platform/tui/main.tsx
13009
+ import gradient from "gradient-string";
12758
13010
  import { jsx as jsx8 } from "react/jsx-runtime";
12759
13011
  initDebugLogger();
12760
13012
  var program = new Command();
@@ -12763,13 +13015,13 @@ program.command("interactive", { isDefault: true }).alias("i").description("Star
12763
13015
  const opts = program.opts();
12764
13016
  const skipPermissions = opts.dangerouslySkipPermissions || false;
12765
13017
  console.clear();
12766
- console.log(chalk.hex(THEME.primary)(ASCII_BANNER));
13018
+ console.log(gradient([HEX.primary, HEX.cyan, HEX.teal]).multiline(ASCII_BANNER));
12767
13019
  console.log(
12768
- " " + chalk.hex(THEME.text.secondary)(`v${APP_VERSION}`) + chalk.hex(THEME.text.muted)(" \u2502 ") + chalk.hex(THEME.primary)("Type /help for commands") + "\n"
13020
+ " " + chalk.hex(HEX.gray)(`v${APP_VERSION}`) + chalk.hex(HEX.gray)(" \u2502 ") + chalk.hex(HEX.primary)("Type /help for commands") + "\n"
12769
13021
  );
12770
13022
  if (skipPermissions) {
12771
- console.log(chalk.hex(THEME.status.error)("[!] WARNING: Running with --dangerously-skip-permissions"));
12772
- console.log(chalk.hex(THEME.status.error)("[!] All tool executions will be auto-approved!\n"));
13023
+ console.log(chalk.hex(HEX.red)("[!] WARNING: Running with --dangerously-skip-permissions"));
13024
+ console.log(chalk.hex(HEX.red)("[!] All tool executions will be auto-approved!\n"));
12773
13025
  }
12774
13026
  const { waitUntilExit } = render(
12775
13027
  /* @__PURE__ */ jsx8(
@@ -12785,11 +13037,11 @@ program.command("interactive", { isDefault: true }).alias("i").description("Star
12785
13037
  program.command("run <objective>").alias("r").description("Run a single objective and exit").option("-o, --output <file>", "Output file for results").option("--max-steps <n>", "Maximum number of steps", String(CLI_DEFAULT.MAX_STEPS)).action(async (objective, options) => {
12786
13038
  const opts = program.opts();
12787
13039
  const skipPermissions = opts.dangerouslySkipPermissions || false;
12788
- console.log(chalk.hex(THEME.primary)(ASCII_BANNER));
13040
+ console.log(gradient([HEX.primary, HEX.cyan, HEX.teal]).multiline(ASCII_BANNER));
12789
13041
  if (skipPermissions) {
12790
- console.log(chalk.hex(THEME.status.error)("[!] WARNING: Running with --dangerously-skip-permissions\n"));
13042
+ console.log(chalk.hex(HEX.red)("[!] WARNING: Running with --dangerously-skip-permissions\n"));
12791
13043
  }
12792
- console.log(chalk.hex(THEME.primary)(`[target] Objective: ${objective}
13044
+ console.log(chalk.hex(HEX.primary)(`[target] Objective: ${objective}
12793
13045
  `));
12794
13046
  const agent = AgentFactory.createMainAgent(skipPermissions);
12795
13047
  if (skipPermissions) {
@@ -12806,18 +13058,18 @@ program.command("run <objective>").alias("r").description("Run a single objectiv
12806
13058
  process.on("SIGTERM", () => shutdown(EXIT_CODES.SIGTERM));
12807
13059
  try {
12808
13060
  const result2 = await agent.execute(objective);
12809
- console.log(chalk.hex(THEME.status.success)("\n[+] Assessment complete!\n"));
13061
+ console.log(chalk.hex(HEX.gray)("\n[+] Assessment complete!\n"));
12810
13062
  console.log(result2);
12811
13063
  if (options.output) {
12812
13064
  const fs = await import("fs/promises");
12813
13065
  await fs.writeFile(options.output, JSON.stringify({ result: result2 }, null, 2));
12814
- console.log(chalk.hex(THEME.primary)(`
13066
+ console.log(chalk.hex(HEX.primary)(`
12815
13067
  [+] Report saved to: ${options.output}`));
12816
13068
  }
12817
13069
  await shutdown(0);
12818
13070
  } catch (error) {
12819
13071
  const errorMessage = error instanceof Error ? error.message : String(error);
12820
- console.error(chalk.hex(THEME.status.error)(`
13072
+ console.error(chalk.hex(HEX.red)(`
12821
13073
  [-] Failed: ${errorMessage}`));
12822
13074
  await shutdown(1);
12823
13075
  }
@@ -12825,8 +13077,8 @@ program.command("run <objective>").alias("r").description("Run a single objectiv
12825
13077
  program.command("scan <target>").description("Quick scan a target").option("-s, --scan-type <type>", `Scan type (${CLI_SCAN_TYPES.join("|")})`, CLI_DEFAULT.SCAN_TYPE).option("-p, --ports <ports>", "Specific ports to scan").action(async (target, options) => {
12826
13078
  const opts = program.opts();
12827
13079
  const skipPermissions = opts.dangerouslySkipPermissions || false;
12828
- console.log(chalk.hex(THEME.primary)(ASCII_BANNER));
12829
- console.log(chalk.hex(THEME.primary)(`
13080
+ console.log(gradient([HEX.primary, HEX.cyan, HEX.teal]).multiline(ASCII_BANNER));
13081
+ console.log(chalk.hex(HEX.primary)(`
12830
13082
  [scan] Target: ${target} (${options.scanType})
12831
13083
  `));
12832
13084
  const agent = AgentFactory.createMainAgent(skipPermissions);
@@ -12839,62 +13091,61 @@ program.command("scan <target>").description("Quick scan a target").option("-s,
12839
13091
  process.on("SIGTERM", () => shutdown(EXIT_CODES.SIGTERM));
12840
13092
  try {
12841
13093
  await agent.execute(`Perform a ${options.scanType} scan on ${target}${options.ports ? ` focusing on ports ${options.ports}` : ""}. Analyze the results and identify potential vulnerabilities.`);
12842
- console.log(chalk.hex(THEME.status.success)("[+] Scan complete!"));
13094
+ console.log(chalk.hex(HEX.gray)("[+] Scan complete!"));
12843
13095
  await shutdown(0);
12844
13096
  } catch (error) {
12845
13097
  const errorMessage = error instanceof Error ? error.message : String(error);
12846
- console.error(chalk.hex(THEME.status.error)(`[-] Scan failed: ${errorMessage}`));
13098
+ console.error(chalk.hex(HEX.red)(`[-] Scan failed: ${errorMessage}`));
12847
13099
  await shutdown(1);
12848
13100
  }
12849
13101
  });
12850
13102
  program.command("help-extended").description("Show extended help with examples").action(() => {
12851
- console.log(chalk.hex(THEME.primary)(ASCII_BANNER));
13103
+ console.log(gradient([HEX.primary, HEX.cyan, HEX.teal]).multiline(ASCII_BANNER));
12852
13104
  console.log(`
12853
- ${chalk.hex(THEME.primary)(APP_NAME + " - Autonomous Penetration Testing AI")}
13105
+ ${chalk.hex(HEX.primary)(APP_NAME + " - Autonomous Penetration Testing AI")}
12854
13106
 
12855
- ${chalk.hex(THEME.status.warning)("Usage:")}
13107
+ ${chalk.hex(HEX.yellow)("Usage:")}
12856
13108
 
12857
- ${chalk.hex(THEME.status.success)("$ pentesting")} Start interactive mode
12858
- ${chalk.hex(THEME.status.success)("$ pentesting -t 192.168.1.1")} Start with target
12859
- ${chalk.hex(THEME.status.success)("$ pentesting --dangerously-skip-permissions")} Auto-approve all tools
13109
+ ${chalk.hex(HEX.gray)("$ pentesting")} Start interactive mode
13110
+ ${chalk.hex(HEX.gray)("$ pentesting -t 192.168.1.1")} Start with target
13111
+ ${chalk.hex(HEX.gray)("$ pentesting --dangerously-skip-permissions")} Auto-approve all tools
12860
13112
 
12861
- ${chalk.hex(THEME.status.warning)("Commands:")}
13113
+ ${chalk.hex(HEX.yellow)("Commands:")}
12862
13114
 
12863
- ${chalk.hex(THEME.primary)("pentesting")} Interactive TUI mode
12864
- ${chalk.hex(THEME.primary)("pentesting run <objective>")} Run single objective
12865
- ${chalk.hex(THEME.primary)("pentesting scan <target>")} Quick scan target
13115
+ ${chalk.hex(HEX.primary)("pentesting")} Interactive TUI mode
13116
+ ${chalk.hex(HEX.primary)("pentesting run <objective>")} Run single objective
13117
+ ${chalk.hex(HEX.primary)("pentesting scan <target>")} Quick scan target
12866
13118
 
12867
- ${chalk.hex(THEME.status.warning)("Options:")}
13119
+ ${chalk.hex(HEX.yellow)("Options:")}
12868
13120
 
12869
- ${chalk.hex(THEME.primary)("--dangerously-skip-permissions")} Skip all permission prompts
12870
- ${chalk.hex(THEME.primary)("-t, --target <ip>")} Set target
12871
- ${chalk.hex(THEME.primary)("-o, --output <file>")} Save results to file
13121
+ ${chalk.hex(HEX.primary)("--dangerously-skip-permissions")} Skip all permission prompts
13122
+ ${chalk.hex(HEX.primary)("-t, --target <ip>")} Set target
13123
+ ${chalk.hex(HEX.primary)("-o, --output <file>")} Save results to file
12872
13124
 
12873
- ${chalk.hex(THEME.status.warning)("Interactive Commands:")}
13125
+ ${chalk.hex(HEX.yellow)("Interactive Commands:")}
12874
13126
 
12875
- ${chalk.hex(THEME.primary)("/target <ip>")} Set target
12876
- ${chalk.hex(THEME.primary)("/start")} Start autonomous mode
12877
- ${chalk.hex(THEME.primary)("/config")} Manage configuration
12878
- ${chalk.hex(THEME.primary)("/hint <text>")} Provide hint
12879
- ${chalk.hex(THEME.primary)("/findings")} Show findings
12880
- ${chalk.hex(THEME.primary)("/reset")} Reset session
13127
+ ${chalk.hex(HEX.primary)("/target <ip>")} Set target
13128
+ ${chalk.hex(HEX.primary)("/start")} Start autonomous mode
13129
+ ${chalk.hex(HEX.primary)("/hint <text>")} Provide hint
13130
+ ${chalk.hex(HEX.primary)("/findings")} Show findings
13131
+ ${chalk.hex(HEX.primary)("/clear")} Reset session
12881
13132
 
12882
- ${chalk.hex(THEME.status.warning)("Examples:")}
13133
+ ${chalk.hex(HEX.yellow)("Examples:")}
12883
13134
 
12884
- ${chalk.hex(THEME.text.muted)("# Full autonomous mode")}
13135
+ ${chalk.hex(HEX.gray)("# Full autonomous mode")}
12885
13136
  $ pentesting --dangerously-skip-permissions -t 10.10.10.5
12886
13137
 
12887
- ${chalk.hex(THEME.text.muted)("# Run specific objective")}
13138
+ ${chalk.hex(HEX.gray)("# Run specific objective")}
12888
13139
  $ pentesting run "Find SQL injection" -t http://target.com -o report.json
12889
13140
 
12890
- ${chalk.hex(THEME.text.muted)("# Quick vulnerability scan")}
13141
+ ${chalk.hex(HEX.gray)("# Quick vulnerability scan")}
12891
13142
  $ pentesting scan 192.168.1.1 -s vuln
12892
13143
 
12893
- ${chalk.hex(THEME.status.warning)("Environment:")}
13144
+ ${chalk.hex(HEX.yellow)("Environment:")}
12894
13145
 
12895
- ${chalk.hex(THEME.primary)("PENTEST_API_KEY")} Required - LLM API key
12896
- ${chalk.hex(THEME.primary)("PENTEST_BASE_URL")} Optional - AI API base URL
12897
- ${chalk.hex(THEME.primary)("PENTEST_MODEL")} Optional - Model override
13146
+ ${chalk.hex(HEX.primary)("PENTEST_API_KEY")} Required - LLM API key
13147
+ ${chalk.hex(HEX.primary)("PENTEST_BASE_URL")} Optional - AI API base URL
13148
+ ${chalk.hex(HEX.primary)("PENTEST_MODEL")} Optional - Model override
12898
13149
  `);
12899
13150
  });
12900
13151
  program.parse();