erosolar-cli 2.1.241 → 2.1.243

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.
Files changed (41) hide show
  1. package/dist/capabilities/iMessageVerificationCapability.d.ts +31 -0
  2. package/dist/capabilities/iMessageVerificationCapability.d.ts.map +1 -0
  3. package/dist/capabilities/iMessageVerificationCapability.js +56 -0
  4. package/dist/capabilities/iMessageVerificationCapability.js.map +1 -0
  5. package/dist/capabilities/index.d.ts +1 -0
  6. package/dist/capabilities/index.d.ts.map +1 -1
  7. package/dist/capabilities/index.js +1 -0
  8. package/dist/capabilities/index.js.map +1 -1
  9. package/dist/core/agentOrchestrator.d.ts +79 -1
  10. package/dist/core/agentOrchestrator.d.ts.map +1 -1
  11. package/dist/core/agentOrchestrator.js +494 -19
  12. package/dist/core/agentOrchestrator.js.map +1 -1
  13. package/dist/core/iMessageVerification.d.ts +408 -0
  14. package/dist/core/iMessageVerification.d.ts.map +1 -0
  15. package/dist/core/iMessageVerification.js +883 -0
  16. package/dist/core/iMessageVerification.js.map +1 -0
  17. package/dist/core/techFraudInvestigator.d.ts +131 -0
  18. package/dist/core/techFraudInvestigator.d.ts.map +1 -0
  19. package/dist/core/techFraudInvestigator.js +992 -0
  20. package/dist/core/techFraudInvestigator.js.map +1 -0
  21. package/dist/plugins/tools/imessageVerification/iMessageVerificationPlugin.d.ts +3 -0
  22. package/dist/plugins/tools/imessageVerification/iMessageVerificationPlugin.d.ts.map +1 -0
  23. package/dist/plugins/tools/imessageVerification/iMessageVerificationPlugin.js +14 -0
  24. package/dist/plugins/tools/imessageVerification/iMessageVerificationPlugin.js.map +1 -0
  25. package/dist/plugins/tools/nodeDefaults.d.ts.map +1 -1
  26. package/dist/plugins/tools/nodeDefaults.js +2 -0
  27. package/dist/plugins/tools/nodeDefaults.js.map +1 -1
  28. package/dist/tools/iMessageVerificationTools.d.ts +17 -0
  29. package/dist/tools/iMessageVerificationTools.d.ts.map +1 -0
  30. package/dist/tools/iMessageVerificationTools.js +842 -0
  31. package/dist/tools/iMessageVerificationTools.js.map +1 -0
  32. package/dist/tools/taoTools.d.ts.map +1 -1
  33. package/dist/tools/taoTools.js +1277 -1
  34. package/dist/tools/taoTools.js.map +1 -1
  35. package/dist/ui/UnifiedUIRenderer.js +5 -5
  36. package/dist/ui/UnifiedUIRenderer.js.map +1 -1
  37. package/dist/ui/display.d.ts +14 -0
  38. package/dist/ui/display.d.ts.map +1 -1
  39. package/dist/ui/display.js +42 -24
  40. package/dist/ui/display.js.map +1 -1
  41. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
1
  import { TASK_FULLY_COMPLETE } from './constants.js';
2
2
  import { TaskCompletionDetector } from './taskCompletionDetector.js';
