@pensar/apex 0.0.14 → 0.0.16

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.
@@ -44495,6 +44495,143 @@ function saveSubagentMessages(orchestratorSession, subagentId, messages) {
44495
44495
  fs.writeFileSync(`${subagentDir}/messages.json`, JSON.stringify(messages, null, 2));
44496
44496
  }
44497
44497
 
44498
+ // node_modules/yocto-queue/index.js
44499
+ class Node {
44500
+ value;
44501
+ next;
44502
+ constructor(value) {
44503
+ this.value = value;
44504
+ }
44505
+ }
44506
+
44507
+ class Queue {
44508
+ #head;
44509
+ #tail;
44510
+ #size;
44511
+ constructor() {
44512
+ this.clear();
44513
+ }
44514
+ enqueue(value) {
44515
+ const node = new Node(value);
44516
+ if (this.#head) {
44517
+ this.#tail.next = node;
44518
+ this.#tail = node;
44519
+ } else {
44520
+ this.#head = node;
44521
+ this.#tail = node;
44522
+ }
44523
+ this.#size++;
44524
+ }
44525
+ dequeue() {
44526
+ const current = this.#head;
44527
+ if (!current) {
44528
+ return;
44529
+ }
44530
+ this.#head = this.#head.next;
44531
+ this.#size--;
44532
+ return current.value;
44533
+ }
44534
+ peek() {
44535
+ if (!this.#head) {
44536
+ return;
44537
+ }
44538
+ return this.#head.value;
44539
+ }
44540
+ clear() {
44541
+ this.#head = undefined;
44542
+ this.#tail = undefined;
44543
+ this.#size = 0;
44544
+ }
44545
+ get size() {
44546
+ return this.#size;
44547
+ }
44548
+ *[Symbol.iterator]() {
44549
+ let current = this.#head;
44550
+ while (current) {
44551
+ yield current.value;
44552
+ current = current.next;
44553
+ }
44554
+ }
44555
+ *drain() {
44556
+ while (this.#head) {
44557
+ yield this.dequeue();
44558
+ }
44559
+ }
44560
+ }
44561
+
44562
+ // node_modules/p-limit/index.js
44563
+ function pLimit(concurrency) {
44564
+ validateConcurrency(concurrency);
44565
+ const queue = new Queue;
44566
+ let activeCount = 0;
44567
+ const resumeNext = () => {
44568
+ if (activeCount < concurrency && queue.size > 0) {
44569
+ activeCount++;
44570
+ queue.dequeue()();
44571
+ }
44572
+ };
44573
+ const next = () => {
44574
+ activeCount--;
44575
+ resumeNext();
44576
+ };
44577
+ const run = async (function_, resolve2, arguments_) => {
44578
+ const result = (async () => function_(...arguments_))();
44579
+ resolve2(result);
44580
+ try {
44581
+ await result;
44582
+ } catch {}
44583
+ next();
44584
+ };
44585
+ const enqueue = (function_, resolve2, arguments_) => {
44586
+ new Promise((internalResolve) => {
44587
+ queue.enqueue(internalResolve);
44588
+ }).then(run.bind(undefined, function_, resolve2, arguments_));
44589
+ if (activeCount < concurrency) {
44590
+ resumeNext();
44591
+ }
44592
+ };
44593
+ const generator = (function_, ...arguments_) => new Promise((resolve2) => {
44594
+ enqueue(function_, resolve2, arguments_);
44595
+ });
44596
+ Object.defineProperties(generator, {
44597
+ activeCount: {
44598
+ get: () => activeCount
44599
+ },
44600
+ pendingCount: {
44601
+ get: () => queue.size
44602
+ },
44603
+ clearQueue: {
44604
+ value() {
44605
+ queue.clear();
44606
+ }
44607
+ },
44608
+ concurrency: {
44609
+ get: () => concurrency,
44610
+ set(newConcurrency) {
44611
+ validateConcurrency(newConcurrency);
44612
+ concurrency = newConcurrency;
44613
+ queueMicrotask(() => {
44614
+ while (activeCount < concurrency && queue.size > 0) {
44615
+ resumeNext();
44616
+ }
44617
+ });
44618
+ }
44619
+ },
44620
+ map: {
44621
+ async value(iterable, function_) {
44622
+ const promises = Array.from(iterable, (value, index) => this(function_, value, index));
44623
+ return Promise.all(promises);
44624
+ }
44625
+ }
44626
+ });
44627
+ return generator;
44628
+ }
44629
+ function validateConcurrency(concurrency) {
44630
+ if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
44631
+ throw new TypeError("Expected `concurrency` to be a number from 1 and up");
44632
+ }
44633
+ }
44634
+
44498
44635
  // src/core/agent/thoroughPentestAgent/agent.ts
