pentesting 0.8.39 → 0.8.42
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/README.md +115 -18
- package/dist/index.js +1131 -17
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -32,8 +32,8 @@ import TextInput from "ink-text-input";
|
|
|
32
32
|
import Spinner from "ink-spinner";
|
|
33
33
|
|
|
34
34
|
// src/core/agent/autonomous-agent.ts
|
|
35
|
-
import
|
|
36
|
-
import { EventEmitter as
|
|
35
|
+
import Anthropic3 from "@anthropic-ai/sdk";
|
|
36
|
+
import { EventEmitter as EventEmitter7 } from "events";
|
|
37
37
|
|
|
38
38
|
// src/core/prompts/autonomous-prompt.ts
|
|
39
39
|
var AUTONOMOUS_HACKING_PROMPT = `You are Pentesting, an elite autonomous penetration testing AI designed for CTF competitions and professional security assessments. You operate with minimal human intervention, making intelligent decisions, adapting to obstacles, and persistently pursuing objectives until complete system compromise.
|
|
@@ -220,6 +220,115 @@ Analyze your situation honestly:
|
|
|
220
220
|
- Manual testing vs. automated?
|
|
221
221
|
|
|
222
222
|
Based on this reflection, propose 3 completely different approaches to try next.`;
|
|
223
|
+
var STRATEGY_PLANNING_PROMPT = `You are the strategic coordinator for this penetration test. Before executing ANY action, validate the strategy.
|
|
224
|
+
|
|
225
|
+
## STRATEGIC VALIDATION PROCESS
|
|
226
|
+
|
|
227
|
+
### 1. INTENT VERIFICATION
|
|
228
|
+
\`\`\`
|
|
229
|
+
[INTENT CHECK]
|
|
230
|
+
- What is the user's objective?
|
|
231
|
+
- What phase are we in?
|
|
232
|
+
- What have we accomplished so far?
|
|
233
|
+
- What is the immediate goal?
|
|
234
|
+
\`\`\`
|
|
235
|
+
|
|
236
|
+
### 2. PLAN FORMULATION
|
|
237
|
+
\`\`\`
|
|
238
|
+
[PLAN PROPOSAL]
|
|
239
|
+
Given the current state, I propose:
|
|
240
|
+
1. [Primary approach] - Expected outcome: X
|
|
241
|
+
2. [Backup approach] - If primary fails: Y
|
|
242
|
+
3. [Alternative] - If we need to pivot: Z
|
|
243
|
+
\`\`\`
|
|
244
|
+
|
|
245
|
+
### 3. PLAN VALIDATION (Self-Check)
|
|
246
|
+
\`\`\`
|
|
247
|
+
[VALIDATION]
|
|
248
|
+
\u25A1 Is this aligned with BFS (surface mapping first)?
|
|
249
|
+
\u25A1 Have I completed reconnaissance before deep testing?
|
|
250
|
+
\u25A1 Is this the highest ROI action right now?
|
|
251
|
+
\u25A1 Am I avoiding rabbit holes (SSL testing too early, etc.)?
|
|
252
|
+
\u25A1 Is the tool/command correct for this task?
|
|
253
|
+
\`\`\`
|
|
254
|
+
|
|
255
|
+
### 4. AGENT COORDINATION
|
|
256
|
+
When switching agents or techniques:
|
|
257
|
+
\`\`\`
|
|
258
|
+
[HANDOFF]
|
|
259
|
+
From: [current agent/approach]
|
|
260
|
+
To: [next agent/approach]
|
|
261
|
+
Reason: [why this switch makes sense]
|
|
262
|
+
Context passed: [what the next agent needs to know]
|
|
263
|
+
\`\`\`
|
|
264
|
+
|
|
265
|
+
### 5. DECISION LOG
|
|
266
|
+
After each significant decision:
|
|
267
|
+
\`\`\`
|
|
268
|
+
[DECISION]
|
|
269
|
+
Action: [what was done]
|
|
270
|
+
Result: [what happened]
|
|
271
|
+
Learning: [what we now know]
|
|
272
|
+
Next: [logical next step]
|
|
273
|
+
\`\`\`
|
|
274
|
+
|
|
275
|
+
## ANTI-PATTERNS TO DETECT AND PREVENT
|
|
276
|
+
|
|
277
|
+
- \u274C SSL/TLS testing before full surface mapping
|
|
278
|
+
- \u274C Deep diving one endpoint before discovering others
|
|
279
|
+
- \u274C Brute force without any intelligence
|
|
280
|
+
- \u274C Repeating failed commands with minor changes
|
|
281
|
+
- \u274C Ignoring discovered information
|
|
282
|
+
- \u274C Skipping subdomain/directory enumeration
|
|
283
|
+
|
|
284
|
+
## SUCCESS PATTERNS TO FOLLOW
|
|
285
|
+
|
|
286
|
+
- \u2705 Port scan \u2192 Web discovery (subdomain + directory) \u2192 Technology detection \u2192 CVE check
|
|
287
|
+
- \u2705 Find all endpoints first, then prioritize by value
|
|
288
|
+
- \u2705 Use discovered info (usernames, versions) in subsequent attacks
|
|
289
|
+
- \u2705 Pivot quickly when stuck (max 3 attempts per approach)
|
|
290
|
+
- \u2705 Document findings as we go`;
|
|
291
|
+
var AGENT_COLLABORATION_PROMPT = `When collaborating with other specialized agents:
|
|
292
|
+
|
|
293
|
+
## AGENT ROLES
|
|
294
|
+
- **Recon Agent**: Surface discovery, OSINT, subdomain/directory enumeration
|
|
295
|
+
- **Exploit Agent**: Vulnerability exploitation, payload delivery
|
|
296
|
+
- **PrivEsc Agent**: Privilege escalation on compromised hosts
|
|
297
|
+
- **Web Agent**: Web application testing, injection attacks
|
|
298
|
+
- **Crypto Agent**: Cryptographic analysis, hash cracking
|
|
299
|
+
|
|
300
|
+
## COLLABORATION PROTOCOL
|
|
301
|
+
|
|
302
|
+
### 1. TASK DELEGATION
|
|
303
|
+
\`\`\`
|
|
304
|
+
[DELEGATE]
|
|
305
|
+
From: [current agent]
|
|
306
|
+
To: [specialized agent]
|
|
307
|
+
Task: [specific task]
|
|
308
|
+
Expected output: [what we need back]
|
|
309
|
+
\`\`\`
|
|
310
|
+
|
|
311
|
+
### 2. RESULTS HANDOFF
|
|
312
|
+
\`\`\`
|
|
313
|
+
[HANDOFF]
|
|
314
|
+
Agent: [which agent completed]
|
|
315
|
+
Findings: [key discoveries]
|
|
316
|
+
Recommended next: [what should happen next]
|
|
317
|
+
\`\`\`
|
|
318
|
+
|
|
319
|
+
### 3. CONFLICT RESOLUTION
|
|
320
|
+
When agents disagree on approach:
|
|
321
|
+
- Consider ROI of each approach
|
|
322
|
+
- Check if one requires prerequisites the other provides
|
|
323
|
+
- Default to BFS (broader coverage) over DFS (deep focus)
|
|
324
|
+
- Escalate to user only if truly ambiguous
|
|
325
|
+
|
|
326
|
+
### 4. KNOWLEDGE SHARING
|
|
327
|
+
All agents maintain shared context:
|
|
328
|
+
- Discovered hosts and services
|
|
329
|
+
- Obtained credentials
|
|
330
|
+
- Failed approaches (don't repeat)
|
|
331
|
+
- Current attack surface map`;
|
|
223
332
|
|
|
224
333
|
// src/core/tools/tool-definitions.ts
|
|
225
334
|
var SYSTEM_TOOLS = [
|
|
@@ -4150,6 +4259,868 @@ function buildAgentSystemPrompt(basePrompt, agent) {
|
|
|
4150
4259
|
${agent.systemPrompt}`;
|
|
4151
4260
|
}
|
|
4152
4261
|
|
|
4262
|
+
// src/core/agent/agent-orchestrator.ts
|
|
4263
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
4264
|
+
import { EventEmitter as EventEmitter4 } from "events";
|
|
4265
|
+
var ORCHESTRATOR_EVENT = {
|
|
4266
|
+
AGENT_START: "agent_start",
|
|
4267
|
+
AGENT_COMPLETE: "agent_complete",
|
|
4268
|
+
AGENT_ERROR: "agent_error",
|
|
4269
|
+
ALL_COMPLETE: "all_complete",
|
|
4270
|
+
FINDING: "finding"
|
|
4271
|
+
};
|
|
4272
|
+
var AgentOrchestrator = class extends EventEmitter4 {
|
|
4273
|
+
client;
|
|
4274
|
+
agents = /* @__PURE__ */ new Map();
|
|
4275
|
+
initialized = false;
|
|
4276
|
+
constructor(apiKey) {
|
|
4277
|
+
super();
|
|
4278
|
+
this.client = new Anthropic({
|
|
4279
|
+
apiKey: apiKey || LLM_API_KEY || process.env.PENTEST_API_KEY,
|
|
4280
|
+
baseURL: LLM_BASE_URL
|
|
4281
|
+
});
|
|
4282
|
+
}
|
|
4283
|
+
/**
|
|
4284
|
+
* Initialize with built-in agents
|
|
4285
|
+
*/
|
|
4286
|
+
async initialize() {
|
|
4287
|
+
if (this.initialized) return;
|
|
4288
|
+
for (const agent of BUILTIN_AGENTS) {
|
|
4289
|
+
this.agents.set(agent.name, agent);
|
|
4290
|
+
}
|
|
4291
|
+
this.initialized = true;
|
|
4292
|
+
}
|
|
4293
|
+
/**
|
|
4294
|
+
* Launch multiple agents in parallel
|
|
4295
|
+
*/
|
|
4296
|
+
async launchParallel(tasks) {
|
|
4297
|
+
await this.initialize();
|
|
4298
|
+
console.log(`\u{1F680} Launching ${tasks.length} agents in parallel...`);
|
|
4299
|
+
const startTime = Date.now();
|
|
4300
|
+
const promises = tasks.map((task) => this.executeAgent(task));
|
|
4301
|
+
const results = await Promise.allSettled(promises);
|
|
4302
|
+
const duration = Date.now() - startTime;
|
|
4303
|
+
console.log(`\u2705 All agents completed in ${(duration / 1e3).toFixed(1)}s`);
|
|
4304
|
+
const finalResults = results.map((result, index) => {
|
|
4305
|
+
if (result.status === "fulfilled") {
|
|
4306
|
+
return result.value;
|
|
4307
|
+
} else {
|
|
4308
|
+
return {
|
|
4309
|
+
agent: tasks[index].agent,
|
|
4310
|
+
success: false,
|
|
4311
|
+
output: "",
|
|
4312
|
+
findings: [],
|
|
4313
|
+
duration: 0,
|
|
4314
|
+
error: result.reason?.message || "Agent failed"
|
|
4315
|
+
};
|
|
4316
|
+
}
|
|
4317
|
+
});
|
|
4318
|
+
this.emit(ORCHESTRATOR_EVENT.ALL_COMPLETE, {
|
|
4319
|
+
results: finalResults,
|
|
4320
|
+
duration
|
|
4321
|
+
});
|
|
4322
|
+
return finalResults;
|
|
4323
|
+
}
|
|
4324
|
+
/**
|
|
4325
|
+
* Execute a single agent
|
|
4326
|
+
*/
|
|
4327
|
+
async executeAgent(task) {
|
|
4328
|
+
const startTime = Date.now();
|
|
4329
|
+
this.emit(ORCHESTRATOR_EVENT.AGENT_START, { agent: task.agent, prompt: task.prompt });
|
|
4330
|
+
try {
|
|
4331
|
+
const agentDef = this.agents.get(task.agent);
|
|
4332
|
+
const systemPrompt = agentDef ? buildAgentSystemPrompt("You are a security expert. Report findings clearly.", agentDef) : `You are a security expert focused on ${task.agent}. Be thorough and report findings in JSON format.`;
|
|
4333
|
+
const response = await withRetry(
|
|
4334
|
+
() => this.client.messages.create({
|
|
4335
|
+
model: LLM_MODEL,
|
|
4336
|
+
max_tokens: LLM_MAX_TOKENS,
|
|
4337
|
+
system: systemPrompt,
|
|
4338
|
+
messages: [
|
|
4339
|
+
{ role: "user", content: task.prompt }
|
|
4340
|
+
]
|
|
4341
|
+
}),
|
|
4342
|
+
{ maxRetries: 2 }
|
|
4343
|
+
);
|
|
4344
|
+
const output = response.content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
|
|
4345
|
+
const findings = this.parseFindings(output, task.agent);
|
|
4346
|
+
const duration = Date.now() - startTime;
|
|
4347
|
+
const result = {
|
|
4348
|
+
agent: task.agent,
|
|
4349
|
+
success: true,
|
|
4350
|
+
output,
|
|
4351
|
+
findings,
|
|
4352
|
+
duration
|
|
4353
|
+
};
|
|
4354
|
+
this.emit(ORCHESTRATOR_EVENT.AGENT_COMPLETE, result);
|
|
4355
|
+
for (const finding of findings) {
|
|
4356
|
+
this.emit(ORCHESTRATOR_EVENT.FINDING, finding);
|
|
4357
|
+
}
|
|
4358
|
+
return result;
|
|
4359
|
+
} catch (error) {
|
|
4360
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4361
|
+
this.emit(ORCHESTRATOR_EVENT.AGENT_ERROR, { agent: task.agent, error: errorMsg });
|
|
4362
|
+
return {
|
|
4363
|
+
agent: task.agent,
|
|
4364
|
+
success: false,
|
|
4365
|
+
output: "",
|
|
4366
|
+
findings: [],
|
|
4367
|
+
duration: Date.now() - startTime,
|
|
4368
|
+
error: errorMsg
|
|
4369
|
+
};
|
|
4370
|
+
}
|
|
4371
|
+
}
|
|
4372
|
+
/**
|
|
4373
|
+
* Parse findings from agent output
|
|
4374
|
+
*/
|
|
4375
|
+
parseFindings(output, agentName) {
|
|
4376
|
+
const findings = [];
|
|
4377
|
+
const jsonMatch = output.match(/```json\n?([\s\S]*?)\n?```/);
|
|
4378
|
+
if (jsonMatch) {
|
|
4379
|
+
try {
|
|
4380
|
+
const parsed = JSON.parse(jsonMatch[1]);
|
|
4381
|
+
if (Array.isArray(parsed)) {
|
|
4382
|
+
return parsed.map((f) => this.normalizeFinding(f, agentName));
|
|
4383
|
+
} else if (parsed.findings) {
|
|
4384
|
+
return parsed.findings.map((f) => this.normalizeFinding(f, agentName));
|
|
4385
|
+
}
|
|
4386
|
+
} catch {
|
|
4387
|
+
}
|
|
4388
|
+
}
|
|
4389
|
+
const patterns = [
|
|
4390
|
+
{ regex: /CVE-\d{4}-\d+/gi, type: "vulnerability", severity: "high" },
|
|
4391
|
+
{ regex: /open port[s]?[:\s]+(\d+)/gi, type: "info", severity: "info" },
|
|
4392
|
+
{ regex: /admin|root|password/gi, type: "credential", severity: "high" },
|
|
4393
|
+
{ regex: /shell access|RCE|command injection/gi, type: "vulnerability", severity: "critical" },
|
|
4394
|
+
{ regex: /SQL injection|XSS|SSRF/gi, type: "vulnerability", severity: "high" }
|
|
4395
|
+
];
|
|
4396
|
+
for (const pattern of patterns) {
|
|
4397
|
+
const matches = output.match(pattern.regex);
|
|
4398
|
+
if (matches) {
|
|
4399
|
+
for (const match of [...new Set(matches)]) {
|
|
4400
|
+
findings.push({
|
|
4401
|
+
id: `finding_${Date.now()}_${Math.random().toString(36).substring(2, 6)}`,
|
|
4402
|
+
type: pattern.type,
|
|
4403
|
+
title: match,
|
|
4404
|
+
description: `Found by ${agentName}: ${match}`,
|
|
4405
|
+
confidence: 60,
|
|
4406
|
+
severity: pattern.severity,
|
|
4407
|
+
evidence: [match],
|
|
4408
|
+
exploitability: "possible"
|
|
4409
|
+
});
|
|
4410
|
+
}
|
|
4411
|
+
}
|
|
4412
|
+
}
|
|
4413
|
+
return findings;
|
|
4414
|
+
}
|
|
4415
|
+
/**
|
|
4416
|
+
* Normalize a finding object
|
|
4417
|
+
*/
|
|
4418
|
+
normalizeFinding(raw, agentName) {
|
|
4419
|
+
const f = raw;
|
|
4420
|
+
return {
|
|
4421
|
+
id: f.id || `finding_${Date.now()}_${Math.random().toString(36).substring(2, 6)}`,
|
|
4422
|
+
type: f.type || "info",
|
|
4423
|
+
title: f.title || "Unknown finding",
|
|
4424
|
+
description: f.description || `Found by ${agentName}`,
|
|
4425
|
+
confidence: f.confidence || 50,
|
|
4426
|
+
severity: f.severity || "medium",
|
|
4427
|
+
evidence: f.evidence || [],
|
|
4428
|
+
exploitability: f.exploitability || "possible",
|
|
4429
|
+
nextSteps: f.nextSteps
|
|
4430
|
+
};
|
|
4431
|
+
}
|
|
4432
|
+
/**
|
|
4433
|
+
* Get available agent names
|
|
4434
|
+
*/
|
|
4435
|
+
getAgentNames() {
|
|
4436
|
+
return Array.from(this.agents.keys());
|
|
4437
|
+
}
|
|
4438
|
+
};
|
|
4439
|
+
function consolidateFindings(results) {
|
|
4440
|
+
const allFindings = [];
|
|
4441
|
+
for (const result of results) {
|
|
4442
|
+
allFindings.push(...result.findings);
|
|
4443
|
+
}
|
|
4444
|
+
const findingMap = /* @__PURE__ */ new Map();
|
|
4445
|
+
for (const finding of allFindings) {
|
|
4446
|
+
const key = `${finding.type}:${finding.title}`;
|
|
4447
|
+
if (findingMap.has(key)) {
|
|
4448
|
+
const existing = findingMap.get(key);
|
|
4449
|
+
existing.confidence = Math.min(100, existing.confidence + 10);
|
|
4450
|
+
existing.evidence.push(...finding.evidence);
|
|
4451
|
+
} else {
|
|
4452
|
+
findingMap.set(key, { ...finding });
|
|
4453
|
+
}
|
|
4454
|
+
}
|
|
4455
|
+
return Array.from(findingMap.values()).sort((a, b) => b.confidence - a.confidence);
|
|
4456
|
+
}
|
|
4457
|
+
var orchestratorInstance = null;
|
|
4458
|
+
function getOrchestrator(apiKey) {
|
|
4459
|
+
if (!orchestratorInstance) {
|
|
4460
|
+
orchestratorInstance = new AgentOrchestrator(apiKey);
|
|
4461
|
+
}
|
|
4462
|
+
return orchestratorInstance;
|
|
4463
|
+
}
|
|
4464
|
+
|
|
4465
|
+
// src/core/agent/agent-memory.ts
|
|
4466
|
+
import { EventEmitter as EventEmitter5 } from "events";
|
|
4467
|
+
var MEMORY_EVENT = {
|
|
4468
|
+
ENTRY_ADDED: "entry_added",
|
|
4469
|
+
STATE_UPDATED: "state_updated",
|
|
4470
|
+
HANDOFF: "handoff",
|
|
4471
|
+
COMPACTION: "compaction"
|
|
4472
|
+
};
|
|
4473
|
+
var AgentMemory = class _AgentMemory extends EventEmitter5 {
|
|
4474
|
+
// Short-term memory (current session)
|
|
4475
|
+
shortTermMemory = [];
|
|
4476
|
+
// Long-term memory (persisted across sessions)
|
|
4477
|
+
longTermMemory = [];
|
|
4478
|
+
// Episodic memory (specific events/interactions)
|
|
4479
|
+
episodicMemory = /* @__PURE__ */ new Map();
|
|
4480
|
+
// Shared state across all agents
|
|
4481
|
+
sharedState;
|
|
4482
|
+
// Agent contexts
|
|
4483
|
+
agentContexts = /* @__PURE__ */ new Map();
|
|
4484
|
+
// Memory limits
|
|
4485
|
+
SHORT_TERM_LIMIT = 100;
|
|
4486
|
+
LONG_TERM_LIMIT = 1e3;
|
|
4487
|
+
COMPACTION_THRESHOLD = 80;
|
|
4488
|
+
// % of limit
|
|
4489
|
+
constructor(target = "") {
|
|
4490
|
+
super();
|
|
4491
|
+
this.sharedState = this.initializeState(target);
|
|
4492
|
+
}
|
|
4493
|
+
// ===== State Management =====
|
|
4494
|
+
initializeState(target) {
|
|
4495
|
+
return {
|
|
4496
|
+
target,
|
|
4497
|
+
discoveredHosts: [],
|
|
4498
|
+
discoveredServices: /* @__PURE__ */ new Map(),
|
|
4499
|
+
credentials: [],
|
|
4500
|
+
currentPhase: "recon",
|
|
4501
|
+
completedPhases: [],
|
|
4502
|
+
attackSurface: /* @__PURE__ */ new Map(),
|
|
4503
|
+
vulnerabilities: [],
|
|
4504
|
+
failedApproaches: []
|
|
4505
|
+
};
|
|
4506
|
+
}
|
|
4507
|
+
getSharedState() {
|
|
4508
|
+
return this.sharedState;
|
|
4509
|
+
}
|
|
4510
|
+
updateState(updates) {
|
|
4511
|
+
this.sharedState = { ...this.sharedState, ...updates };
|
|
4512
|
+
this.emit(MEMORY_EVENT.STATE_UPDATED, this.sharedState);
|
|
4513
|
+
}
|
|
4514
|
+
// ===== Memory Operations =====
|
|
4515
|
+
addMemory(entry) {
|
|
4516
|
+
const fullEntry = {
|
|
4517
|
+
...entry,
|
|
4518
|
+
id: `mem_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`,
|
|
4519
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
4520
|
+
};
|
|
4521
|
+
this.shortTermMemory.push(fullEntry);
|
|
4522
|
+
if (entry.importance >= 70) {
|
|
4523
|
+
this.longTermMemory.push(fullEntry);
|
|
4524
|
+
}
|
|
4525
|
+
if (!this.episodicMemory.has(entry.agent)) {
|
|
4526
|
+
this.episodicMemory.set(entry.agent, []);
|
|
4527
|
+
}
|
|
4528
|
+
this.episodicMemory.get(entry.agent).push(fullEntry);
|
|
4529
|
+
this.emit(MEMORY_EVENT.ENTRY_ADDED, fullEntry);
|
|
4530
|
+
this.checkCompaction();
|
|
4531
|
+
return fullEntry;
|
|
4532
|
+
}
|
|
4533
|
+
// ===== Memory Retrieval =====
|
|
4534
|
+
getRecentMemories(count = 10) {
|
|
4535
|
+
return this.shortTermMemory.slice(-count);
|
|
4536
|
+
}
|
|
4537
|
+
getMemoriesByAgent(agentName) {
|
|
4538
|
+
return this.episodicMemory.get(agentName) || [];
|
|
4539
|
+
}
|
|
4540
|
+
getMemoriesByType(type) {
|
|
4541
|
+
return [...this.shortTermMemory, ...this.longTermMemory].filter((m) => m.type === type);
|
|
4542
|
+
}
|
|
4543
|
+
getImportantMemories(threshold = 70) {
|
|
4544
|
+
return this.longTermMemory.filter((m) => m.importance >= threshold);
|
|
4545
|
+
}
|
|
4546
|
+
searchMemories(query) {
|
|
4547
|
+
const lowerQuery = query.toLowerCase();
|
|
4548
|
+
return [...this.shortTermMemory, ...this.longTermMemory].filter((m) => m.content.toLowerCase().includes(lowerQuery)).sort((a, b) => b.importance - a.importance);
|
|
4549
|
+
}
|
|
4550
|
+
// ===== Agent Context Management =====
|
|
4551
|
+
setAgentContext(context) {
|
|
4552
|
+
this.agentContexts.set(context.agentName, context);
|
|
4553
|
+
}
|
|
4554
|
+
getAgentContext(agentName) {
|
|
4555
|
+
return this.agentContexts.get(agentName);
|
|
4556
|
+
}
|
|
4557
|
+
getAllAgentContexts() {
|
|
4558
|
+
return Array.from(this.agentContexts.values());
|
|
4559
|
+
}
|
|
4560
|
+
// ===== Agent Handoff =====
|
|
4561
|
+
/**
|
|
4562
|
+
* Create a handoff package for transferring context between agents
|
|
4563
|
+
*/
|
|
4564
|
+
createHandoff(fromAgent, toAgent, task, relevantContext) {
|
|
4565
|
+
const handoffId = `handoff_${Date.now()}`;
|
|
4566
|
+
const relevantMemories = this.shortTermMemory.filter(
|
|
4567
|
+
(m) => m.agent === fromAgent || relevantContext.some((c) => m.content.includes(c))
|
|
4568
|
+
).slice(-20);
|
|
4569
|
+
const contextSummary = this.createContextSummary(fromAgent, task);
|
|
4570
|
+
this.addMemory({
|
|
4571
|
+
type: "handoff",
|
|
4572
|
+
agent: "orchestrator",
|
|
4573
|
+
content: `Handoff: ${fromAgent} \u2192 ${toAgent} for task: ${task}`,
|
|
4574
|
+
importance: 80,
|
|
4575
|
+
metadata: { fromAgent, toAgent, task, handoffId }
|
|
4576
|
+
});
|
|
4577
|
+
this.emit(MEMORY_EVENT.HANDOFF, { fromAgent, toAgent, task, handoffId });
|
|
4578
|
+
return {
|
|
4579
|
+
handoffId,
|
|
4580
|
+
context: contextSummary,
|
|
4581
|
+
sharedState: this.sharedState,
|
|
4582
|
+
relevantMemories
|
|
4583
|
+
};
|
|
4584
|
+
}
|
|
4585
|
+
/**
|
|
4586
|
+
* Create a summarized context for handoff
|
|
4587
|
+
*/
|
|
4588
|
+
createContextSummary(agentName, task) {
|
|
4589
|
+
const agentMemories = this.getMemoriesByAgent(agentName).slice(-10);
|
|
4590
|
+
const findings = this.getMemoriesByType("finding").slice(-5);
|
|
4591
|
+
const errors = this.getMemoriesByType("error").slice(-3);
|
|
4592
|
+
return `
|
|
4593
|
+
## Context Summary for: ${task}
|
|
4594
|
+
|
|
4595
|
+
### Target
|
|
4596
|
+
- Primary: ${this.sharedState.target}
|
|
4597
|
+
- Phase: ${this.sharedState.currentPhase}
|
|
4598
|
+
- Hosts discovered: ${this.sharedState.discoveredHosts.length}
|
|
4599
|
+
|
|
4600
|
+
### Recent Actions by ${agentName}
|
|
4601
|
+
${agentMemories.map((m) => `- ${m.content.slice(0, 100)}`).join("\n")}
|
|
4602
|
+
|
|
4603
|
+
### Key Findings
|
|
4604
|
+
${findings.map((f) => `- [${f.agent}] ${f.content.slice(0, 100)}`).join("\n")}
|
|
4605
|
+
|
|
4606
|
+
### Failed Approaches (avoid these)
|
|
4607
|
+
${this.sharedState.failedApproaches.slice(-5).map((f) => `- ${f.approach}: ${f.reason}`).join("\n")}
|
|
4608
|
+
|
|
4609
|
+
### Credentials Found
|
|
4610
|
+
${this.sharedState.credentials.map((c) => `- ${c.username}:${c.password} (${c.source})`).join("\n") || "None"}
|
|
4611
|
+
`;
|
|
4612
|
+
}
|
|
4613
|
+
// ===== Attack Surface Tracking =====
|
|
4614
|
+
addToAttackSurface(category, items) {
|
|
4615
|
+
const existing = this.sharedState.attackSurface.get(category) || [];
|
|
4616
|
+
const newItems = items.filter((i) => !existing.includes(i));
|
|
4617
|
+
this.sharedState.attackSurface.set(category, [...existing, ...newItems]);
|
|
4618
|
+
if (newItems.length > 0) {
|
|
4619
|
+
this.addMemory({
|
|
4620
|
+
type: "discovery",
|
|
4621
|
+
agent: "orchestrator",
|
|
4622
|
+
content: `Attack surface expanded [${category}]: ${newItems.join(", ")}`,
|
|
4623
|
+
importance: 60
|
|
4624
|
+
});
|
|
4625
|
+
}
|
|
4626
|
+
}
|
|
4627
|
+
getAttackSurface() {
|
|
4628
|
+
return this.sharedState.attackSurface;
|
|
4629
|
+
}
|
|
4630
|
+
getAttackSurfaceSummary() {
|
|
4631
|
+
const summary = [];
|
|
4632
|
+
for (const [category, items] of this.sharedState.attackSurface) {
|
|
4633
|
+
summary.push(`${category}: ${items.length} items`);
|
|
4634
|
+
}
|
|
4635
|
+
return summary.join(", ");
|
|
4636
|
+
}
|
|
4637
|
+
// ===== Failed Approach Tracking =====
|
|
4638
|
+
recordFailedApproach(approach, reason) {
|
|
4639
|
+
this.sharedState.failedApproaches.push({
|
|
4640
|
+
approach,
|
|
4641
|
+
reason,
|
|
4642
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
4643
|
+
});
|
|
4644
|
+
}
|
|
4645
|
+
hasFailedBefore(approach) {
|
|
4646
|
+
return this.sharedState.failedApproaches.some(
|
|
4647
|
+
(f) => f.approach.toLowerCase().includes(approach.toLowerCase())
|
|
4648
|
+
);
|
|
4649
|
+
}
|
|
4650
|
+
// ===== Memory Compaction =====
|
|
4651
|
+
checkCompaction() {
|
|
4652
|
+
if (this.shortTermMemory.length >= this.SHORT_TERM_LIMIT * (this.COMPACTION_THRESHOLD / 100)) {
|
|
4653
|
+
this.compactShortTermMemory();
|
|
4654
|
+
}
|
|
4655
|
+
if (this.longTermMemory.length >= this.LONG_TERM_LIMIT * (this.COMPACTION_THRESHOLD / 100)) {
|
|
4656
|
+
this.compactLongTermMemory();
|
|
4657
|
+
}
|
|
4658
|
+
}
|
|
4659
|
+
compactShortTermMemory() {
|
|
4660
|
+
const sorted = [...this.shortTermMemory].sort((a, b) => {
|
|
4661
|
+
const importanceDiff = b.importance - a.importance;
|
|
4662
|
+
if (importanceDiff !== 0) return importanceDiff;
|
|
4663
|
+
return b.timestamp.getTime() - a.timestamp.getTime();
|
|
4664
|
+
});
|
|
4665
|
+
this.shortTermMemory = sorted.slice(0, Math.floor(this.SHORT_TERM_LIMIT * 0.5));
|
|
4666
|
+
this.emit(MEMORY_EVENT.COMPACTION, { type: "short-term", remaining: this.shortTermMemory.length });
|
|
4667
|
+
}
|
|
4668
|
+
compactLongTermMemory() {
|
|
4669
|
+
this.longTermMemory = this.longTermMemory.sort((a, b) => b.importance - a.importance).slice(0, Math.floor(this.LONG_TERM_LIMIT * 0.7));
|
|
4670
|
+
this.emit(MEMORY_EVENT.COMPACTION, { type: "long-term", remaining: this.longTermMemory.length });
|
|
4671
|
+
}
|
|
4672
|
+
// ===== Serialization =====
|
|
4673
|
+
toJSON() {
|
|
4674
|
+
return {
|
|
4675
|
+
shortTermMemory: this.shortTermMemory,
|
|
4676
|
+
longTermMemory: this.longTermMemory,
|
|
4677
|
+
sharedState: {
|
|
4678
|
+
...this.sharedState,
|
|
4679
|
+
discoveredServices: Object.fromEntries(this.sharedState.discoveredServices),
|
|
4680
|
+
attackSurface: Object.fromEntries(this.sharedState.attackSurface)
|
|
4681
|
+
},
|
|
4682
|
+
agentContexts: Object.fromEntries(this.agentContexts)
|
|
4683
|
+
};
|
|
4684
|
+
}
|
|
4685
|
+
static fromJSON(data) {
|
|
4686
|
+
const memory = new _AgentMemory();
|
|
4687
|
+
const parsed = data;
|
|
4688
|
+
memory.shortTermMemory = parsed.shortTermMemory || [];
|
|
4689
|
+
memory.longTermMemory = parsed.longTermMemory || [];
|
|
4690
|
+
if (parsed.sharedState) {
|
|
4691
|
+
memory.sharedState = {
|
|
4692
|
+
...parsed.sharedState,
|
|
4693
|
+
discoveredServices: new Map(Object.entries(parsed.sharedState.discoveredServices || {})),
|
|
4694
|
+
attackSurface: new Map(Object.entries(parsed.sharedState.attackSurface || {}))
|
|
4695
|
+
};
|
|
4696
|
+
}
|
|
4697
|
+
if (parsed.agentContexts) {
|
|
4698
|
+
memory.agentContexts = new Map(Object.entries(parsed.agentContexts));
|
|
4699
|
+
}
|
|
4700
|
+
return memory;
|
|
4701
|
+
}
|
|
4702
|
+
};
|
|
4703
|
+
var memoryInstance = null;
|
|
4704
|
+
function getAgentMemory(target) {
|
|
4705
|
+
if (!memoryInstance) {
|
|
4706
|
+
memoryInstance = new AgentMemory(target);
|
|
4707
|
+
}
|
|
4708
|
+
return memoryInstance;
|
|
4709
|
+
}
|
|
4710
|
+
|
|
4711
|
+
// src/core/agent/supervisor-agent.ts
|
|
4712
|
+
import Anthropic2 from "@anthropic-ai/sdk";
|
|
4713
|
+
import { EventEmitter as EventEmitter6 } from "events";
|
|
4714
|
+
var SUPERVISOR_EVENT = {
|
|
4715
|
+
PLAN_CREATED: "plan_created",
|
|
4716
|
+
PHASE_STARTED: "phase_started",
|
|
4717
|
+
PHASE_COMPLETED: "phase_completed",
|
|
4718
|
+
TASK_DELEGATED: "task_delegated",
|
|
4719
|
+
TASK_COMPLETED: "task_completed",
|
|
4720
|
+
STRATEGY_ADJUSTED: "strategy_adjusted",
|
|
4721
|
+
DECISION_MADE: "decision_made"
|
|
4722
|
+
};
|
|
4723
|
+
var SupervisorAgent = class extends EventEmitter6 {
|
|
4724
|
+
client;
|
|
4725
|
+
orchestrator;
|
|
4726
|
+
memory;
|
|
4727
|
+
currentPlan = null;
|
|
4728
|
+
// Agent capability mapping
|
|
4729
|
+
agentCapabilities = /* @__PURE__ */ new Map([
|
|
4730
|
+
["target-explorer", ["recon", "scanning", "enumeration", "osint", "subdomain", "directory"]],
|
|
4731
|
+
["exploit-researcher", ["cve", "exploit", "vulnerability", "payload", "metasploit"]],
|
|
4732
|
+
["privesc-master", ["privilege", "escalation", "root", "system", "linux", "windows"]],
|
|
4733
|
+
["web-hacker", ["web", "sql", "xss", "injection", "http", "api", "form"]],
|
|
4734
|
+
["crypto-solver", ["crypto", "hash", "password", "decrypt", "encode"]],
|
|
4735
|
+
["forensics-analyst", ["forensics", "memory", "file", "log", "steganography"]],
|
|
4736
|
+
["reverse-engineer", ["binary", "reverse", "debug", "exploit", "buffer"]],
|
|
4737
|
+
["attack-architect", ["strategy", "plan", "attack", "chain", "prioritize"]],
|
|
4738
|
+
["finding-reviewer", ["validate", "verify", "review", "confidence", "false positive"]]
|
|
4739
|
+
]);
|
|
4740
|
+
constructor(apiKey) {
|
|
4741
|
+
super();
|
|
4742
|
+
this.client = new Anthropic2({
|
|
4743
|
+
apiKey: apiKey || LLM_API_KEY || process.env.PENTEST_API_KEY,
|
|
4744
|
+
baseURL: LLM_BASE_URL
|
|
4745
|
+
});
|
|
4746
|
+
this.orchestrator = getOrchestrator(apiKey);
|
|
4747
|
+
this.memory = getAgentMemory();
|
|
4748
|
+
}
|
|
4749
|
+
// ===== Task Planning =====
|
|
4750
|
+
/**
|
|
4751
|
+
* Create an execution plan for the given objective
|
|
4752
|
+
*/
|
|
4753
|
+
async createPlan(objective, target) {
|
|
4754
|
+
const planPrompt = `
|
|
4755
|
+
${STRATEGY_PLANNING_PROMPT}
|
|
4756
|
+
|
|
4757
|
+
Create an execution plan for:
|
|
4758
|
+
Objective: ${objective}
|
|
4759
|
+
Target: ${target}
|
|
4760
|
+
|
|
4761
|
+
Respond with a JSON plan:
|
|
4762
|
+
{
|
|
4763
|
+
"phases": [
|
|
4764
|
+
{
|
|
4765
|
+
"name": "Phase name",
|
|
4766
|
+
"description": "What this phase accomplishes",
|
|
4767
|
+
"agents": ["agent-name"],
|
|
4768
|
+
"tasks": [
|
|
4769
|
+
{
|
|
4770
|
+
"description": "Task description",
|
|
4771
|
+
"assignedAgent": "agent-name"
|
|
4772
|
+
}
|
|
4773
|
+
]
|
|
4774
|
+
}
|
|
4775
|
+
]
|
|
4776
|
+
}
|
|
4777
|
+
|
|
4778
|
+
Rules:
|
|
4779
|
+
1. Start with reconnaissance (BFS - map attack surface first)
|
|
4780
|
+
2. Discovery before exploitation
|
|
4781
|
+
3. Use specialized agents for their strengths
|
|
4782
|
+
4. Include validation steps
|
|
4783
|
+
5. Plan for fallbacks
|
|
4784
|
+
`;
|
|
4785
|
+
try {
|
|
4786
|
+
const response = await this.client.messages.create({
|
|
4787
|
+
model: LLM_MODEL,
|
|
4788
|
+
max_tokens: 2048,
|
|
4789
|
+
messages: [{ role: "user", content: planPrompt }]
|
|
4790
|
+
});
|
|
4791
|
+
const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
4792
|
+
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
4793
|
+
if (jsonMatch) {
|
|
4794
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
4795
|
+
this.currentPlan = this.buildPlan(objective, parsed);
|
|
4796
|
+
} else {
|
|
4797
|
+
this.currentPlan = this.createDefaultPlan(objective, target);
|
|
4798
|
+
}
|
|
4799
|
+
} catch {
|
|
4800
|
+
this.currentPlan = this.createDefaultPlan(objective, target);
|
|
4801
|
+
}
|
|
4802
|
+
this.emit(SUPERVISOR_EVENT.PLAN_CREATED, this.currentPlan);
|
|
4803
|
+
this.memory.addMemory({
|
|
4804
|
+
type: "decision",
|
|
4805
|
+
agent: "supervisor",
|
|
4806
|
+
content: `Plan created: ${this.currentPlan.phases.length} phases for ${objective}`,
|
|
4807
|
+
importance: 90
|
|
4808
|
+
});
|
|
4809
|
+
return this.currentPlan;
|
|
4810
|
+
}
|
|
4811
|
+
buildPlan(objective, parsed) {
|
|
4812
|
+
return {
|
|
4813
|
+
id: `plan_${Date.now()}`,
|
|
4814
|
+
objective,
|
|
4815
|
+
status: "planning",
|
|
4816
|
+
currentPhaseIndex: 0,
|
|
4817
|
+
phases: parsed.phases.map((phase, idx) => ({
|
|
4818
|
+
id: `phase_${idx}`,
|
|
4819
|
+
name: phase.name,
|
|
4820
|
+
description: phase.description,
|
|
4821
|
+
agents: phase.agents || [],
|
|
4822
|
+
status: "pending",
|
|
4823
|
+
findings: [],
|
|
4824
|
+
tasks: (phase.tasks || []).map((task, tidx) => ({
|
|
4825
|
+
id: `task_${idx}_${tidx}`,
|
|
4826
|
+
description: task.description,
|
|
4827
|
+
assignedAgent: task.assignedAgent || this.selectBestAgent(task.description),
|
|
4828
|
+
status: "pending"
|
|
4829
|
+
}))
|
|
4830
|
+
}))
|
|
4831
|
+
};
|
|
4832
|
+
}
|
|
4833
|
+
createDefaultPlan(objective, target) {
|
|
4834
|
+
return {
|
|
4835
|
+
id: `plan_${Date.now()}`,
|
|
4836
|
+
objective,
|
|
4837
|
+
status: "planning",
|
|
4838
|
+
currentPhaseIndex: 0,
|
|
4839
|
+
phases: [
|
|
4840
|
+
{
|
|
4841
|
+
id: "phase_0",
|
|
4842
|
+
name: "Reconnaissance",
|
|
4843
|
+
description: "Map attack surface - ports, subdomains, directories",
|
|
4844
|
+
agents: ["target-explorer"],
|
|
4845
|
+
status: "pending",
|
|
4846
|
+
findings: [],
|
|
4847
|
+
tasks: [
|
|
4848
|
+
{ id: "task_0_0", description: `Port scan ${target}`, assignedAgent: "target-explorer", status: "pending" },
|
|
4849
|
+
{ id: "task_0_1", description: `Subdomain enumeration for ${target}`, assignedAgent: "target-explorer", status: "pending" },
|
|
4850
|
+
{ id: "task_0_2", description: `Directory bruteforce on web services`, assignedAgent: "web-hacker", status: "pending" }
|
|
4851
|
+
]
|
|
4852
|
+
},
|
|
4853
|
+
{
|
|
4854
|
+
id: "phase_1",
|
|
4855
|
+
name: "Analysis",
|
|
4856
|
+
description: "Analyze findings and identify vulnerabilities",
|
|
4857
|
+
agents: ["attack-architect", "finding-reviewer"],
|
|
4858
|
+
status: "pending",
|
|
4859
|
+
findings: [],
|
|
4860
|
+
tasks: [
|
|
4861
|
+
{ id: "task_1_0", description: "Analyze attack surface and prioritize targets", assignedAgent: "attack-architect", status: "pending" },
|
|
4862
|
+
{ id: "task_1_1", description: "CVE research for discovered services", assignedAgent: "exploit-researcher", status: "pending" }
|
|
4863
|
+
]
|
|
4864
|
+
},
|
|
4865
|
+
{
|
|
4866
|
+
id: "phase_2",
|
|
4867
|
+
name: "Exploitation",
|
|
4868
|
+
description: "Attempt exploitation of identified vulnerabilities",
|
|
4869
|
+
agents: ["exploit-researcher", "web-hacker"],
|
|
4870
|
+
status: "pending",
|
|
4871
|
+
findings: [],
|
|
4872
|
+
tasks: [
|
|
4873
|
+
{ id: "task_2_0", description: "Exploit high-priority vulnerabilities", assignedAgent: "exploit-researcher", status: "pending" }
|
|
4874
|
+
]
|
|
4875
|
+
}
|
|
4876
|
+
]
|
|
4877
|
+
};
|
|
4878
|
+
}
|
|
4879
|
+
// ===== Agent Selection =====
|
|
4880
|
+
/**
|
|
4881
|
+
* Select the best agent for a given task
|
|
4882
|
+
*/
|
|
4883
|
+
selectBestAgent(taskDescription) {
|
|
4884
|
+
const lowerTask = taskDescription.toLowerCase();
|
|
4885
|
+
let bestAgent = "target-explorer";
|
|
4886
|
+
let bestScore = 0;
|
|
4887
|
+
for (const [agent, capabilities] of this.agentCapabilities) {
|
|
4888
|
+
const score = capabilities.filter((cap) => lowerTask.includes(cap)).length;
|
|
4889
|
+
if (score > bestScore) {
|
|
4890
|
+
bestScore = score;
|
|
4891
|
+
bestAgent = agent;
|
|
4892
|
+
}
|
|
4893
|
+
}
|
|
4894
|
+
return bestAgent;
|
|
4895
|
+
}
|
|
4896
|
+
/**
|
|
4897
|
+
* Get agents for a specific phase
|
|
4898
|
+
*/
|
|
4899
|
+
getAgentsForPhase(phaseName) {
|
|
4900
|
+
const phaseMapping = {
|
|
4901
|
+
"recon": ["target-explorer"],
|
|
4902
|
+
"reconnaissance": ["target-explorer"],
|
|
4903
|
+
"scan": ["target-explorer"],
|
|
4904
|
+
"enum": ["target-explorer", "web-hacker"],
|
|
4905
|
+
"enumeration": ["target-explorer", "web-hacker"],
|
|
4906
|
+
"analysis": ["attack-architect", "finding-reviewer"],
|
|
4907
|
+
"vuln": ["exploit-researcher", "web-hacker"],
|
|
4908
|
+
"vulnerability": ["exploit-researcher", "web-hacker"],
|
|
4909
|
+
"exploit": ["exploit-researcher", "web-hacker"],
|
|
4910
|
+
"exploitation": ["exploit-researcher", "web-hacker"],
|
|
4911
|
+
"privesc": ["privesc-master"],
|
|
4912
|
+
"privilege": ["privesc-master"],
|
|
4913
|
+
"post": ["privesc-master", "forensics-analyst"]
|
|
4914
|
+
};
|
|
4915
|
+
const lowerPhase = phaseName.toLowerCase();
|
|
4916
|
+
for (const [key, agents] of Object.entries(phaseMapping)) {
|
|
4917
|
+
if (lowerPhase.includes(key)) {
|
|
4918
|
+
return agents;
|
|
4919
|
+
}
|
|
4920
|
+
}
|
|
4921
|
+
return ["target-explorer"];
|
|
4922
|
+
}
|
|
4923
|
+
// ===== Task Execution =====
|
|
4924
|
+
/**
|
|
4925
|
+
* Execute the current plan
|
|
4926
|
+
*/
|
|
4927
|
+
async executePlan() {
|
|
4928
|
+
if (!this.currentPlan) {
|
|
4929
|
+
throw new Error("No plan created. Call createPlan() first.");
|
|
4930
|
+
}
|
|
4931
|
+
this.currentPlan.status = "executing";
|
|
4932
|
+
const allFindings = [];
|
|
4933
|
+
for (let i = 0; i < this.currentPlan.phases.length; i++) {
|
|
4934
|
+
this.currentPlan.currentPhaseIndex = i;
|
|
4935
|
+
const phase = this.currentPlan.phases[i];
|
|
4936
|
+
this.emit(SUPERVISOR_EVENT.PHASE_STARTED, phase);
|
|
4937
|
+
phase.status = "in_progress";
|
|
4938
|
+
try {
|
|
4939
|
+
const phaseFindings = await this.executePhase(phase);
|
|
4940
|
+
phase.findings = phaseFindings;
|
|
4941
|
+
allFindings.push(...phaseFindings);
|
|
4942
|
+
phase.status = "completed";
|
|
4943
|
+
this.emit(SUPERVISOR_EVENT.PHASE_COMPLETED, phase);
|
|
4944
|
+
await this.evaluateProgress(phaseFindings);
|
|
4945
|
+
} catch (error) {
|
|
4946
|
+
phase.status = "failed";
|
|
4947
|
+
this.memory.recordFailedApproach(
|
|
4948
|
+
phase.name,
|
|
4949
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
4950
|
+
);
|
|
4951
|
+
}
|
|
4952
|
+
}
|
|
4953
|
+
this.currentPlan.status = "completed";
|
|
4954
|
+
return consolidateFindings(allFindings.map((f) => ({
|
|
4955
|
+
agent: "supervisor",
|
|
4956
|
+
success: true,
|
|
4957
|
+
output: "",
|
|
4958
|
+
findings: [f],
|
|
4959
|
+
duration: 0
|
|
4960
|
+
})));
|
|
4961
|
+
}
|
|
4962
|
+
/**
|
|
4963
|
+
* Execute a single phase
|
|
4964
|
+
*/
|
|
4965
|
+
async executePhase(phase) {
|
|
4966
|
+
const tasks = phase.tasks.map((task) => ({
|
|
4967
|
+
agent: task.assignedAgent,
|
|
4968
|
+
prompt: this.buildTaskPrompt(task, phase),
|
|
4969
|
+
priority: 1
|
|
4970
|
+
}));
|
|
4971
|
+
await this.orchestrator.initialize();
|
|
4972
|
+
const results = await this.orchestrator.launchParallel(tasks);
|
|
4973
|
+
for (let i = 0; i < phase.tasks.length; i++) {
|
|
4974
|
+
const task = phase.tasks[i];
|
|
4975
|
+
const result = results[i];
|
|
4976
|
+
task.status = result.success ? "completed" : "failed";
|
|
4977
|
+
task.result = result.output;
|
|
4978
|
+
task.error = result.error;
|
|
4979
|
+
this.emit(SUPERVISOR_EVENT.TASK_COMPLETED, { task, result });
|
|
4980
|
+
this.memory.addMemory({
|
|
4981
|
+
type: result.success ? "action" : "error",
|
|
4982
|
+
agent: task.assignedAgent,
|
|
4983
|
+
content: result.success ? `Completed: ${task.description}` : `Failed: ${task.description} - ${result.error}`,
|
|
4984
|
+
importance: result.success ? 60 : 70
|
|
4985
|
+
});
|
|
4986
|
+
}
|
|
4987
|
+
return consolidateFindings(results);
|
|
4988
|
+
}
|
|
4989
|
+
buildTaskPrompt(task, phase) {
|
|
4990
|
+
const handoff = this.memory.createHandoff(
|
|
4991
|
+
"supervisor",
|
|
4992
|
+
task.assignedAgent,
|
|
4993
|
+
task.description,
|
|
4994
|
+
[phase.name, task.description]
|
|
4995
|
+
);
|
|
4996
|
+
return `
|
|
4997
|
+
${AGENT_COLLABORATION_PROMPT}
|
|
4998
|
+
|
|
4999
|
+
## Your Task
|
|
5000
|
+
${task.description}
|
|
5001
|
+
|
|
5002
|
+
## Phase Context
|
|
5003
|
+
Phase: ${phase.name}
|
|
5004
|
+
Description: ${phase.description}
|
|
5005
|
+
|
|
5006
|
+
## Shared Context
|
|
5007
|
+
${handoff.context}
|
|
5008
|
+
|
|
5009
|
+
## Instructions
|
|
5010
|
+
1. Complete the task thoroughly
|
|
5011
|
+
2. Report all findings in structured format
|
|
5012
|
+
3. Suggest next steps based on discoveries
|
|
5013
|
+
4. Be efficient - BFS over DFS
|
|
5014
|
+
|
|
5015
|
+
Provide your findings in JSON format when possible.
|
|
5016
|
+
`;
|
|
5017
|
+
}
|
|
5018
|
+
// ===== Strategy Evaluation =====
|
|
5019
|
+
/**
|
|
5020
|
+
* Evaluate progress and adjust strategy if needed
|
|
5021
|
+
*/
|
|
5022
|
+
async evaluateProgress(findings) {
|
|
5023
|
+
const criticalFindings = findings.filter((f) => f.severity === "critical" || f.severity === "high");
|
|
5024
|
+
const failedApproaches = this.memory.getSharedState().failedApproaches;
|
|
5025
|
+
if (criticalFindings.length > 0) {
|
|
5026
|
+
this.memory.addMemory({
|
|
5027
|
+
type: "decision",
|
|
5028
|
+
agent: "supervisor",
|
|
5029
|
+
content: `Strategy adjustment: Found ${criticalFindings.length} critical/high findings. Prioritizing exploitation.`,
|
|
5030
|
+
importance: 85
|
|
5031
|
+
});
|
|
5032
|
+
this.emit(SUPERVISOR_EVENT.STRATEGY_ADJUSTED, {
|
|
5033
|
+
reason: "Critical findings discovered",
|
|
5034
|
+
adjustment: "Prioritize exploitation",
|
|
5035
|
+
findings: criticalFindings
|
|
5036
|
+
});
|
|
5037
|
+
}
|
|
5038
|
+
if (failedApproaches.length >= 3) {
|
|
5039
|
+
this.memory.addMemory({
|
|
5040
|
+
type: "decision",
|
|
5041
|
+
agent: "supervisor",
|
|
5042
|
+
content: `Strategy adjustment: ${failedApproaches.length} failed approaches. Recommending pivot.`,
|
|
5043
|
+
importance: 80
|
|
5044
|
+
});
|
|
5045
|
+
}
|
|
5046
|
+
}
|
|
5047
|
+
// ===== Decision Making =====
|
|
5048
|
+
/**
|
|
5049
|
+
* Make a decision about next action
|
|
5050
|
+
*/
|
|
5051
|
+
async makeDecision(situation) {
|
|
5052
|
+
const decisionPrompt = `
|
|
5053
|
+
${STRATEGY_PLANNING_PROMPT}
|
|
5054
|
+
|
|
5055
|
+
Current situation:
|
|
5056
|
+
${situation}
|
|
5057
|
+
|
|
5058
|
+
Shared state:
|
|
5059
|
+
- Target: ${this.memory.getSharedState().target}
|
|
5060
|
+
- Phase: ${this.memory.getSharedState().currentPhase}
|
|
5061
|
+
- Attack surface: ${this.memory.getAttackSurfaceSummary()}
|
|
5062
|
+
- Failed approaches: ${this.memory.getSharedState().failedApproaches.map((f) => f.approach).join(", ")}
|
|
5063
|
+
|
|
5064
|
+
Make a decision:
|
|
5065
|
+
1. What action should we take?
|
|
5066
|
+
2. Which agent should handle it?
|
|
5067
|
+
3. What's your reasoning?
|
|
5068
|
+
4. What are alternatives if this fails?
|
|
5069
|
+
|
|
5070
|
+
Respond with JSON:
|
|
5071
|
+
{
|
|
5072
|
+
"action": "description of action",
|
|
5073
|
+
"agent": "agent-name",
|
|
5074
|
+
"reasoning": "why this is the best choice",
|
|
5075
|
+
"confidence": 0-100,
|
|
5076
|
+
"alternatives": ["alt1", "alt2"]
|
|
5077
|
+
}
|
|
5078
|
+
`;
|
|
5079
|
+
try {
|
|
5080
|
+
const response = await this.client.messages.create({
|
|
5081
|
+
model: LLM_MODEL,
|
|
5082
|
+
max_tokens: 1024,
|
|
5083
|
+
messages: [{ role: "user", content: decisionPrompt }]
|
|
5084
|
+
});
|
|
5085
|
+
const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
5086
|
+
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
5087
|
+
if (jsonMatch) {
|
|
5088
|
+
const decision = JSON.parse(jsonMatch[0]);
|
|
5089
|
+
this.emit(SUPERVISOR_EVENT.DECISION_MADE, decision);
|
|
5090
|
+
this.memory.addMemory({
|
|
5091
|
+
type: "decision",
|
|
5092
|
+
agent: "supervisor",
|
|
5093
|
+
content: `Decision: ${decision.action} (${decision.agent}, ${decision.confidence}% confidence)`,
|
|
5094
|
+
importance: 75
|
|
5095
|
+
});
|
|
5096
|
+
return decision;
|
|
5097
|
+
}
|
|
5098
|
+
} catch {
|
|
5099
|
+
}
|
|
5100
|
+
return {
|
|
5101
|
+
action: "Continue with reconnaissance",
|
|
5102
|
+
agent: "target-explorer",
|
|
5103
|
+
reasoning: "Default action when unsure",
|
|
5104
|
+
confidence: 50,
|
|
5105
|
+
alternatives: ["web-hacker", "exploit-researcher"]
|
|
5106
|
+
};
|
|
5107
|
+
}
|
|
5108
|
+
// ===== Getters =====
|
|
5109
|
+
getCurrentPlan() {
|
|
5110
|
+
return this.currentPlan;
|
|
5111
|
+
}
|
|
5112
|
+
getMemory() {
|
|
5113
|
+
return this.memory;
|
|
5114
|
+
}
|
|
5115
|
+
};
|
|
5116
|
+
var supervisorInstance = null;
|
|
5117
|
+
function getSupervisor(apiKey) {
|
|
5118
|
+
if (!supervisorInstance) {
|
|
5119
|
+
supervisorInstance = new SupervisorAgent(apiKey);
|
|
5120
|
+
}
|
|
5121
|
+
return supervisorInstance;
|
|
5122
|
+
}
|
|
5123
|
+
|
|
4153
5124
|
// src/commands/index.ts
|
|
4154
5125
|
var SCAN_COMMAND = {
|
|
4155
5126
|
name: "scan",
|
|
@@ -4466,7 +5437,7 @@ var DEFAULT_PHASES = [
|
|
|
4466
5437
|
{ id: PHASE_ID.EXFIL, name: "Data Exfiltration", shortName: "Exfil", status: PHASE_STATUS.PENDING, attempts: 0 },
|
|
4467
5438
|
{ id: PHASE_ID.REPORT, name: "Reporting", shortName: "Report", status: PHASE_STATUS.PENDING, attempts: 0 }
|
|
4468
5439
|
];
|
|
4469
|
-
var AutonomousHackingAgent = class extends
|
|
5440
|
+
var AutonomousHackingAgent = class extends EventEmitter7 {
|
|
4470
5441
|
client;
|
|
4471
5442
|
state;
|
|
4472
5443
|
config;
|
|
@@ -4481,6 +5452,9 @@ var AutonomousHackingAgent = class extends EventEmitter4 {
|
|
|
4481
5452
|
mcpManager;
|
|
4482
5453
|
contextManager;
|
|
4483
5454
|
approvalManager;
|
|
5455
|
+
orchestrator;
|
|
5456
|
+
agentMemory;
|
|
5457
|
+
supervisor;
|
|
4484
5458
|
// Token usage tracking
|
|
4485
5459
|
tokenUsage = {
|
|
4486
5460
|
input: 0,
|
|
@@ -4499,7 +5473,7 @@ var AutonomousHackingAgent = class extends EventEmitter4 {
|
|
|
4499
5473
|
// Max attempts per phase
|
|
4500
5474
|
constructor(apiKey, config) {
|
|
4501
5475
|
super();
|
|
4502
|
-
this.client = new
|
|
5476
|
+
this.client = new Anthropic3({
|
|
4503
5477
|
apiKey: apiKey || LLM_API_KEY || process.env.PENTEST_API_KEY,
|
|
4504
5478
|
baseURL: LLM_BASE_URL
|
|
4505
5479
|
});
|
|
@@ -4509,6 +5483,9 @@ var AutonomousHackingAgent = class extends EventEmitter4 {
|
|
|
4509
5483
|
this.mcpManager = getMCPManager();
|
|
4510
5484
|
this.contextManager = new ContextManager(this.client);
|
|
4511
5485
|
this.approvalManager = getApprovalManager({ yoloMode: config?.autoApprove });
|
|
5486
|
+
this.orchestrator = getOrchestrator(apiKey);
|
|
5487
|
+
this.agentMemory = getAgentMemory();
|
|
5488
|
+
this.supervisor = getSupervisor(apiKey);
|
|
4512
5489
|
this.state = this.createInitialState();
|
|
4513
5490
|
this.initSystems();
|
|
4514
5491
|
}
|
|
@@ -4919,25 +5896,161 @@ What went wrong and what different approach should be tried?
|
|
|
4919
5896
|
this.think(THOUGHT_TYPE.REFLECTION, reflection);
|
|
4920
5897
|
return reflection;
|
|
4921
5898
|
}
|
|
5899
|
+
// ===== Strategy Validation =====
|
|
5900
|
+
async validateStrategy(proposedAction) {
|
|
5901
|
+
this.think(THOUGHT_TYPE.PLANNING, "[strategy] Validating action strategy...");
|
|
5902
|
+
const validationPrompt = `
|
|
5903
|
+
${STRATEGY_PLANNING_PROMPT}
|
|
5904
|
+
|
|
5905
|
+
Current situation:
|
|
5906
|
+
- Target: ${this.state.target.primary}
|
|
5907
|
+
- Current phase: ${this.getCurrentPhase().shortName}
|
|
5908
|
+
- Discovered services: ${this.state.target.services.map((s) => `${s.host}:${s.port} (${s.service})`).join(", ") || "none"}
|
|
5909
|
+
- Attack surface mapped: ${this.state.target.discovered.length} hosts, ${this.state.target.services.length} services
|
|
5910
|
+
- Subdomains found: ${this.state.target.discovered.filter((d) => d.includes(".")).length}
|
|
5911
|
+
|
|
5912
|
+
Proposed action: ${proposedAction}
|
|
5913
|
+
|
|
5914
|
+
Validate this action:
|
|
5915
|
+
1. Is this aligned with BFS (surface mapping first)?
|
|
5916
|
+
2. Have we completed reconnaissance before deep testing?
|
|
5917
|
+
3. Is this the highest ROI action right now?
|
|
5918
|
+
4. Should we adjust the approach?
|
|
5919
|
+
|
|
5920
|
+
Respond with JSON:
|
|
5921
|
+
{
|
|
5922
|
+
"valid": true/false,
|
|
5923
|
+
"adjustedAction": "if invalid, suggest better action",
|
|
5924
|
+
"reasoning": "brief explanation"
|
|
5925
|
+
}
|
|
5926
|
+
`;
|
|
5927
|
+
try {
|
|
5928
|
+
const response = await this.client.messages.create({
|
|
5929
|
+
model: LLM_MODEL,
|
|
5930
|
+
max_tokens: 1024,
|
|
5931
|
+
messages: [{ role: "user", content: validationPrompt }]
|
|
5932
|
+
});
|
|
5933
|
+
const text = response.content.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
5934
|
+
const jsonMatch = text.match(/\{[\s\S]*\}/);
|
|
5935
|
+
if (jsonMatch) {
|
|
5936
|
+
const result = JSON.parse(jsonMatch[0]);
|
|
5937
|
+
if (!result.valid) {
|
|
5938
|
+
this.think(THOUGHT_TYPE.REFLECTION, `[strategy] Action adjusted: ${result.reasoning}`);
|
|
5939
|
+
}
|
|
5940
|
+
return result;
|
|
5941
|
+
}
|
|
5942
|
+
} catch {
|
|
5943
|
+
}
|
|
5944
|
+
return { valid: true, reasoning: "Validation skipped" };
|
|
5945
|
+
}
|
|
5946
|
+
// ===== Agent Collaboration =====
|
|
5947
|
+
/**
|
|
5948
|
+
* Delegate a task to a specialist agent and get results
|
|
5949
|
+
*/
|
|
5950
|
+
async delegateToSpecialist(agentName, task, context) {
|
|
5951
|
+
this.think(THOUGHT_TYPE.PLANNING, `[delegate] Delegating to ${agentName}: ${task.slice(0, 50)}...`);
|
|
5952
|
+
await this.orchestrator.initialize();
|
|
5953
|
+
const agentTask = {
|
|
5954
|
+
agent: agentName,
|
|
5955
|
+
prompt: `${AGENT_COLLABORATION_PROMPT}
|
|
5956
|
+
|
|
5957
|
+
Context from main agent:
|
|
5958
|
+
${context}
|
|
5959
|
+
|
|
5960
|
+
Your task:
|
|
5961
|
+
${task}
|
|
5962
|
+
|
|
5963
|
+
Provide your analysis and findings. Include:
|
|
5964
|
+
1. Key discoveries
|
|
5965
|
+
2. Recommended next actions
|
|
5966
|
+
3. Any concerns or issues found`,
|
|
5967
|
+
priority: 1
|
|
5968
|
+
};
|
|
5969
|
+
try {
|
|
5970
|
+
const results = await this.orchestrator.launchParallel([agentTask]);
|
|
5971
|
+
const result = results[0];
|
|
5972
|
+
if (result.success) {
|
|
5973
|
+
const consolidatedFindings = consolidateFindings(results);
|
|
5974
|
+
const findingsSummary = consolidatedFindings.map((f) => `- [${f.severity.toUpperCase()}] ${f.title} (${f.confidence}% confidence)`).join("\n");
|
|
5975
|
+
this.think(THOUGHT_TYPE.REFLECTION, `[delegate] ${agentName} completed: ${result.findings.length} findings`);
|
|
5976
|
+
return {
|
|
5977
|
+
success: true,
|
|
5978
|
+
findings: findingsSummary || result.output.slice(0, 500),
|
|
5979
|
+
recommendation: result.findings[0]?.nextSteps?.join(", ") || "Continue with main approach"
|
|
5980
|
+
};
|
|
5981
|
+
}
|
|
5982
|
+
return {
|
|
5983
|
+
success: false,
|
|
5984
|
+
findings: result.error || "Agent failed",
|
|
5985
|
+
recommendation: "Try alternative approach"
|
|
5986
|
+
};
|
|
5987
|
+
} catch (error) {
|
|
5988
|
+
return {
|
|
5989
|
+
success: false,
|
|
5990
|
+
findings: error instanceof Error ? error.message : "Unknown error",
|
|
5991
|
+
recommendation: "Fallback to main agent"
|
|
5992
|
+
};
|
|
5993
|
+
}
|
|
5994
|
+
}
|
|
5995
|
+
/**
|
|
5996
|
+
* Consult multiple agents for strategy validation
|
|
5997
|
+
*/
|
|
5998
|
+
async consultAgents(question) {
|
|
5999
|
+
this.think(THOUGHT_TYPE.PLANNING, "[consult] Consulting specialist agents...");
|
|
6000
|
+
const tasks = [
|
|
6001
|
+
{
|
|
6002
|
+
agent: "attack-architect",
|
|
6003
|
+
prompt: `Strategic question: ${question}
|
|
6004
|
+
|
|
6005
|
+
Provide your expert opinion on the best approach.`,
|
|
6006
|
+
priority: 1
|
|
6007
|
+
},
|
|
6008
|
+
{
|
|
6009
|
+
agent: "finding-reviewer",
|
|
6010
|
+
prompt: `Review this approach: ${question}
|
|
6011
|
+
|
|
6012
|
+
Validate if this is the right strategy.`,
|
|
6013
|
+
priority: 1
|
|
6014
|
+
}
|
|
6015
|
+
];
|
|
6016
|
+
await this.orchestrator.initialize();
|
|
6017
|
+
const results = await this.orchestrator.launchParallel(tasks);
|
|
6018
|
+
const opinions = results.filter((r) => r.success).map((r) => `**${r.agent}**: ${r.output.slice(0, 200)}...`).join("\n\n");
|
|
6019
|
+
this.think(THOUGHT_TYPE.REFLECTION, "[consult] Received opinions from specialists");
|
|
6020
|
+
return opinions || "No specialist opinions available";
|
|
6021
|
+
}
|
|
4922
6022
|
// ===== Progress Detection =====
|
|
4923
6023
|
recordProgress(type) {
|
|
4924
6024
|
this.resetStuckCounter();
|
|
4925
6025
|
this.state.lastProgressTime = /* @__PURE__ */ new Date();
|
|
6026
|
+
let message = "";
|
|
6027
|
+
let importance = 60;
|
|
4926
6028
|
switch (type) {
|
|
4927
6029
|
case "discovery":
|
|
4928
|
-
|
|
6030
|
+
message = "[target] New target discovered!";
|
|
6031
|
+
importance = 65;
|
|
4929
6032
|
break;
|
|
4930
6033
|
case "credential":
|
|
4931
|
-
|
|
6034
|
+
message = "[cred] Credential obtained!";
|
|
6035
|
+
importance = 85;
|
|
4932
6036
|
break;
|
|
4933
6037
|
case "access":
|
|
4934
|
-
|
|
6038
|
+
message = "[access] Access obtained!";
|
|
6039
|
+
importance = 90;
|
|
4935
6040
|
break;
|
|
4936
6041
|
case "exploit":
|
|
4937
|
-
|
|
6042
|
+
message = "[exploit] Exploit successful!";
|
|
6043
|
+
importance = 95;
|
|
4938
6044
|
this.state.successfulExploits++;
|
|
4939
6045
|
break;
|
|
4940
6046
|
}
|
|
6047
|
+
this.think(THOUGHT_TYPE.BREAKTHROUGH, message);
|
|
6048
|
+
this.agentMemory.addMemory({
|
|
6049
|
+
type: type === "exploit" ? "action" : type === "credential" ? "credential" : "discovery",
|
|
6050
|
+
agent: this.currentAgent?.name || "autonomous-agent",
|
|
6051
|
+
content: message,
|
|
6052
|
+
importance
|
|
6053
|
+
});
|
|
4941
6054
|
}
|
|
4942
6055
|
// ===== Finding Management =====
|
|
4943
6056
|
addFinding(finding) {
|
|
@@ -5686,14 +6799,14 @@ Respond helpfully to the user's message. If they ask to perform security testing
|
|
|
5686
6799
|
// src/core/session/session-manager.ts
|
|
5687
6800
|
import * as fs4 from "fs/promises";
|
|
5688
6801
|
import * as path4 from "path";
|
|
5689
|
-
import { EventEmitter as
|
|
6802
|
+
import { EventEmitter as EventEmitter8 } from "events";
|
|
5690
6803
|
var SESSIONS_DIR = ".pentesting/sessions";
|
|
5691
6804
|
function generateSessionId() {
|
|
5692
6805
|
const timestamp = Date.now().toString(36);
|
|
5693
6806
|
const random = Math.random().toString(36).substring(2, 8);
|
|
5694
6807
|
return `session_${timestamp}_${random}`;
|
|
5695
6808
|
}
|
|
5696
|
-
var SessionManager = class extends
|
|
6809
|
+
var SessionManager = class extends EventEmitter8 {
|
|
5697
6810
|
sessionsDir;
|
|
5698
6811
|
currentSession = null;
|
|
5699
6812
|
constructor(baseDir) {
|
|
@@ -6012,7 +7125,7 @@ function getSlashCommandRegistry() {
|
|
|
6012
7125
|
// src/core/context/context-manager.ts
|
|
6013
7126
|
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync, renameSync } from "fs";
|
|
6014
7127
|
import { join as join4, dirname as dirname3 } from "path";
|
|
6015
|
-
import { EventEmitter as
|
|
7128
|
+
import { EventEmitter as EventEmitter9 } from "events";
|
|
6016
7129
|
var CONTEXT_EVENT = {
|
|
6017
7130
|
MESSAGE_ADDED: "message_added",
|
|
6018
7131
|
CHECKPOINT_CREATED: "checkpoint_created",
|
|
@@ -6020,7 +7133,7 @@ var CONTEXT_EVENT = {
|
|
|
6020
7133
|
CLEARED: "cleared",
|
|
6021
7134
|
COMPACTED: "compacted"
|
|
6022
7135
|
};
|
|
6023
|
-
var ContextManager2 = class extends
|
|
7136
|
+
var ContextManager2 = class extends EventEmitter9 {
|
|
6024
7137
|
filePath;
|
|
6025
7138
|
state;
|
|
6026
7139
|
maxMessages;
|
|
@@ -6731,9 +7844,9 @@ import { homedir } from "os";
|
|
|
6731
7844
|
import { join as join7 } from "path";
|
|
6732
7845
|
|
|
6733
7846
|
// src/cli/utils/keyboard-listener.ts
|
|
6734
|
-
import { EventEmitter as
|
|
7847
|
+
import { EventEmitter as EventEmitter10 } from "events";
|
|
6735
7848
|
import * as readline2 from "readline";
|
|
6736
|
-
var KeyboardListener = class extends
|
|
7849
|
+
var KeyboardListener = class extends EventEmitter10 {
|
|
6737
7850
|
isListening = false;
|
|
6738
7851
|
isPaused = false;
|
|
6739
7852
|
stdin = process.stdin;
|
|
@@ -6958,7 +8071,7 @@ function getKeyboardListener() {
|
|
|
6958
8071
|
}
|
|
6959
8072
|
|
|
6960
8073
|
// src/utils/input-queue.ts
|
|
6961
|
-
import { EventEmitter as
|
|
8074
|
+
import { EventEmitter as EventEmitter11 } from "events";
|
|
6962
8075
|
var INPUT_QUEUE_EVENT = {
|
|
6963
8076
|
QUEUED: "queued",
|
|
6964
8077
|
// Message added to queue
|
|
@@ -6971,7 +8084,7 @@ var INPUT_QUEUE_EVENT = {
|
|
|
6971
8084
|
SHUTDOWN: "shutdown"
|
|
6972
8085
|
// Queue shutdown
|
|
6973
8086
|
};
|
|
6974
|
-
var InputQueue = class extends
|
|
8087
|
+
var InputQueue = class extends EventEmitter11 {
|
|
6975
8088
|
queue = [];
|
|
6976
8089
|
isShutdown = false;
|
|
6977
8090
|
isPaused = false;
|
|
@@ -7442,7 +8555,8 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
7442
8555
|
inputQueue.enqueue(trimmed);
|
|
7443
8556
|
setQueuedCount(inputQueue.length);
|
|
7444
8557
|
setInput("");
|
|
7445
|
-
addMessage(MESSAGE_TYPE.
|
|
8558
|
+
addMessage(MESSAGE_TYPE.USER, trimmed);
|
|
8559
|
+
addMessage(MESSAGE_TYPE.SYSTEM, ` \u{1F4E5} Queued (${inputQueue.length} pending)`);
|
|
7446
8560
|
return;
|
|
7447
8561
|
}
|
|
7448
8562
|
setInput("");
|