@pensar/apex 0.0.36-canary.0 → 0.0.39-canary.2f181ec5

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/build/index.js CHANGED
@@ -27321,6 +27321,12 @@ var OPENROUTER_MODELS = [
27321
27321
  provider: "openrouter",
27322
27322
  contextLength: 64000
27323
27323
  },
27324
+ {
27325
+ id: "mistralai/mistral-large-2512",
27326
+ name: "Mistral Large 3 2512",
27327
+ provider: "openrouter",
27328
+ contextLength: 262144
27329
+ },
27324
27330
  {
27325
27331
  id: "moonshotai/kimi-k2-thinking",
27326
27332
  name: "Kimi K2 Thinking",
@@ -27843,24 +27849,40 @@ function useAgent() {
27843
27849
  }
27844
27850
  function AgentProvider({ children }) {
27845
27851
  const [model, setModel] = import_react10.useState(AVAILABLE_MODELS[0]);
27846
- const [tokenCount, setTokenCount] = import_react10.useState(0);
27852
+ const [tokenUsage, setTokenUsage] = import_react10.useState({
27853
+ inputTokens: 0,
27854
+ outputTokens: 0,
27855
+ totalTokens: 0
27856
+ });
27857
+ const [hasExecuted, setHasExecuted] = import_react10.useState(false);
27847
27858
  const [thinking, setThinking] = import_react10.useState(false);
27848
27859
  const [isExecuting, setIsExecuting] = import_react10.useState(false);
27849
- const addTokens = (tokens) => {
27850
- setTokenCount((prev) => prev + tokens);
27851
- };
27860
+ const addTokenUsage = import_react10.useCallback((input, output) => {
27861
+ setHasExecuted(true);
27862
+ setTokenUsage((prev) => ({
27863
+ inputTokens: prev.inputTokens + input,
27864
+ outputTokens: prev.outputTokens + output,
27865
+ totalTokens: prev.totalTokens + input + output
27866
+ }));
27867
+ }, []);
27868
+ const resetTokenUsage = import_react10.useCallback(() => {
27869
+ setHasExecuted(false);
27870
+ setTokenUsage({ inputTokens: 0, outputTokens: 0, totalTokens: 0 });
27871
+ }, []);
27872
+ const contextValue = import_react10.useMemo(() => ({
27873
+ model,
27874
+ setModel,
27875
+ tokenUsage,
27876
+ addTokenUsage,
27877
+ resetTokenUsage,
27878
+ hasExecuted,
27879
+ thinking,
27880
+ setThinking,
27881
+ isExecuting,
27882
+ setIsExecuting
27883
+ }), [model, tokenUsage, hasExecuted, thinking, isExecuting, addTokenUsage, resetTokenUsage]);
27852
27884
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(AgentContext.Provider, {
27853
- value: {
27854
- model,
27855
- setModel,
27856
- tokenCount,
27857
- setTokenCount,
27858
- addTokens,
27859
- thinking,
27860
- setThinking,
27861
- isExecuting,
27862
- setIsExecuting
27863
- },
27885
+ value: contextValue,
27864
27886
  children
27865
27887
  }, undefined, false, undefined, this);
27866
27888
  }
@@ -27934,7 +27956,7 @@ function Footer({
27934
27956
  showExitWarning = false
27935
27957
  }) {
27936
27958
  cwd = "~" + cwd.split(os2.homedir()).pop() || "";
27937
- const { model, tokenCount, thinking, isExecuting } = useAgent();
27959
+ const { model, tokenUsage, hasExecuted, thinking, isExecuting } = useAgent();
27938
27960
  const hotkeys = isExecuting ? [{ key: "Ctrl+C", label: "Stop Execution" }] : [{ key: "Ctrl+C", label: "Clear/Exit" }];
27939
27961
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
27940
27962
  flexDirection: "row",
@@ -28000,27 +28022,21 @@ function Footer({
28000
28022
  }, undefined, true, undefined, this);
28001
28023
  }
28002
28024
  function AgentStatus() {
28003
- const { tokenCount, thinking, isExecuting } = useAgent();
28025
+ const { tokenUsage, hasExecuted, thinking, isExecuting } = useAgent();
28004
28026
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
28005
28027
  flexDirection: "row",
28006
28028
  gap: 1,
28007
28029
  children: [
28008
- tokenCount > 0 && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
28030
+ hasExecuted && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
28009
28031
  children: [
28010
28032
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
28011
28033
  border: ["right"],
28012
28034
  borderColor: "green"
28013
28035
  }, undefined, false, undefined, this),
28014
28036
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
28015
- fg: "gray",
28016
- children: [
28017
- "■ ",
28018
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
28019
- fg: "white",
28020
- children: formatTokenCount(tokenCount)
28021
- }, undefined, false, undefined, this)
28022
- ]
28023
- }, undefined, true, undefined, this),
28037
+ fg: "white",
28038
+ children: `↓${formatTokenCount(tokenUsage.inputTokens)} ↑${formatTokenCount(tokenUsage.outputTokens)} Σ${formatTokenCount(tokenUsage.totalTokens)}`
28039
+ }, undefined, false, undefined, this),
28024
28040
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ContextProgress, {
28025
28041
  width: 10
28026
28042
  }, undefined, false, undefined, this)