44499
44636
  function runAgent3(opts) {
44500
44637
  const {
@@ -44608,9 +44745,13 @@ Objective: ${objective}`,
44608
44745
  currentAssistantText += delta.text;
44609
44746
  const lastMessage = allMessages[allMessages.length - 1];
44610
44747
  if (lastMessage && lastMessage.role === "assistant") {
44611
- lastMessage.content = currentAssistantText;
44748
+ const updatedMessage = {
44749
+ ...lastMessage,
44750
+ content: currentAssistantText
44751
+ };
44752
+ allMessages[allMessages.length - 1] = updatedMessage;
44612
44753
  if (onSubagentMessage) {
44613
- onSubagentMessage(subagentId, lastMessage);
44754
+ onSubagentMessage(subagentId, updatedMessage);
44614
44755
  }
44615
44756
  } else {
44616
44757
  const newMessage = {
@@ -44644,10 +44785,14 @@ Objective: ${objective}`,
44644
44785
  const existingToolMessageIndex = allMessages.findIndex((msg) => msg.role === "tool" && msg.toolCallId === delta.toolCallId);
44645
44786
  if (existingToolMessageIndex !== -1) {
44646
44787
  const existingMessage = allMessages[existingToolMessageIndex];
44647
- existingMessage.status = "completed";
44648
- existingMessage.content = delta.input.toolCallDescription ? `✓ ${delta.input.toolCallDescription}` : `✓ Tool ${delta.toolName}`;
44788
+ const updatedMessage = {
44789
+ ...existingMessage,
44790
+ status: "completed",
44791
+ content: delta.input.toolCallDescription ? `✓ ${delta.input.toolCallDescription}` : `✓ Tool ${delta.toolName}`
44792
+ };
44793
+ allMessages[existingToolMessageIndex] = updatedMessage;
44649
44794
  if (onSubagentMessage) {
44650
- onSubagentMessage(subagentId, existingMessage);
44795
+ onSubagentMessage(subagentId, updatedMessage);
44651
44796
  }
44652
44797
  }
44653
44798
  }
@@ -44735,7 +44880,8 @@ You can spawn multiple agents in parallel - they will run concurrently.`,
44735
44880
  execute: async ({ targets }) => {
44736
44881
  try {
44737
44882
  logger?.log(`[Orchestrator] Spawning ${targets.length} pentest agents...`);
44738
- const promises = targets.map(async (targetInfo, index) => {
44883
+ const limit = pLimit(5);
44884
+ const promises = targets.map((targetInfo, index) => limit(async () => {
44739
44885
  try {
44740
44886
  const { streamResult: result } = runAgent({
44741
44887
  session,
@@ -44775,9 +44921,13 @@ Objective: ${targetInfo.objective}`,
44775
44921
  currentAssistantText += delta.text;
44776
44922
  const lastMessage = allMessages[allMessages.length - 1];
44777
44923
  if (lastMessage && lastMessage.role === "assistant") {
44778
- lastMessage.content = currentAssistantText;
44924
+ const updatedMessage = {
44925
+ ...lastMessage,
44926
+ content: currentAssistantText
44927
+ };
44928
+ allMessages[allMessages.length - 1] = updatedMessage;
44779
44929
  if (onSubagentMessage) {
44780
- onSubagentMessage(subagentId, lastMessage);
44930
+ onSubagentMessage(subagentId, updatedMessage);
44781
44931
  }
44782
44932
  } else {
44783
44933
  const newMessage = {
@@ -44811,10 +44961,14 @@ Objective: ${targetInfo.objective}`,
44811
44961
  const existingToolMessageIndex = allMessages.findIndex((msg) => msg.role === "tool" && msg.toolCallId === delta.toolCallId);
44812
44962
  if (existingToolMessageIndex !== -1) {
44813
44963
  const existingMessage = allMessages[existingToolMessageIndex];
44814
- existingMessage.status = "completed";
44815
- existingMessage.content = delta.input.toolCallDescription ? `✓ ${delta.input.toolCallDescription}` : `✓ Tool ${delta.toolName}`;
44964
+ const updatedMessage = {
44965
+ ...existingMessage,
44966
+ status: "completed",
44967
+ content: delta.input.toolCallDescription ? `✓ ${delta.input.toolCallDescription}` : `✓ Tool ${delta.toolName}`
44968
+ };
44969
+ allMessages[existingToolMessageIndex] = updatedMessage;
44816
44970
  if (onSubagentMessage) {
44817
- onSubagentMessage(subagentId, existingMessage);
44971
+ onSubagentMessage(subagentId, updatedMessage);
44818
44972
  }
44819
44973
  }
44820
44974
  }
@@ -44855,7 +45009,7 @@ Objective: ${targetInfo.objective}`,
44855
45009
  error: error46.message
44856
45010
  };
44857
45011
  }
44858
- });
45012
+ }));
44859
45013
  const results = await Promise.all(promises);
44860
45014
  const successful = results.filter((r) => r.success).length;
44861
45015
  const failed = results.filter((r) => !r.success).length;
package/build/index.js CHANGED
@@ -74997,32 +74997,24 @@ var Qt = b.parse;
74997
74997
  var Ut = x.lex;
74998
74998
 
74999
74999
  // src/tui/components/agent-display.tsx