3
+ import { planAttackChain, selectNextTechnique, executeTechniqueInChain, techniqueRegistry, parseNaturalLanguageIntent, validateAuthorizationContext, } from '../tools/tao/index.js';
3
4
  /**
4
5
  * Single-pass orchestration: drive the agent to finish as much as possible
5
6
  * in one go, with strong bias toward real actions over planning.
@@ -10,6 +11,42 @@ export class AgentOrchestrator {
10
11
  this.agent = agent;
11
12
  }
12
13
  async runToCompletion(request, options) {
14
+ // Early safety check for potentially harmful requests
15
+ const harmfulCheck = this.checkForHarmfulRequest(request);
16
+ if (harmfulCheck.isHarmful) {
17
+ return {
18
+ finalResponse: harmfulCheck.response,
19
+ toolsUsed: [],
20
+ planOnly: false,
21
+ tookAction: false,
22
+ completion: {
23
+ isComplete: true,
24
+ confidence: 1.0,
25
+ signals: {
26
+ hasExplicitCompletionStatement: true,
27
+ hasIncompleteWorkIndicators: false,
28
+ hasPendingActionIndicators: false,
29
+ hasErrorIndicators: false,
30
+ hasFollowUpQuestions: false,
31
+ toolsUsedInLastResponse: 0,
32
+ lastToolWasReadOnly: false,
33
+ consecutiveResponsesWithoutTools: 0,
34
+ hasRecentFileWrites: false,
35
+ hasRecentCommits: false,
36
+ todoItemsPending: 0,
37
+ todoItemsCompleted: 0,
38
+ mentionsFutureWork: false,
39
+ completionConfidence: 1.0,
40
+ },
41
+ reason: 'Request declined for safety reasons',
42
+ shouldVerify: false,
43
+ },
44
+ exitReason: 'refusal',
45
+ statusSummary: null,
46
+ limitations: [],
47
+ recommendations: [],
48
+ };
49
+ }
13
50
  const streaming = options?.streaming ?? true;
14
51
  const enforceActions = options?.enforceActions ?? true;
15
52
  const verificationMode = options?.verificationMode ?? 'auto';
@@ -52,15 +89,28 @@ export class AgentOrchestrator {
52
89
  if (attempt.exitReason === 'refusal') {
53
90
  break;
54
91
  }
55
- // Track consecutive no-progress attempts (no tools, no completion)
56
- // Also treat refusal/blocked/empty as no-progress to prevent spin loops
92
+ // Track consecutive no-progress attempts
93
+ // Key insight: tool usage alone doesn't mean progress if the model is stuck
94
+ // or refusing. We need to detect actual forward momentum.
57
95
  const terminalNoProgress = attempt.exitReason === 'empty-response' ||
58
96
  attempt.exitReason === 'no-action' ||
59
97
  attempt.exitReason === 'blocked';
60
- const hasProgress = attempt.toolsUsed.length > 0 ||
61
- attempt.exitReason === 'complete' ||
98
+ // Detect response repetition - if we keep getting similar responses, we're stuck
99
+ // This catches cases where model calls tools but produces same reasoning/refusal
100
+ const responseFingerprint = this.computeResponseFingerprint(attempt.response);
101
+ const previousAttempt = attempts.length >= 2 ? attempts[attempts.length - 2] : undefined;
102
+ const isRepeatedResponse = previousAttempt !== undefined &&
103
+ this.computeResponseFingerprint(previousAttempt.response) === responseFingerprint &&
104
+ responseFingerprint !== '';
105
+ // Progress is considered made when:
106
+ // 1. Task is complete or needs verification
107
+ // 2. OR tools were used with a DIFFERENT response (model is working through the problem)
108
+ const isCompleting = attempt.exitReason === 'complete' ||
62
109
  attempt.exitReason === 'verification-needed';
63
- if (!hasProgress || terminalNoProgress) {
110
+ const isProgressingWithTools = attempt.toolsUsed.length > 0 && !isRepeatedResponse;
111
+ const hasRealProgress = isCompleting || isProgressingWithTools;
112
+ // No progress if: terminal state, no completion AND no tool progress, OR repeated response
113
+ if (terminalNoProgress || (!hasRealProgress && !isCompleting) || (isRepeatedResponse && !isCompleting)) {
64
114
  consecutiveNoProgress++;
65
115
  }
66
116
  else {
@@ -366,18 +416,22 @@ ${actionLine}
366
416
  if (attempts.length >= maxAttempts) {
367
417
  return false;
368
418
  }
369
- // AI safety refusal - never retry, respect the model's decision
370
- if (exitReason === 'refusal') {
419
+ // Terminal states - never retry
420
+ if (exitReason === 'refusal' || exitReason === 'complete') {
371
421
  return false;
372
422
  }
373
- // Detect stuck loops early - if we have 2+ consecutive attempts with no tools, stop
374
- // This catches cases where the model refuses silently or returns empty/no-action repeatedly
423
+ // Detect stuck loops early - if we have 2+ consecutive attempts without completion, stop
424
+ // This catches cases where the model refuses silently, calls tools without progress,
425
+ // or returns empty/no-action repeatedly
375
426
  if (attempts.length >= 2) {
376
427
  const recentAttempts = attempts.slice(-2);
377
- const allNoProgress = recentAttempts.every(a => a.toolsUsed.length === 0 &&
378
- (a.exitReason === 'empty-response' || a.exitReason === 'no-action' || a.exitReason === 'incomplete'));
379
- if (allNoProgress) {
380
- return false; // Stuck in a loop with no progress
428
+ const allNoCompletion = recentAttempts.every(a => a.exitReason !== 'complete' && a.exitReason !== 'verification-needed');
429
+ if (allNoCompletion) {
430
+ // Check if responses are similar (stuck in a loop)
431
+ const fingerprints = recentAttempts.map(a => this.computeResponseFingerprint(a.response));
432
+ if (fingerprints[0] === fingerprints[1] && fingerprints[0] !== '') {
433
+ return false; // Stuck producing same response
434
+ }
381
435
  }
382
436
  }
383
437
  // Allow recovery attempts for common failure modes before giving up
@@ -397,12 +451,16 @@ ${actionLine}
397
451
  const blockedResponses = attempts.filter((a) => a.exitReason === 'blocked').length;
398
452
  return blockedResponses < 2;
399
453
  }
400
- // Detect repetition loops - if last 3 attempts had no new tool usage, stop
401
- if (exitReason === 'incomplete' && attempts.length >= 3) {
402
- const recentAttempts = attempts.slice(-3);
403
- const allNoTools = recentAttempts.every(a => a.toolsUsed.length === 0);
404
- if (allNoTools) {
405
- return false; // Stuck in a loop with no progress
454
+ // Detect tool loops - if last 2 attempts used tools, are incomplete, AND produced similar responses
455
+ if (exitReason === 'incomplete' && attempts.length >= 2) {
456
+ const recentAttempts = attempts.slice(-2);
457
+ const allIncomplete = recentAttempts.every(a => a.exitReason === 'incomplete' && a.toolsUsed.length > 0);
458
+ if (allIncomplete) {
459
+ // Only stop if responses are also similar (model is stuck in a pattern)
460
+ const fingerprints = recentAttempts.map(a => this.computeResponseFingerprint(a.response));
461
+ if (fingerprints[0] === fingerprints[1] && fingerprints[0] !== '') {
462
+ return false; // Same response pattern with tools - stuck in a loop
463
+ }
406
464
  }
407
465
  }
408
466
  return exitReason === 'incomplete';
@@ -711,6 +769,22 @@ ${actionLine}
711
769
  return 0;
712
770
  return text.trim().split(/\s+/).length;
713
771
  }
772
+ /**
773
+ * Compute a simple fingerprint of a response for repetition detection.
774
+ * Normalizes whitespace and takes first N significant words to detect
775
+ * when the model is producing the same response repeatedly.
776
+ */
777
+ computeResponseFingerprint(response) {
778
+ if (!response?.trim())
779
+ return '';
780
+ // Normalize: lowercase, collapse whitespace, take first 200 chars
781
+ const normalized = response.toLowerCase().replace(/\s+/g, ' ').trim();
782
+ // Extract key content - skip common filler words
783
+ const words = normalized.split(' ')
784
+ .filter(w => w.length > 3)
785
+ .slice(0, 30);
786
+ return words.join(' ');
787
+ }
714
788
  /**
715
789
  * Detects when a response is sufficiently detailed to stand on its own,
716
790
  * avoiding false "incomplete" flags for thorough answers without explicit
@@ -725,5 +799,406 @@ ${actionLine}
725
799
  const threshold = options.tookAction ? 80 : 120;
726
800
  return options.wordCount >= threshold;
727
801
  }
802
+ /**
803
+ * Early safety check for potentially harmful requests.
804
+ * Returns immediately with a helpful response instead of looping.
805
+ *
806
+ * This catches obvious cases before the model even processes them,
807
+ * preventing wasted compute and infinite "thinking" loops.
808
+ */
809
+ checkForHarmfulRequest(request) {
810
+ const lower = request.toLowerCase();
811
+ // Known major companies/services that should NEVER be attacked without explicit authorization
812
+ const protectedTargets = [
813
+ 'google', 'microsoft', 'apple', 'amazon', 'meta', 'facebook',
814
+ 'twitter', 'x.com', 'linkedin', 'github', 'gitlab', 'netflix',
815
+ 'spotify', 'openai', 'anthropic', 'cloudflare', 'aws', 'azure',
816
+ 'gcp', 'oracle', 'ibm', 'cisco', 'adobe', 'salesforce', 'slack',
817
+ 'zoom', 'dropbox', 'paypal', 'stripe', 'visa', 'mastercard',
818
+ ];
819
+ // Action words that indicate malicious intent
820
+ const maliciousActions = [
821
+ 'attack', 'hack', 'pwn', 'exploit', 'compromise', 'breach',
822
+ 'ddos', 'dos', 'flood', 'crash', 'destroy', 'deface',
823
+ 'steal', 'exfiltrate', 'dump', 'leak',
824
+ ];
825
+ // Check for combined patterns: malicious action + protected target
826
+ const hasMaliciousAction = maliciousActions.some(action => lower.includes(action));
827
+ const hasProtectedTarget = protectedTargets.some(target => lower.includes(target));
828
+ // Only trigger for clear cases of unauthorized attacks
829
+ if (hasMaliciousAction && hasProtectedTarget) {
830
+ // Check for legitimate contexts that override this
831
+ const legitimateContexts = [
832
+ 'ctf', 'capture the flag', 'hackthebox', 'tryhackme',
833
+ 'lab', 'sandbox', 'authorized', 'pentest', 'penetration test',
834
+ 'bug bounty', 'scope', 'engagement', 'test environment',
835
+ ];
836
+ const hasLegitimateContext = legitimateContexts.some(ctx => lower.includes(ctx));
837
+ if (!hasLegitimateContext) {
838
+ return {
839
+ isHarmful: true,
840
+ response: `I can't help with unauthorized attacks against production systems or services.
841
+
842
+ For legitimate security work, I can help with:
843
+ • **CTF challenges** - Specify the CTF platform or challenge name
844
+ • **Authorized pentests** - Share the scope document or confirm you have written authorization
845
+ • **Bug bounty** - Confirm the target is in scope for a bug bounty program
846
+ • **Lab environments** - Specify it's a personal lab, HackTheBox, TryHackMe, etc.
847
+ • **Security research** - Describe the defensive or educational purpose
848
+
849
+ What authorized security work can I help you with?`,
850
+ };
851
+ }
852
+ }
853
+ // Also catch generic "attack everything" requests without any target
854
+ if (hasMaliciousAction && !lower.includes('ctf') && !lower.includes('lab')) {
855
+ const veryGenericPatterns = [
856
+ /attack\s+(any|random|all)/i,
857
+ /hack\s+(into|the|any)/i,
858
+ /ddos\s+(the|any|all)/i,
859
+ ];
860
+ if (veryGenericPatterns.some(p => p.test(request))) {
861
+ return {
862
+ isHarmful: true,
863
+ response: `I need more context about what you're trying to accomplish.
864
+
865
+ If this is for:
866
+ • **CTF/Competition** - What platform or challenge?
867
+ • **Learning** - What concept are you trying to understand?
868
+ • **Authorized testing** - What's the scope and authorization?
869
+
870
+ Please provide context so I can help appropriately.`,
871
+ };
872
+ }
873
+ }
874
+ return { isHarmful: false, response: '' };
875
+ }
876
+ // ═══════════════════════════════════════════════════════════════════════════════
877
+ // ATTACK CHAIN EXECUTION
878
+ // ═══════════════════════════════════════════════════════════════════════════════
879
+ /**
880
+ * Detect if a request should trigger attack chain execution.
881
+ * Uses natural language parsing to identify security operation intents.
882
+ */
883
+ shouldExecuteAttackChain(request, mode = 'auto') {
884
+ if (mode === 'disabled') {
885
+ return { shouldExecute: false, intent: null };
886
+ }
887
+ if (mode === 'forced') {
888
+ const intent = parseNaturalLanguageIntent(request);
889
+ return { shouldExecute: true, intent };
890
+ }
891
+ // Auto mode: parse intent and check for security operations
892
+ const intent = parseNaturalLanguageIntent(request);
893
+ const securityActions = ['recon', 'scan', 'enumerate', 'exploit', 'extract', 'test', 'monitor'];
894
+ const isSecurityOp = securityActions.includes(intent.action);
895
+ const hasTargets = intent.targets.length > 0;
896
+ return {
897
+ shouldExecute: isSecurityOp && hasTargets,
898
+ intent: isSecurityOp ? intent : null,
899
+ };
900
+ }
901
+ /**
902
+ * Validate authorization context for attack chain execution.
903
+ * Ensures proper authorization before executing security techniques.
904
+ */
905
+ validateAttackAuthorization(intent, authContext) {
906
+ // Use the TAO authorization validator
907
+ const validation = validateAuthorizationContext(intent);
908
+ // Additional checks for protected targets
909
+ const protectedTargets = [
910
+ 'google.com', 'microsoft.com', 'apple.com', 'amazon.com',
911
+ 'facebook.com', 'twitter.com', 'github.com',
912
+ ];
913
+ const targetingProtected = intent.targets.some(t => protectedTargets.some(pt => t.toLowerCase().includes(pt)));
914
+ if (targetingProtected && !validation.valid) {
915
+ return {
916
+ authorized: false,
917
+ reason: 'Targeting protected production systems without explicit authorization',
918
+ warnings: [
919
+ 'Protected targets detected. Provide CTF/lab context or authorization scope.',
920
+ ...validation.warnings,
921
+ ],
922
+ };
923
+ }
924
+ // Check for explicit authorization context
925
+ if (authContext) {
926
+ const authorizedContexts = ['ctf', 'lab', 'pentest', 'bug-bounty', 'authorized'];
927
+ const hasExplicitAuth = authorizedContexts.some(ctx => authContext.toLowerCase().includes(ctx));
928
+ if (hasExplicitAuth) {
929
+ return {
930
+ authorized: true,
931
+ reason: `Explicit authorization: ${authContext}`,
932
+ warnings: validation.warnings,
933
+ };
934
+ }
935
+ }
936
+ return {
937
+ authorized: validation.valid,
938
+ reason: validation.type,
939
+ warnings: validation.warnings,
940
+ };
941
+ }
942
+ /**
943
+ * Execute an attack chain with TAO techniques.
944
+ * Returns results including all executed techniques and artifacts.
945
+ */
946
+ async executeAttackChain(request, options = {}) {
947
+ const intent = parseNaturalLanguageIntent(request);
948
+ const targets = options.targets ?? intent.targets;
949
+ // Normalize depth - map 'comprehensive' to 'deep'
950
+ const rawDepth = options.depth ?? intent.depth;
951
+ const depth = rawDepth === 'comprehensive' ? 'deep' : rawDepth;
952
+ const stealth = options.stealth ?? intent.constraints.includes('stealth');
953
+ // Validate authorization
954
+ const auth = this.validateAttackAuthorization(intent, options.authContext);
955
+ if (!auth.authorized) {
956
+ throw new Error(`Attack chain execution not authorized: ${auth.reason}`);
957
+ }
958
+ const executedTechniques = [];
959
+ const phasesCompleted = new Set();
960
+ const startTime = Date.now();
961
+ // Execute chain for each target
962
+ for (const target of targets) {
963
+ const chain = planAttackChain(intent, `Attack chain: ${target}`);
964
+ while (chain.state === 'planning' || chain.state === 'executing') {
965
+ const action = selectNextTechnique(chain);
966
+ if (!action)
967
+ break;
968
+ const technique = techniqueRegistry.get(action.id);
969
+ if (!technique)
970
+ continue;
971
+ const params = {
972
+ target,
973
+ depth,
974
+ stealth,
975
+ timeout: depth === 'deep' ? 60000 : depth === 'standard' ? 30000 : 10000,
976
+ context: {
977
+ chainId: chain.id,
978
+ phase: technique.phase,
979
+ previousArtifacts: executedTechniques
980
+ .filter(t => t.success)
981
+ .flatMap(t => t.artifacts),
982
+ },
983
+ };
984
+ try {
985
+ const { result } = await executeTechniqueInChain(chain, action, params);
986
+ executedTechniques.push({
987
+ id: technique.id,
988
+ name: technique.name,
989
+ phase: technique.phase,
990
+ success: result.success,
991
+ duration: result.duration,
992
+ artifacts: result.artifacts,
993
+ });
994
+ if (result.success) {
995
+ phasesCompleted.add(technique.phase);
996
+ }
997
+ options.onProgress?.(chain, technique.id, result);
998
+ }
999
+ catch (err) {
1000
+ // Log but continue chain execution
1001
+ executedTechniques.push({
1002
+ id: technique.id,
1003
+ name: technique.name,
1004
+ phase: technique.phase,
1005
+ success: false,
1006
+ duration: 0,
1007
+ artifacts: [{ type: 'error', data: String(err) }],
1008
+ });
1009
+ }
1010
+ }
1011
+ }
1012
+ const successCount = executedTechniques.filter(t => t.success).length;
1013
+ return {
1014
+ chain: planAttackChain(intent, request), // Return final chain state
1015
+ techniques: executedTechniques,
1016
+ totalDuration: Date.now() - startTime,
1017
+ successRate: executedTechniques.length > 0
1018
+ ? successCount / executedTechniques.length
1019
+ : 0,
1020
+ phasesCompleted: Array.from(phasesCompleted),
1021
+ };
1022
+ }
1023
+ /**
1024
+ * Run orchestration with optional attack chain integration.
1025
+ * When attack chain mode is enabled, security operations are executed
1026
+ * directly through TAO techniques rather than relying on LLM tool calls.
1027
+ */
1028
+ async runWithAttackChain(request, options = {}) {
1029
+ const attackChainMode = options.attackChainMode ?? 'auto';
1030
+ // Check if we should execute attack chain
1031
+ const { shouldExecute, intent } = this.shouldExecuteAttackChain(request, attackChainMode);
1032
+ if (!shouldExecute || !intent) {
1033
+ // Fall back to normal orchestration
1034
+ return this.runToCompletion(request, options);
1035
+ }
1036
+ // Validate authorization
1037
+ const auth = this.validateAttackAuthorization(intent, options.authorizationContext);
1038
+ if (!auth.authorized) {
1039
+ return {
1040
+ finalResponse: `Cannot execute security operation: ${auth.reason}\n\n${auth.warnings.join('\n')}`,
1041
+ toolsUsed: [],
1042
+ planOnly: false,
1043
+ tookAction: false,
1044
+ completion: {
1045
+ isComplete: true,
1046
+ confidence: 1.0,
1047
+ signals: {
1048
+ hasExplicitCompletionStatement: true,
1049
+ hasIncompleteWorkIndicators: false,
1050
+ hasPendingActionIndicators: false,
1051
+ hasErrorIndicators: false,
1052
+ hasFollowUpQuestions: false,
1053
+ toolsUsedInLastResponse: 0,
1054
+ lastToolWasReadOnly: false,
1055
+ consecutiveResponsesWithoutTools: 0,
1056
+ hasRecentFileWrites: false,
1057
+ hasRecentCommits: false,
1058
+ todoItemsPending: 0,
1059
+ todoItemsCompleted: 0,
1060
+ mentionsFutureWork: false,
1061
+ completionConfidence: 1.0,
1062
+ },
1063
+ reason: 'Authorization required',
1064
+ shouldVerify: false,
1065
+ },
1066
+ exitReason: 'attack-chain-aborted',
1067
+ statusSummary: `Authorization required: ${auth.reason}`,
1068
+ limitations: auth.warnings,
1069
+ recommendations: [
1070
+ 'Provide explicit authorization context (CTF, lab, pentest scope)',
1071
+ 'Use --auth-context flag to specify authorization',
1072
+ ],
1073
+ };
1074
+ }
1075
+ // Execute attack chain
1076
+ try {
1077
+ // Normalize depth for attack chain
1078
+ const attackRawDepth = options.attackDepth ?? intent.depth;
1079
+ const attackDepth = attackRawDepth === 'comprehensive' ? 'deep' : attackRawDepth;
1080
+ const chainResult = await this.executeAttackChain(request, {
1081
+ targets: options.attackTargets ?? intent.targets,
1082
+ depth: attackDepth,
1083
+ stealth: options.stealthMode ?? intent.constraints.includes('stealth'),
1084
+ authContext: options.authorizationContext,
1085
+ onProgress: options.onAttackChainProgress,
1086
+ });
1087
+ // Build response summary
1088
+ const summary = this.buildAttackChainSummary(chainResult);
1089
+ return {
1090
+ finalResponse: summary,
1091
+ toolsUsed: chainResult.techniques.map(t => t.id),
1092
+ planOnly: false,
1093
+ tookAction: true,
1094
+ completion: {
1095
+ isComplete: true,
1096
+ confidence: chainResult.successRate,
1097
+ signals: {
1098
+ hasExplicitCompletionStatement: true,
1099
+ hasIncompleteWorkIndicators: false,
1100
+ hasPendingActionIndicators: false,
1101
+ hasErrorIndicators: chainResult.successRate < 0.5,
1102
+ hasFollowUpQuestions: false,
1103
+ toolsUsedInLastResponse: chainResult.techniques.length,
1104
+ lastToolWasReadOnly: false,
1105
+ consecutiveResponsesWithoutTools: 0,
1106
+ hasRecentFileWrites: false,
1107
+ hasRecentCommits: false,
1108
+ todoItemsPending: 0,
1109
+ todoItemsCompleted: chainResult.techniques.length,
1110
+ mentionsFutureWork: false,
1111
+ completionConfidence: chainResult.successRate,
1112
+ },
1113
+ reason: 'Attack chain completed',
1114
+ shouldVerify: chainResult.successRate < 1.0,
1115
+ },
1116
+ exitReason: 'attack-chain-complete',
1117
+ statusSummary: `Attack chain: ${chainResult.techniques.length} techniques, ${Math.round(chainResult.successRate * 100)}% success`,
1118
+ limitations: [],
1119
+ recommendations: chainResult.successRate < 1.0
1120
+ ? ['Review failed techniques and adjust approach']
1121
+ : [],
1122
+ attackChainResult: chainResult,
1123
+ };
1124
+ }
1125
+ catch (err) {
1126
+ return {
1127
+ finalResponse: `Attack chain execution failed: ${String(err)}`,
1128
+ toolsUsed: [],
1129
+ planOnly: false,
1130
+ tookAction: false,
1131
+ completion: {
1132
+ isComplete: true,
1133
+ confidence: 0,
1134
+ signals: {
1135
+ hasExplicitCompletionStatement: true,
1136
+ hasIncompleteWorkIndicators: false,
1137
+ hasPendingActionIndicators: false,
1138
+ hasErrorIndicators: true,
1139
+ hasFollowUpQuestions: false,
1140
+ toolsUsedInLastResponse: 0,
1141
+ lastToolWasReadOnly: false,
1142
+ consecutiveResponsesWithoutTools: 0,
1143
+ hasRecentFileWrites: false,
1144
+ hasRecentCommits: false,
1145
+ todoItemsPending: 0,
1146
+ todoItemsCompleted: 0,
1147
+ mentionsFutureWork: false,
1148
+ completionConfidence: 0,
1149
+ },
1150
+ reason: `Error: ${String(err)}`,
1151
+ shouldVerify: false,
1152
+ },
1153
+ exitReason: 'attack-chain-aborted',
1154
+ statusSummary: `Attack chain failed: ${String(err)}`,
1155
+ limitations: [String(err)],
1156
+ recommendations: ['Check target connectivity', 'Verify authorization context'],
1157
+ };
1158
+ }
1159
+ }
1160
+ /**
1161
+ * Build a human-readable summary of attack chain execution.
1162
+ */
1163
+ buildAttackChainSummary(result) {
1164
+ const lines = [];
1165
+ lines.push('## Attack Chain Execution Summary\n');
1166
+ // Overall stats
1167
+ lines.push(`**Duration:** ${Math.round(result.totalDuration / 1000)}s`);
1168
+ lines.push(`**Success Rate:** ${Math.round(result.successRate * 100)}%`);
1169
+ lines.push(`**Phases Completed:** ${result.phasesCompleted.join(', ')}\n`);
1170
+ // Technique breakdown
1171
+ lines.push('### Techniques Executed\n');
1172
+ const byPhase = new Map();
1173
+ for (const tech of result.techniques) {
1174
+ const list = byPhase.get(tech.phase) || [];
1175
+ list.push(tech);
1176
+ byPhase.set(tech.phase, list);
1177
+ }
1178
+ for (const [phase, techniques] of byPhase) {
1179
+ lines.push(`#### ${phase}`);
1180
+ for (const tech of techniques) {
1181
+ const status = tech.success ? '✓' : '✗';
1182
+ lines.push(`- ${status} **${tech.name}** (${Math.round(tech.duration / 1000)}s)`);
1183
+ if (tech.artifacts.length > 0) {
1184
+ lines.push(` - Artifacts: ${tech.artifacts.length} collected`);
1185
+ }
1186
+ }
1187
+ lines.push('');
1188
+ }
1189
+ // Artifacts summary
1190
+ const allArtifacts = result.techniques.flatMap(t => t.artifacts);
1191
+ if (allArtifacts.length > 0) {
1192
+ lines.push('### Collected Artifacts\n');
1193
+ const artifactsByType = new Map();
1194
+ for (const artifact of allArtifacts) {
1195
+ artifactsByType.set(artifact.type, (artifactsByType.get(artifact.type) || 0) + 1);
1196
+ }
1197
+ for (const [type, count] of artifactsByType) {
1198
+ lines.push(`- **${type}:** ${count}`);
1199
+ }
1200
+ }
1201
+ return lines.join('\n');
1202
+ }
728
1203
  }
729
1204
  //# sourceMappingURL=agentOrchestrator.js.map