@pensar/apex 0.0.37-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/README.md +29 -1
- package/build/benchmark.js +121 -22
- package/build/index.js +180 -62
- package/build/pentest.js +121 -22
- package/build/quicktest.js +90 -16
- package/build/swarm.js +90 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
<h1 align="center">Pensar Apex</h1>
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<a href="https://www.npmjs.com/package/@pensar/apex"><img src="https://img.shields.io/npm/v/@pensar/apex" alt="npm version"></a>
|
|
4
|
+
<a href="https://www.npmjs.com/package/@pensar/apex"><img src="https://img.shields.io/npm/v/@pensar/apex?label=latest" alt="npm version"></a>
|
|
5
|
+
<a href="https://www.npmjs.com/package/@pensar/apex"><img src="https://img.shields.io/npm/v/@pensar/apex/canary?label=prerelease&color=yellow" alt="npm prerelease version"></a>
|
|
6
|
+
<!-- <a href="https://www.npmjs.com/package/@pensar/apex"><img src="https://img.shields.io/npm/dm/@pensar/apex" alt="npm downloads"></a> -->
|
|
7
|
+
<a href="https://github.com/pensarai/homebrew-tap"><img src="https://img.shields.io/github/v/release/pensarai/apex?label=homebrew&logo=homebrew&color=orange" alt="Homebrew"></a>
|
|
8
|
+
<a href="./LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-blue" alt="Apache 2.0 License"></a>
|
|
9
|
+
<a href="https://docs.pensar.dev/apex"><img src="https://img.shields.io/badge/docs-docs.pensar.dev/apex-purple?logo=readthedocs&logoColor=white" alt="Documentation"></a>
|
|
10
|
+
</p>
|
|
11
|
+
|
|
5
12
|
<p align="center">
|
|
6
13
|
<img src="screenshot.png" alt="Pensar Apex Screenshot" width="800">
|
|
7
14
|
</p>
|
|
@@ -40,6 +47,27 @@ Download installer from `https://nmap.org/download.html` and ensure `nmap` is on
|
|
|
40
47
|
|
|
41
48
|
### Install Apex
|
|
42
49
|
|
|
50
|
+
#### macOS / Linux (Quick Install)
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
curl -fsSL https://pensarai.com/install.sh | bash
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
#### Homebrew
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
brew tap pensarai/tap
|
|
60
|
+
brew install apex
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
#### Windows (PowerShell)
|
|
64
|
+
|
|
65
|
+
```powershell
|
|
66
|
+
irm https://pensarai.com/apex.ps1 | iex
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
#### npm
|
|
70
|
+
|
|
43
71
|
```bash
|
|
44
72
|
npm install -g @pensar/apex
|
|
45
73
|
```
|
package/build/benchmark.js
CHANGED
|
@@ -121801,11 +121801,38 @@ async function summarizeConversation(messages, opts, model) {
|
|
|
121801
121801
|
content: `Summarize this conversation to pass to another agent. This was the system prompt: ${opts.system} `
|
|
121802
121802
|
}
|
|
121803
121803
|
];
|
|
121804
|
-
const { text: summary } = await generateText({
|
|
121804
|
+
const { text: summary, usage: summaryUsage } = await generateText({
|
|
121805
121805
|
model,
|
|
121806
121806
|
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.`,
|
|
121807
121807
|
messages: summarizedMessages
|
|
121808
121808
|
});
|
|
121809
|
+
if (opts.onStepFinish && summaryUsage) {
|
|
121810
|
+
opts.onStepFinish({
|
|
121811
|
+
text: "",
|
|
121812
|
+
reasoning: undefined,
|
|
121813
|
+
reasoningDetails: [],
|
|
121814
|
+
files: [],
|
|
121815
|
+
sources: [],
|
|
121816
|
+
toolCalls: [],
|
|
121817
|
+
toolResults: [],
|
|
121818
|
+
finishReason: "stop",
|
|
121819
|
+
usage: {
|
|
121820
|
+
inputTokens: summaryUsage.inputTokens ?? 0,
|
|
121821
|
+
outputTokens: summaryUsage.outputTokens ?? 0,
|
|
121822
|
+
totalTokens: summaryUsage.totalTokens ?? 0
|
|
121823
|
+
},
|
|
121824
|
+
warnings: [],
|
|
121825
|
+
request: {},
|
|
121826
|
+
response: {
|
|
121827
|
+
id: "summarization",
|
|
121828
|
+
timestamp: new Date,
|
|
121829
|
+
modelId: ""
|
|
121830
|
+
},
|
|
121831
|
+
providerMetadata: undefined,
|
|
121832
|
+
stepType: "initial",
|
|
121833
|
+
isContinued: false
|
|
121834
|
+
});
|
|
121835
|
+
}
|
|
121809
121836
|
const originalLength = typeof opts.prompt === "string" ? opts.prompt.length : 0;
|
|
121810
121837
|
const enhancedPrompt = originalLength > 1e5 ? `Context: The previous conversation contained very long content that was summarized.
|
|
121811
121838
|
|
|
@@ -121968,6 +121995,7 @@ function streamResponse(opts) {
|
|
|
121968
121995
|
} = opts;
|
|
121969
121996
|
const messagesContainer = { current: messages || [] };
|
|
121970
121997
|
const providerModel = getProviderModel(model, authConfig);
|
|
121998
|
+
let rateLimitRetryCount = 0;
|
|
121971
121999
|
try {
|
|
121972
122000
|
const response = streamText({
|
|
121973
122001
|
model: providerModel,
|
|
@@ -121981,6 +122009,16 @@ function streamResponse(opts) {
|
|
|
121981
122009
|
messagesContainer.current = opts2.messages;
|
|
121982
122010
|
return;
|
|
121983
122011
|
},
|
|
122012
|
+
onError: async ({ error: error46 }) => {
|
|
122013
|
+
if (error46.message.toLowerCase().includes("too many tokens") || error46.message.toLowerCase().includes("overloaded")) {
|
|
122014
|
+
rateLimitRetryCount++;
|
|
122015
|
+
await new Promise((resolve2) => setTimeout(resolve2, 1000 * rateLimitRetryCount));
|
|
122016
|
+
if (rateLimitRetryCount < 20) {
|
|
122017
|
+
return;
|
|
122018
|
+
}
|
|
122019
|
+
}
|
|
122020
|
+
throw error46;
|
|
122021
|
+
},
|
|
121984
122022
|
onStepFinish,
|
|
121985
122023
|
abortSignal,
|
|
121986
122024
|
activeTools,
|
|
@@ -121999,7 +122037,7 @@ function streamResponse(opts) {
|
|
|
121999
122037
|
throw new Error(`Tool ${toolCall.toolName} not found or has no schema`);
|
|
122000
122038
|
}
|
|
122001
122039
|
const jsonSchema2 = inputSchema({ toolName: toolCall.toolName });
|
|
122002
|
-
const { object: repairedArgs } = await generateObject({
|
|
122040
|
+
const { object: repairedArgs, usage: repairUsage } = await generateObject({
|
|
122003
122041
|
model: providerModel,
|
|
122004
122042
|
schema: tool2.inputSchema,
|
|
122005
122043
|
prompt: [
|
|
@@ -122012,6 +122050,33 @@ function streamResponse(opts) {
|
|
|
122012
122050
|
].join(`
|
|
122013
122051
|
`)
|
|
122014
122052
|
});
|
|
122053
|
+
if (onStepFinish && repairUsage) {
|
|
122054
|
+
onStepFinish({
|
|
122055
|
+
text: "",
|
|
122056
|
+
reasoning: undefined,
|
|
122057
|
+
reasoningDetails: [],
|
|
122058
|
+
files: [],
|
|
122059
|
+
sources: [],
|
|
122060
|
+
toolCalls: [],
|
|
122061
|
+
toolResults: [],
|
|
122062
|
+
finishReason: "stop",
|
|
122063
|
+
usage: {
|
|
122064
|
+
inputTokens: repairUsage.inputTokens ?? 0,
|
|
122065
|
+
outputTokens: repairUsage.outputTokens ?? 0,
|
|
122066
|
+
totalTokens: repairUsage.totalTokens ?? 0
|
|
122067
|
+
},
|
|
122068
|
+
warnings: [],
|
|
122069
|
+
request: {},
|
|
122070
|
+
response: {
|
|
122071
|
+
id: "tool-repair",
|
|
122072
|
+
timestamp: new Date,
|
|
122073
|
+
modelId: ""
|
|
122074
|
+
},
|
|
122075
|
+
providerMetadata: undefined,
|
|
122076
|
+
stepType: "initial",
|
|
122077
|
+
isContinued: false
|
|
122078
|
+
});
|
|
122079
|
+
}
|
|
122015
122080
|
return { ...toolCall, input: JSON.stringify(repairedArgs) };
|
|
122016
122081
|
} catch (repairError) {
|
|
122017
122082
|
if (!silent) {
|
|
@@ -122038,9 +122103,9 @@ function streamResponse(opts) {
|
|
|
122038
122103
|
}
|
|
122039
122104
|
}
|
|
122040
122105
|
async function generateObjectResponse(opts) {
|
|
122041
|
-
const { model, schema, prompt, system, maxTokens, temperature, authConfig } = opts;
|
|
122106
|
+
const { model, schema, prompt, system, maxTokens, temperature, authConfig, onTokenUsage } = opts;
|
|
122042
122107
|
const providerModel = getProviderModel(model, authConfig);
|
|
122043
|
-
const { object: object3 } = await generateObject({
|
|
122108
|
+
const { object: object3, usage } = await generateObject({
|
|
122044
122109
|
model: providerModel,
|
|
122045
122110
|
schema,
|
|
122046
122111
|
prompt,
|
|
@@ -122048,6 +122113,9 @@ async function generateObjectResponse(opts) {
|
|
|
122048
122113
|
maxTokens,
|
|
122049
122114
|
temperature
|
|
122050
122115
|
});
|
|
122116
|
+
if (onTokenUsage && usage) {
|
|
122117
|
+
onTokenUsage(usage.inputTokens ?? 0, usage.outputTokens ?? 0);
|
|
122118
|
+
}
|
|
122051
122119
|
return object3;
|
|
122052
122120
|
}
|
|
122053
122121
|
// src/core/agent/benchmark/prompts.ts
|
|
@@ -126861,6 +126929,7 @@ function runAgent(opts) {
|
|
|
126861
126929
|
objective,
|
|
126862
126930
|
model,
|
|
126863
126931
|
onStepFinish,
|
|
126932
|
+
onToolTokenUsage,
|
|
126864
126933
|
abortSignal,
|
|
126865
126934
|
silent,
|
|
126866
126935
|
authConfig,
|
|
@@ -126880,7 +126949,7 @@ function runAgent(opts) {
|
|
|
126880
126949
|
analyze_scan,
|
|
126881
126950
|
scratchpad,
|
|
126882
126951
|
generate_report
|
|
126883
|
-
} = createPentestTools(session, undefined, toolOverride);
|
|
126952
|
+
} = createPentestTools(session, undefined, toolOverride, onToolTokenUsage);
|
|
126884
126953
|
const document_finding = tool({
|
|
126885
126954
|
name: "document_finding",
|
|
126886
126955
|
description: `Document a security finding with severity, impact, and remediation guidance.
|
|
@@ -128711,7 +128780,7 @@ Example workflow:
|
|
|
128711
128780
|
execute: async (params) => recordTestResultCore(session, params)
|
|
128712
128781
|
});
|
|
128713
128782
|
}
|
|
128714
|
-
async function generateTestStrategy(params, model) {
|
|
128783
|
+
async function generateTestStrategy(params, model, onTokenUsage) {
|
|
128715
128784
|
const prompt = `You are a penetration testing expert. Generate a concise testing strategy:
|
|
128716
128785
|
|
|
128717
128786
|
Attack Type: ${params.knowledge.name}
|
|
@@ -128737,12 +128806,15 @@ Be tactical and specific.`;
|
|
|
128737
128806
|
model: providerModel,
|
|
128738
128807
|
prompt
|
|
128739
128808
|
});
|
|
128809
|
+
if (onTokenUsage && result.usage) {
|
|
128810
|
+
onTokenUsage(result.usage.inputTokens ?? 0, result.usage.outputTokens ?? 0);
|
|
128811
|
+
}
|
|
128740
128812
|
return result.text;
|
|
128741
128813
|
} catch (error46) {
|
|
128742
128814
|
return params.knowledge.adaptiveStrategy;
|
|
128743
128815
|
}
|
|
128744
128816
|
}
|
|
128745
|
-
async function generatePayload(params, model) {
|
|
128817
|
+
async function generatePayload(params, model, onTokenUsage) {
|
|
128746
128818
|
const prompt = `Generate ONE ${params.knowledge.name} payload for testing.
|
|
128747
128819
|
|
|
128748
128820
|
Techniques:
|
|
@@ -128766,7 +128838,8 @@ Generate ONE specific payload. Return ONLY JSON:
|
|
|
128766
128838
|
const result = await generateObjectResponse({
|
|
128767
128839
|
model,
|
|
128768
128840
|
schema: PayloadSchema,
|
|
128769
|
-
prompt
|
|
128841
|
+
prompt,
|
|
128842
|
+
onTokenUsage
|
|
128770
128843
|
});
|
|
128771
128844
|
return result;
|
|
128772
128845
|
} catch (error46) {
|
|
@@ -128779,7 +128852,7 @@ Generate ONE specific payload. Return ONLY JSON:
|
|
|
128779
128852
|
technique: technique.name
|
|
128780
128853
|
};
|
|
128781
128854
|
}
|
|
128782
|
-
async function analyzeResponse(params, model) {
|
|
128855
|
+
async function analyzeResponse(params, model, onTokenUsage) {
|
|
128783
128856
|
const prompt = `Analyze this security test response:
|
|
128784
128857
|
|
|
128785
128858
|
Attack: ${params.knowledge.name}
|
|
@@ -128807,7 +128880,8 @@ Analyze: Is this vulnerable? Return ONLY JSON:
|
|
|
128807
128880
|
const result = await generateObjectResponse({
|
|
128808
128881
|
model,
|
|
128809
128882
|
schema: AnalysisSchema,
|
|
128810
|
-
prompt
|
|
128883
|
+
prompt,
|
|
128884
|
+
onTokenUsage
|
|
128811
128885
|
});
|
|
128812
128886
|
return result;
|
|
128813
128887
|
} catch (error46) {
|
|
@@ -128826,7 +128900,7 @@ Analyze: Is this vulnerable? Return ONLY JSON:
|
|
|
128826
128900
|
suggestedNextTest: "Try alternative payload or technique"
|
|
128827
128901
|
};
|
|
128828
128902
|
}
|
|
128829
|
-
function createSmartTestTool(session, model) {
|
|
128903
|
+
function createSmartTestTool(session, model, onTokenUsage) {
|
|
128830
128904
|
return tool({
|
|
128831
128905
|
name: "test_parameter",
|
|
128832
128906
|
description: `Intelligently test a parameter for a vulnerability using AI-powered adaptive testing.
|
|
@@ -128896,7 +128970,7 @@ test_parameter({
|
|
|
128896
128970
|
parameter,
|
|
128897
128971
|
endpoint,
|
|
128898
128972
|
context
|
|
128899
|
-
}, model);
|
|
128973
|
+
}, model, onTokenUsage);
|
|
128900
128974
|
console.log(`Strategy: ${strategy}`);
|
|
128901
128975
|
const results = [];
|
|
128902
128976
|
let vulnerable = false;
|
|
@@ -128909,7 +128983,7 @@ test_parameter({
|
|
|
128909
128983
|
context: { ...context, parameter, endpoint },
|
|
128910
128984
|
previousResults: results,
|
|
128911
128985
|
round
|
|
128912
|
-
}, model);
|
|
128986
|
+
}, model, onTokenUsage);
|
|
128913
128987
|
console.log(` Payload: ${payloadData.payload}`);
|
|
128914
128988
|
console.log(` Reasoning: ${payloadData.reasoning}`);
|
|
128915
128989
|
let response;
|
|
@@ -128938,7 +129012,7 @@ test_parameter({
|
|
|
128938
129012
|
attackType,
|
|
128939
129013
|
knowledge,
|
|
128940
129014
|
previousResults: results
|
|
128941
|
-
}, model);
|
|
129015
|
+
}, model, onTokenUsage);
|
|
128942
129016
|
console.log(` Analysis: ${analysis.reasoning}`);
|
|
128943
129017
|
console.log(` Vulnerable: ${analysis.vulnerable} (confidence: ${analysis.confidence})`);
|
|
128944
129018
|
results.push({
|
|
@@ -129388,7 +129462,7 @@ function wrapCommandWithHeaders(command, headers) {
|
|
|
129388
129462
|
}
|
|
129389
129463
|
return wrapped;
|
|
129390
129464
|
}
|
|
129391
|
-
function createPentestTools(session, model, toolOverride) {
|
|
129465
|
+
function createPentestTools(session, model, toolOverride, onTokenUsage) {
|
|
129392
129466
|
const offensiveHeaders = getOffensiveHeaders(session);
|
|
129393
129467
|
const rateLimiter = session._rateLimiter;
|
|
129394
129468
|
const executeCommand = tool({
|
|
@@ -129562,7 +129636,7 @@ COMMON TESTING PATTERNS:
|
|
|
129562
129636
|
http_request: httpRequest,
|
|
129563
129637
|
document_finding: createDocumentFindingTool(session),
|
|
129564
129638
|
record_test_result: createRecordTestResultTool(session),
|
|
129565
|
-
test_parameter: createSmartTestTool(session, model || "claude-sonnet-4-20250514"),
|
|
129639
|
+
test_parameter: createSmartTestTool(session, model || "claude-sonnet-4-20250514", onTokenUsage),
|
|
129566
129640
|
check_testing_coverage: createCheckTestingCoverageTool(session),
|
|
129567
129641
|
validate_completeness: createValidateCompletenessTool(session),
|
|
129568
129642
|
enumerate_endpoints: createEnumerateEndpointsTool(session),
|
|
@@ -129578,7 +129652,7 @@ COMMON TESTING PATTERNS:
|
|
|
129578
129652
|
import { join as join5 } from "path";
|
|
129579
129653
|
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync7 } from "fs";
|
|
129580
129654
|
function runAgent2(opts) {
|
|
129581
|
-
const { target, model, onStepFinish, abortSignal } = opts;
|
|
129655
|
+
const { target, model, onStepFinish, onToolTokenUsage, abortSignal } = opts;
|
|
129582
129656
|
const session = opts.session || createSession(target);
|
|
129583
129657
|
const subagentId = `attack-surface-${nanoid3(6)}`;
|
|
129584
129658
|
console.log(`Created attack surface session: ${session.id}`);
|
|
@@ -129587,7 +129661,7 @@ function runAgent2(opts) {
|
|
|
129587
129661
|
if (!existsSync7(assetsPath)) {
|
|
129588
129662
|
mkdirSync5(assetsPath, { recursive: true });
|
|
129589
129663
|
}
|
|
129590
|
-
const { analyze_scan, execute_command, http_request } = createPentestTools(session, model);
|
|
129664
|
+
const { analyze_scan, execute_command, http_request } = createPentestTools(session, model, undefined, onToolTokenUsage);
|
|
129591
129665
|
const document_asset = tool({
|
|
129592
129666
|
name: "document_asset",
|
|
129593
129667
|
description: `Document a discovered asset during attack surface analysis.
|
|
@@ -129748,13 +129822,14 @@ function runAgent3(opts) {
|
|
|
129748
129822
|
onSubagentSpawn,
|
|
129749
129823
|
onSubagentMessage,
|
|
129750
129824
|
onSubagentComplete,
|
|
129825
|
+
onSubagentTokenUsage,
|
|
129751
129826
|
session: sessionProp
|
|
129752
129827
|
} = opts;
|
|
129753
129828
|
const session = sessionProp || createSession(target, undefined, undefined, sessionConfig);
|
|
129754
129829
|
const logger = new Logger(session);
|
|
129755
129830
|
logger.log(`Created thorough pentest session: ${session.id}`);
|
|
129756
129831
|
logger.log(`Session path: ${session.rootPath}`);
|
|
129757
|
-
const tools2 = createOrchestratorTools(session, model, abortSignal, onSubagentSpawn, onSubagentMessage, onSubagentComplete, logger);
|
|
129832
|
+
const tools2 = createOrchestratorTools(session, model, abortSignal, onSubagentSpawn, onSubagentMessage, onSubagentComplete, onSubagentTokenUsage, logger);
|
|
129758
129833
|
const enhancedPrompt = `
|
|
129759
129834
|
TARGET: ${target}
|
|
129760
129835
|
|
|
@@ -129790,7 +129865,7 @@ Begin by using the get_attack_surface tool to map the complete attack surface of
|
|
|
129790
129865
|
streamResult.session = session;
|
|
129791
129866
|
return { streamResult, session };
|
|
129792
129867
|
}
|
|
129793
|
-
function createOrchestratorTools(session, model, abortSignal, onSubagentSpawn, onSubagentMessage, onSubagentComplete, logger) {
|
|
129868
|
+
function createOrchestratorTools(session, model, abortSignal, onSubagentSpawn, onSubagentMessage, onSubagentComplete, onSubagentTokenUsage, logger) {
|
|
129794
129869
|
const getAttackSurface = tool({
|
|
129795
129870
|
name: "get_attack_surface",
|
|
129796
129871
|
description: `Run the attack surface analysis agent to discover all assets and identify targets.
|
|
@@ -129822,7 +129897,19 @@ Use this as the FIRST step in your thorough penetration test.`,
|
|
|
129822
129897
|
target,
|
|
129823
129898
|
objective,
|
|
129824
129899
|
model,
|
|
129825
|
-
abortSignal
|
|
129900
|
+
abortSignal,
|
|
129901
|
+
onStepFinish: ({ usage }) => {
|
|
129902
|
+
if (onSubagentTokenUsage) {
|
|
129903
|
+
const inputTokens = usage.inputTokens ?? 0;
|
|
129904
|
+
const outputTokens = usage.outputTokens ?? 0;
|
|
129905
|
+
onSubagentTokenUsage(subagentId, inputTokens, outputTokens);
|
|
129906
|
+
}
|
|
129907
|
+
},
|
|
129908
|
+
onToolTokenUsage: (inputTokens, outputTokens) => {
|
|
129909
|
+
if (onSubagentTokenUsage) {
|
|
129910
|
+
onSubagentTokenUsage(subagentId, inputTokens, outputTokens);
|
|
129911
|
+
}
|
|
129912
|
+
}
|
|
129826
129913
|
});
|
|
129827
129914
|
const allMessages = [];
|
|
129828
129915
|
let currentAssistantText = "";
|
|
@@ -129997,7 +130084,19 @@ You can spawn multiple agents in parallel - they will run concurrently.`,
|
|
|
129997
130084
|
target: targetInfo.target,
|
|
129998
130085
|
objective: targetInfo.objective,
|
|
129999
130086
|
model,
|
|
130000
|
-
abortSignal
|
|
130087
|
+
abortSignal,
|
|
130088
|
+
onStepFinish: ({ usage }) => {
|
|
130089
|
+
if (onSubagentTokenUsage) {
|
|
130090
|
+
const inputTokens = usage.inputTokens ?? 0;
|
|
130091
|
+
const outputTokens = usage.outputTokens ?? 0;
|
|
130092
|
+
onSubagentTokenUsage(subagentId, inputTokens, outputTokens);
|
|
130093
|
+
}
|
|
130094
|
+
},
|
|
130095
|
+
onToolTokenUsage: (inputTokens, outputTokens) => {
|
|
130096
|
+
if (onSubagentTokenUsage) {
|
|
130097
|
+
onSubagentTokenUsage(subagentId, inputTokens, outputTokens);
|
|
130098
|
+
}
|
|
130099
|
+
}
|
|
130001
130100
|
});
|
|
130002
130101
|
const allMessages = [];
|
|
130003
130102
|
let currentAssistantText = "";
|