75000
- var messageIdCacheByContext = new Map;
75001
- var idCounter2 = 0;
75002
- function getCacheForContext(contextId) {
75003
- let cache = messageIdCacheByContext.get(contextId);
75004
- if (!cache) {
75005
- cache = new WeakMap;
75006
- messageIdCacheByContext.set(contextId, cache);
75007
- }
75008
- return cache;
75000
+ import fs3 from "fs";
75001
+ var LOG_FILE = "/tmp/apex-debug.log";
75002
+ function logToFile(message, data) {
75003
+ const timestamp = new Date().toISOString();
75004
+ const logLine = `[${timestamp}] ${message} ${data ? JSON.stringify(data, null, 2) : ""}
75005
+ `;
75006
+ fs3.appendFileSync(LOG_FILE, logLine);
75009
75007
  }
75010
75008
  function getStableKey(item, contextId = "root") {
75011
- const cache = getCacheForContext(contextId);
75012
- let cachedId = cache.get(item);
75013
- if (cachedId) {
75014
- return cachedId;
75015
- }
75016
- let newId;
75017
75009
  if ("messages" in item) {
75018
- newId = `subagent-${item.id}`;
75010
+ return `subagent-${item.id}`;
75019
75011
  } else if (item.role === "tool" && "toolCallId" in item) {
75020
- newId = `${contextId}-tool-${item.toolCallId}`;
75012
+ return `${contextId}-tool-${item.toolCallId}`;
75021
75013
  } else {
75022
- newId = `${contextId}-${item.role}-${item.createdAt.getTime()}-${idCounter2++}`;
75014
+ const content = typeof item.content === "string" ? item.content : JSON.stringify(item.content);
75015
+ const contentHash = content.length;
75016
+ return `${contextId}-${item.role}-${item.createdAt.getTime()}-${contentHash}`;
75023
75017
  }
75024
- cache.set(item, newId);
75025
- return newId;
75026
75018
  }
75027
75019
  function markdownToStyledText(content) {
75028
75020
  try {
@@ -75195,8 +75187,14 @@ function AgentDisplay({
75195
75187
  ]
75196
75188
  }, undefined, true, undefined, this);
75197
75189
  }
75198
- function SubAgentDisplay({ subagent }) {
75190
+ var SubAgentDisplay = import_react19.memo(function SubAgentDisplay2({ subagent }) {
75199
75191
  const [open, setOpen] = import_react19.useState(false);
75192
+ logToFile(`[Render] SubAgentDisplay for ${subagent.id}:`, {
75193
+ name: subagent.name,
75194
+ nameLength: subagent.name?.length || 0,
75195
+ status: subagent.status,
75196
+ messageCount: subagent.messages.length
75197
+ });
75200
75198
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
75201
75199
  height: open ? 40 : "auto",
75202
75200
  onMouseDown: () => setOpen(!open),
@@ -75242,8 +75240,8 @@ function SubAgentDisplay({ subagent }) {
75242
75240
  }, undefined, false, undefined, this)
75243
75241
  ]
75244
75242
  }, undefined, true, undefined, this);
75245
- }
75246
- function AgentMessage({ message }) {
75243
+ });
75244
+ var AgentMessage = import_react19.memo(function AgentMessage2({ message }) {
75247
75245
  let content = "";
75248
75246
  if (typeof message.content === "string") {
75249
75247
  content = message.content;
@@ -75258,6 +75256,15 @@ function AgentMessage({ message }) {
75258
75256
  } else {
75259
75257
  content = JSON.stringify(message.content, null, 2);
75260
75258
  }
75259
+ if (message.role === "tool") {
75260
+ logToFile(`[Render] AgentMessage (tool):`, {
75261
+ toolCallId: message.toolCallId,
75262
+ content: content.substring(0, 50),
75263
+ contentLength: content.length,
75264
+ isEmpty: content === "",
75265
+ status: message.status
75266
+ });
75267
+ }
75261
75268
  const displayContent = message.role === "assistant" ? markdownToStyledText(content) : content;
75262
75269
  const isPendingTool = message.role === "tool" && message.status === "pending";
75263
75270
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
@@ -75304,7 +75311,7 @@ function AgentMessage({ message }) {
75304
75311
  }, undefined, false, undefined, this)
75305
75312
  ]
75306
75313
  }, undefined, true, undefined, this);