@@ -28042,10 +28058,11 @@ function AgentStatus() {
28042
28058
  }, undefined, true, undefined, this);
28043
28059
  }
28044
28060
  function ContextProgress({ width }) {
28045
- const { model, tokenCount, thinking } = useAgent();
28046
- const contextProgress = Number((tokenCount / (model.contextLength ?? 200000) * 100).toFixed(2));
28047
- if (!thinking)
28061
+ const { model, tokenUsage, thinking } = useAgent();
28062
+ if (!thinking || tokenUsage.totalTokens === 0)
28048
28063
  return null;
28064
+ const contextLength = model.contextLength ?? 200000;
28065
+ const contextProgress = Math.max(0, Math.min(100, Number((tokenUsage.totalTokens / contextLength * 100).toFixed(2))));
28049
28066
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ProgressBar, {
28050
28067
  value: contextProgress,
28051
28068
  width
@@ -65011,11 +65028,38 @@ async function summarizeConversation(messages, opts, model) {
65011
65028
  content: `Summarize this conversation to pass to another agent. This was the system prompt: ${opts.system} `
65012
65029
  }
65013
65030
  ];
65014
- const { text: summary } = await generateText({
65031
+ const { text: summary, usage: summaryUsage } = await generateText({
65015
65032
  model,
65016
65033
  system: `You are a helpful assistant that summarizes conversations to pass to another agent. Review the conversation and system prompt at the end provided by the user.`,
65017
65034
  messages: summarizedMessages
65018
65035
  });
65036
+ if (opts.onStepFinish && summaryUsage) {
65037
+ opts.onStepFinish({
65038
+ text: "",
65039
+ reasoning: undefined,
65040
+ reasoningDetails: [],
65041
+ files: [],
65042
+ sources: [],
65043
+ toolCalls: [],
65044
+ toolResults: [],
65045
+ finishReason: "stop",
65046
+ usage: {
65047
+ inputTokens: summaryUsage.inputTokens ?? 0,
65048
+ outputTokens: summaryUsage.outputTokens ?? 0,
65049
+ totalTokens: summaryUsage.totalTokens ?? 0
65050
+ },
65051
+ warnings: [],
65052
+ request: {},
65053
+ response: {
65054
+ id: "summarization",
65055
+ timestamp: new Date,
65056
+ modelId: ""
65057
+ },
65058
+ providerMetadata: undefined,
65059
+ stepType: "initial",
65060
+ isContinued: false
65061
+ });
65062
+ }
65019
65063
  const originalLength = typeof opts.prompt === "string" ? opts.prompt.length : 0;
65020
65064
  const enhancedPrompt = originalLength > 1e5 ? `Context: The previous conversation contained very long content that was summarized.
65021
65065
 
@@ -65178,6 +65222,7 @@ function streamResponse(opts) {
65178
65222
  } = opts;
65179
65223
  const messagesContainer = { current: messages || [] };
65180
65224
  const providerModel = getProviderModel(model, authConfig);
65225
+ let rateLimitRetryCount = 0;
65181
65226
  try {
65182
65227
  const response = streamText({
65183
65228
  model: providerModel,
@@ -65191,6 +65236,16 @@ function streamResponse(opts) {
65191
65236
  messagesContainer.current = opts2.messages;
65192
65237
  return;
65193
65238
  },
65239
+ onError: async ({ error: error46 }) => {
65240
+ if (error46.message.toLowerCase().includes("too many tokens") || error46.message.toLowerCase().includes("overloaded")) {
65241
+ rateLimitRetryCount++;
65242
+ await new Promise((resolve4) => setTimeout(resolve4, 1000 * rateLimitRetryCount));
65243
+ if (rateLimitRetryCount < 20) {
65244
+ return;
65245
+ }
65246
+ }
65247
+ throw error46;
65248
+ },
65194
65249
  onStepFinish,
65195
65250
  abortSignal,
65196
65251
  activeTools,
@@ -65209,7 +65264,7 @@ function streamResponse(opts) {
65209
65264
  throw new Error(`Tool ${toolCall.toolName} not found or has no schema`);
65210
65265
  }
65211
65266
  const jsonSchema2 = inputSchema({ toolName: toolCall.toolName });
65212
- const { object: repairedArgs } = await generateObject({
65267
+ const { object: repairedArgs, usage: repairUsage } = await generateObject({
65213
65268
  model: providerModel,
65214
65269
  schema: tool2.inputSchema,
65215
65270
  prompt: [
@@ -65222,6 +65277,33 @@ function streamResponse(opts) {
65222
65277
  ].join(`
65223
65278
  `)
65224
65279
  });
65280
+ if (onStepFinish && repairUsage) {
65281
+ onStepFinish({
65282
+ text: "",
65283
+ reasoning: undefined,
65284
+ reasoningDetails: [],
65285
+ files: [],
65286
+ sources: [],
65287
+ toolCalls: [],
65288
+ toolResults: [],
65289
+ finishReason: "stop",
65290
+ usage: {
65291
+ inputTokens: repairUsage.inputTokens ?? 0,
65292
+ outputTokens: repairUsage.outputTokens ?? 0,
65293
+ totalTokens: repairUsage.totalTokens ?? 0
65294
+ },
65295
+ warnings: [],
65296
+ request: {},
65297
+ response: {
65298
+ id: "tool-repair",
65299
+ timestamp: new Date,
65300
+ modelId: ""
65301
+ },
65302
+ providerMetadata: undefined,
65303
+ stepType: "initial",
65304
+ isContinued: false
65305
+ });
65306
+ }
65225
65307
  return { ...toolCall, input: JSON.stringify(repairedArgs) };
65226
65308
  } catch (repairError) {
65227
65309
  if (!silent) {
@@ -65248,9 +65330,9 @@ function streamResponse(opts) {
65248
65330
  }
65249
65331
  }
65250
65332
  async function generateObjectResponse(opts) {
65251
- const { model, schema, prompt, system, maxTokens, temperature, authConfig } = opts;
65333
+ const { model, schema, prompt, system, maxTokens, temperature, authConfig, onTokenUsage } = opts;
65252
65334
  const providerModel = getProviderModel(model, authConfig);
65253
- const { object: object3 } = await generateObject({
65335
+ const { object: object3, usage } = await generateObject({
65254
65336
  model: providerModel,
65255
65337
  schema,
65256
65338
  prompt,
@@ -65258,6 +65340,9 @@ async function generateObjectResponse(opts) {
65258
65340
  maxTokens,
65259
65341
  temperature
65260
65342
  });
65343
+ if (onTokenUsage && usage) {
65344
+ onTokenUsage(usage.inputTokens ?? 0, usage.outputTokens ?? 0);
65345
+ }
65261
65346
  return object3;
65262
65347
  }
65263
65348
  // src/core/agent/pentestAgent/prompts.ts
@@ -68639,7 +68724,7 @@ Example workflow:
68639
68724
  execute: async (params) => recordTestResultCore(session, params)
68640
68725
  });
68641
68726
  }
68642
- async function generateTestStrategy(params, model) {
68727
+ async function generateTestStrategy(params, model, onTokenUsage) {
68643
68728
  const prompt = `You are a penetration testing expert. Generate a concise testing strategy:
68644
68729
 
68645
68730
  Attack Type: ${params.knowledge.name}
@@ -68665,12 +68750,15 @@ Be tactical and specific.`;
68665
68750
  model: providerModel,
68666
68751
  prompt
68667
68752
  });
68753
+ if (onTokenUsage && result.usage) {
68754
+ onTokenUsage(result.usage.inputTokens ?? 0, result.usage.outputTokens ?? 0);
68755
+ }
68668
68756
  return result.text;
68669
68757
  } catch (error46) {
68670
68758
  return params.knowledge.adaptiveStrategy;
68671
68759
  }
68672
68760
  }
68673
- async function generatePayload(params, model) {
68761
+ async function generatePayload(params, model, onTokenUsage) {
68674
68762
  const prompt = `Generate ONE ${params.knowledge.name} payload for testing.
68675
68763
 
68676
68764
  Techniques:
@@ -68694,7 +68782,8 @@ Generate ONE specific payload. Return ONLY JSON:
68694
68782
  const result = await generateObjectResponse({
68695
68783
  model,
68696
68784
  schema: PayloadSchema,
68697
- prompt
68785
+ prompt,
68786
+ onTokenUsage
68698
68787
  });
68699
68788
  return result;
68700
68789
  } catch (error46) {
@@ -68707,7 +68796,7 @@ Generate ONE specific payload. Return ONLY JSON:
68707
68796
  technique: technique.name
68708
68797
  };
68709
68798
  }
68710
- async function analyzeResponse(params, model) {
68799
+ async function analyzeResponse(params, model, onTokenUsage) {
68711
68800
  const prompt = `Analyze this security test response:
68712
68801
 
68713
68802
  Attack: ${params.knowledge.name}
@@ -68735,7 +68824,8 @@ Analyze: Is this vulnerable? Return ONLY JSON:
68735
68824
  const result = await generateObjectResponse({
68736
68825
  model,
68737
68826
  schema: AnalysisSchema,
68738
- prompt
68827
+ prompt,
68828
+ onTokenUsage
68739
68829
  });
68740
68830
  return result;
68741
68831
  } catch (error46) {
@@ -68754,7 +68844,7 @@ Analyze: Is this vulnerable? Return ONLY JSON:
68754
68844
  suggestedNextTest: "Try alternative payload or technique"
68755
68845
  };
68756
68846
  }
68757
- function createSmartTestTool(session, model) {
68847
+ function createSmartTestTool(session, model, onTokenUsage) {
68758
68848
  return tool({
68759
68849
  name: "test_parameter",
68760
68850
  description: `Intelligently test a parameter for a vulnerability using AI-powered adaptive testing.
@@ -68824,7 +68914,7 @@ test_parameter({
68824
68914
  parameter,
68825
68915
  endpoint,
68826
68916
  context
68827
- }, model);
68917
+ }, model, onTokenUsage);
68828
68918
  console.log(`Strategy: ${strategy}`);
68829
68919
  const results = [];
68830
68920
  let vulnerable = false;
@@ -68837,7 +68927,7 @@ test_parameter({
68837
68927
  context: { ...context, parameter, endpoint },
68838
68928
  previousResults: results,
68839
68929
  round
68840
- }, model);
68930
+ }, model, onTokenUsage);
68841
68931
  console.log(` Payload: ${payloadData.payload}`);
