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