75307
- }
75314
+ });
75308
75315
  function ToolArgs({ message }) {
75309
75316
  const [open, setOpen] = import_react19.useState(false);
75310
75317
  if (message.role !== "tool" || !("args" in message)) {
@@ -75312,7 +75319,10 @@ function ToolArgs({ message }) {
75312
75319
  }
75313
75320
  const args = message.args;
75314
75321
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
75315
- onMouseDown: () => setOpen(!open),
75322
+ onMouseDown: (e) => {
75323
+ e.stopPropagation();
75324
+ setOpen(!open);
75325
+ },
75316
75326
  children: [
75317
75327
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
75318
75328
  flexDirection: "row",
@@ -75330,28 +75340,28 @@ function ToolArgs({ message }) {
75330
75340
  }
75331
75341
 
75332
75342
  // src/tui/components/commands/pentest-agent-display.tsx
75333
- import fs4 from "fs";
75343
+ import fs5 from "fs";
75334
75344
 
75335
75345
  // src/core/messages/index.ts
75336
- import fs3 from "fs";
75346
+ import fs4 from "fs";
75337
75347
  function getMessages(session) {
75338
- const messages = fs3.readFileSync(session.rootPath + "/messages.json", "utf8");
75348
+ const messages = fs4.readFileSync(session.rootPath + "/messages.json", "utf8");
75339
75349
  return JSON.parse(messages);
75340
75350
  }
75341
75351
  function saveMessages(session, messages) {
75342
- fs3.writeFileSync(session.rootPath + "/messages.json", JSON.stringify(messages, null, 2));
75352
+ fs4.writeFileSync(session.rootPath + "/messages.json", JSON.stringify(messages, null, 2));
75343
75353
  }
75344
75354
  function saveSubagentMessages(orchestratorSession, subagentId, messages) {
75345
75355
  const subagentDir = `${orchestratorSession.rootPath}/subagents/${subagentId}`;
75346
- if (!fs3.existsSync(`${orchestratorSession.rootPath}/subagents`)) {
75347
- fs3.mkdirSync(`${orchestratorSession.rootPath}/subagents`, {
75356
+ if (!fs4.existsSync(`${orchestratorSession.rootPath}/subagents`)) {
75357
+ fs4.mkdirSync(`${orchestratorSession.rootPath}/subagents`, {
75348
75358
  recursive: true
75349
75359
  });
75350
75360
  }
75351
- if (!fs3.existsSync(subagentDir)) {
75352
- fs3.mkdirSync(subagentDir, { recursive: true });
75361
+ if (!fs4.existsSync(subagentDir)) {
75362
+ fs4.mkdirSync(subagentDir, { recursive: true });
75353
75363
  }
75354
- fs3.writeFileSync(`${subagentDir}/messages.json`, JSON.stringify(messages, null, 2));
75364
+ fs4.writeFileSync(`${subagentDir}/messages.json`, JSON.stringify(messages, null, 2));
75355
75365
  }
75356
75366
 
75357
75367
  // src/tui/components/commands/pentest-agent-display.tsx
@@ -75492,7 +75502,7 @@ Path: ${result.session.rootPath}`,
75492
75502
  }
75493
75503
  saveMessages(result.session, allMessages);
75494
75504
  }
75495
- if (fs4.existsSync(result.session.rootPath + "/pentest-report.md")) {
75505
+ if (fs5.existsSync(result.session.rootPath + "/pentest-report.md")) {
75496
75506
  setIsCompleted(true);
75497
75507
  }
75498
75508
  setThinking(false);
@@ -75664,7 +75674,7 @@ var import_react23 = __toESM(require_react(), 1);
75664
75674
 
75665
75675
  // src/tui/components/hooks/thoroughPentestAgent.ts
75666
75676
  var import_react22 = __toESM(require_react(), 1);
75667
- import fs5 from "fs";
75677
+ import fs6 from "fs";
75668
75678
 
75669
75679
  // src/core/agent/thoroughPentestAgent/prompts.ts
75670
75680
  var SYSTEM2 = `You are an expert penetration testing orchestrator agent specializing in coordinating comprehensive security assessments. Your role is to AUTONOMOUSLY manage a complete penetration testing engagement by coordinating specialized sub-agents.
@@ -77501,6 +77511,143 @@ You MUST provide the details final report using create_attack_surface_report too
77501
77511
  return { streamResult, session };
77502
77512
  }
77503
77513
 
77514
+ // node_modules/yocto-queue/index.js
77515
+ class Node {
77516
+ value;
77517
+ next;
77518
+ constructor(value) {
77519
+ this.value = value;
77520
+ }
77521
+ }
77522
+
77523
+ class Queue {
77524
+ #head;
77525
+ #tail;
77526
+ #size;
77527
+ constructor() {
77528
+ this.clear();
77529
+ }
77530
+ enqueue(value) {
77531
+ const node = new Node(value);
77532
+ if (this.#head) {
77533
+ this.#tail.next = node;
77534
+ this.#tail = node;
77535
+ } else {
77536
+ this.#head = node;
77537
+ this.#tail = node;
77538
+ }
77539
+ this.#size++;
77540
+ }
77541
+ dequeue() {
77542
+ const current = this.#head;
77543
+ if (!current) {
77544
+ return;
77545
+ }
77546
+ this.#head = this.#head.next;
77547
+ this.#size--;
77548
+ return current.value;
77549
+ }
77550
+ peek() {
77551
+ if (!this.#head) {
77552
+ return;
77553
+ }
77554
+ return this.#head.value;
77555
+ }
77556
+ clear() {
77557
+ this.#head = undefined;
77558
+ this.#tail = undefined;
77559
+ this.#size = 0;
77560
+ }
77561
+ get size() {
77562
+ return this.#size;
77563
+ }
77564
+ *[Symbol.iterator]() {
77565
+ let current = this.#head;
77566
+ while (current) {
77567
+ yield current.value;
77568
+ current = current.next;
77569
+ }
77570
+ }
77571
+ *drain() {
77572
+ while (this.#head) {
77573
+ yield this.dequeue();
77574
+ }
77575
+ }
77576
+ }
77577
+
77578
+ // node_modules/p-limit/index.js
77579
+ function pLimit(concurrency) {
77580
+ validateConcurrency(concurrency);
77581
+ const queue = new Queue;
77582
+ let activeCount = 0;
77583
+ const resumeNext = () => {
77584
+ if (activeCount < concurrency && queue.size > 0) {
77585
+ activeCount++;
77586
+ queue.dequeue()();
77587
+ }
77588
+ };
77589
+ const next = () => {
77590
+ activeCount--;
77591
+ resumeNext();
77592
+ };
77593
+ const run = async (function_, resolve4, arguments_) => {
77594
+ const result = (async () => function_(...arguments_))();
77595
+ resolve4(result);
77596
+ try {
77597
+ await result;
77598
+ } catch {}
77599
+ next();
77600
+ };
77601
+ const enqueue = (function_, resolve4, arguments_) => {
77602
+ new Promise((internalResolve) => {
77603
+ queue.enqueue(internalResolve);
77604
+ }).then(run.bind(undefined, function_, resolve4, arguments_));
77605
+ if (activeCount < concurrency) {
77606
+ resumeNext();
77607
+ }
77608
+ };
77609
+ const generator = (function_, ...arguments_) => new Promise((resolve4) => {
77610
+ enqueue(function_, resolve4, arguments_);
77611
+ });
77612
+ Object.defineProperties(generator, {
77613
+ activeCount: {
77614
+ get: () => activeCount
77615
+ },
77616
+ pendingCount: {
77617
+ get: () => queue.size
77618
+ },
77619
+ clearQueue: {
77620
+ value() {
77621
+ queue.clear();
77622
+ }
77623
+ },
77624
+ concurrency: {
77625
+ get: () => concurrency,
77626
+ set(newConcurrency) {
77627
+ validateConcurrency(newConcurrency);
77628
+ concurrency = newConcurrency;
77629
+ queueMicrotask(() => {
77630
+ while (activeCount < concurrency && queue.size > 0) {
77631
+ resumeNext();
77632
+ }
77633
+ });
77634
+ }
77635
+ },
77636
+ map: {
77637
+ async value(iterable, function_) {
77638
+ const promises = Array.from(iterable, (value, index) => this(function_, value, index));
77639
+ return Promise.all(promises);
77640
+ }
77641
+ }
77642
+ });
77643
+ return generator;
77644
+ }
77645
+ function validateConcurrency(concurrency) {
77646
+ if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
77647
+ throw new TypeError("Expected `concurrency` to be a number from 1 and up");
77648
+ }
77649
+ }
77650
+
77504
77651
  // src/core/agent/thoroughPentestAgent/agent.ts
77505
77652
  function runAgent3(opts) {
77506
77653
  const {
@@ -77614,9 +77761,13 @@ Objective: ${objective}`,
77614
77761
  currentAssistantText += delta.text;
77615
77762
  const lastMessage = allMessages[allMessages.length - 1];
77616
77763
  if (lastMessage && lastMessage.role === "assistant") {
77617
- lastMessage.content = currentAssistantText;
77764
+ const updatedMessage = {
77765
+ ...lastMessage,
77766
+ content: currentAssistantText
77767
+ };
77768
+ allMessages[allMessages.length - 1] = updatedMessage;
77618
77769
  if (onSubagentMessage) {
77619
- onSubagentMessage(subagentId, lastMessage);
77770
+ onSubagentMessage(subagentId, updatedMessage);
77620
77771
  }
77621
77772
  } else {
77622
77773
  const newMessage = {
@@ -77650,10 +77801,14 @@ Objective: ${objective}`,
77650
77801
  const existingToolMessageIndex = allMessages.findIndex((msg) => msg.role === "tool" && msg.toolCallId === delta.toolCallId);
77651
77802
  if (existingToolMessageIndex !== -1) {
77652
77803
  const existingMessage = allMessages[existingToolMessageIndex];
77653
- existingMessage.status = "completed";
77654
- existingMessage.content = delta.input.toolCallDescription ? `✓ ${delta.input.toolCallDescription}` : `✓ Tool ${delta.toolName}`;
77804
+ const updatedMessage = {
77805
+ ...existingMessage,
77806
+ status: "completed",
77807
+ content: delta.input.toolCallDescription ? `✓ ${delta.input.toolCallDescription}` : `✓ Tool ${delta.toolName}`
77808
+ };
77809
+ allMessages[existingToolMessageIndex] = updatedMessage;
77655
77810
  if (onSubagentMessage) {
77656
- onSubagentMessage(subagentId, existingMessage);
77811
+ onSubagentMessage(subagentId, updatedMessage);
77657
77812
  }
77658
77813
  }
77659
77814
  }
@@ -77741,7 +77896,8 @@ You can spawn multiple agents in parallel - they will run concurrently.`,
77741
77896
  execute: async ({ targets }) => {
77742
77897
  try {
77743
77898
  logger?.log(`[Orchestrator] Spawning ${targets.length} pentest agents...`);
77744
- const promises = targets.map(async (targetInfo, index) => {
77899
+ const limit = pLimit(5);
77900
+ const promises = targets.map((targetInfo, index) => limit(async () => {
77745
77901
  try {
77746
77902
  const { streamResult: result } = runAgent({
77747
77903
  session,
@@ -77781,9 +77937,13 @@ Objective: ${targetInfo.objective}`,
77781
77937
  currentAssistantText += delta.text;
77782
77938
  const lastMessage = allMessages[allMessages.length - 1];
77783
77939
  if (lastMessage && lastMessage.role === "assistant") {
77784
- lastMessage.content = currentAssistantText;
77940
+ const updatedMessage = {
77941
+ ...lastMessage,
77942
+ content: currentAssistantText
77943
+ };
77944
+ allMessages[allMessages.length - 1] = updatedMessage;
77785
77945
  if (onSubagentMessage) {
77786
- onSubagentMessage(subagentId, lastMessage);
77946
+ onSubagentMessage(subagentId, updatedMessage);
77787
77947
  }
77788
77948
  } else {
77789
77949
  const newMessage = {
@@ -77817,10 +77977,14 @@ Objective: ${targetInfo.objective}`,
77817
77977
  const existingToolMessageIndex = allMessages.findIndex((msg) => msg.role === "tool" && msg.toolCallId === delta.toolCallId);
77818
77978
  if (existingToolMessageIndex !== -1) {
77819
77979
  const existingMessage = allMessages[existingToolMessageIndex];
77820
- existingMessage.status = "completed";
77821
- existingMessage.content = delta.input.toolCallDescription ? `✓ ${delta.input.toolCallDescription}` : `✓ Tool ${delta.toolName}`;
77980
+ const updatedMessage = {
77981
+ ...existingMessage,
77982
+ status: "completed",
77983
+ content: delta.input.toolCallDescription ? `✓ ${delta.input.toolCallDescription}` : `✓ Tool ${delta.toolName}`
77984
+ };
77985
+ allMessages[existingToolMessageIndex] = updatedMessage;
77822
77986
  if (onSubagentMessage) {
77823
- onSubagentMessage(subagentId, existingMessage);
77987
+ onSubagentMessage(subagentId, updatedMessage);
77824
77988
  }
77825
77989
  }
77826
77990
  }
@@ -77861,7 +78025,7 @@ Objective: ${targetInfo.objective}`,
77861
78025
  error: error46.message
77862
78026
  };
77863
78027
  }
77864
- });
78028
+ }));
77865
78029
  const results = await Promise.all(promises);
77866
78030
  const successful = results.filter((r) => r.success).length;
77867
78031
  const failed = results.filter((r) => !r.success).length;
@@ -78015,6 +78179,13 @@ ${pentestSessionPaths.map((path5, idx) => `${idx + 1}. ${path5}`).join(`
78015
78179
 
78016
78180
  // src/tui/components/hooks/thoroughPentestAgent.ts
78017
78181
  import { exec as exec5 } from "child_process";
78182
+ var LOG_FILE2 = "/tmp/apex-debug.log";
78183
+ function logToFile2(message, data) {
78184
+ const timestamp = new Date().toISOString();
78185
+ const logLine = `[${timestamp}] ${message} ${data ? JSON.stringify(data, null, 2) : ""}
78186
+ `;
78187
+ fs6.appendFileSync(LOG_FILE2, logLine);
78188
+ }
78018
78189
  function useThoroughPentestAgent() {
78019
78190
  const [target, setTarget] = import_react22.useState("");
78020
78191
  const [messages, setMessages] = import_react22.useState([]);
@@ -78034,7 +78205,7 @@ function useThoroughPentestAgent() {
78034
78205
  function openReport() {
78035
78206
  if (sessionPath) {
78036
78207
  const reportPath = `${sessionPath}/comprehensive-pentest-report.md`;
78037
- if (fs5.existsSync(reportPath)) {
78208
+ if (fs6.existsSync(reportPath)) {
78038
78209
  exec5(`open "${reportPath}"`, (error46) => {
78039
78210
  if (error46) {
78040
78211
  console.error("Error opening report:", error46);
@@ -78080,30 +78251,76 @@ function useThoroughPentestAgent() {
78080
78251
  ]);
78081
78252
  },
78082
78253
  onSubagentMessage: (subagentId, message) => {
78083
- setSubagents((prev) => prev.map((sub) => {
78084
- if (sub.id !== subagentId)
78085
- return sub;
78254
+ logToFile2(`[Hook] Received message for ${subagentId}:`, {
78255
+ role: message.role,
78256
+ content: typeof message.content === "string" ? message.content.substring(0, 50) : message.content,
78257
+ contentLength: typeof message.content === "string" ? message.content.length : "not-string",
78258
+ hasContent: !!message.content,
78259
+ toolCallId: message.role === "tool" ? message.toolCallId : "n/a"
78260
+ });
78261
+ setSubagents((prev) => {
78262
+ const subIndex = prev.findIndex((s) => s.id === subagentId);
78263
+ if (subIndex === -1) {
78264
+ logToFile2(`[Hook] Subagent ${subagentId} not found!`);
78265
+ return prev;
78266
+ }
78267
+ const sub = prev[subIndex];
78086
78268
  const messages2 = [...sub.messages];
78269
+ const getMessageId = (msg) => {
78270
+ if (msg.role === "tool" && "toolCallId" in msg) {
78271
+ return `tool-${msg.toolCallId}`;
78272
+ } else if (msg.role === "assistant") {
78273
+ const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
78274
+ return `assistant-${msg.createdAt.getTime()}-${content.length}`;
78275
+ } else {
78276
+ return `${msg.role}-${msg.createdAt.getTime()}`;
78277
+ }
78278
+ };
78279
+ const messageId = getMessageId(message);
78087
78280
  if (message.role === "tool") {
78088
78281
  const toolMsg = message;
78089
78282
  const existingIndex = messages2.findIndex((m2) => m2.role === "tool" && m2.toolCallId === toolMsg.toolCallId);
78090
78283
  if (existingIndex !== -1) {
78091
78284
  messages2[existingIndex] = message;
78092
78285
  } else {
78093
- messages2.push(message);
78286
+ const insertIndex = messages2.findIndex((m2) => m2.createdAt.getTime() > message.createdAt.getTime());
78287
+ if (insertIndex === -1) {
78288
+ messages2.push(message);
78289
+ } else {
78290
+ messages2.splice(insertIndex, 0, message);
78291
+ }
78094
78292
  }
78095
78293
  } else if (message.role === "assistant") {
78096
78294
  const lastMessage = messages2[messages2.length - 1];
78097
78295
  if (lastMessage && lastMessage.role === "assistant") {
78098
- messages2[messages2.length - 1] = message;
78296
+ if (lastMessage.createdAt.getTime() === message.createdAt.getTime()) {
78297
+ messages2[messages2.length - 1] = message;
78298
+ } else {
78299
+ messages2.push(message);
78300
+ }
78099
78301
  } else {
78100
78302
  messages2.push(message);
78101
78303
  }
78102
78304
  } else {
78103
- messages2.push(message);
78305
+ const existingIndex = messages2.findIndex((m2) => getMessageId(m2) === messageId);
78306
+ if (existingIndex === -1) {
78307
+ messages2.push(message);
78308
+ }
78104
78309
  }
78105
- return { ...sub, messages: messages2 };
78106
- }));
78310
+ const updatedSub = { ...sub, messages: messages2 };
78311
+ logToFile2(`[Hook] Updated ${subagentId}, now has ${messages2.length} messages`);
78312
+ if (message.role === "tool") {
78313
+ const toolInArray = messages2.find((m2) => m2.role === "tool" && m2.toolCallId === message.toolCallId);
78314
+ logToFile2(`[Hook] Tool message in array:`, {
78315
+ found: !!toolInArray,
78316
+ content: toolInArray?.content,
78317
+ contentLength: typeof toolInArray?.content === "string" ? toolInArray.content.length : "not-string"
78318
+ });
78319
+ }
78320
+ const newSubagents = [...prev];
78321
+ newSubagents[subIndex] = updatedSub;
78322
+ return newSubagents;
78323
+ });
78107
78324
  },
78108
78325
  onSubagentComplete: (subagentId, success2) => {
78109
78326
  setSubagents((prev) => prev.map((sub) => sub.id === subagentId ? { ...sub, status: success2 ? "completed" : "failed" } : sub));
@@ -78169,7 +78386,7 @@ Mode: Pentest (Orchestrator)`,
78169
78386
  }
78170
78387
  saveMessages(result.session, allMessages);
78171
78388
  }
78172
- if (fs5.existsSync(result.session.rootPath + "/comprehensive-pentest-report.md")) {
78389
+ if (fs6.existsSync(result.session.rootPath + "/comprehensive-pentest-report.md")) {
78173
78390
  setIsCompleted(true);
78174
78391
  }
78175
78392
  setThinking(false);
package/build/swarm.js CHANGED
@@ -42390,6 +42390,145 @@ Remember to follow a systematic methodology and explain your reasoning for each
42390
42390
  }
42391
42391
  // scripts/swarm.ts
42392
42392
  import { readFileSync as readFileSync4 } from "fs";
42393
+
42394
+ // node_modules/yocto-queue/index.js
42395
+ class Node {
42396
+ value;
42397
+ next;
42398
+ constructor(value) {
42399
+ this.value = value;
42400
+ }
42401
+ }
42402
+
42403
+ class Queue {
42404
+ #head;
42405
+ #tail;
42406
+ #size;
42407
+ constructor() {
42408
+ this.clear();
42409
+ }
42410
+ enqueue(value) {
42411
+ const node = new Node(value);
42412
+ if (this.#head) {
42413
+ this.#tail.next = node;
42414
+ this.#tail = node;
42415
+ } else {
42416
+ this.#head = node;
42417
+ this.#tail = node;
42418
+ }
42419
+ this.#size++;
42420
+ }
42421
+ dequeue() {
42422
+ const current = this.#head;
42423
+ if (!current) {
42424
+ return;
42425
+ }
42426
+ this.#head = this.#head.next;
42427
+ this.#size--;
42428
+ return current.value;
42429
+ }
42430
+ peek() {
42431
+ if (!this.#head) {
42432
+ return;
42433
+ }
42434
+ return this.#head.value;
42435
+ }
42436
+ clear() {
42437
+ this.#head = undefined;
42438
+ this.#tail = undefined;
42439
+ this.#size = 0;
42440
+ }
42441
+ get size() {
42442
+ return this.#size;
42443
+ }
42444
+ *[Symbol.iterator]() {
42445
+ let current = this.#head;
42446
+ while (current) {
42447
+ yield current.value;
42448
+ current = current.next;
42449
+ }
42450
+ }
42451
+ *drain() {
42452
+ while (this.#head) {
42453
+ yield this.dequeue();
42454
+ }
42455
+ }
42456
+ }
42457
+
42458
+ // node_modules/p-limit/index.js
42459
+ function pLimit(concurrency) {
42460
+ validateConcurrency(concurrency);
42461
+ const queue = new Queue;
42462
+ let activeCount = 0;
42463
+ const resumeNext = () => {
42464
+ if (activeCount < concurrency && queue.size > 0) {
42465
+ activeCount++;
42466
+ queue.dequeue()();
42467
+ }
42468
+ };
42469
+ const next = () => {
42470
+ activeCount--;
42471
+ resumeNext();
42472
+ };
42473
+ const run = async (function_, resolve2, arguments_) => {
42474
+ const result = (async () => function_(...arguments_))();
42475
+ resolve2(result);
42476
+ try {
42477
+ await result;
42478
+ } catch {}
42479
+ next();
42480
+ };
42481
+ const enqueue = (function_, resolve2, arguments_) => {
42482
+ new Promise((internalResolve) => {
42483
+ queue.enqueue(internalResolve);
42484
+ }).then(run.bind(undefined, function_, resolve2, arguments_));
42485
+ if (activeCount < concurrency) {
42486
+ resumeNext();
42487
+ }
42488
+ };
42489
+ const generator = (function_, ...arguments_) => new Promise((resolve2) => {
42490
+ enqueue(function_, resolve2, arguments_);
42491
+ });
42492
+ Object.defineProperties(generator, {
42493
+ activeCount: {
42494
+ get: () => activeCount
42495
+ },
42496
+ pendingCount: {
42497
+ get: () => queue.size
42498
+ },
42499
+ clearQueue: {
42500
+ value() {
42501
+ queue.clear();
42502
+ }
42503
+ },
42504
+ concurrency: {
42505
+ get: () => concurrency,
42506
+ set(newConcurrency) {
42507
+ validateConcurrency(newConcurrency);
42508
+ concurrency = newConcurrency;
42509
+ queueMicrotask(() => {
42510
+ while (activeCount < concurrency && queue.size > 0) {
42511
+ resumeNext();
42512
+ }
42513
+ });
42514
+ }
42515
+ },
42516
+ map: {
42517
+ async value(iterable, function_) {
42518
+ const promises = Array.from(iterable, (value, index) => this(function_, value, index));
42519
+ return Promise.all(promises);
42520
+ }
42521
+ }
42522
+ });
42523
+ return generator;
42524
+ }
42525
+ function validateConcurrency(concurrency) {
42526
+ if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
42527
+ throw new TypeError("Expected `concurrency` to be a number from 1 and up");
42528
+ }
42529
+ }
42530
+
42531
+ // scripts/swarm.ts
42393
42532
  var TargetSchema = exports_external.array(exports_external.object({
42394
42533
  target: exports_external.string().describe("The target to perform a pentest on"),
42395
42534
  objective: exports_external.string().describe("The objective of the pentest")
@@ -42433,7 +42572,8 @@ async function swarm(options) {
42433
42572
  }
42434
42573
  const session = createSession("swarm", "Multi-target swarm pentest");
42435
42574
  const results = [];
42436
- const promises = targetsArray.map(async (target, idx) => {
42575
+ const limit = pLimit(5);
42576
+ const promises = targetsArray.map((target, idx) => limit(async () => {
42437
42577
  if (!silent) {
42438
42578
  console.log("=".repeat(80));
42439
42579
  console.log(`[${idx + 1}/${targetsArray.length}] Starting pentest for: ${target.target}`);
@@ -42473,8 +42613,8 @@ async function swarm(options) {
42473
42613
  error: error46.message
42474
42614
  });
42475
42615
  }
42476
- });
42477
- await Promise.all(promises);
42616
+ }));
42617
+ await Promise.allSettled(promises);
42478
42618
  if (!silent) {
42479
42619
  console.log("=".repeat(80));
42480
42620
  console.log("SWARM PENTEST SUMMARY");
@@ -42571,7 +42711,9 @@ async function main() {
42571
42711
  objective: session.objective
42572
42712
  }, null, 2));
42573
42713
  } catch (error46) {
42574
- console.error("Fatal error:", error46.message);
42714
+ if (!silent) {
42715
+ console.error("Fatal error:", error46.message);
42716
+ }
42575
42717
  process.exit(1);
42576
42718
  }
42577
42719
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pensar/apex",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "description": "AI-powered penetration testing CLI tool with terminal UI",
5
5
  "module": "src/tui/index.tsx",
6
6
  "main": "build/index.js",
@@ -59,6 +59,7 @@
59
59
  "@opentui/react": "0.1.26",
60
60
  "ai": "^5.0.68",
61
61
  "marked": "^16.4.0",
62
+ "p-limit": "^7.2.0",
62
63
  "react": "^19.2.0",
63
64
  "sharp": "^0.34.4",
64
65
  "zod": "^4.1.12"