68842
68932
  console.log(` Reasoning: ${payloadData.reasoning}`);
68843
68933
  let response;
@@ -68866,7 +68956,7 @@ test_parameter({
68866
68956
  attackType,
68867
68957
  knowledge,
68868
68958
  previousResults: results
68869
- }, model);
68959
+ }, model, onTokenUsage);
68870
68960
  console.log(` Analysis: ${analysis.reasoning}`);
68871
68961
  console.log(` Vulnerable: ${analysis.vulnerable} (confidence: ${analysis.confidence})`);
68872
68962
  results.push({
@@ -69316,7 +69406,7 @@ function wrapCommandWithHeaders(command, headers) {
69316
69406
  }
69317
69407
  return wrapped;
69318
69408
  }
69319
- function createPentestTools(session, model, toolOverride) {
69409
+ function createPentestTools(session, model, toolOverride, onTokenUsage) {
69320
69410
  const offensiveHeaders = getOffensiveHeaders(session);
69321
69411
  const rateLimiter = session._rateLimiter;
69322
69412
  const executeCommand = tool({
@@ -69490,7 +69580,7 @@ COMMON TESTING PATTERNS:
69490
69580
  http_request: httpRequest,
69491
69581
  document_finding: createDocumentFindingTool(session),
69492
69582
  record_test_result: createRecordTestResultTool(session),
69493
- test_parameter: createSmartTestTool(session, model || "claude-sonnet-4-20250514"),
69583
+ test_parameter: createSmartTestTool(session, model || "claude-sonnet-4-20250514", onTokenUsage),
69494
69584
  check_testing_coverage: createCheckTestingCoverageTool(session),
69495
69585
  validate_completeness: createValidateCompletenessTool(session),
69496
69586
  enumerate_endpoints: createEnumerateEndpointsTool(session),
@@ -70549,6 +70639,7 @@ function runAgent(opts) {
70549
70639
  objective,
70550
70640
  model,
70551
70641
  onStepFinish,
70642
+ onToolTokenUsage,
70552
70643
  abortSignal,
70553
70644
  silent,
70554
70645
  authConfig,
@@ -70568,7 +70659,7 @@ function runAgent(opts) {
70568
70659
  analyze_scan,
70569
70660
  scratchpad,
70570
70661
  generate_report
70571
- } = createPentestTools(session, undefined, toolOverride);
70662
+ } = createPentestTools(session, undefined, toolOverride, onToolTokenUsage);
70572
70663
  const document_finding = tool({
70573
70664
  name: "document_finding",
70574
70665
  description: `Document a security finding with severity, impact, and remediation guidance.
@@ -72334,8 +72425,8 @@ function PentestAgentDisplay() {
72334
72425
  const { closePentest } = useCommand();
72335
72426
  const {
72336
72427
  model,
72337
- addTokens,
72338
- setTokenCount,
72428
+ addTokenUsage,
72429
+ resetTokenUsage,
72339
72430
  setThinking,
72340
72431
  isExecuting,
72341
72432
  setIsExecuting
@@ -72500,6 +72591,7 @@ function PentestAgentDisplay() {
72500
72591
  setHasStarted(true);
72501
72592
  setThinking(true);
72502
72593
  setIsExecuting(true);
72594
+ resetTokenUsage();
72503
72595
  const controller = new AbortController;
72504
72596
  setAbortController(controller);
72505
72597
  try {
@@ -72510,8 +72602,12 @@ function PentestAgentDisplay() {
72510
72602
  abortSignal: controller.signal,
72511
72603
  sessionConfig,
72512
72604
  onStepFinish: ({ usage }) => {
72513
- const stepTokens = (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
72514
- setTokenCount(stepTokens);
72605
+ const inputTokens = usage.inputTokens ?? 0;
72606
+ const outputTokens = usage.outputTokens ?? 0;
72607
+ addTokenUsage(inputTokens, outputTokens);
72608
+ },
72609
+ onToolTokenUsage: (inputTokens, outputTokens) => {
72610
+ addTokenUsage(inputTokens, outputTokens);
72515
72611
  }
72516
72612
  });
72517
72613
  setSessionPath(result.session.rootPath);
@@ -72529,7 +72625,6 @@ Path: ${result.session.rootPath}`,
72529
72625
  for await (const delta of result.fullStream) {
72530
72626
  if (delta.type === "text-delta") {
72531
72627
  currentAssistantText += delta.text;
72532
- addTokens(1);
72533
72628
  const lastMessage = allMessages[allMessages.length - 1];
72534
72629
  if (lastMessage && lastMessage.role === "assistant") {
72535
72630
  lastMessage.content = currentAssistantText;
@@ -74821,7 +74916,7 @@ If resuming from summarization, review the assets in the session assets folder a
74821
74916
  import { join as join5 } from "path";
74822
74917
  import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync10 } from "fs";
74823
74918
  function runAgent2(opts) {
74824
- const { target, model, onStepFinish, abortSignal } = opts;
74919
+ const { target, model, onStepFinish, onToolTokenUsage, abortSignal } = opts;
74825
74920
  const session = opts.session || createSession(target);
74826
74921
  const subagentId = `attack-surface-${nanoid3(6)}`;
74827
74922
  console.log(`Created attack surface session: ${session.id}`);
@@ -74830,7 +74925,7 @@ function runAgent2(opts) {
74830
74925
  if (!existsSync10(assetsPath)) {
74831
74926
  mkdirSync5(assetsPath, { recursive: true });
74832
74927
  }
74833
- const { analyze_scan, execute_command, http_request } = createPentestTools(session, model);
74928
+ const { analyze_scan, execute_command, http_request } = createPentestTools(session, model, undefined, onToolTokenUsage);
74834
74929
  const document_asset = tool({
74835
74930
  name: "document_asset",
74836
74931
  description: `Document a discovered asset during attack surface analysis.
@@ -75127,13 +75222,14 @@ function runAgent3(opts) {
75127
75222
  onSubagentSpawn,
75128
75223
  onSubagentMessage,
75129
75224
  onSubagentComplete,
75225
+ onSubagentTokenUsage,
75130
75226
  session: sessionProp
75131
75227
  } = opts;
75132
75228
  const session = sessionProp || createSession(target, undefined, undefined, sessionConfig);
75133
75229
  const logger = new Logger(session);
75134
75230
  logger.log(`Created thorough pentest session: ${session.id}`);
75135
75231
  logger.log(`Session path: ${session.rootPath}`);
75136
- const tools2 = createOrchestratorTools(session, model, abortSignal, onSubagentSpawn, onSubagentMessage, onSubagentComplete, logger);
75232
+ const tools2 = createOrchestratorTools(session, model, abortSignal, onSubagentSpawn, onSubagentMessage, onSubagentComplete, onSubagentTokenUsage, logger);
75137
75233
  const enhancedPrompt = `
75138
75234
  TARGET: ${target}
75139
75235
 
@@ -75169,7 +75265,7 @@ Begin by using the get_attack_surface tool to map the complete attack surface of
75169
75265
  streamResult.session = session;
75170
75266
  return { streamResult, session };
75171
75267
  }
75172
- function createOrchestratorTools(session, model, abortSignal, onSubagentSpawn, onSubagentMessage, onSubagentComplete, logger) {
75268
+ function createOrchestratorTools(session, model, abortSignal, onSubagentSpawn, onSubagentMessage, onSubagentComplete, onSubagentTokenUsage, logger) {
75173
75269
  const getAttackSurface = tool({
75174
75270
  name: "get_attack_surface",
75175
75271
  description: `Run the attack surface analysis agent to discover all assets and identify targets.
@@ -75201,7 +75297,19 @@ Use this as the FIRST step in your thorough penetration test.`,
75201
75297
  target,
75202
75298
  objective,
75203
75299
  model,
75204
- abortSignal
75300
+ abortSignal,
75301
+ onStepFinish: ({ usage }) => {
75302
+ if (onSubagentTokenUsage) {
75303
+ const inputTokens = usage.inputTokens ?? 0;
75304
+ const outputTokens = usage.outputTokens ?? 0;
75305
+ onSubagentTokenUsage(subagentId, inputTokens, outputTokens);
75306
+ }
75307
+ },
75308
+ onToolTokenUsage: (inputTokens, outputTokens) => {
75309
+ if (onSubagentTokenUsage) {
75310
+ onSubagentTokenUsage(subagentId, inputTokens, outputTokens);
75311
+ }
75312
+ }
75205
75313
  });
75206
75314
  const allMessages = [];
75207
75315
  let currentAssistantText = "";
@@ -75376,7 +75484,19 @@ You can spawn multiple agents in parallel - they will run concurrently.`,
75376
75484
  target: targetInfo.target,
75377
75485
  objective: targetInfo.objective,
75378
75486
  model,
75379
- abortSignal
75487
+ abortSignal,
75488
+ onStepFinish: ({ usage }) => {
75489
+ if (onSubagentTokenUsage) {
75490
+ const inputTokens = usage.inputTokens ?? 0;
75491
+ const outputTokens = usage.outputTokens ?? 0;
75492
+ onSubagentTokenUsage(subagentId, inputTokens, outputTokens);
75493
+ }
75494
+ },
75495
+ onToolTokenUsage: (inputTokens, outputTokens) => {
75496
+ if (onSubagentTokenUsage) {
75497
+ onSubagentTokenUsage(subagentId, inputTokens, outputTokens);
75498
+ }
75499
+ }
75380
75500
  });
75381
75501
  const allMessages = [];
75382
75502
  let currentAssistantText = "";
@@ -75703,8 +75823,8 @@ function useThoroughPentestAgent() {
75703
75823
  const [abortController, setAbortController] = import_react22.useState(null);
75704
75824
  const {
75705
75825
  model,
75706
- addTokens,
75707
- setTokenCount,
75826
+ addTokenUsage,
75827
+ resetTokenUsage,
75708
75828
  setThinking,
75709
75829
  isExecuting,
75710
75830
  setIsExecuting
@@ -75732,6 +75852,7 @@ function useThoroughPentestAgent() {
75732
75852
  setHasStarted(true);
75733
75853
  setThinking(true);
75734
75854
  setIsExecuting(true);
75855
+ resetTokenUsage();
75735
75856
  const controller = new AbortController;
75736
75857
  setAbortController(controller);
75737
75858
  try {
@@ -75741,8 +75862,12 @@ function useThoroughPentestAgent() {
75741
75862
  abortSignal: controller.signal,
75742
75863
  sessionConfig,
75743
75864
  onStepFinish: ({ usage }) => {
75744
- const stepTokens = (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
75745
- setTokenCount(stepTokens);
75865
+ const inputTokens = usage.inputTokens ?? 0;
75866
+ const outputTokens = usage.outputTokens ?? 0;
75867
+ addTokenUsage(inputTokens, outputTokens);
75868
+ },
75869
+ onSubagentTokenUsage: (subagentId, input, output) => {
75870
+ addTokenUsage(input, output);
75746
75871
  },
75747
75872
  onSubagentSpawn: (subagentInfo) => {
75748
75873
  setSubagents((prev) => [
@@ -75853,7 +75978,6 @@ Mode: Pentest (Orchestrator)`,
75853
75978
  await consumeStream2(result, {
75854
75979
  onTextDelta: (delta) => {
75855
75980
  currentAssistantText += delta.text;
75856
- addTokens(1);
75857
75981
  const lastMessage = allMessages[allMessages.length - 1];
75858
75982
  if (lastMessage && lastMessage.role === "assistant") {
75859
75983
  lastMessage.content = currentAssistantText;