pentesting 0.73.2 → 0.73.4
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 +121 -0
- package/dist/agent-tool-MP274HWD.js +989 -0
- package/dist/{chunk-BGEXGHPB.js → chunk-3KWJPPYB.js} +917 -430
- package/dist/{chunk-KBJPZDIL.js → chunk-7E2VUIFU.js} +456 -211
- package/dist/chunk-I52SWXYV.js +1122 -0
- package/dist/main.js +2285 -474
- package/dist/{persistence-VFIOGTRC.js → persistence-BNVN3WW6.js} +2 -2
- package/dist/{process-registry-GSHEX2LT.js → process-registry-BI7BKPHN.js} +1 -1
- package/dist/prompts/main-agent.md +35 -1
- package/dist/prompts/strategist-system.md +34 -0
- package/package.json +3 -4
- package/dist/agent-tool-HYQGTZC4.js +0 -256
- package/dist/chunk-YFDJI3GO.js +0 -331
package/dist/main.js
CHANGED
|
@@ -28,9 +28,12 @@ import {
|
|
|
28
28
|
formatChallengeAnalysis,
|
|
29
29
|
formatTurnRecord,
|
|
30
30
|
getApiKey,
|
|
31
|
+
getGlobalRequestCount,
|
|
32
|
+
getGlobalTokenUsage,
|
|
31
33
|
getModel,
|
|
32
34
|
getNextTurnNumber,
|
|
33
35
|
getServiceContext,
|
|
36
|
+
getShellSupervisorLifecycleSnapshot,
|
|
34
37
|
getTimeAdaptiveStrategy,
|
|
35
38
|
parseTurnNumbers,
|
|
36
39
|
readJournalSummary,
|
|
@@ -39,7 +42,7 @@ import {
|
|
|
39
42
|
rotateTurnRecords,
|
|
40
43
|
setCurrentTurn,
|
|
41
44
|
writePolicyDocument
|
|
42
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-3KWJPPYB.js";
|
|
43
46
|
import {
|
|
44
47
|
AGENT_ROLES,
|
|
45
48
|
APP_DESCRIPTION,
|
|
@@ -72,20 +75,22 @@ import {
|
|
|
72
75
|
generateId,
|
|
73
76
|
getErrorMessage,
|
|
74
77
|
getProcessOutput,
|
|
78
|
+
getWorkspaceConfig,
|
|
75
79
|
initDebugLogger,
|
|
76
80
|
listBackgroundProcesses,
|
|
77
81
|
loadState,
|
|
82
|
+
readRuntimeAssetFile,
|
|
83
|
+
resolveRuntimeAssetPath,
|
|
78
84
|
saveState,
|
|
79
85
|
setActiveSessionRuntime,
|
|
80
86
|
setTorEnabled,
|
|
81
87
|
snapshotToPrompt
|
|
82
|
-
} from "./chunk-
|
|
88
|
+
} from "./chunk-7E2VUIFU.js";
|
|
83
89
|
import {
|
|
84
90
|
EXIT_CODES,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
} from "./chunk-YFDJI3GO.js";
|
|
91
|
+
getOptionalRuntimeSection,
|
|
92
|
+
getRequiredRuntimeSection
|
|
93
|
+
} from "./chunk-I52SWXYV.js";
|
|
89
94
|
|
|
90
95
|
// src/platform/tui/main.tsx
|
|
91
96
|
import chalk5 from "chalk";
|
|
@@ -291,17 +296,6 @@ var CLI_SCAN_TYPES = Object.freeze([
|
|
|
291
296
|
CLI_SCAN_TYPE.VULN
|
|
292
297
|
]);
|
|
293
298
|
|
|
294
|
-
// src/platform/tui/cli/commands/interactive.tsx
|
|
295
|
-
import React16 from "react";
|
|
296
|
-
import { render } from "ink";
|
|
297
|
-
import chalk from "chalk";
|
|
298
|
-
|
|
299
|
-
// src/platform/tui/app.tsx
|
|
300
|
-
import { Box as Box18 } from "ink";
|
|
301
|
-
|
|
302
|
-
// src/platform/tui/hooks/useAgent.ts
|
|
303
|
-
import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2, useRef as useRef3 } from "react";
|
|
304
|
-
|
|
305
299
|
// src/engine/events.ts
|
|
306
300
|
var AgentEventEmitter = class {
|
|
307
301
|
listeners = /* @__PURE__ */ new Map();
|
|
@@ -588,13 +582,25 @@ var ApprovalGate = class {
|
|
|
588
582
|
return this.shouldAutoApprove;
|
|
589
583
|
}
|
|
590
584
|
/**
|
|
591
|
-
*
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
585
|
+
* Expose the effective runtime approval mode for UI/help text and tests.
|
|
586
|
+
*/
|
|
587
|
+
getMode() {
|
|
588
|
+
return this.mode;
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Whether tool execution is currently gated on the explicit auto-approve toggle.
|
|
592
|
+
*/
|
|
593
|
+
requiresAutoApprove() {
|
|
594
|
+
return this.mode === "require_auto_approve";
|
|
595
|
+
}
|
|
596
|
+
/**
|
|
597
|
+
* Runtime-aware approval.
|
|
598
|
+
*
|
|
599
|
+
* In strict mode we currently have one explicit gate:
|
|
600
|
+
* the operator must enable auto-approve before tool execution proceeds.
|
|
595
601
|
*/
|
|
596
602
|
async request(toolCall) {
|
|
597
|
-
if (this.
|
|
603
|
+
if (this.requiresAutoApprove() && !this.shouldAutoApprove) {
|
|
598
604
|
return {
|
|
599
605
|
isApproved: false,
|
|
600
606
|
reason: AUTO_APPROVE_REQUIRED_REASON(toolCall.name)
|
|
@@ -605,34 +611,67 @@ var ApprovalGate = class {
|
|
|
605
611
|
};
|
|
606
612
|
|
|
607
613
|
// src/agents/prompt-builder/prompt-loader.ts
|
|
608
|
-
import { readFileSync as readFileSync2
|
|
614
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
609
615
|
import { join } from "path";
|
|
616
|
+
|
|
617
|
+
// src/agents/prompt-sources.ts
|
|
618
|
+
var DEFAULT_PROMPT_BASE_DIR = "src/agents/prompts/";
|
|
619
|
+
var EMPTY_TECHNIQUE_SOURCES = [];
|
|
620
|
+
var EMPTY_CORE_KNOWLEDGE_SOURCES = [];
|
|
621
|
+
function getPromptSources() {
|
|
622
|
+
return getRequiredRuntimeSection("prompt_sources");
|
|
623
|
+
}
|
|
624
|
+
function getPromptSourceConfigValue(key) {
|
|
625
|
+
return getPromptSources()[key];
|
|
626
|
+
}
|
|
627
|
+
function getPhasePromptSource(phase) {
|
|
628
|
+
return getPromptSourceConfigValue("phase_prompts")?.[phase];
|
|
629
|
+
}
|
|
630
|
+
function getPromptBaseDir() {
|
|
631
|
+
return getPromptSourceConfigValue("base_dir") ?? DEFAULT_PROMPT_BASE_DIR;
|
|
632
|
+
}
|
|
633
|
+
function getPhaseTechniqueSources(phase) {
|
|
634
|
+
return getPromptSourceConfigValue("phase_techniques")?.[phase] ?? EMPTY_TECHNIQUE_SOURCES;
|
|
635
|
+
}
|
|
636
|
+
function getTechniqueBaseDir() {
|
|
637
|
+
return getPromptSourceConfigValue("techniques_dir");
|
|
638
|
+
}
|
|
639
|
+
function getCoreKnowledgeSources() {
|
|
640
|
+
return getPromptSourceConfigValue("core_knowledge") ?? EMPTY_CORE_KNOWLEDGE_SOURCES;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// src/agents/prompt-builder/prompt-loader.ts
|
|
610
644
|
var _promptsDir = null;
|
|
611
645
|
var _techniquesDir = null;
|
|
612
646
|
function getPromptsDir() {
|
|
613
647
|
if (!_promptsDir) {
|
|
614
|
-
const
|
|
615
|
-
_promptsDir = join(process.cwd(),
|
|
648
|
+
const baseDir = getPromptBaseDir();
|
|
649
|
+
_promptsDir = resolveRuntimeAssetPath(baseDir) ?? join(process.cwd(), baseDir);
|
|
616
650
|
}
|
|
617
651
|
return _promptsDir;
|
|
618
652
|
}
|
|
619
653
|
function getTechniquesDir() {
|
|
620
654
|
if (!_techniquesDir) {
|
|
621
|
-
const
|
|
622
|
-
_techniquesDir =
|
|
655
|
+
const techniquesDir = getTechniqueBaseDir();
|
|
656
|
+
_techniquesDir = techniquesDir ? resolveRuntimeAssetPath(techniquesDir) ?? join(process.cwd(), techniquesDir) : join(getPromptsDir(), PROMPT_PATHS.TECHNIQUES_DIR);
|
|
623
657
|
}
|
|
624
658
|
return _techniquesDir;
|
|
625
659
|
}
|
|
626
660
|
function loadPromptFile(filename) {
|
|
627
|
-
const
|
|
628
|
-
|
|
661
|
+
const path2 = join(getPromptsDir(), filename);
|
|
662
|
+
try {
|
|
663
|
+
const resolved = resolveRuntimeAssetPath(path2) ?? path2;
|
|
664
|
+
return readFileSync2(resolved, PROMPT_CONFIG.ENCODING);
|
|
665
|
+
} catch {
|
|
666
|
+
return "";
|
|
667
|
+
}
|
|
629
668
|
}
|
|
630
669
|
function loadTechniqueFile(techniqueName) {
|
|
631
670
|
const filename = techniqueName.endsWith(".md") ? techniqueName : `${techniqueName}.md`;
|
|
632
671
|
const filePath = join(getTechniquesDir(), filename);
|
|
633
672
|
try {
|
|
634
|
-
|
|
635
|
-
return readFileSync2(
|
|
673
|
+
const resolved = resolveRuntimeAssetPath(filePath) ?? filePath;
|
|
674
|
+
return readFileSync2(resolved, PROMPT_CONFIG.ENCODING);
|
|
636
675
|
} catch {
|
|
637
676
|
return "";
|
|
638
677
|
}
|
|
@@ -791,68 +830,32 @@ function buildUserContextFragment(userInput) {
|
|
|
791
830
|
return PROMPT_DEFAULTS.USER_CONTEXT(userInput);
|
|
792
831
|
}
|
|
793
832
|
|
|
794
|
-
// src/agents/prompt-builder/
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
"state.scope": (ctx) => buildScopeFragment(ctx.state),
|
|
799
|
-
"state.todo": (ctx) => buildTodoFragment(ctx.state),
|
|
800
|
-
"state.time": (ctx) => buildTimeFragment(ctx.state),
|
|
801
|
-
"state.serialized": (ctx) => buildStateFragment(ctx.state, StateSerializer.toPrompt(ctx.state)),
|
|
802
|
-
"state.workingMemory": (ctx) => buildWorkingMemoryFragment(ctx.state),
|
|
803
|
-
"state.challengeAnalysis": (ctx) => buildChallengeAnalysisFragment(ctx.state),
|
|
804
|
-
"state.attackGraph": (ctx) => buildAttackGraphFragment(ctx.state),
|
|
805
|
-
"state.episodicMemory": (ctx) => buildEpisodicMemoryFragment(ctx.state),
|
|
806
|
-
"state.dynamicTechniques": (ctx) => buildDynamicTechniquesFragment(ctx.state),
|
|
807
|
-
"state.lastReflection": (ctx) => buildLastReflectionFragment(ctx.state.lastReflection),
|
|
808
|
-
"state.targets_ports": (ctx) => buildAttackIntelligenceFragment(ctx.state),
|
|
809
|
-
// persistent memory는 별도 처리 (import 순서 문제 방지)
|
|
810
|
-
"state.persistentMemory": (ctx) => buildPersistentMemoryFragment(ctx.state)
|
|
811
|
-
};
|
|
812
|
-
async function executeLayer(layer, ctx) {
|
|
813
|
-
try {
|
|
814
|
-
switch (layer.type) {
|
|
815
|
-
case "file":
|
|
816
|
-
return executeFileLayer(layer);
|
|
817
|
-
case "file_phase_mapped":
|
|
818
|
-
return executePhaseFileLayer(ctx.phase);
|
|
819
|
-
case "files":
|
|
820
|
-
return executeFilesLayer(layer, ctx.phase);
|
|
821
|
-
case "files_phase_mapped":
|
|
822
|
-
return executePhaseFilesLayer(layer, ctx.phase);
|
|
823
|
-
case "state":
|
|
824
|
-
return executeStateLayer(layer, ctx);
|
|
825
|
-
case "static":
|
|
826
|
-
return layer.content?.trim() ?? null;
|
|
827
|
-
case "file_read":
|
|
828
|
-
return executeFileReadLayer(layer) || null;
|
|
829
|
-
case "user_input":
|
|
830
|
-
return buildUserContextFragment(ctx.userInput) || null;
|
|
831
|
-
case "memory": {
|
|
832
|
-
const key = layer.source?.replace(/^memory\./, "") || "";
|
|
833
|
-
return ctx.memory?.[key] || null;
|
|
834
|
-
}
|
|
835
|
-
default:
|
|
836
|
-
return null;
|
|
837
|
-
}
|
|
838
|
-
} catch {
|
|
839
|
-
return null;
|
|
833
|
+
// src/agents/prompt-builder/layer-wrapping.ts
|
|
834
|
+
function wrapLayerContent(template, content, tokens = {}) {
|
|
835
|
+
if (!template) {
|
|
836
|
+
return content;
|
|
840
837
|
}
|
|
838
|
+
return Object.entries({ ...tokens, content }).reduce(
|
|
839
|
+
(acc, [key, value]) => acc.replace(`{${key}}`, value),
|
|
840
|
+
template
|
|
841
|
+
);
|
|
842
|
+
}
|
|
843
|
+
function wrapTechniqueReference(name, content) {
|
|
844
|
+
return `<technique-reference category="${name}">
|
|
845
|
+
${content}
|
|
846
|
+
</technique-reference>`;
|
|
841
847
|
}
|
|
848
|
+
|
|
849
|
+
// src/agents/prompt-builder/file-layer-executors.ts
|
|
842
850
|
function executeFileReadLayer(layer) {
|
|
843
851
|
const source = layer.source?.trim();
|
|
844
852
|
if (!source) return null;
|
|
845
853
|
if (source.includes("{N-1}")) {
|
|
846
854
|
return buildJournalFragment() || null;
|
|
847
855
|
}
|
|
848
|
-
const
|
|
849
|
-
if (!existsSync3(absolutePath)) return null;
|
|
850
|
-
const content = readFileSync3(absolutePath, "utf-8").trim();
|
|
856
|
+
const content = readRuntimeAssetFile(source, "utf-8")?.trim();
|
|
851
857
|
if (!content) return null;
|
|
852
|
-
|
|
853
|
-
return layer.wrap.replace("{content}", content);
|
|
854
|
-
}
|
|
855
|
-
return content;
|
|
858
|
+
return wrapLayerContent(layer.wrap, content);
|
|
856
859
|
}
|
|
857
860
|
function executeFileLayer(layer) {
|
|
858
861
|
if (!layer.source) return null;
|
|
@@ -861,54 +864,149 @@ function executeFileLayer(layer) {
|
|
|
861
864
|
return loadPromptFile(filename) || null;
|
|
862
865
|
}
|
|
863
866
|
function executePhaseFileLayer(phase) {
|
|
864
|
-
const
|
|
865
|
-
const file = sources.phase_prompts?.[phase];
|
|
867
|
+
const file = getPhasePromptSource(phase);
|
|
866
868
|
if (!file) return null;
|
|
867
869
|
return loadPromptFile(file) || null;
|
|
868
870
|
}
|
|
869
871
|
function executeFilesLayer(layer, phase) {
|
|
870
|
-
const
|
|
871
|
-
const
|
|
872
|
-
const phaseFile = sources.phase_prompts?.[phase];
|
|
872
|
+
const files = getCoreKnowledgeSources();
|
|
873
|
+
const phaseFile = getPhasePromptSource(phase);
|
|
873
874
|
const filtered = layer.exclude_current_phase_file && phaseFile ? files.filter((f) => f !== phaseFile) : files;
|
|
874
|
-
const parts = filtered.map((
|
|
875
|
-
const content = loadPromptFile(
|
|
875
|
+
const parts = filtered.map((file) => {
|
|
876
|
+
const content = loadPromptFile(file);
|
|
876
877
|
if (!content) return "";
|
|
877
|
-
const label =
|
|
878
|
-
|
|
879
|
-
return layer.wrap.replace("{label}", label).replace("{content}", content);
|
|
880
|
-
}
|
|
881
|
-
return content;
|
|
878
|
+
const label = file.replace(".md", "");
|
|
879
|
+
return wrapLayerContent(layer.wrap, content, { label });
|
|
882
880
|
});
|
|
883
881
|
return parts.filter(Boolean).join("\n\n") || null;
|
|
884
882
|
}
|
|
885
883
|
function executePhaseFilesLayer(layer, phase) {
|
|
886
|
-
const
|
|
887
|
-
const files = sources.phase_techniques?.[phase] ?? [];
|
|
884
|
+
const files = getPhaseTechniqueSources(phase);
|
|
888
885
|
if (files.length === 0) return null;
|
|
889
886
|
const parts = files.map((name) => {
|
|
890
887
|
const content = loadTechniqueFile(name);
|
|
891
888
|
if (!content) return "";
|
|
892
|
-
if (layer.wrap) {
|
|
893
|
-
|
|
894
|
-
}
|
|
895
|
-
return `<technique-reference category="${name}">
|
|
896
|
-
${content}
|
|
897
|
-
</technique-reference>`;
|
|
889
|
+
if (layer.wrap) return wrapLayerContent(layer.wrap, content, { name });
|
|
890
|
+
return wrapTechniqueReference(name, content);
|
|
898
891
|
});
|
|
899
892
|
return parts.filter(Boolean).join("\n\n") || null;
|
|
900
893
|
}
|
|
894
|
+
|
|
895
|
+
// src/agents/prompt-builder/state-layer-registry.ts
|
|
896
|
+
var STATE_LAYER_REGISTRY = {
|
|
897
|
+
"state.scope": (ctx) => buildScopeFragment(ctx.state),
|
|
898
|
+
"state.todo": (ctx) => buildTodoFragment(ctx.state),
|
|
899
|
+
"state.time": (ctx) => buildTimeFragment(ctx.state),
|
|
900
|
+
"state.serialized": (ctx) => buildStateFragment(ctx.state, StateSerializer.toPrompt(ctx.state)),
|
|
901
|
+
"state.workingMemory": (ctx) => buildWorkingMemoryFragment(ctx.state),
|
|
902
|
+
"state.challengeAnalysis": (ctx) => buildChallengeAnalysisFragment(ctx.state),
|
|
903
|
+
"state.attackGraph": (ctx) => buildAttackGraphFragment(ctx.state),
|
|
904
|
+
"state.episodicMemory": (ctx) => buildEpisodicMemoryFragment(ctx.state),
|
|
905
|
+
"state.dynamicTechniques": (ctx) => buildDynamicTechniquesFragment(ctx.state),
|
|
906
|
+
"state.lastReflection": (ctx) => buildLastReflectionFragment(ctx.state.lastReflection),
|
|
907
|
+
"state.targets_ports": (ctx) => buildAttackIntelligenceFragment(ctx.state),
|
|
908
|
+
"state.persistentMemory": (ctx) => buildPersistentMemoryFragment(ctx.state)
|
|
909
|
+
};
|
|
910
|
+
|
|
911
|
+
// src/agents/prompt-builder/stateful-layer-executors.ts
|
|
901
912
|
function executeStateLayer(layer, ctx) {
|
|
902
913
|
const source = layer.source ?? "";
|
|
903
914
|
if (source === "state.blind_spots") {
|
|
904
915
|
return buildBlindSpotsCheckFragment() || null;
|
|
905
916
|
}
|
|
906
|
-
const fn =
|
|
917
|
+
const fn = STATE_LAYER_REGISTRY[source];
|
|
907
918
|
if (!fn) return null;
|
|
908
919
|
const fragment = fn(ctx);
|
|
909
920
|
if (!fragment && layer.on_empty) return layer.on_empty;
|
|
910
921
|
return fragment || null;
|
|
911
922
|
}
|
|
923
|
+
function executeMemoryLayer(layer, ctx) {
|
|
924
|
+
const key = layer.source?.replace(/^memory\./, "") || "";
|
|
925
|
+
return ctx.memory?.[key] || null;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// src/agents/prompt-builder/layer-executor.ts
|
|
929
|
+
var LAYER_EXECUTORS = {
|
|
930
|
+
file: (layer) => executeFileLayer(layer),
|
|
931
|
+
file_phase_mapped: (_layer, ctx) => executePhaseFileLayer(ctx.phase),
|
|
932
|
+
files: (layer, ctx) => executeFilesLayer(layer, ctx.phase),
|
|
933
|
+
files_phase_mapped: (layer, ctx) => executePhaseFilesLayer(layer, ctx.phase),
|
|
934
|
+
state: (layer, ctx) => executeStateLayer(layer, ctx),
|
|
935
|
+
static: (layer) => layer.content?.trim() ?? null,
|
|
936
|
+
file_read: (layer) => executeFileReadLayer(layer),
|
|
937
|
+
user_input: (_layer, ctx) => buildUserContextFragment(ctx.userInput) || null,
|
|
938
|
+
memory: (layer, ctx) => executeMemoryLayer(layer, ctx)
|
|
939
|
+
};
|
|
940
|
+
async function executeLayer(layer, ctx) {
|
|
941
|
+
try {
|
|
942
|
+
return LAYER_EXECUTORS[layer.type]?.(layer, ctx) ?? null;
|
|
943
|
+
} catch {
|
|
944
|
+
return null;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// src/agents/prompt-builder/prompt-section-builder.ts
|
|
949
|
+
function sortLayersByPriority(layers) {
|
|
950
|
+
return [...layers].sort((a, b) => a.id - b.id);
|
|
951
|
+
}
|
|
952
|
+
async function buildPromptSections(layers, ctx) {
|
|
953
|
+
const sortedLayers = sortLayersByPriority(layers);
|
|
954
|
+
const alwaysIncluded = sortedLayers.filter((layer) => layer.always_included);
|
|
955
|
+
const regularLayers = sortedLayers.filter((layer) => !layer.always_included);
|
|
956
|
+
const regularSections = await renderLayerGroup(regularLayers, ctx);
|
|
957
|
+
const alwaysSections = await renderLayerGroup(alwaysIncluded, ctx);
|
|
958
|
+
return { regularSections, alwaysSections };
|
|
959
|
+
}
|
|
960
|
+
async function renderLayerGroup(layers, ctx) {
|
|
961
|
+
const sections = [];
|
|
962
|
+
for (const layer of layers) {
|
|
963
|
+
const fragment = await executeLayer(layer, ctx);
|
|
964
|
+
if (fragment) {
|
|
965
|
+
sections.push(fragment);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
return sections;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// src/agents/prompt-builder/prompt-truncation.ts
|
|
972
|
+
var TRUNCATION_NOTICE = "... [PROMPT TRUNCATED: content exceeded context limit] ...";
|
|
973
|
+
var TRUNCATION_NOTICE_RESERVE = 100;
|
|
974
|
+
function joinPromptSections(sections) {
|
|
975
|
+
const alwaysText = sections.alwaysSections.filter(Boolean).join("\n\n");
|
|
976
|
+
const fullBody = sections.regularSections.filter(Boolean).join("\n\n");
|
|
977
|
+
const full = alwaysText ? `${fullBody}
|
|
978
|
+
|
|
979
|
+
${alwaysText}` : fullBody;
|
|
980
|
+
return { fullBody, alwaysText, full };
|
|
981
|
+
}
|
|
982
|
+
function applyPromptTruncation(fullBody, alwaysText, maxChars, userInput) {
|
|
983
|
+
const full = alwaysText ? `${fullBody}
|
|
984
|
+
|
|
985
|
+
${alwaysText}` : fullBody;
|
|
986
|
+
if (full.length <= maxChars) {
|
|
987
|
+
return full;
|
|
988
|
+
}
|
|
989
|
+
const reservedLen = alwaysText.length + TRUNCATION_NOTICE_RESERVE;
|
|
990
|
+
const truncated = fullBody.slice(0, maxChars - reservedLen);
|
|
991
|
+
return alwaysText ? `${truncated}
|
|
992
|
+
|
|
993
|
+
${TRUNCATION_NOTICE}
|
|
994
|
+
|
|
995
|
+
${alwaysText}` : `${truncated}
|
|
996
|
+
|
|
997
|
+
${TRUNCATION_NOTICE}
|
|
998
|
+
|
|
999
|
+
${buildUserContextFragment(userInput)}`;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// src/agents/prompt-builder-config.ts
|
|
1003
|
+
var DEFAULT_PROMPT_BUILDER_CONFIG = {};
|
|
1004
|
+
function getPromptBuilderConfig() {
|
|
1005
|
+
return getOptionalRuntimeSection("prompt_builder") ?? DEFAULT_PROMPT_BUILDER_CONFIG;
|
|
1006
|
+
}
|
|
1007
|
+
function getPromptBuilderLayers() {
|
|
1008
|
+
return getPromptBuilderConfig().layers ?? [];
|
|
1009
|
+
}
|
|
912
1010
|
|
|
913
1011
|
// src/agents/prompt-builder/prompt-builder.ts
|
|
914
1012
|
var DEFAULT_MAX_CHARS = 4e5;
|
|
@@ -918,51 +1016,23 @@ var PromptBuilder = class {
|
|
|
918
1016
|
this.state = state;
|
|
919
1017
|
}
|
|
920
1018
|
/**
|
|
921
|
-
* Build the system prompt by executing
|
|
1019
|
+
* Build the system prompt by executing declarative prompt layers in order.
|
|
922
1020
|
*
|
|
923
|
-
* Layer order is determined by `id` in
|
|
924
|
-
* To add/remove/reorder layers: edit pipeline.yaml only.
|
|
1021
|
+
* Layer order is determined by `id` in runtime config prompt_builder.layers.
|
|
925
1022
|
*/
|
|
926
1023
|
async build(userInput, phase, memory) {
|
|
927
1024
|
const config = getPromptBuilderConfig();
|
|
928
1025
|
const maxChars = config.max_chars ?? DEFAULT_MAX_CHARS;
|
|
929
|
-
const layers =
|
|
930
|
-
const alwaysIncluded = layers.filter((l) => l.always_included);
|
|
931
|
-
const regularLayers = layers.filter((l) => !l.always_included);
|
|
1026
|
+
const layers = getPromptBuilderLayers();
|
|
932
1027
|
const ctx = {
|
|
933
1028
|
state: this.state,
|
|
934
1029
|
phase,
|
|
935
1030
|
userInput,
|
|
936
1031
|
memory: memory ?? {}
|
|
937
1032
|
};
|
|
938
|
-
const
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
if (fragment) regularSections.push(fragment);
|
|
942
|
-
}
|
|
943
|
-
const alwaysSections = [];
|
|
944
|
-
for (const layer of alwaysIncluded) {
|
|
945
|
-
const fragment = await executeLayer(layer, ctx);
|
|
946
|
-
if (fragment) alwaysSections.push(fragment);
|
|
947
|
-
}
|
|
948
|
-
const alwaysText = alwaysSections.filter(Boolean).join("\n\n");
|
|
949
|
-
const fullBody = regularSections.filter(Boolean).join("\n\n");
|
|
950
|
-
const full = alwaysText ? `${fullBody}
|
|
951
|
-
|
|
952
|
-
${alwaysText}` : fullBody;
|
|
953
|
-
if (full.length <= maxChars) return full;
|
|
954
|
-
const reservedLen = alwaysText.length + 100;
|
|
955
|
-
const truncated = fullBody.slice(0, maxChars - reservedLen);
|
|
956
|
-
const notice = "... [PROMPT TRUNCATED: content exceeded context limit] ...";
|
|
957
|
-
return alwaysText ? `${truncated}
|
|
958
|
-
|
|
959
|
-
${notice}
|
|
960
|
-
|
|
961
|
-
${alwaysText}` : `${truncated}
|
|
962
|
-
|
|
963
|
-
${notice}
|
|
964
|
-
|
|
965
|
-
${buildUserContextFragment(userInput)}`;
|
|
1033
|
+
const sections = await buildPromptSections(layers, ctx);
|
|
1034
|
+
const { fullBody, alwaysText } = joinPromptSections(sections);
|
|
1035
|
+
return applyPromptTruncation(fullBody, alwaysText, maxChars, userInput);
|
|
966
1036
|
}
|
|
967
1037
|
};
|
|
968
1038
|
|
|
@@ -1088,9 +1158,6 @@ var ActionNode = class {
|
|
|
1088
1158
|
// src/engine/pipeline/builder.ts
|
|
1089
1159
|
var flagsCaptured = (ctx) => ctx.flagsAfter > ctx.flagsBefore;
|
|
1090
1160
|
|
|
1091
|
-
// src/engine/pipeline/loader.ts
|
|
1092
|
-
import { parse as yamlParse } from "yaml";
|
|
1093
|
-
|
|
1094
1161
|
// src/engine/pipeline/io-registry.ts
|
|
1095
1162
|
var SOURCE_REGISTRY = {
|
|
1096
1163
|
// ── Reflector input fields (ReflectorInput) ───────────────────────────────
|
|
@@ -1409,7 +1476,7 @@ var makeRotateTurns = (_llm, _opts) => async (_ctx) => {
|
|
|
1409
1476
|
};
|
|
1410
1477
|
var makeSaveSession = (_llm, _opts) => async (ctx) => {
|
|
1411
1478
|
try {
|
|
1412
|
-
const { saveState: saveState2 } = await import("./persistence-
|
|
1479
|
+
const { saveState: saveState2 } = await import("./persistence-BNVN3WW6.js");
|
|
1413
1480
|
saveState2(ctx.state);
|
|
1414
1481
|
} catch {
|
|
1415
1482
|
}
|
|
@@ -1722,17 +1789,1674 @@ function initializeTask(state) {
|
|
|
1722
1789
|
state.addTodo(INITIAL_TASKS.RECON, PRIORITIES.HIGH);
|
|
1723
1790
|
}
|
|
1724
1791
|
}
|
|
1725
|
-
async function resetSessionAction(state, userInputQueue) {
|
|
1726
|
-
await cleanupAllProcesses().catch(() => {
|
|
1792
|
+
async function resetSessionAction(state, userInputQueue) {
|
|
1793
|
+
await cleanupAllProcesses().catch(() => {
|
|
1794
|
+
});
|
|
1795
|
+
state.reset();
|
|
1796
|
+
userInputQueue.clear();
|
|
1797
|
+
return clearWorkspace();
|
|
1798
|
+
}
|
|
1799
|
+
var session = {
|
|
1800
|
+
save: (state) => saveState(state),
|
|
1801
|
+
load: (state) => loadState(state)
|
|
1802
|
+
};
|
|
1803
|
+
|
|
1804
|
+
// src/agents/runtime-pipeline-config.ts
|
|
1805
|
+
function readRuntimePipelineNodes() {
|
|
1806
|
+
return getRequiredRuntimeSection("nodes");
|
|
1807
|
+
}
|
|
1808
|
+
function readRuntimeTurnCycle() {
|
|
1809
|
+
return getRequiredRuntimeSection("turn_cycle");
|
|
1810
|
+
}
|
|
1811
|
+
function getRuntimePipelineConfig() {
|
|
1812
|
+
return {
|
|
1813
|
+
version: getOptionalRuntimeSection("version"),
|
|
1814
|
+
nodes: readRuntimePipelineNodes(),
|
|
1815
|
+
turn_cycle: readRuntimeTurnCycle()
|
|
1816
|
+
};
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
// src/agents/main-agent/delegated-task-scheduler.ts
|
|
1820
|
+
function getDelegatedTaskPriority(task) {
|
|
1821
|
+
const workerPriority = task.nextWorkerType || task.workerType;
|
|
1822
|
+
if (workerPriority === "shell-supervisor" && task.status === "waiting") return 0;
|
|
1823
|
+
if (workerPriority === "shell-supervisor" && task.status === "running") return 1;
|
|
1824
|
+
if (task.status === "waiting") return 2;
|
|
1825
|
+
return 3;
|
|
1826
|
+
}
|
|
1827
|
+
function buildSelectionReason(task) {
|
|
1828
|
+
const preferredWorkerType = task.nextWorkerType || task.workerType;
|
|
1829
|
+
if (preferredWorkerType === "shell-supervisor" && task.status === "waiting") {
|
|
1830
|
+
return "Selected first because shell supervision is waiting on an external callback and should be polled before duplicate chains start.";
|
|
1831
|
+
}
|
|
1832
|
+
if (preferredWorkerType === "shell-supervisor" && task.status === "running") {
|
|
1833
|
+
return "Selected first because shell supervision is already active and should stay attached to the existing access path.";
|
|
1834
|
+
}
|
|
1835
|
+
if (task.status === "waiting") {
|
|
1836
|
+
return "Selected because this delegated task is blocked on an external event and needs explicit supervision.";
|
|
1837
|
+
}
|
|
1838
|
+
return "Selected because this delegated task is already running and should progress before opening a fresh chain.";
|
|
1839
|
+
}
|
|
1840
|
+
function selectDelegatedTaskForResume(activeTasks, autoResumeCounts, lastAutoResumeTaskId) {
|
|
1841
|
+
if (activeTasks.length === 0) {
|
|
1842
|
+
return null;
|
|
1843
|
+
}
|
|
1844
|
+
const rankedTasks = [...activeTasks].sort((left, right) => {
|
|
1845
|
+
const leftCount = autoResumeCounts.get(left.id) || 0;
|
|
1846
|
+
const rightCount = autoResumeCounts.get(right.id) || 0;
|
|
1847
|
+
if (leftCount !== rightCount) {
|
|
1848
|
+
return leftCount - rightCount;
|
|
1849
|
+
}
|
|
1850
|
+
const leftPriority = getDelegatedTaskPriority(left);
|
|
1851
|
+
const rightPriority = getDelegatedTaskPriority(right);
|
|
1852
|
+
if (leftPriority !== rightPriority) {
|
|
1853
|
+
return leftPriority - rightPriority;
|
|
1854
|
+
}
|
|
1855
|
+
if (left.updatedAt !== right.updatedAt) {
|
|
1856
|
+
return left.updatedAt - right.updatedAt;
|
|
1857
|
+
}
|
|
1858
|
+
return left.createdAt - right.createdAt;
|
|
1859
|
+
});
|
|
1860
|
+
const selectedTask = rankedTasks.length > 1 ? rankedTasks.find((task) => task.id !== lastAutoResumeTaskId) || rankedTasks[0] : rankedTasks[0];
|
|
1861
|
+
if (!selectedTask || selectedTask.id === lastAutoResumeTaskId) {
|
|
1862
|
+
return null;
|
|
1863
|
+
}
|
|
1864
|
+
return {
|
|
1865
|
+
task: selectedTask,
|
|
1866
|
+
preferredWorkerType: selectedTask.nextWorkerType || selectedTask.workerType,
|
|
1867
|
+
reason: buildSelectionReason(selectedTask),
|
|
1868
|
+
resumeCount: autoResumeCounts.get(selectedTask.id) || 0
|
|
1869
|
+
};
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
// src/agents/main-agent/delegated-task-queue.ts
|
|
1873
|
+
var DelegatedTaskQueue = class {
|
|
1874
|
+
lastTaskId = null;
|
|
1875
|
+
resumeCounts = /* @__PURE__ */ new Map();
|
|
1876
|
+
reset() {
|
|
1877
|
+
this.lastTaskId = null;
|
|
1878
|
+
this.resumeCounts.clear();
|
|
1879
|
+
}
|
|
1880
|
+
sync(activeTasks) {
|
|
1881
|
+
const activeIds = new Set(activeTasks.map((task) => task.id));
|
|
1882
|
+
if (activeTasks.length === 0) {
|
|
1883
|
+
this.reset();
|
|
1884
|
+
return;
|
|
1885
|
+
}
|
|
1886
|
+
for (const taskId of this.resumeCounts.keys()) {
|
|
1887
|
+
if (!activeIds.has(taskId)) {
|
|
1888
|
+
this.resumeCounts.delete(taskId);
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
if (this.lastTaskId && !activeIds.has(this.lastTaskId)) {
|
|
1892
|
+
this.lastTaskId = null;
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
next(activeTasks) {
|
|
1896
|
+
this.sync(activeTasks);
|
|
1897
|
+
return selectDelegatedTaskForResume(activeTasks, this.resumeCounts, this.lastTaskId);
|
|
1898
|
+
}
|
|
1899
|
+
acknowledge(taskId) {
|
|
1900
|
+
this.lastTaskId = taskId;
|
|
1901
|
+
const nextCount = (this.resumeCounts.get(taskId) || 0) + 1;
|
|
1902
|
+
this.resumeCounts.set(taskId, nextCount);
|
|
1903
|
+
return nextCount;
|
|
1904
|
+
}
|
|
1905
|
+
getLastTaskId() {
|
|
1906
|
+
return this.lastTaskId;
|
|
1907
|
+
}
|
|
1908
|
+
};
|
|
1909
|
+
|
|
1910
|
+
// src/agents/main-agent/exploit-runtime-signals.ts
|
|
1911
|
+
function outputContainsAny(output, patterns) {
|
|
1912
|
+
return patterns.some((pattern) => output.includes(pattern.toLowerCase()));
|
|
1913
|
+
}
|
|
1914
|
+
function getExploitResultSignals(result) {
|
|
1915
|
+
const output = (result.output || "").toLowerCase();
|
|
1916
|
+
const hasSession = outputContainsAny(output, ["[sessions]", "shell", "session"]);
|
|
1917
|
+
const hasLoot = outputContainsAny(output, ["[loot]", "password", "credential", "token", "hash"]);
|
|
1918
|
+
const hasAsset = outputContainsAny(output, ["[assets]", "artifact", "file", "payload"]);
|
|
1919
|
+
return {
|
|
1920
|
+
hasSession,
|
|
1921
|
+
hasLoot,
|
|
1922
|
+
hasAsset,
|
|
1923
|
+
indicatesProgress: hasSession || hasLoot || hasAsset || outputContainsAny(output, ["[status] running", "[status] success"])
|
|
1924
|
+
};
|
|
1925
|
+
}
|
|
1926
|
+
function buildExploitExecutionAnalysis(phase, signals) {
|
|
1927
|
+
if (signals.hasSession) {
|
|
1928
|
+
return {
|
|
1929
|
+
phaseObservation: "exploit chain produced a foothold or authenticated session",
|
|
1930
|
+
nextHint: "reuse the current foothold and continue from the confirmed access path"
|
|
1931
|
+
};
|
|
1932
|
+
}
|
|
1933
|
+
if (phase === "credential_followup") {
|
|
1934
|
+
return {
|
|
1935
|
+
phaseObservation: signals.hasLoot ? "credential follow-up produced reusable credential material" : "credential follow-up step executed",
|
|
1936
|
+
nextHint: "reuse validated credentials before changing exploit vectors"
|
|
1937
|
+
};
|
|
1938
|
+
}
|
|
1939
|
+
if (phase === "artifact_ready") {
|
|
1940
|
+
return {
|
|
1941
|
+
phaseObservation: signals.hasAsset ? "artifact validation produced a reusable artifact or asset" : "artifact validation step executed",
|
|
1942
|
+
nextHint: "advance the current artifact before replacing it"
|
|
1943
|
+
};
|
|
1944
|
+
}
|
|
1945
|
+
return {
|
|
1946
|
+
phaseObservation: signals.indicatesProgress ? "exploit chain advanced and produced follow-up signals" : "delegated exploit step executed",
|
|
1947
|
+
nextHint: "continue the selected exploit chain before switching vectors"
|
|
1948
|
+
};
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
// src/agents/main-agent/pwn-runtime-signals.ts
|
|
1952
|
+
function outputContainsAny2(output, patterns) {
|
|
1953
|
+
return patterns.some((pattern) => output.includes(pattern.toLowerCase()));
|
|
1954
|
+
}
|
|
1955
|
+
function getPwnResultSignals(result) {
|
|
1956
|
+
const output = (result.output || "").toLowerCase();
|
|
1957
|
+
const hasFoothold2 = outputContainsAny2(output, ["[sessions]", "shell", "uid=", "got shell"]);
|
|
1958
|
+
const stillCrashing = outputContainsAny2(output, ["crash", "segfault", "core"]);
|
|
1959
|
+
const hasFindings = outputContainsAny2(output, ["[findings]", "rip controlled", "offset", "eip", "pc controlled"]);
|
|
1960
|
+
return {
|
|
1961
|
+
hasFoothold: hasFoothold2,
|
|
1962
|
+
stillCrashing,
|
|
1963
|
+
hasFindings,
|
|
1964
|
+
indicatesProgress: hasFoothold2 || hasFindings || outputContainsAny2(output, ["[status] running", "[status] success", "[nextworker] pwn"])
|
|
1965
|
+
};
|
|
1966
|
+
}
|
|
1967
|
+
function buildPwnExecutionAnalysis(phase, signals) {
|
|
1968
|
+
if (signals.hasFoothold) {
|
|
1969
|
+
return {
|
|
1970
|
+
phaseObservation: "pwn chain achieved code execution or an interactive foothold",
|
|
1971
|
+
nextHint: "reuse the achieved foothold and continue from the working exploit state"
|
|
1972
|
+
};
|
|
1973
|
+
}
|
|
1974
|
+
if (phase === "offset_known") {
|
|
1975
|
+
return {
|
|
1976
|
+
phaseObservation: signals.hasFindings ? "offset-guided pwn iteration confirmed control signals" : "offset-guided pwn iteration executed",
|
|
1977
|
+
nextHint: "continue from the known offset and latest payload revision"
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1980
|
+
if (phase === "crash_iteration") {
|
|
1981
|
+
return {
|
|
1982
|
+
phaseObservation: signals.stillCrashing ? "crash-driven pwn iteration preserved the crash state" : "crash-driven pwn iteration executed",
|
|
1983
|
+
nextHint: "preserve crash evidence and narrow the next payload iteration"
|
|
1984
|
+
};
|
|
1985
|
+
}
|
|
1986
|
+
return {
|
|
1987
|
+
phaseObservation: signals.indicatesProgress ? "pwn chain advanced and produced follow-up signals" : "delegated pwn step executed",
|
|
1988
|
+
nextHint: "continue the selected exploit-development loop before broadening scope"
|
|
1989
|
+
};
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1992
|
+
// src/agents/main-agent/delegated-execution-analysis.ts
|
|
1993
|
+
function getBoundedCredentialService(request) {
|
|
1994
|
+
const command = request.boundedCommand || "";
|
|
1995
|
+
if (command.includes("sshpass")) return "ssh";
|
|
1996
|
+
if (command.includes("smbclient")) return "smb";
|
|
1997
|
+
if (command.includes("evil-winrm")) return "winrm";
|
|
1998
|
+
if (command.includes("xfreerdp")) return "rdp";
|
|
1999
|
+
if (command.includes("ldapwhoami") || command.includes("ldapsearch")) return "ldap";
|
|
2000
|
+
if (command.includes("mysql ")) return "mysql";
|
|
2001
|
+
if (command.includes("psql ")) return "postgres";
|
|
2002
|
+
if (command.includes("curl -fsSIL -u")) return "http";
|
|
2003
|
+
if (command.includes("curl -fsS --user")) return "ftp";
|
|
2004
|
+
if (command.includes("Authorization: Bearer") || command.includes("X-API-Key")) return "token_or_api";
|
|
2005
|
+
return null;
|
|
2006
|
+
}
|
|
2007
|
+
function getBoundedArtifactKind(request) {
|
|
2008
|
+
const command = request.boundedCommand || "";
|
|
2009
|
+
if (command.includes("curl -fsSIL") && command.includes("| head -n 5")) return "webshell";
|
|
2010
|
+
if (command.includes("head -n 5") && command.includes(".sql")) return "database_dump";
|
|
2011
|
+
if (command.includes("head -n 5") && command.includes(".db")) return "database_dump";
|
|
2012
|
+
if (command.includes("smbclient -L")) return "smb_share";
|
|
2013
|
+
if (command.includes("curl -fsSIL")) return "url";
|
|
2014
|
+
if (command.includes("test -e")) return "file";
|
|
2015
|
+
return null;
|
|
2016
|
+
}
|
|
2017
|
+
function getBoundedPwnProbeKind(request) {
|
|
2018
|
+
const command = request.boundedCommand || "";
|
|
2019
|
+
if (command.includes("gdb -q") && command.includes("info registers") && command.includes("bt")) {
|
|
2020
|
+
return "core_gdb";
|
|
2021
|
+
}
|
|
2022
|
+
if (command.startsWith("gdb ")) {
|
|
2023
|
+
return "gdb_replay";
|
|
2024
|
+
}
|
|
2025
|
+
if (command.includes("timeout 5")) {
|
|
2026
|
+
return "binary_smoke";
|
|
2027
|
+
}
|
|
2028
|
+
return null;
|
|
2029
|
+
}
|
|
2030
|
+
function getPhaseFromContext(context, key) {
|
|
2031
|
+
if (!context) {
|
|
2032
|
+
return null;
|
|
2033
|
+
}
|
|
2034
|
+
const match = context.match(new RegExp(`${key}=([a-z_]+)`));
|
|
2035
|
+
return match?.[1] ?? null;
|
|
2036
|
+
}
|
|
2037
|
+
function analyzeDelegatedExecutionResult(request, result) {
|
|
2038
|
+
if (!request || !result) {
|
|
2039
|
+
return null;
|
|
2040
|
+
}
|
|
2041
|
+
if (!result.success) {
|
|
2042
|
+
return {
|
|
2043
|
+
phaseObservation: "delegated step failed",
|
|
2044
|
+
nextHint: "retry the same chain only after using the error to narrow the next step"
|
|
2045
|
+
};
|
|
2046
|
+
}
|
|
2047
|
+
if (request.workerType === "shell-supervisor") {
|
|
2048
|
+
const phase = getPhaseFromContext(request.context, "shell_phase");
|
|
2049
|
+
if (phase === "listener_waiting") {
|
|
2050
|
+
return {
|
|
2051
|
+
phaseObservation: "listener supervision step executed",
|
|
2052
|
+
nextHint: "if callback evidence appeared, promote and validate the shell immediately"
|
|
2053
|
+
};
|
|
2054
|
+
}
|
|
2055
|
+
if (phase === "active_shell_stabilizing") {
|
|
2056
|
+
return {
|
|
2057
|
+
phaseObservation: "shell stabilization step executed",
|
|
2058
|
+
nextHint: "finish PTY stabilization before broader enumeration"
|
|
2059
|
+
};
|
|
2060
|
+
}
|
|
2061
|
+
if (phase === "post_exploitation_active") {
|
|
2062
|
+
return {
|
|
2063
|
+
phaseObservation: "post-exploitation enumeration step executed",
|
|
2064
|
+
nextHint: "continue controlled enumeration through the same shell"
|
|
2065
|
+
};
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
2068
|
+
if (request.workerType === "exploit") {
|
|
2069
|
+
if (request.boundedTool === "exploit_credential_check") {
|
|
2070
|
+
const service = getBoundedCredentialService(request);
|
|
2071
|
+
return {
|
|
2072
|
+
phaseObservation: service ? `bounded ${service} credential validation executed for the exploit chain` : "bounded credential validation executed for the exploit chain",
|
|
2073
|
+
nextHint: service ? `reuse the validated ${service} access path or convert it into a foothold before changing vectors` : "reuse the validated credentials or convert the access path into a foothold before changing vectors"
|
|
2074
|
+
};
|
|
2075
|
+
}
|
|
2076
|
+
if (request.boundedTool === "exploit_artifact_check") {
|
|
2077
|
+
const artifactKind = getBoundedArtifactKind(request);
|
|
2078
|
+
return {
|
|
2079
|
+
phaseObservation: artifactKind ? `bounded ${artifactKind} artifact validation executed for the exploit chain` : "bounded artifact validation executed for the exploit chain",
|
|
2080
|
+
nextHint: artifactKind === "webshell" ? "reuse the validated webshell artifact as a foothold before changing exploit delivery" : artifactKind === "database_dump" ? "advance the validated dump into credential extraction, parsing, or authenticated follow-up" : artifactKind === "smb_share" ? "reuse the validated share path for file access or lateral follow-up before changing vectors" : "advance the validated artifact into data access, authenticated use, or foothold confirmation"
|
|
2081
|
+
};
|
|
2082
|
+
}
|
|
2083
|
+
if (request.boundedTool === "exploit_foothold_check") {
|
|
2084
|
+
return {
|
|
2085
|
+
phaseObservation: "bounded foothold validation executed for the exploit chain",
|
|
2086
|
+
nextHint: "reuse the foothold and continue from the confirmed access path instead of restarting delivery"
|
|
2087
|
+
};
|
|
2088
|
+
}
|
|
2089
|
+
if (request.boundedTool === "exploit_vector_check") {
|
|
2090
|
+
return {
|
|
2091
|
+
phaseObservation: "bounded exploit-vector reachability check executed",
|
|
2092
|
+
nextHint: "if the vector is still reachable, continue adapting the current chain before switching to a new one"
|
|
2093
|
+
};
|
|
2094
|
+
}
|
|
2095
|
+
const phase = getPhaseFromContext(request.context, "exploit_phase");
|
|
2096
|
+
return buildExploitExecutionAnalysis(phase, getExploitResultSignals(result));
|
|
2097
|
+
}
|
|
2098
|
+
if (request.workerType === "pwn") {
|
|
2099
|
+
if (request.boundedTool === "pwn_offset_check") {
|
|
2100
|
+
const probeKind = getBoundedPwnProbeKind(request);
|
|
2101
|
+
return {
|
|
2102
|
+
phaseObservation: probeKind === "core_gdb" ? "bounded gdb core-state verification executed for the pwn loop" : probeKind === "gdb_replay" ? "bounded gdb replay executed for the pwn loop" : "bounded offset verification executed for the pwn loop",
|
|
2103
|
+
nextHint: probeKind === "core_gdb" ? "reuse the saved core-state evidence and narrow the next payload iteration from the confirmed crash context" : probeKind === "gdb_replay" ? "continue from the gdb-confirmed state and narrow the next payload iteration" : "continue from the known offset and narrow the next payload iteration"
|
|
2104
|
+
};
|
|
2105
|
+
}
|
|
2106
|
+
if (request.boundedTool === "pwn_crash_repro") {
|
|
2107
|
+
const probeKind = getBoundedPwnProbeKind(request);
|
|
2108
|
+
return {
|
|
2109
|
+
phaseObservation: probeKind === "core_gdb" ? "bounded core-backed crash inspection executed for the pwn loop" : probeKind === "gdb_replay" ? "bounded gdb-backed crash replay executed for the pwn loop" : "bounded crash reproduction executed for the pwn loop",
|
|
2110
|
+
nextHint: probeKind === "core_gdb" ? "preserve the core-backed crash state and apply the narrowest next debugging step" : probeKind === "gdb_replay" ? "preserve the gdb replay state and apply the narrowest next debugging or payload mutation step" : "preserve the crash state and apply the narrowest next debugging or payload mutation step"
|
|
2111
|
+
};
|
|
2112
|
+
}
|
|
2113
|
+
if (request.boundedTool === "pwn_payload_smoke") {
|
|
2114
|
+
const probeKind = getBoundedPwnProbeKind(request);
|
|
2115
|
+
return {
|
|
2116
|
+
phaseObservation: probeKind === "gdb_replay" ? "bounded gdb-guided smoke test executed for the pwn loop" : "bounded payload smoke test executed for the pwn loop",
|
|
2117
|
+
nextHint: probeKind === "gdb_replay" ? "preserve the gdb-guided payload state and continue narrowing the next test" : "preserve the last payload revision and continue narrowing the next test"
|
|
2118
|
+
};
|
|
2119
|
+
}
|
|
2120
|
+
const phase = getPhaseFromContext(request.context, "pwn_phase");
|
|
2121
|
+
return buildPwnExecutionAnalysis(phase, getPwnResultSignals(result));
|
|
2122
|
+
}
|
|
2123
|
+
return {
|
|
2124
|
+
phaseObservation: "delegated step executed",
|
|
2125
|
+
nextHint: "continue the selected delegated chain before switching focus"
|
|
2126
|
+
};
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
// src/agents/main-agent/delegated-execution-plan.ts
|
|
2130
|
+
function getPhaseFromContext2(context, key) {
|
|
2131
|
+
if (!context) {
|
|
2132
|
+
return null;
|
|
2133
|
+
}
|
|
2134
|
+
const match = context.match(new RegExp(`${key}=([a-z_]+)`));
|
|
2135
|
+
return match?.[1] ?? null;
|
|
2136
|
+
}
|
|
2137
|
+
function getRecommendedBoundedTool(request) {
|
|
2138
|
+
if (request.workerType === "shell-supervisor") {
|
|
2139
|
+
const phase = getPhaseFromContext2(request.context, "shell_phase");
|
|
2140
|
+
if (phase === "listener_waiting") return "listener_status";
|
|
2141
|
+
if (phase === "listener_callback_detected") return "shell_promote";
|
|
2142
|
+
if (phase === "active_shell_stabilizing") return "shell_upgrade";
|
|
2143
|
+
if (phase === "active_shell_stabilized" || phase === "post_exploitation_active") return "shell_check";
|
|
2144
|
+
return null;
|
|
2145
|
+
}
|
|
2146
|
+
if (request.workerType === "exploit") {
|
|
2147
|
+
const phase = getPhaseFromContext2(request.context, "exploit_phase");
|
|
2148
|
+
if (phase === "foothold_active") return "exploit_foothold_check";
|
|
2149
|
+
if (phase === "credential_followup") return "exploit_credential_check";
|
|
2150
|
+
if (phase === "artifact_ready") return "exploit_artifact_check";
|
|
2151
|
+
return null;
|
|
2152
|
+
}
|
|
2153
|
+
if (request.workerType === "pwn") {
|
|
2154
|
+
const phase = getPhaseFromContext2(request.context, "pwn_phase");
|
|
2155
|
+
if (phase === "offset_known") return "pwn_offset_check";
|
|
2156
|
+
if (phase === "crash_iteration") return "pwn_crash_repro";
|
|
2157
|
+
if (phase === "payload_iteration") return "pwn_payload_smoke";
|
|
2158
|
+
return null;
|
|
2159
|
+
}
|
|
2160
|
+
return null;
|
|
2161
|
+
}
|
|
2162
|
+
function buildDelegatedExecutionPlan(request) {
|
|
2163
|
+
if (request.workerType === "shell-supervisor") {
|
|
2164
|
+
const phase = getPhaseFromContext2(request.context, "shell_phase");
|
|
2165
|
+
if (phase === "listener_waiting") return "Poll the listener and promote immediately if a callback appears.";
|
|
2166
|
+
if (phase === "listener_callback_detected") return "Promote the callback path and validate the resulting shell.";
|
|
2167
|
+
if (phase === "active_shell_stabilizing") return "Finish PTY stabilization before broader post-exploitation.";
|
|
2168
|
+
if (phase === "active_shell_stabilized") return "Reuse the stabilized shell for controlled enumeration.";
|
|
2169
|
+
if (phase === "post_exploitation_active") return "Continue post-exploitation through the existing shell rather than opening a new chain.";
|
|
2170
|
+
return "Continue supervising the current shell/listener chain first.";
|
|
2171
|
+
}
|
|
2172
|
+
if (request.workerType === "exploit") {
|
|
2173
|
+
const phase = getPhaseFromContext2(request.context, "exploit_phase");
|
|
2174
|
+
if (phase === "foothold_active") return "Reuse the existing foothold and prefer exploit_foothold_check for bounded access validation.";
|
|
2175
|
+
if (phase === "credential_followup") return "Validate and reuse the obtained credentials or tokens, preferring exploit_credential_check for bounded probes.";
|
|
2176
|
+
if (phase === "artifact_ready") return "Validate the current exploit artifact, preferring exploit_artifact_check before building a replacement.";
|
|
2177
|
+
return "Continue the strongest surviving exploit vector and preserve branch evidence.";
|
|
2178
|
+
}
|
|
2179
|
+
if (request.workerType === "pwn") {
|
|
2180
|
+
const phase = getPhaseFromContext2(request.context, "pwn_phase");
|
|
2181
|
+
if (phase === "foothold_active") return "Reuse the achieved execution foothold and continue from there.";
|
|
2182
|
+
if (phase === "offset_known") return "Resume from the known offset and latest payload revision, preferring pwn_offset_check for bounded verification.";
|
|
2183
|
+
if (phase === "crash_iteration") return "Reproduce the latest crash and continue debugging from the preserved crash state, preferring pwn_crash_repro.";
|
|
2184
|
+
return "Continue the active payload-design loop with narrower iterations, preferring pwn_payload_smoke for the next bounded test.";
|
|
2185
|
+
}
|
|
2186
|
+
return "Continue the selected delegated chain before starting unrelated work.";
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2189
|
+
// src/agents/main-agent/delegated-execution-formatting.ts
|
|
2190
|
+
var OUTPUT_PREVIEW_LIMIT = 240;
|
|
2191
|
+
var FIELD_PREVIEW_LIMIT = 180;
|
|
2192
|
+
function getDelegatedExecutionSignature(request) {
|
|
2193
|
+
if (!request) return null;
|
|
2194
|
+
return JSON.stringify(request);
|
|
2195
|
+
}
|
|
2196
|
+
function summarizeDelegatedExecutionField(value, limit = FIELD_PREVIEW_LIMIT) {
|
|
2197
|
+
const normalized = value.replace(/\s+/g, " ").trim();
|
|
2198
|
+
if (!normalized) {
|
|
2199
|
+
return "";
|
|
2200
|
+
}
|
|
2201
|
+
if (normalized.length <= limit) {
|
|
2202
|
+
return normalized;
|
|
2203
|
+
}
|
|
2204
|
+
return `${normalized.slice(0, limit - 3)}...`;
|
|
2205
|
+
}
|
|
2206
|
+
function summarizeDelegatedExecutionOutput(output) {
|
|
2207
|
+
return summarizeDelegatedExecutionField(output, OUTPUT_PREVIEW_LIMIT);
|
|
2208
|
+
}
|
|
2209
|
+
function buildDelegatedExecutionRequestFragment(request) {
|
|
2210
|
+
if (!request) {
|
|
2211
|
+
return "";
|
|
2212
|
+
}
|
|
2213
|
+
const executionPlan = buildDelegatedExecutionPlan(request);
|
|
2214
|
+
const lines = [
|
|
2215
|
+
"<delegated-execution-request>",
|
|
2216
|
+
`task: ${summarizeDelegatedExecutionField(request.task)}`,
|
|
2217
|
+
request.workerType ? `worker_type: ${request.workerType}` : "",
|
|
2218
|
+
request.resumeTaskId ? `resume_task_id: ${request.resumeTaskId}` : "",
|
|
2219
|
+
request.target ? `target: ${request.target}` : "",
|
|
2220
|
+
request.context ? `context: ${summarizeDelegatedExecutionField(request.context)}` : "",
|
|
2221
|
+
request.boundedTool ? `bounded_tool: ${request.boundedTool}` : "",
|
|
2222
|
+
request.boundedCommand ? `bounded_command: ${summarizeDelegatedExecutionField(request.boundedCommand)}` : "",
|
|
2223
|
+
request.boundedTimeout ? `bounded_timeout: ${request.boundedTimeout}` : "",
|
|
2224
|
+
request.boundedInstruction ? `bounded_instruction: ${summarizeDelegatedExecutionField(request.boundedInstruction)}` : "",
|
|
2225
|
+
executionPlan ? `execution_plan: ${summarizeDelegatedExecutionField(executionPlan)}` : "",
|
|
2226
|
+
"</delegated-execution-request>"
|
|
2227
|
+
].filter(Boolean);
|
|
2228
|
+
return lines.join("\n");
|
|
2229
|
+
}
|
|
2230
|
+
function buildDelegatedExecutionResultFragment(result, analysis) {
|
|
2231
|
+
if (!result) {
|
|
2232
|
+
return "";
|
|
2233
|
+
}
|
|
2234
|
+
const outputPreview = summarizeDelegatedExecutionOutput(result.output);
|
|
2235
|
+
const lines = [
|
|
2236
|
+
"<delegated-execution-result>",
|
|
2237
|
+
`success: ${result.success ? "true" : "false"}`,
|
|
2238
|
+
result.error ? `error: ${result.error}` : "",
|
|
2239
|
+
outputPreview ? `output_preview: ${outputPreview}` : "",
|
|
2240
|
+
analysis?.phaseObservation ? `phase_observation: ${analysis.phaseObservation}` : "",
|
|
2241
|
+
analysis?.nextHint ? `next_hint: ${analysis.nextHint}` : "",
|
|
2242
|
+
"</delegated-execution-result>"
|
|
2243
|
+
].filter(Boolean);
|
|
2244
|
+
return lines.join("\n");
|
|
2245
|
+
}
|
|
2246
|
+
|
|
2247
|
+
// src/agents/main-agent/delegated-execution-state.ts
|
|
2248
|
+
function createDelegatedExecutionState() {
|
|
2249
|
+
return {
|
|
2250
|
+
pendingRequest: null,
|
|
2251
|
+
lastAttemptedSignature: null
|
|
2252
|
+
};
|
|
2253
|
+
}
|
|
2254
|
+
function setDelegatedExecutionState(currentState, request) {
|
|
2255
|
+
const nextRequest = request || null;
|
|
2256
|
+
const nextSignature = getDelegatedExecutionSignature(nextRequest);
|
|
2257
|
+
const currentSignature = getDelegatedExecutionSignature(currentState.pendingRequest);
|
|
2258
|
+
const requestChanged = nextSignature !== currentSignature;
|
|
2259
|
+
return {
|
|
2260
|
+
pendingRequest: nextRequest,
|
|
2261
|
+
lastAttemptedSignature: requestChanged ? null : currentState.lastAttemptedSignature,
|
|
2262
|
+
requestChanged
|
|
2263
|
+
};
|
|
2264
|
+
}
|
|
2265
|
+
function canAutoExecuteDelegatedState(state) {
|
|
2266
|
+
const pendingSignature = getDelegatedExecutionSignature(state.pendingRequest);
|
|
2267
|
+
return pendingSignature !== null && pendingSignature !== state.lastAttemptedSignature;
|
|
2268
|
+
}
|
|
2269
|
+
function markDelegatedExecutionAttempt(state) {
|
|
2270
|
+
return {
|
|
2271
|
+
...state,
|
|
2272
|
+
lastAttemptedSignature: getDelegatedExecutionSignature(state.pendingRequest)
|
|
2273
|
+
};
|
|
2274
|
+
}
|
|
2275
|
+
function consumeDelegatedExecutionState(state) {
|
|
2276
|
+
return {
|
|
2277
|
+
consumedRequest: state.pendingRequest,
|
|
2278
|
+
nextState: createDelegatedExecutionState()
|
|
2279
|
+
};
|
|
2280
|
+
}
|
|
2281
|
+
|
|
2282
|
+
// src/agents/main-agent/delegated-execution-runtime.ts
|
|
2283
|
+
var DelegatedExecutionRuntime = class {
|
|
2284
|
+
pendingRequest = createDelegatedExecutionState().pendingRequest;
|
|
2285
|
+
lastAttemptedSignature = createDelegatedExecutionState().lastAttemptedSignature;
|
|
2286
|
+
lastExecutionResult = null;
|
|
2287
|
+
lastExecutionAnalysis = null;
|
|
2288
|
+
set(request) {
|
|
2289
|
+
const nextState = setDelegatedExecutionState(
|
|
2290
|
+
{
|
|
2291
|
+
pendingRequest: this.pendingRequest,
|
|
2292
|
+
lastAttemptedSignature: this.lastAttemptedSignature
|
|
2293
|
+
},
|
|
2294
|
+
request
|
|
2295
|
+
);
|
|
2296
|
+
this.pendingRequest = nextState.pendingRequest;
|
|
2297
|
+
this.lastAttemptedSignature = nextState.lastAttemptedSignature;
|
|
2298
|
+
if (nextState.requestChanged) {
|
|
2299
|
+
this.lastExecutionResult = null;
|
|
2300
|
+
this.lastExecutionAnalysis = null;
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
peek() {
|
|
2304
|
+
return this.pendingRequest;
|
|
2305
|
+
}
|
|
2306
|
+
canAutoExecute() {
|
|
2307
|
+
return canAutoExecuteDelegatedState({
|
|
2308
|
+
pendingRequest: this.pendingRequest,
|
|
2309
|
+
lastAttemptedSignature: this.lastAttemptedSignature
|
|
2310
|
+
});
|
|
2311
|
+
}
|
|
2312
|
+
markAutoExecutionAttempt() {
|
|
2313
|
+
const nextState = markDelegatedExecutionAttempt({
|
|
2314
|
+
pendingRequest: this.pendingRequest,
|
|
2315
|
+
lastAttemptedSignature: this.lastAttemptedSignature
|
|
2316
|
+
});
|
|
2317
|
+
this.lastAttemptedSignature = nextState.lastAttemptedSignature;
|
|
2318
|
+
}
|
|
2319
|
+
setLastExecutionResult(result) {
|
|
2320
|
+
this.lastExecutionResult = result;
|
|
2321
|
+
this.lastExecutionAnalysis = analyzeDelegatedExecutionResult(this.pendingRequest, result);
|
|
2322
|
+
}
|
|
2323
|
+
consume() {
|
|
2324
|
+
const consumed = consumeDelegatedExecutionState({
|
|
2325
|
+
pendingRequest: this.pendingRequest,
|
|
2326
|
+
lastAttemptedSignature: this.lastAttemptedSignature
|
|
2327
|
+
});
|
|
2328
|
+
this.pendingRequest = consumed.nextState.pendingRequest;
|
|
2329
|
+
this.lastAttemptedSignature = consumed.nextState.lastAttemptedSignature;
|
|
2330
|
+
this.lastExecutionAnalysis = null;
|
|
2331
|
+
return consumed.consumedRequest;
|
|
2332
|
+
}
|
|
2333
|
+
reset() {
|
|
2334
|
+
const resetState = createDelegatedExecutionState();
|
|
2335
|
+
this.pendingRequest = resetState.pendingRequest;
|
|
2336
|
+
this.lastAttemptedSignature = resetState.lastAttemptedSignature;
|
|
2337
|
+
this.lastExecutionResult = null;
|
|
2338
|
+
this.lastExecutionAnalysis = null;
|
|
2339
|
+
}
|
|
2340
|
+
toPromptFragment() {
|
|
2341
|
+
return buildDelegatedExecutionRequestFragment(this.pendingRequest);
|
|
2342
|
+
}
|
|
2343
|
+
toResultFragment() {
|
|
2344
|
+
return buildDelegatedExecutionResultFragment(
|
|
2345
|
+
this.lastExecutionResult,
|
|
2346
|
+
this.lastExecutionAnalysis
|
|
2347
|
+
);
|
|
2348
|
+
}
|
|
2349
|
+
};
|
|
2350
|
+
|
|
2351
|
+
// src/agents/main-agent/delegated-auto-executor-helpers.ts
|
|
2352
|
+
function combineToolResults(results) {
|
|
2353
|
+
const success = results.every((result) => result.success);
|
|
2354
|
+
return {
|
|
2355
|
+
success,
|
|
2356
|
+
output: results.map((result, index) => `Step ${index + 1}:
|
|
2357
|
+
${result.output}`).join("\n\n"),
|
|
2358
|
+
...success ? {} : {
|
|
2359
|
+
error: results.find((result) => !result.success)?.error || "delegated auto-execution failed"
|
|
2360
|
+
}
|
|
2361
|
+
};
|
|
2362
|
+
}
|
|
2363
|
+
function buildPatchedContext(context, key, value) {
|
|
2364
|
+
const parts = (context || "").split("|").map((part) => part.trim()).filter(Boolean).filter((part) => !part.startsWith(`${key}=`));
|
|
2365
|
+
parts.push(`${key}=${value}`);
|
|
2366
|
+
return parts.join(" | ");
|
|
2367
|
+
}
|
|
2368
|
+
function getPhaseFromContext3(context, key) {
|
|
2369
|
+
if (!context) {
|
|
2370
|
+
return null;
|
|
2371
|
+
}
|
|
2372
|
+
const match = context.match(new RegExp(`${key}=([a-z_]+)`));
|
|
2373
|
+
return match?.[1] ?? null;
|
|
2374
|
+
}
|
|
2375
|
+
function buildRunTaskInput(request, task, workerType, context) {
|
|
2376
|
+
return {
|
|
2377
|
+
name: TOOL_NAMES.RUN_TASK,
|
|
2378
|
+
input: {
|
|
2379
|
+
task,
|
|
2380
|
+
worker_type: workerType,
|
|
2381
|
+
...request.resumeTaskId ? { resume_task_id: request.resumeTaskId } : {},
|
|
2382
|
+
...request.target ? { target: request.target } : {},
|
|
2383
|
+
...context ? { context } : {}
|
|
2384
|
+
}
|
|
2385
|
+
};
|
|
2386
|
+
}
|
|
2387
|
+
function shouldAutoExecuteDelegatedRequest(request) {
|
|
2388
|
+
if (!request || typeof request.resumeTaskId !== "string") {
|
|
2389
|
+
return false;
|
|
2390
|
+
}
|
|
2391
|
+
if (request.workerType === "shell-supervisor") {
|
|
2392
|
+
return true;
|
|
2393
|
+
}
|
|
2394
|
+
if (request.workerType === "exploit") {
|
|
2395
|
+
const phase = getPhaseFromContext3(request.context, "exploit_phase");
|
|
2396
|
+
return phase === "artifact_ready" || phase === "credential_followup" || phase === "foothold_active";
|
|
2397
|
+
}
|
|
2398
|
+
if (request.workerType === "pwn") {
|
|
2399
|
+
const phase = getPhaseFromContext3(request.context, "pwn_phase");
|
|
2400
|
+
return phase === "offset_known" || phase === "crash_iteration" || phase === "foothold_active" || phase === "payload_iteration";
|
|
2401
|
+
}
|
|
2402
|
+
return false;
|
|
2403
|
+
}
|
|
2404
|
+
async function executeTwoStepLoop(execute, firstCall, buildFollowupCall, getSignals) {
|
|
2405
|
+
const firstResult = await execute(firstCall);
|
|
2406
|
+
if (!firstResult.success) {
|
|
2407
|
+
return firstResult;
|
|
2408
|
+
}
|
|
2409
|
+
const followupCall = buildFollowupCall(firstResult);
|
|
2410
|
+
if (!followupCall || !getSignals(firstResult).indicatesProgress) {
|
|
2411
|
+
return firstResult;
|
|
2412
|
+
}
|
|
2413
|
+
const followupResult = await execute(followupCall);
|
|
2414
|
+
return combineToolResults([firstResult, followupResult]);
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
// src/agents/main-agent/delegated-auto-executor-shell.ts
|
|
2418
|
+
function didStatusDetectConnection(result) {
|
|
2419
|
+
const output = result.output || "";
|
|
2420
|
+
return output.includes("CONNECTION DETECTED") || output.includes("Promote to shell");
|
|
2421
|
+
}
|
|
2422
|
+
function didShellCheckShowStability(result) {
|
|
2423
|
+
const output = (result.output || "").toLowerCase();
|
|
2424
|
+
return output.includes("xterm") || output.includes("rows") || output.includes("columns") || output.includes("/dev/pts/");
|
|
2425
|
+
}
|
|
2426
|
+
async function executeShellUpgradeSequence(toolRegistry, processId) {
|
|
2427
|
+
const initialCheck = await toolRegistry.execute({
|
|
2428
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2429
|
+
input: {
|
|
2430
|
+
process_id: processId,
|
|
2431
|
+
profile: "stability"
|
|
2432
|
+
}
|
|
2433
|
+
});
|
|
2434
|
+
if (!initialCheck.success || didShellCheckShowStability(initialCheck)) {
|
|
2435
|
+
return initialCheck;
|
|
2436
|
+
}
|
|
2437
|
+
const pythonUpgrade = await toolRegistry.execute({
|
|
2438
|
+
name: TOOL_NAMES.SHELL_UPGRADE,
|
|
2439
|
+
input: {
|
|
2440
|
+
process_id: processId,
|
|
2441
|
+
method: "python_pty"
|
|
2442
|
+
}
|
|
2443
|
+
});
|
|
2444
|
+
if (!pythonUpgrade.success) {
|
|
2445
|
+
return combineToolResults([initialCheck, pythonUpgrade]);
|
|
2446
|
+
}
|
|
2447
|
+
const postPythonCheck = await toolRegistry.execute({
|
|
2448
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2449
|
+
input: {
|
|
2450
|
+
process_id: processId,
|
|
2451
|
+
profile: "stability"
|
|
2452
|
+
}
|
|
2453
|
+
});
|
|
2454
|
+
if (!postPythonCheck.success || didShellCheckShowStability(postPythonCheck)) {
|
|
2455
|
+
return combineToolResults([initialCheck, pythonUpgrade, postPythonCheck]);
|
|
2456
|
+
}
|
|
2457
|
+
const scriptUpgrade = await toolRegistry.execute({
|
|
2458
|
+
name: TOOL_NAMES.SHELL_UPGRADE,
|
|
2459
|
+
input: {
|
|
2460
|
+
process_id: processId,
|
|
2461
|
+
method: "script"
|
|
2462
|
+
}
|
|
2463
|
+
});
|
|
2464
|
+
if (!scriptUpgrade.success) {
|
|
2465
|
+
return combineToolResults([initialCheck, pythonUpgrade, postPythonCheck, scriptUpgrade]);
|
|
2466
|
+
}
|
|
2467
|
+
const finalCheck = await toolRegistry.execute({
|
|
2468
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2469
|
+
input: {
|
|
2470
|
+
process_id: processId,
|
|
2471
|
+
profile: "stability"
|
|
2472
|
+
}
|
|
2473
|
+
});
|
|
2474
|
+
return combineToolResults([initialCheck, pythonUpgrade, postPythonCheck, scriptUpgrade, finalCheck]);
|
|
2475
|
+
}
|
|
2476
|
+
function buildShellSupervisorFastPathCall() {
|
|
2477
|
+
const snapshot = getShellSupervisorLifecycleSnapshot();
|
|
2478
|
+
switch (snapshot.phase) {
|
|
2479
|
+
case "post_exploitation_active":
|
|
2480
|
+
return snapshot.activeShellId ? {
|
|
2481
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2482
|
+
input: {
|
|
2483
|
+
process_id: snapshot.activeShellId,
|
|
2484
|
+
profile: "post"
|
|
2485
|
+
}
|
|
2486
|
+
} : null;
|
|
2487
|
+
case "active_shell_stabilized":
|
|
2488
|
+
return snapshot.activeShellId ? {
|
|
2489
|
+
name: TOOL_NAMES.SHELL_EXEC,
|
|
2490
|
+
input: {
|
|
2491
|
+
process_id: snapshot.activeShellId,
|
|
2492
|
+
command: "pwd && uname -a"
|
|
2493
|
+
}
|
|
2494
|
+
} : null;
|
|
2495
|
+
case "active_shell_stabilizing":
|
|
2496
|
+
return snapshot.activeShellId ? {
|
|
2497
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2498
|
+
input: {
|
|
2499
|
+
process_id: snapshot.activeShellId,
|
|
2500
|
+
profile: "stability"
|
|
2501
|
+
}
|
|
2502
|
+
} : null;
|
|
2503
|
+
case "listener_waiting":
|
|
2504
|
+
return snapshot.listenerId ? {
|
|
2505
|
+
name: TOOL_NAMES.LISTENER_STATUS,
|
|
2506
|
+
input: {
|
|
2507
|
+
process_id: snapshot.listenerId
|
|
2508
|
+
}
|
|
2509
|
+
} : null;
|
|
2510
|
+
case "listener_callback_detected":
|
|
2511
|
+
return snapshot.listenerId ? {
|
|
2512
|
+
name: TOOL_NAMES.SHELL_PROMOTE,
|
|
2513
|
+
input: {
|
|
2514
|
+
process_id: snapshot.listenerId
|
|
2515
|
+
}
|
|
2516
|
+
} : null;
|
|
2517
|
+
case "active_shell_ready":
|
|
2518
|
+
return snapshot.activeShellId ? {
|
|
2519
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2520
|
+
input: {
|
|
2521
|
+
process_id: snapshot.activeShellId,
|
|
2522
|
+
profile: "identity"
|
|
2523
|
+
}
|
|
2524
|
+
} : null;
|
|
2525
|
+
default:
|
|
2526
|
+
return null;
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
async function executeShellSupervisorMiniLoop(toolRegistry) {
|
|
2530
|
+
const snapshot = getShellSupervisorLifecycleSnapshot();
|
|
2531
|
+
if (snapshot.phase === "post_exploitation_active" && snapshot.activeShellId) {
|
|
2532
|
+
const identityResult = await toolRegistry.execute({
|
|
2533
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2534
|
+
input: {
|
|
2535
|
+
process_id: snapshot.activeShellId,
|
|
2536
|
+
profile: "identity"
|
|
2537
|
+
}
|
|
2538
|
+
});
|
|
2539
|
+
if (!identityResult.success) {
|
|
2540
|
+
return identityResult;
|
|
2541
|
+
}
|
|
2542
|
+
const environmentResult = await toolRegistry.execute({
|
|
2543
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2544
|
+
input: {
|
|
2545
|
+
process_id: snapshot.activeShellId,
|
|
2546
|
+
profile: "environment"
|
|
2547
|
+
}
|
|
2548
|
+
});
|
|
2549
|
+
if (!environmentResult.success) {
|
|
2550
|
+
return combineToolResults([identityResult, environmentResult]);
|
|
2551
|
+
}
|
|
2552
|
+
const networkResult = await toolRegistry.execute({
|
|
2553
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2554
|
+
input: {
|
|
2555
|
+
process_id: snapshot.activeShellId,
|
|
2556
|
+
profile: "post"
|
|
2557
|
+
}
|
|
2558
|
+
});
|
|
2559
|
+
return combineToolResults([identityResult, environmentResult, networkResult]);
|
|
2560
|
+
}
|
|
2561
|
+
if (snapshot.phase === "active_shell_stabilizing" && snapshot.activeShellId) {
|
|
2562
|
+
return executeShellUpgradeSequence(toolRegistry, snapshot.activeShellId);
|
|
2563
|
+
}
|
|
2564
|
+
if (snapshot.phase === "listener_waiting" && snapshot.listenerId) {
|
|
2565
|
+
const statusResult = await toolRegistry.execute({
|
|
2566
|
+
name: TOOL_NAMES.LISTENER_STATUS,
|
|
2567
|
+
input: {
|
|
2568
|
+
process_id: snapshot.listenerId
|
|
2569
|
+
}
|
|
2570
|
+
});
|
|
2571
|
+
if (!statusResult.success || !didStatusDetectConnection(statusResult)) {
|
|
2572
|
+
return statusResult;
|
|
2573
|
+
}
|
|
2574
|
+
const promoteResult = await toolRegistry.execute({
|
|
2575
|
+
name: TOOL_NAMES.SHELL_PROMOTE,
|
|
2576
|
+
input: {
|
|
2577
|
+
process_id: snapshot.listenerId
|
|
2578
|
+
}
|
|
2579
|
+
});
|
|
2580
|
+
if (!promoteResult.success) {
|
|
2581
|
+
return combineToolResults([statusResult, promoteResult]);
|
|
2582
|
+
}
|
|
2583
|
+
const interactResult = await toolRegistry.execute({
|
|
2584
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2585
|
+
input: {
|
|
2586
|
+
process_id: snapshot.listenerId,
|
|
2587
|
+
profile: "identity"
|
|
2588
|
+
}
|
|
2589
|
+
});
|
|
2590
|
+
return combineToolResults([statusResult, promoteResult, interactResult]);
|
|
2591
|
+
}
|
|
2592
|
+
if (snapshot.phase === "listener_callback_detected" && snapshot.listenerId) {
|
|
2593
|
+
const promoteResult = await toolRegistry.execute({
|
|
2594
|
+
name: TOOL_NAMES.SHELL_PROMOTE,
|
|
2595
|
+
input: {
|
|
2596
|
+
process_id: snapshot.listenerId
|
|
2597
|
+
}
|
|
2598
|
+
});
|
|
2599
|
+
if (!promoteResult.success) {
|
|
2600
|
+
return promoteResult;
|
|
2601
|
+
}
|
|
2602
|
+
const interactResult = await toolRegistry.execute({
|
|
2603
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2604
|
+
input: {
|
|
2605
|
+
process_id: snapshot.listenerId,
|
|
2606
|
+
profile: "identity"
|
|
2607
|
+
}
|
|
2608
|
+
});
|
|
2609
|
+
return combineToolResults([promoteResult, interactResult]);
|
|
2610
|
+
}
|
|
2611
|
+
const singleCall = buildShellSupervisorFastPathCall();
|
|
2612
|
+
if (!singleCall) {
|
|
2613
|
+
return null;
|
|
2614
|
+
}
|
|
2615
|
+
return toolRegistry.execute(singleCall);
|
|
2616
|
+
}
|
|
2617
|
+
|
|
2618
|
+
// src/agents/main-agent/delegated-auto-executor-offense.ts
|
|
2619
|
+
function buildExploitPhaseRunTaskCall(request) {
|
|
2620
|
+
const phase = getPhaseFromContext3(request.context, "exploit_phase");
|
|
2621
|
+
const phaseTask = phase === "foothold_active" ? "Reuse the existing foothold and continue the exploit chain from the current access path." : phase === "credential_followup" ? "Validate and reuse the obtained credentials or tokens before changing exploit vectors." : phase === "artifact_ready" ? "Validate and advance the current exploit artifact before switching to a different vector." : request.task;
|
|
2622
|
+
return buildRunTaskInput(request, phaseTask, "exploit", request.context);
|
|
2623
|
+
}
|
|
2624
|
+
function buildExploitFollowupRunTaskCall(request, previousResult) {
|
|
2625
|
+
const phase = getPhaseFromContext3(request.context, "exploit_phase");
|
|
2626
|
+
const signals = previousResult ? getExploitResultSignals(previousResult) : null;
|
|
2627
|
+
const hasSession = signals?.hasSession ?? false;
|
|
2628
|
+
const hasLoot = signals?.hasLoot ?? false;
|
|
2629
|
+
if (phase === "credential_followup" || hasLoot) {
|
|
2630
|
+
return buildRunTaskInput(
|
|
2631
|
+
request,
|
|
2632
|
+
hasSession ? "Reuse the confirmed foothold or authenticated path and continue the exploit chain without restarting delivery." : "If credential reuse succeeded, continue from the confirmed foothold or authenticated path without restarting the exploit chain.",
|
|
2633
|
+
"exploit",
|
|
2634
|
+
buildPatchedContext(request.context, "exploit_phase", hasSession ? "foothold_active" : "credential_followup")
|
|
2635
|
+
);
|
|
2636
|
+
}
|
|
2637
|
+
if (phase === "artifact_ready") {
|
|
2638
|
+
return buildRunTaskInput(
|
|
2639
|
+
request,
|
|
2640
|
+
hasSession ? "A foothold exists from the validated artifact. Reuse it and continue the chain from that access path." : "If the artifact validated successfully, turn it into data extraction, authenticated access, or foothold confirmation before changing vectors.",
|
|
2641
|
+
"exploit",
|
|
2642
|
+
buildPatchedContext(request.context, "exploit_phase", hasSession ? "foothold_active" : "artifact_ready")
|
|
2643
|
+
);
|
|
2644
|
+
}
|
|
2645
|
+
return null;
|
|
2646
|
+
}
|
|
2647
|
+
function buildPwnPhaseRunTaskCall(request) {
|
|
2648
|
+
const phase = getPhaseFromContext3(request.context, "pwn_phase");
|
|
2649
|
+
const phaseTask = phase === "foothold_active" ? "Reuse the existing execution foothold and continue post-exploitation or objective completion." : phase === "offset_known" ? "Resume the exploit loop from the known offset and latest payload revision." : phase === "crash_iteration" ? "Reproduce the latest crash and continue debugging from the preserved crash state." : request.task;
|
|
2650
|
+
return buildRunTaskInput(request, phaseTask, "pwn", request.context);
|
|
2651
|
+
}
|
|
2652
|
+
function buildPwnFollowupRunTaskCall(request, previousResult) {
|
|
2653
|
+
const phase = getPhaseFromContext3(request.context, "pwn_phase");
|
|
2654
|
+
const signals = previousResult ? getPwnResultSignals(previousResult) : null;
|
|
2655
|
+
const hasFoothold2 = signals?.hasFoothold ?? false;
|
|
2656
|
+
const stillCrashing = signals?.stillCrashing ?? false;
|
|
2657
|
+
if (phase === "offset_known") {
|
|
2658
|
+
return buildRunTaskInput(
|
|
2659
|
+
request,
|
|
2660
|
+
hasFoothold2 ? "Execution is confirmed. Reuse the achieved foothold and continue from the working exploit state." : "Use the known offset to perform the narrowest next payload smoke test and confirm progress toward execution.",
|
|
2661
|
+
"pwn",
|
|
2662
|
+
buildPatchedContext(request.context, "pwn_phase", hasFoothold2 ? "foothold_active" : "payload_iteration")
|
|
2663
|
+
);
|
|
2664
|
+
}
|
|
2665
|
+
if (phase === "crash_iteration") {
|
|
2666
|
+
return buildRunTaskInput(
|
|
2667
|
+
request,
|
|
2668
|
+
stillCrashing ? "The crash state persists. Apply the narrowest next debugging or payload mutation step from the preserved crash state." : hasFoothold2 ? "Execution is confirmed. Reuse the achieved foothold and continue from the working exploit state." : "Apply the narrowest next debugging or payload mutation step from the preserved crash state.",
|
|
2669
|
+
"pwn",
|
|
2670
|
+
buildPatchedContext(
|
|
2671
|
+
request.context,
|
|
2672
|
+
"pwn_phase",
|
|
2673
|
+
hasFoothold2 ? "foothold_active" : stillCrashing ? "crash_iteration" : "payload_iteration"
|
|
2674
|
+
)
|
|
2675
|
+
);
|
|
2676
|
+
}
|
|
2677
|
+
return null;
|
|
2678
|
+
}
|
|
2679
|
+
async function executeExploitMiniLoop(toolRegistry, request) {
|
|
2680
|
+
return executeTwoStepLoop(
|
|
2681
|
+
(call) => toolRegistry.execute(call),
|
|
2682
|
+
buildExploitPhaseRunTaskCall(request),
|
|
2683
|
+
(firstResult) => buildExploitFollowupRunTaskCall(request, firstResult),
|
|
2684
|
+
getExploitResultSignals
|
|
2685
|
+
);
|
|
2686
|
+
}
|
|
2687
|
+
async function executeExploitDirectBoundedLoop(toolRegistry, request, boundedCall) {
|
|
2688
|
+
if (!boundedCall) {
|
|
2689
|
+
return executeExploitMiniLoop(toolRegistry, request);
|
|
2690
|
+
}
|
|
2691
|
+
return executeTwoStepLoop(
|
|
2692
|
+
(call) => toolRegistry.execute(call),
|
|
2693
|
+
boundedCall,
|
|
2694
|
+
(firstResult) => buildExploitFollowupRunTaskCall(request, firstResult),
|
|
2695
|
+
getExploitResultSignals
|
|
2696
|
+
);
|
|
2697
|
+
}
|
|
2698
|
+
async function executePwnMiniLoop(toolRegistry, request) {
|
|
2699
|
+
return executeTwoStepLoop(
|
|
2700
|
+
(call) => toolRegistry.execute(call),
|
|
2701
|
+
buildPwnPhaseRunTaskCall(request),
|
|
2702
|
+
(firstResult) => buildPwnFollowupRunTaskCall(request, firstResult),
|
|
2703
|
+
getPwnResultSignals
|
|
2704
|
+
);
|
|
2705
|
+
}
|
|
2706
|
+
async function executePwnDirectBoundedLoop(toolRegistry, request, boundedCall) {
|
|
2707
|
+
if (!boundedCall) {
|
|
2708
|
+
return executePwnMiniLoop(toolRegistry, request);
|
|
2709
|
+
}
|
|
2710
|
+
return executeTwoStepLoop(
|
|
2711
|
+
(call) => toolRegistry.execute(call),
|
|
2712
|
+
boundedCall,
|
|
2713
|
+
(firstResult) => buildPwnFollowupRunTaskCall(request, firstResult),
|
|
2714
|
+
getPwnResultSignals
|
|
2715
|
+
);
|
|
2716
|
+
}
|
|
2717
|
+
|
|
2718
|
+
// src/agents/main-agent/delegated-auto-executor-calls.ts
|
|
2719
|
+
function buildDelegatedRunTaskCall(request) {
|
|
2720
|
+
return {
|
|
2721
|
+
name: TOOL_NAMES.RUN_TASK,
|
|
2722
|
+
input: {
|
|
2723
|
+
task: request.task,
|
|
2724
|
+
...request.workerType ? { worker_type: request.workerType } : {},
|
|
2725
|
+
...request.resumeTaskId ? { resume_task_id: request.resumeTaskId } : {},
|
|
2726
|
+
...request.target ? { target: request.target } : {},
|
|
2727
|
+
...request.context ? { context: request.context } : {}
|
|
2728
|
+
}
|
|
2729
|
+
};
|
|
2730
|
+
}
|
|
2731
|
+
function buildBoundedDelegatedToolCall(request) {
|
|
2732
|
+
if (!request.boundedTool || !request.boundedCommand) {
|
|
2733
|
+
return null;
|
|
2734
|
+
}
|
|
2735
|
+
return {
|
|
2736
|
+
name: request.boundedTool,
|
|
2737
|
+
input: {
|
|
2738
|
+
command: request.boundedCommand,
|
|
2739
|
+
...request.boundedTimeout ? { timeout: request.boundedTimeout } : {}
|
|
2740
|
+
}
|
|
2741
|
+
};
|
|
2742
|
+
}
|
|
2743
|
+
|
|
2744
|
+
// src/agents/main-agent/delegated-auto-execution-strategies.ts
|
|
2745
|
+
var DEFAULT_DELEGATED_EXECUTION_STRATEGY = async (toolRegistry, request) => toolRegistry.execute(buildDelegatedRunTaskCall(request));
|
|
2746
|
+
var SHELL_SUPERVISOR_STRATEGY = async (toolRegistry, request) => await executeShellSupervisorMiniLoop(toolRegistry) || await toolRegistry.execute(buildDelegatedRunTaskCall(request));
|
|
2747
|
+
var EXPLOIT_STRATEGY = async (toolRegistry, request) => executeExploitDirectBoundedLoop(
|
|
2748
|
+
toolRegistry,
|
|
2749
|
+
request,
|
|
2750
|
+
buildBoundedDelegatedToolCall(request)
|
|
2751
|
+
);
|
|
2752
|
+
var PWN_STRATEGY = async (toolRegistry, request) => executePwnDirectBoundedLoop(
|
|
2753
|
+
toolRegistry,
|
|
2754
|
+
request,
|
|
2755
|
+
buildBoundedDelegatedToolCall(request)
|
|
2756
|
+
);
|
|
2757
|
+
var DELEGATED_EXECUTION_STRATEGIES = {
|
|
2758
|
+
"shell-supervisor": SHELL_SUPERVISOR_STRATEGY,
|
|
2759
|
+
exploit: EXPLOIT_STRATEGY,
|
|
2760
|
+
pwn: PWN_STRATEGY
|
|
2761
|
+
};
|
|
2762
|
+
async function executeDelegatedRequestByStrategy(toolRegistry, request) {
|
|
2763
|
+
const strategy = request.workerType ? DELEGATED_EXECUTION_STRATEGIES[request.workerType] : void 0;
|
|
2764
|
+
return (strategy ?? DEFAULT_DELEGATED_EXECUTION_STRATEGY)(toolRegistry, request);
|
|
2765
|
+
}
|
|
2766
|
+
|
|
2767
|
+
// src/agents/main-agent/delegated-auto-executor.ts
|
|
2768
|
+
var DelegatedAutoExecutor = class {
|
|
2769
|
+
canExecutePending(runtime) {
|
|
2770
|
+
return runtime.canAutoExecute() && shouldAutoExecuteDelegatedRequest(runtime.peek());
|
|
2771
|
+
}
|
|
2772
|
+
async executePending(runtime, toolRegistry) {
|
|
2773
|
+
if (!toolRegistry) {
|
|
2774
|
+
return null;
|
|
2775
|
+
}
|
|
2776
|
+
const request = runtime.peek();
|
|
2777
|
+
if (!request || !this.canExecutePending(runtime)) {
|
|
2778
|
+
return null;
|
|
2779
|
+
}
|
|
2780
|
+
runtime.markAutoExecutionAttempt();
|
|
2781
|
+
const result = await executeDelegatedRequestByStrategy(toolRegistry, request);
|
|
2782
|
+
runtime.setLastExecutionResult(result);
|
|
2783
|
+
if (result.success) {
|
|
2784
|
+
runtime.consume();
|
|
2785
|
+
}
|
|
2786
|
+
return result;
|
|
2787
|
+
}
|
|
2788
|
+
};
|
|
2789
|
+
|
|
2790
|
+
// src/agents/main-agent/prompt-assembly.ts
|
|
2791
|
+
async function buildMainAgentPrompt(promptBuilder, state, userInput, delegatedExecution, memory) {
|
|
2792
|
+
const basePrompt = await promptBuilder.build(
|
|
2793
|
+
userInput,
|
|
2794
|
+
state.getPhase() || PHASES.RECON,
|
|
2795
|
+
memory
|
|
2796
|
+
);
|
|
2797
|
+
return [basePrompt, delegatedExecution.request, delegatedExecution.result].filter(Boolean).join("\n\n");
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
// src/agents/main-agent/exploit-lifecycle.ts
|
|
2801
|
+
function shellEscape(value) {
|
|
2802
|
+
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
2803
|
+
}
|
|
2804
|
+
function getReplayableExploitCommand(task) {
|
|
2805
|
+
const candidates = [task.resumeHint || "", ...task.tried].map((value) => value.trim()).filter(Boolean);
|
|
2806
|
+
return candidates.find(
|
|
2807
|
+
(candidate) => /^(?:curl|hydra|sqlmap|psql|mysql|ssh|ftp|smbclient|xfreerdp|evil-winrm)\s+/i.test(candidate)
|
|
2808
|
+
) || null;
|
|
2809
|
+
}
|
|
2810
|
+
function getUrlCandidate(task) {
|
|
2811
|
+
const candidates = [
|
|
2812
|
+
task.target || "",
|
|
2813
|
+
...task.assets,
|
|
2814
|
+
...task.findings,
|
|
2815
|
+
task.summary,
|
|
2816
|
+
task.resumeHint || ""
|
|
2817
|
+
].map((value) => value.trim()).filter(Boolean);
|
|
2818
|
+
for (const candidate of candidates) {
|
|
2819
|
+
const urlMatch = candidate.match(/https?:\/\/[^\s"'`]+/i);
|
|
2820
|
+
if (urlMatch?.[0]) {
|
|
2821
|
+
return urlMatch[0];
|
|
2822
|
+
}
|
|
2823
|
+
const assetMatch = candidate.match(/^url:(https?:\/\/.+)$/i);
|
|
2824
|
+
if (assetMatch?.[1]) {
|
|
2825
|
+
return assetMatch[1].trim();
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2828
|
+
return null;
|
|
2829
|
+
}
|
|
2830
|
+
function getTokenProbe(task) {
|
|
2831
|
+
for (const item of task.loot) {
|
|
2832
|
+
const normalized = item.trim();
|
|
2833
|
+
const tokenMatch = normalized.match(/(?:bearer\s+token|token)\s*:\s*(.+)$/i);
|
|
2834
|
+
if (tokenMatch?.[1]) {
|
|
2835
|
+
return {
|
|
2836
|
+
header: "Authorization",
|
|
2837
|
+
secret: `Bearer ${tokenMatch[1].trim()}`
|
|
2838
|
+
};
|
|
2839
|
+
}
|
|
2840
|
+
const apiKeyMatch = normalized.match(/(?:api[_ -]?key|apikey)\s*:\s*(.+)$/i);
|
|
2841
|
+
if (apiKeyMatch?.[1]) {
|
|
2842
|
+
return {
|
|
2843
|
+
header: "X-API-Key",
|
|
2844
|
+
secret: apiKeyMatch[1].trim()
|
|
2845
|
+
};
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
return null;
|
|
2849
|
+
}
|
|
2850
|
+
function getJoinedExploitEvidence(task) {
|
|
2851
|
+
return [
|
|
2852
|
+
task.target || "",
|
|
2853
|
+
task.summary,
|
|
2854
|
+
task.resumeHint || "",
|
|
2855
|
+
...task.assets,
|
|
2856
|
+
...task.findings,
|
|
2857
|
+
...task.tried,
|
|
2858
|
+
...task.loot
|
|
2859
|
+
].join(" ").toLowerCase();
|
|
2860
|
+
}
|
|
2861
|
+
function getCredentialPair(task) {
|
|
2862
|
+
let username = null;
|
|
2863
|
+
let password = null;
|
|
2864
|
+
for (const item of task.loot) {
|
|
2865
|
+
const normalized = item.trim();
|
|
2866
|
+
const combinedMatch = normalized.match(/^([^\s:]+)\s+password:\s*(.+)$/i);
|
|
2867
|
+
if (combinedMatch?.[1] && combinedMatch?.[2]) {
|
|
2868
|
+
username = combinedMatch[1].trim();
|
|
2869
|
+
password = combinedMatch[2].trim();
|
|
2870
|
+
break;
|
|
2871
|
+
}
|
|
2872
|
+
const userMatch = normalized.match(/^(?:username|user)\s*:\s*(.+)$/i);
|
|
2873
|
+
if (userMatch?.[1]) {
|
|
2874
|
+
username = userMatch[1].trim();
|
|
2875
|
+
}
|
|
2876
|
+
const passwordMatch = normalized.match(/^password\s*:\s*(.+)$/i);
|
|
2877
|
+
if (passwordMatch?.[1]) {
|
|
2878
|
+
password = passwordMatch[1].trim();
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
return username && password ? { username, password } : null;
|
|
2882
|
+
}
|
|
2883
|
+
function getHostCandidate(task) {
|
|
2884
|
+
const url = getUrlCandidate(task);
|
|
2885
|
+
if (url) {
|
|
2886
|
+
try {
|
|
2887
|
+
return new URL(url).host;
|
|
2888
|
+
} catch {
|
|
2889
|
+
return null;
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
const target = (task.target || "").trim();
|
|
2893
|
+
if (!target) {
|
|
2894
|
+
return null;
|
|
2895
|
+
}
|
|
2896
|
+
if (/^https?:\/\//i.test(target)) {
|
|
2897
|
+
try {
|
|
2898
|
+
return new URL(target).host;
|
|
2899
|
+
} catch {
|
|
2900
|
+
return null;
|
|
2901
|
+
}
|
|
2902
|
+
}
|
|
2903
|
+
return target;
|
|
2904
|
+
}
|
|
2905
|
+
function getServiceHint(task) {
|
|
2906
|
+
const evidence = getJoinedExploitEvidence(task);
|
|
2907
|
+
if (evidence.includes("ssh")) return "ssh";
|
|
2908
|
+
if (evidence.includes("ftp")) return "ftp";
|
|
2909
|
+
if (evidence.includes("smb") || evidence.includes("445")) return "smb";
|
|
2910
|
+
if (evidence.includes("winrm") || evidence.includes("5985") || evidence.includes("5986")) return "winrm";
|
|
2911
|
+
if (evidence.includes("rdp") || evidence.includes("3389")) return "rdp";
|
|
2912
|
+
if (evidence.includes("ldap") || evidence.includes("389") || evidence.includes("636")) return "ldap";
|
|
2913
|
+
if (evidence.includes("mysql")) return "mysql";
|
|
2914
|
+
if (evidence.includes("postgres") || evidence.includes("psql")) return "postgres";
|
|
2915
|
+
if (evidence.includes("http") || evidence.includes("https") || evidence.includes("web")) return "http";
|
|
2916
|
+
return null;
|
|
2917
|
+
}
|
|
2918
|
+
function getServicePort(service) {
|
|
2919
|
+
switch (service) {
|
|
2920
|
+
case "ssh":
|
|
2921
|
+
return 22;
|
|
2922
|
+
case "ftp":
|
|
2923
|
+
return 21;
|
|
2924
|
+
case "http":
|
|
2925
|
+
return 80;
|
|
2926
|
+
case "mysql":
|
|
2927
|
+
return 3306;
|
|
2928
|
+
case "postgres":
|
|
2929
|
+
return 5432;
|
|
2930
|
+
case "smb":
|
|
2931
|
+
return 445;
|
|
2932
|
+
case "winrm":
|
|
2933
|
+
return 5985;
|
|
2934
|
+
case "rdp":
|
|
2935
|
+
return 3389;
|
|
2936
|
+
case "ldap":
|
|
2937
|
+
return 389;
|
|
2938
|
+
default:
|
|
2939
|
+
return null;
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
function getVectorBoundedCommand(task) {
|
|
2943
|
+
const url = getUrlCandidate(task);
|
|
2944
|
+
if (url) {
|
|
2945
|
+
return `curl -fsSIL ${shellEscape(url)}`;
|
|
2946
|
+
}
|
|
2947
|
+
const host = getHostCandidate(task);
|
|
2948
|
+
const service = getServiceHint(task);
|
|
2949
|
+
const port = getServicePort(service);
|
|
2950
|
+
if (host && port) {
|
|
2951
|
+
return `nc -vz -w 5 ${shellEscape(host)} ${port}`;
|
|
2952
|
+
}
|
|
2953
|
+
return void 0;
|
|
2954
|
+
}
|
|
2955
|
+
function getCredentialBoundedCommand(task, replayableCommand) {
|
|
2956
|
+
if (replayableCommand) {
|
|
2957
|
+
return replayableCommand;
|
|
2958
|
+
}
|
|
2959
|
+
const url = getUrlCandidate(task);
|
|
2960
|
+
const tokenProbe = getTokenProbe(task);
|
|
2961
|
+
if (url && tokenProbe) {
|
|
2962
|
+
return `curl -fsSIL -H ${shellEscape(`${tokenProbe.header}: ${tokenProbe.secret}`)} ${shellEscape(url)}`;
|
|
2963
|
+
}
|
|
2964
|
+
const pair = getCredentialPair(task);
|
|
2965
|
+
const host = getHostCandidate(task);
|
|
2966
|
+
const service = getServiceHint(task);
|
|
2967
|
+
if (pair && host && service === "ssh") {
|
|
2968
|
+
return `sshpass -p ${shellEscape(pair.password)} ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 ${shellEscape(`${pair.username}@${host}`)} id`;
|
|
2969
|
+
}
|
|
2970
|
+
if (pair && host && service === "ftp") {
|
|
2971
|
+
return `curl -fsS --user ${shellEscape(`${pair.username}:${pair.password}`)} --max-time 5 ${shellEscape(`ftp://${host}/`)}`;
|
|
2972
|
+
}
|
|
2973
|
+
if (pair && service === "http") {
|
|
2974
|
+
const httpTarget = url || (host ? `http://${host}` : null);
|
|
2975
|
+
if (httpTarget) {
|
|
2976
|
+
return `curl -fsSIL -u ${shellEscape(`${pair.username}:${pair.password}`)} ${shellEscape(httpTarget)}`;
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
if (pair && host && service === "mysql") {
|
|
2980
|
+
return `mysql -h ${shellEscape(host)} -u ${shellEscape(pair.username)} -p${pair.password} -e 'SELECT 1;'`;
|
|
2981
|
+
}
|
|
2982
|
+
if (pair && host && service === "postgres") {
|
|
2983
|
+
return `PGPASSWORD=${shellEscape(pair.password)} psql -h ${shellEscape(host)} -U ${shellEscape(pair.username)} -c 'SELECT 1;' postgres`;
|
|
2984
|
+
}
|
|
2985
|
+
if (pair && host && service === "smb") {
|
|
2986
|
+
return `smbclient -L ${shellEscape(`//${host}/`)} -U ${shellEscape(`${pair.username}%${pair.password}`)} -m SMB3`;
|
|
2987
|
+
}
|
|
2988
|
+
if (pair && host && service === "winrm") {
|
|
2989
|
+
return `evil-winrm -i ${shellEscape(host)} -u ${shellEscape(pair.username)} -p ${shellEscape(pair.password)} -c whoami`;
|
|
2990
|
+
}
|
|
2991
|
+
if (pair && host && service === "rdp") {
|
|
2992
|
+
return `xfreerdp /cert:ignore /auth-only /u:${shellEscape(pair.username)} /p:${shellEscape(pair.password)} /v:${shellEscape(host)}`;
|
|
2993
|
+
}
|
|
2994
|
+
if (pair && host && service === "ldap") {
|
|
2995
|
+
return `ldapwhoami -x -H ${shellEscape(`ldap://${host}`)} -D ${shellEscape(pair.username)} -w ${shellEscape(pair.password)}`;
|
|
2996
|
+
}
|
|
2997
|
+
return void 0;
|
|
2998
|
+
}
|
|
2999
|
+
function hasCredentialLoot(task) {
|
|
3000
|
+
return task.loot.some((item) => {
|
|
3001
|
+
const normalized = item.toLowerCase();
|
|
3002
|
+
return normalized.includes("password") || normalized.includes("credential") || normalized.includes("hash") || normalized.includes("token") || normalized.includes("key");
|
|
3003
|
+
});
|
|
3004
|
+
}
|
|
3005
|
+
function hasFoothold(task) {
|
|
3006
|
+
return task.sessions.length > 0 || task.assets.some((asset) => {
|
|
3007
|
+
const normalized = asset.toLowerCase();
|
|
3008
|
+
return normalized.includes("shell:") || normalized.includes("stabilized_shell:") || normalized.includes("post_exploitation_shell:") || normalized.includes("listener:") || normalized.includes("session:") || normalized.includes("foothold");
|
|
3009
|
+
});
|
|
3010
|
+
}
|
|
3011
|
+
function getArtifactPath(task) {
|
|
3012
|
+
for (const asset of task.assets) {
|
|
3013
|
+
const trimmed = asset.trim();
|
|
3014
|
+
const artifactMatch = trimmed.match(/^artifact:(.+)$/i);
|
|
3015
|
+
if (artifactMatch?.[1]) {
|
|
3016
|
+
return artifactMatch[1].trim();
|
|
3017
|
+
}
|
|
3018
|
+
if (trimmed.startsWith("/") || trimmed.startsWith("./")) {
|
|
3019
|
+
return trimmed;
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
return null;
|
|
3023
|
+
}
|
|
3024
|
+
function getArtifactKind(task, artifactPath, artifactUrl) {
|
|
3025
|
+
const evidence = getJoinedExploitEvidence(task);
|
|
3026
|
+
if (artifactUrl) {
|
|
3027
|
+
if (artifactUrl.match(/\.(php|asp|aspx|jsp|jspx|cfm)(?:[?#].*)?$/i) || evidence.includes("webshell")) {
|
|
3028
|
+
return "webshell";
|
|
3029
|
+
}
|
|
3030
|
+
return "url";
|
|
3031
|
+
}
|
|
3032
|
+
if (!artifactPath) {
|
|
3033
|
+
return null;
|
|
3034
|
+
}
|
|
3035
|
+
if (artifactPath.match(/\.(sql|sqlite|db|mdb|bak|dump)(?:\.[a-z0-9]+)?$/i) || evidence.includes("dump")) {
|
|
3036
|
+
return "database_dump";
|
|
3037
|
+
}
|
|
3038
|
+
if (artifactPath.startsWith("//") || evidence.includes("smb share") || evidence.includes("share:")) {
|
|
3039
|
+
return "smb_share";
|
|
3040
|
+
}
|
|
3041
|
+
return "file";
|
|
3042
|
+
}
|
|
3043
|
+
function getArtifactBoundedCommand(kind, artifactPath, artifactUrl) {
|
|
3044
|
+
if (!kind) {
|
|
3045
|
+
return void 0;
|
|
3046
|
+
}
|
|
3047
|
+
if (kind === "webshell" && artifactUrl) {
|
|
3048
|
+
return `curl -fsSIL ${shellEscape(artifactUrl)} && curl -fsS ${shellEscape(artifactUrl)} | head -n 5`;
|
|
3049
|
+
}
|
|
3050
|
+
if (kind === "url" && artifactUrl) {
|
|
3051
|
+
return `curl -fsSIL ${shellEscape(artifactUrl)}`;
|
|
3052
|
+
}
|
|
3053
|
+
if (kind === "database_dump" && artifactPath) {
|
|
3054
|
+
return `test -e ${artifactPath} && ls -lh ${artifactPath} && file ${artifactPath} 2>/dev/null && head -n 5 ${artifactPath} 2>/dev/null`;
|
|
3055
|
+
}
|
|
3056
|
+
if (kind === "smb_share" && artifactPath) {
|
|
3057
|
+
return `smbclient -L ${shellEscape(artifactPath)} -N -m SMB3`;
|
|
3058
|
+
}
|
|
3059
|
+
if (kind === "file" && artifactPath) {
|
|
3060
|
+
return `test -e ${artifactPath} && ls -l ${artifactPath} && file ${artifactPath} 2>/dev/null`;
|
|
3061
|
+
}
|
|
3062
|
+
return void 0;
|
|
3063
|
+
}
|
|
3064
|
+
function getExploitLifecycleSnapshot(task) {
|
|
3065
|
+
const replayableCommand = getReplayableExploitCommand(task);
|
|
3066
|
+
if (hasFoothold(task)) {
|
|
3067
|
+
return {
|
|
3068
|
+
phase: "foothold_active",
|
|
3069
|
+
recommendation: "A foothold already exists. Reuse the current access path and continue the exploit chain from that foothold.",
|
|
3070
|
+
boundedTool: "exploit_foothold_check",
|
|
3071
|
+
boundedCommand: replayableCommand || void 0,
|
|
3072
|
+
boundedTimeout: 4e3,
|
|
3073
|
+
boundedInstruction: "Run one narrow foothold validation step before expanding the exploit chain."
|
|
3074
|
+
};
|
|
3075
|
+
}
|
|
3076
|
+
if (hasCredentialLoot(task)) {
|
|
3077
|
+
return {
|
|
3078
|
+
phase: "credential_followup",
|
|
3079
|
+
recommendation: "Credentials or tokens exist. Prioritize validation, reuse, and pivot from the current exploit chain.",
|
|
3080
|
+
boundedTool: "exploit_credential_check",
|
|
3081
|
+
boundedCommand: getCredentialBoundedCommand(task, replayableCommand),
|
|
3082
|
+
boundedTimeout: 5e3,
|
|
3083
|
+
boundedInstruction: "Run one bounded credential or token validation step before changing exploit vectors."
|
|
3084
|
+
};
|
|
3085
|
+
}
|
|
3086
|
+
if (task.assets.length > 0) {
|
|
3087
|
+
const artifactPath = getArtifactPath(task);
|
|
3088
|
+
const artifactUrl = getUrlCandidate(task);
|
|
3089
|
+
const artifactKind = getArtifactKind(task, artifactPath, artifactUrl);
|
|
3090
|
+
return {
|
|
3091
|
+
phase: "artifact_ready",
|
|
3092
|
+
recommendation: "The exploit chain produced reusable artifacts. Validate and advance the current artifact before changing vectors.",
|
|
3093
|
+
boundedTool: "exploit_artifact_check",
|
|
3094
|
+
boundedCommand: getArtifactBoundedCommand(artifactKind, artifactPath, artifactUrl),
|
|
3095
|
+
boundedTimeout: 3e3,
|
|
3096
|
+
boundedInstruction: artifactKind === "webshell" ? "Run one bounded webshell validation step before replacing the current foothold artifact." : artifactKind === "database_dump" ? "Run one bounded dump validation step before replacing or parsing the recovered database artifact." : artifactKind === "smb_share" ? "Run one bounded share validation step before changing the current artifact path." : "Run one bounded artifact validation step before replacing the current artifact."
|
|
3097
|
+
};
|
|
3098
|
+
}
|
|
3099
|
+
return {
|
|
3100
|
+
phase: "vector_active",
|
|
3101
|
+
recommendation: "The current exploit vector is still active. Continue adapting it before switching to a new chain.",
|
|
3102
|
+
boundedTool: "exploit_vector_check",
|
|
3103
|
+
boundedCommand: getVectorBoundedCommand(task),
|
|
3104
|
+
boundedTimeout: 5e3,
|
|
3105
|
+
boundedInstruction: "Run one bounded target reachability or service confirmation step before abandoning the current exploit vector."
|
|
3106
|
+
};
|
|
3107
|
+
}
|
|
3108
|
+
|
|
3109
|
+
// src/agents/main-agent/pwn-lifecycle.ts
|
|
3110
|
+
function joinedEvidence(task) {
|
|
3111
|
+
return [
|
|
3112
|
+
task.summary,
|
|
3113
|
+
task.resumeHint,
|
|
3114
|
+
task.waitingOn,
|
|
3115
|
+
...task.tried,
|
|
3116
|
+
...task.findings,
|
|
3117
|
+
...task.assets
|
|
3118
|
+
].filter(Boolean).join(" ").toLowerCase();
|
|
3119
|
+
}
|
|
3120
|
+
function getReplayableCommand(task) {
|
|
3121
|
+
const candidates = [...task.tried, task.resumeHint || ""].map((value) => value.trim()).filter(Boolean);
|
|
3122
|
+
return candidates.find(
|
|
3123
|
+
(candidate) => /^(?:\.\/|\/|python(?:3)?\b|gdb\b|qemu(?:-[\w-]+)?\b|nc\b)/i.test(candidate) || candidate.includes("pwntools")
|
|
3124
|
+
) || null;
|
|
3125
|
+
}
|
|
3126
|
+
function getCandidateBinaryPath(task) {
|
|
3127
|
+
const candidates = [...task.assets, ...task.findings, task.summary, task.task, task.resumeHint || ""].map((value) => value.trim()).filter(Boolean);
|
|
3128
|
+
for (const candidate of candidates) {
|
|
3129
|
+
const artifactMatch = candidate.match(/^artifact:(.+)$/i);
|
|
3130
|
+
const normalized = artifactMatch?.[1]?.trim() || candidate;
|
|
3131
|
+
if (normalized.startsWith("./") || normalized.startsWith("/")) {
|
|
3132
|
+
return normalized;
|
|
3133
|
+
}
|
|
3134
|
+
const embeddedPath = normalized.match(/(\.\/[A-Za-z0-9_./-]+|\/[A-Za-z0-9_./-]+)/);
|
|
3135
|
+
if (embeddedPath?.[1]) {
|
|
3136
|
+
return embeddedPath[1];
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
return null;
|
|
3140
|
+
}
|
|
3141
|
+
function getCandidateCorePath(task) {
|
|
3142
|
+
const candidates = [...task.assets, ...task.findings, task.summary].map((value) => value.trim()).filter(Boolean);
|
|
3143
|
+
for (const candidate of candidates) {
|
|
3144
|
+
const artifactMatch = candidate.match(/^artifact:(.+)$/i);
|
|
3145
|
+
const normalized = artifactMatch?.[1]?.trim() || candidate;
|
|
3146
|
+
if (normalized.match(/(?:^|\/)core(?:\.[A-Za-z0-9_-]+)?$/i)) {
|
|
3147
|
+
return normalized;
|
|
3148
|
+
}
|
|
3149
|
+
const embeddedCore = normalized.match(/(\.\/core(?:\.[A-Za-z0-9_-]+)?|\/[A-Za-z0-9_./-]*core(?:\.[A-Za-z0-9_-]+)?)/i);
|
|
3150
|
+
if (embeddedCore?.[1]) {
|
|
3151
|
+
return embeddedCore[1];
|
|
3152
|
+
}
|
|
3153
|
+
}
|
|
3154
|
+
return null;
|
|
3155
|
+
}
|
|
3156
|
+
function getPwnProbeKind(replayableCommand, binaryPath, corePath) {
|
|
3157
|
+
if (binaryPath && corePath) {
|
|
3158
|
+
return "core_gdb";
|
|
3159
|
+
}
|
|
3160
|
+
if (replayableCommand?.startsWith("gdb ")) {
|
|
3161
|
+
return "gdb_replay";
|
|
3162
|
+
}
|
|
3163
|
+
return "binary_smoke";
|
|
3164
|
+
}
|
|
3165
|
+
function getBoundedPwnCommand(kind, replayableCommand, binaryPath, corePath) {
|
|
3166
|
+
if (kind === "core_gdb" && binaryPath && corePath) {
|
|
3167
|
+
return `gdb -q ${binaryPath} ${corePath} -batch -ex 'info registers' -ex 'bt'`;
|
|
3168
|
+
}
|
|
3169
|
+
if (kind === "gdb_replay" && replayableCommand) {
|
|
3170
|
+
return replayableCommand;
|
|
3171
|
+
}
|
|
3172
|
+
if (replayableCommand) {
|
|
3173
|
+
return replayableCommand;
|
|
3174
|
+
}
|
|
3175
|
+
if (binaryPath) {
|
|
3176
|
+
return `test -x ${binaryPath} && timeout 5 ${binaryPath} </dev/null`;
|
|
3177
|
+
}
|
|
3178
|
+
return void 0;
|
|
3179
|
+
}
|
|
3180
|
+
function getPwnLifecycleSnapshot(task) {
|
|
3181
|
+
const evidence = joinedEvidence(task);
|
|
3182
|
+
const replayableCommand = getReplayableCommand(task);
|
|
3183
|
+
const binaryPath = getCandidateBinaryPath(task);
|
|
3184
|
+
const corePath = getCandidateCorePath(task);
|
|
3185
|
+
const probeKind = getPwnProbeKind(replayableCommand, binaryPath, corePath);
|
|
3186
|
+
if (task.sessions.length > 0 || task.assets.some((asset) => {
|
|
3187
|
+
const normalized = asset.toLowerCase();
|
|
3188
|
+
return normalized.includes("shell:") || normalized.includes("stabilized_shell:") || normalized.includes("post_exploitation_shell:");
|
|
3189
|
+
})) {
|
|
3190
|
+
return {
|
|
3191
|
+
phase: "foothold_active",
|
|
3192
|
+
recommendation: "The pwn chain already produced execution. Reuse the existing foothold instead of restarting the exploit loop."
|
|
3193
|
+
};
|
|
3194
|
+
}
|
|
3195
|
+
if (evidence.includes("offset") || evidence.includes("pattern") || evidence.includes("eip") || evidence.includes("rip")) {
|
|
3196
|
+
return {
|
|
3197
|
+
phase: "offset_known",
|
|
3198
|
+
recommendation: "Offsets or control primitives are known. Resume from the latest payload iteration instead of rediscovering the crash.",
|
|
3199
|
+
boundedTool: "pwn_offset_check",
|
|
3200
|
+
boundedCommand: getBoundedPwnCommand(probeKind, replayableCommand, binaryPath, corePath),
|
|
3201
|
+
boundedTimeout: 5e3,
|
|
3202
|
+
boundedInstruction: probeKind === "core_gdb" ? "Run one bounded gdb register/backtrace check against the saved core state before changing the payload." : probeKind === "gdb_replay" ? "Run one bounded gdb replay step against the latest exploit revision before changing the payload." : "Run one bounded offset verification step against the latest exploit revision."
|
|
3203
|
+
};
|
|
3204
|
+
}
|
|
3205
|
+
if (evidence.includes("crash") || evidence.includes("segfault") || evidence.includes("core")) {
|
|
3206
|
+
return {
|
|
3207
|
+
phase: "crash_iteration",
|
|
3208
|
+
recommendation: "A crash state exists. Preserve it and continue the debug loop from the latest observed crash.",
|
|
3209
|
+
boundedTool: "pwn_crash_repro",
|
|
3210
|
+
boundedCommand: getBoundedPwnCommand(probeKind, replayableCommand, binaryPath, corePath),
|
|
3211
|
+
boundedTimeout: 1e4,
|
|
3212
|
+
boundedInstruction: probeKind === "core_gdb" ? "Inspect the saved core state once and preserve the exact crash context before changing the exploit loop." : probeKind === "gdb_replay" ? "Replay the latest gdb-driven crash once and preserve the exact crash state." : "Reproduce the latest crash once and preserve the exact crash state."
|
|
3213
|
+
};
|
|
3214
|
+
}
|
|
3215
|
+
return {
|
|
3216
|
+
phase: "payload_iteration",
|
|
3217
|
+
recommendation: "The pwn loop is still iterating payload design. Continue from the latest payload revision and observations.",
|
|
3218
|
+
boundedTool: "pwn_payload_smoke",
|
|
3219
|
+
boundedCommand: getBoundedPwnCommand(probeKind, replayableCommand, binaryPath, corePath),
|
|
3220
|
+
boundedTimeout: 5e3,
|
|
3221
|
+
boundedInstruction: probeKind === "gdb_replay" ? "Run one bounded gdb-guided smoke step from the latest payload revision." : "Run one bounded payload smoke test from the latest payload revision."
|
|
3222
|
+
};
|
|
3223
|
+
}
|
|
3224
|
+
|
|
3225
|
+
// src/agents/main-agent/delegated-task-handoff.ts
|
|
3226
|
+
function buildLifecycleSnapshot(decision) {
|
|
3227
|
+
const task = decision.task;
|
|
3228
|
+
if (decision.preferredWorkerType !== "shell-supervisor") {
|
|
3229
|
+
if (decision.preferredWorkerType === "exploit") {
|
|
3230
|
+
return getExploitLifecycleSnapshot(task);
|
|
3231
|
+
}
|
|
3232
|
+
if (decision.preferredWorkerType === "pwn") {
|
|
3233
|
+
return getPwnLifecycleSnapshot(task);
|
|
3234
|
+
}
|
|
3235
|
+
return null;
|
|
3236
|
+
}
|
|
3237
|
+
return getShellSupervisorLifecycleSnapshot();
|
|
3238
|
+
}
|
|
3239
|
+
function buildDelegatedRequestContext(decision, lifecycleSnapshot) {
|
|
3240
|
+
const task = decision.task;
|
|
3241
|
+
if (!lifecycleSnapshot) {
|
|
3242
|
+
return task.summary;
|
|
3243
|
+
}
|
|
3244
|
+
if (decision.preferredWorkerType === "exploit") {
|
|
3245
|
+
return [
|
|
3246
|
+
task.summary,
|
|
3247
|
+
`exploit_phase=${lifecycleSnapshot.phase}`,
|
|
3248
|
+
`recommendation=${lifecycleSnapshot.recommendation}`
|
|
3249
|
+
].filter(Boolean).join(" | ");
|
|
3250
|
+
}
|
|
3251
|
+
if (decision.preferredWorkerType === "pwn") {
|
|
3252
|
+
return [
|
|
3253
|
+
task.summary,
|
|
3254
|
+
`pwn_phase=${lifecycleSnapshot.phase}`,
|
|
3255
|
+
`recommendation=${lifecycleSnapshot.recommendation}`
|
|
3256
|
+
].filter(Boolean).join(" | ");
|
|
3257
|
+
}
|
|
3258
|
+
const shellSnapshot = lifecycleSnapshot;
|
|
3259
|
+
return [
|
|
3260
|
+
task.summary,
|
|
3261
|
+
`shell_phase=${shellSnapshot.phase}`,
|
|
3262
|
+
shellSnapshot.listenerId ? `listener=${shellSnapshot.listenerId}` : "",
|
|
3263
|
+
shellSnapshot.activeShellId ? `active_shell=${shellSnapshot.activeShellId}` : "",
|
|
3264
|
+
`recommendation=${shellSnapshot.recommendation}`
|
|
3265
|
+
].filter(Boolean).join(" | ");
|
|
3266
|
+
}
|
|
3267
|
+
function buildDelegatedExecutionRequest(decision, context, lifecycleSnapshot) {
|
|
3268
|
+
const task = decision.task;
|
|
3269
|
+
const preferredWorkerType = decision.preferredWorkerType;
|
|
3270
|
+
const requestTask = task.resumeHint || task.task;
|
|
3271
|
+
const boundedLifecycle = preferredWorkerType === "exploit" || preferredWorkerType === "pwn" ? lifecycleSnapshot : null;
|
|
3272
|
+
return {
|
|
3273
|
+
task: requestTask,
|
|
3274
|
+
workerType: preferredWorkerType,
|
|
3275
|
+
resumeTaskId: task.id,
|
|
3276
|
+
target: task.target,
|
|
3277
|
+
context,
|
|
3278
|
+
boundedTool: preferredWorkerType ? getRecommendedBoundedTool({
|
|
3279
|
+
task: requestTask,
|
|
3280
|
+
workerType: preferredWorkerType,
|
|
3281
|
+
target: task.target,
|
|
3282
|
+
context
|
|
3283
|
+
}) ?? void 0 : void 0,
|
|
3284
|
+
boundedCommand: boundedLifecycle?.boundedCommand,
|
|
3285
|
+
boundedTimeout: boundedLifecycle?.boundedTimeout,
|
|
3286
|
+
boundedInstruction: preferredWorkerType ? boundedLifecycle?.boundedInstruction || buildDelegatedExecutionPlan({
|
|
3287
|
+
task: requestTask,
|
|
3288
|
+
workerType: preferredWorkerType,
|
|
3289
|
+
target: task.target,
|
|
3290
|
+
context
|
|
3291
|
+
}) : void 0
|
|
3292
|
+
};
|
|
3293
|
+
}
|
|
3294
|
+
function buildDelegatedTaskHandoff(decision) {
|
|
3295
|
+
const task = decision.task;
|
|
3296
|
+
const preferredWorkerType = decision.preferredWorkerType;
|
|
3297
|
+
const lifecycleSnapshot = buildLifecycleSnapshot(decision);
|
|
3298
|
+
const context = buildDelegatedRequestContext(decision, lifecycleSnapshot);
|
|
3299
|
+
const delegatedExecutionRequest = buildDelegatedExecutionRequest(decision, context, lifecycleSnapshot);
|
|
3300
|
+
const executionPlan = buildDelegatedExecutionPlan(delegatedExecutionRequest);
|
|
3301
|
+
const details = [
|
|
3302
|
+
`Resume delegated task: ${task.task}.`,
|
|
3303
|
+
`Delegated task ID: ${task.id}.`,
|
|
3304
|
+
`Scheduler reason: ${decision.reason}`,
|
|
3305
|
+
`Scheduler cycle count: ${decision.resumeCount + 1}.`,
|
|
3306
|
+
`Current status: ${task.status}.`,
|
|
3307
|
+
task.waitingOn ? `Waiting on: ${task.waitingOn}.` : "",
|
|
3308
|
+
task.assets.length > 0 ? `Reuse assets: ${task.assets.join(", ")}.` : "",
|
|
3309
|
+
task.sessions.length > 0 ? `Reuse sessions: ${task.sessions.join(", ")}.` : "",
|
|
3310
|
+
task.resumeHint ? `Resume guidance: ${task.resumeHint}.` : "",
|
|
3311
|
+
preferredWorkerType ? `Preferred worker: ${preferredWorkerType}.` : "",
|
|
3312
|
+
delegatedExecutionRequest.boundedTool ? `Bounded tool: ${delegatedExecutionRequest.boundedTool}.` : "",
|
|
3313
|
+
delegatedExecutionRequest.boundedCommand ? `Bounded command candidate: ${delegatedExecutionRequest.boundedCommand}.` : "",
|
|
3314
|
+
preferredWorkerType ? `Delegation policy: if this requires multi-step follow-up, call run_task with worker_type="${preferredWorkerType}" and resume_task_id="${task.id}" for this selected task before creating any unrelated delegated chain.` : "",
|
|
3315
|
+
`Execution hint: ${executionPlan}`,
|
|
3316
|
+
"Execution policy: continue the selected delegated chain first. Do not switch to a different active delegated task until this one is advanced or invalidated.",
|
|
3317
|
+
"Goal: continue the existing chain instead of creating a duplicate operation."
|
|
3318
|
+
].filter(Boolean);
|
|
3319
|
+
return {
|
|
3320
|
+
shouldForwardToMain: true,
|
|
3321
|
+
forwardedInput: details.join(" "),
|
|
3322
|
+
directResponse: "",
|
|
3323
|
+
shouldWritePolicy: false,
|
|
3324
|
+
policyDocument: "",
|
|
3325
|
+
policyUpdateSummary: "",
|
|
3326
|
+
insightSummary: `Auto-resume delegated task ${task.id} via handoff builder.`,
|
|
3327
|
+
delegatedExecutionRequest,
|
|
3328
|
+
success: true
|
|
3329
|
+
};
|
|
3330
|
+
}
|
|
3331
|
+
|
|
3332
|
+
// src/agents/main-agent/user-input-runtime.ts
|
|
3333
|
+
function collectPendingUserInputs(userInputQueue, pendingInitialUserInput) {
|
|
3334
|
+
const drained = userInputQueue.drain();
|
|
3335
|
+
return [
|
|
3336
|
+
...pendingInitialUserInput ? [pendingInitialUserInput] : [],
|
|
3337
|
+
...drained.map((item) => item.text)
|
|
3338
|
+
].map((text) => text.trim()).filter(Boolean);
|
|
3339
|
+
}
|
|
3340
|
+
function appendUserInput(existing, next) {
|
|
3341
|
+
return existing.trim() === "" ? next : `${existing}
|
|
3342
|
+
|
|
3343
|
+
${next}`;
|
|
3344
|
+
}
|
|
3345
|
+
function normalizeInputProcessorResult(result, fallbackInput) {
|
|
3346
|
+
if (!result.success) {
|
|
3347
|
+
return {
|
|
3348
|
+
shouldForwardToMain: true,
|
|
3349
|
+
forwardedInput: fallbackInput,
|
|
3350
|
+
directResponse: "",
|
|
3351
|
+
shouldWritePolicy: false,
|
|
3352
|
+
policyDocument: "",
|
|
3353
|
+
policyUpdateSummary: "",
|
|
3354
|
+
insightSummary: "Input processor fallback: forwarded raw input.",
|
|
3355
|
+
success: false
|
|
3356
|
+
};
|
|
3357
|
+
}
|
|
3358
|
+
return {
|
|
3359
|
+
...result,
|
|
3360
|
+
forwardedInput: result.forwardedInput.trim() || fallbackInput,
|
|
3361
|
+
directResponse: result.directResponse.trim(),
|
|
3362
|
+
policyDocument: result.policyDocument.trim(),
|
|
3363
|
+
policyUpdateSummary: result.policyUpdateSummary.trim(),
|
|
3364
|
+
insightSummary: result.insightSummary.trim()
|
|
3365
|
+
};
|
|
3366
|
+
}
|
|
3367
|
+
function buildAutoResumeResult(state, delegatedTaskQueue) {
|
|
3368
|
+
const activeTasks = state.getActiveDelegatedTasks();
|
|
3369
|
+
const decision = delegatedTaskQueue.next(activeTasks);
|
|
3370
|
+
if (!decision) {
|
|
3371
|
+
return null;
|
|
3372
|
+
}
|
|
3373
|
+
delegatedTaskQueue.acknowledge(decision.task.id);
|
|
3374
|
+
return buildDelegatedTaskHandoff(decision);
|
|
3375
|
+
}
|
|
3376
|
+
function buildQueueSnapshot(userInputQueue) {
|
|
3377
|
+
return {
|
|
3378
|
+
count: userInputQueue.pendingCount(),
|
|
3379
|
+
preview: userInputQueue.peek().map((item) => item.text)
|
|
3380
|
+
};
|
|
3381
|
+
}
|
|
3382
|
+
|
|
3383
|
+
// src/agents/main-agent/queue-facade.ts
|
|
3384
|
+
function emitMainAgentQueueUpdated(events, userInputQueue) {
|
|
3385
|
+
events.emit({
|
|
3386
|
+
type: EVENT_TYPES.QUEUE_UPDATED,
|
|
3387
|
+
timestamp: Date.now(),
|
|
3388
|
+
data: buildQueueSnapshot(userInputQueue)
|
|
3389
|
+
});
|
|
3390
|
+
}
|
|
3391
|
+
function enqueueMainAgentUserInput(userInputQueue, events, text) {
|
|
3392
|
+
userInputQueue.enqueue(text);
|
|
3393
|
+
emitMainAgentQueueUpdated(events, userInputQueue);
|
|
3394
|
+
}
|
|
3395
|
+
function dequeueMainAgentUserInput(userInputQueue, events) {
|
|
3396
|
+
const value = userInputQueue.dequeueLast();
|
|
3397
|
+
emitMainAgentQueueUpdated(events, userInputQueue);
|
|
3398
|
+
return value;
|
|
3399
|
+
}
|
|
3400
|
+
function getMainAgentPendingUserInputPreview(userInputQueue) {
|
|
3401
|
+
return userInputQueue.peek().map((item) => item.text);
|
|
3402
|
+
}
|
|
3403
|
+
|
|
3404
|
+
// src/agents/main-agent/session-mutations.ts
|
|
3405
|
+
async function resetMainAgentSession(state, userInputQueue) {
|
|
3406
|
+
return resetSessionAction(state, userInputQueue);
|
|
3407
|
+
}
|
|
3408
|
+
function loadMainAgentSession(state) {
|
|
3409
|
+
return session.load(state);
|
|
3410
|
+
}
|
|
3411
|
+
function saveMainAgentSession(state) {
|
|
3412
|
+
return session.save(state);
|
|
3413
|
+
}
|
|
3414
|
+
function applyMainAgentScope(state, allowed, exclusions = []) {
|
|
3415
|
+
state.setScope({
|
|
3416
|
+
allowedCidrs: allowed.filter((a) => a.includes("/")),
|
|
3417
|
+
allowedDomains: allowed.filter((a) => !a.includes("/")),
|
|
3418
|
+
exclusions,
|
|
3419
|
+
isDOSAllowed: false,
|
|
3420
|
+
isSocialAllowed: false
|
|
1727
3421
|
});
|
|
1728
|
-
state.reset();
|
|
1729
|
-
userInputQueue.clear();
|
|
1730
|
-
return clearWorkspace();
|
|
1731
3422
|
}
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
}
|
|
3423
|
+
function addMainAgentTarget(state, events, ip) {
|
|
3424
|
+
state.addTarget({ ip, ports: [], tags: [], firstSeen: Date.now(), hostname: "" });
|
|
3425
|
+
emitStateChange(events, state);
|
|
3426
|
+
}
|
|
3427
|
+
|
|
3428
|
+
// src/agents/main-agent/delegated-execution-facade.ts
|
|
3429
|
+
function resetDelegatedExecutionState(delegatedExecutionRuntime) {
|
|
3430
|
+
delegatedExecutionRuntime.reset();
|
|
3431
|
+
}
|
|
3432
|
+
function setDelegatedExecutionRequest(delegatedExecutionRuntime, request) {
|
|
3433
|
+
delegatedExecutionRuntime.set(request);
|
|
3434
|
+
}
|
|
3435
|
+
function buildDelegatedPromptFragments(delegatedExecutionRuntime) {
|
|
3436
|
+
return {
|
|
3437
|
+
request: delegatedExecutionRuntime.toPromptFragment(),
|
|
3438
|
+
result: delegatedExecutionRuntime.toResultFragment()
|
|
3439
|
+
};
|
|
3440
|
+
}
|
|
3441
|
+
function getDelegatedExecutionRequest(delegatedExecutionRuntime) {
|
|
3442
|
+
return delegatedExecutionRuntime.peek();
|
|
3443
|
+
}
|
|
3444
|
+
function consumeDelegatedExecutionRequest(delegatedExecutionRuntime) {
|
|
3445
|
+
return delegatedExecutionRuntime.consume();
|
|
3446
|
+
}
|
|
3447
|
+
async function executePendingDelegatedRequest(delegatedAutoExecutor, delegatedExecutionRuntime, toolRegistry) {
|
|
3448
|
+
return delegatedAutoExecutor.executePending(delegatedExecutionRuntime, toolRegistry);
|
|
3449
|
+
}
|
|
3450
|
+
async function maybeAutoExecuteDelegatedRequest(delegatedAutoExecutor, delegatedExecutionRuntime, toolRegistry) {
|
|
3451
|
+
if (!delegatedAutoExecutor.canExecutePending(delegatedExecutionRuntime)) {
|
|
3452
|
+
return null;
|
|
3453
|
+
}
|
|
3454
|
+
return executePendingDelegatedRequest(
|
|
3455
|
+
delegatedAutoExecutor,
|
|
3456
|
+
delegatedExecutionRuntime,
|
|
3457
|
+
toolRegistry
|
|
3458
|
+
);
|
|
3459
|
+
}
|
|
1736
3460
|
|
|
1737
3461
|
// src/agents/main-agent/main-agent.ts
|
|
1738
3462
|
var MainAgent = class extends CoreAgent {
|
|
@@ -1743,20 +3467,28 @@ var MainAgent = class extends CoreAgent {
|
|
|
1743
3467
|
turnCounter = 0;
|
|
1744
3468
|
userInputQueue = new UserInputQueue();
|
|
1745
3469
|
pendingInitialUserInput = null;
|
|
3470
|
+
delegatedTaskQueue = new DelegatedTaskQueue();
|
|
3471
|
+
delegatedExecutionRuntime = new DelegatedExecutionRuntime();
|
|
3472
|
+
delegatedAutoExecutor = new DelegatedAutoExecutor();
|
|
1746
3473
|
pipelineRunner;
|
|
1747
3474
|
turnCyclePipeline;
|
|
1748
3475
|
inputProcessor;
|
|
1749
3476
|
sessionRuntime;
|
|
1750
|
-
constructor(
|
|
1751
|
-
super(
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
3477
|
+
constructor(options) {
|
|
3478
|
+
super({
|
|
3479
|
+
agentType: AGENT_ROLES.ORCHESTRATOR,
|
|
3480
|
+
state: options.state,
|
|
3481
|
+
events: options.events,
|
|
3482
|
+
toolRegistry: options.toolRegistry
|
|
3483
|
+
});
|
|
3484
|
+
this.approvalGate = options.approvalGate;
|
|
3485
|
+
this.scopeGuard = options.scopeGuard;
|
|
3486
|
+
this.promptBuilder = new PromptBuilder(options.state);
|
|
1755
3487
|
this.pipelineRunner = new PipelineRunner();
|
|
1756
3488
|
this.inputProcessor = createInputProcessor(this.llm);
|
|
1757
3489
|
this.sessionRuntime = createSessionRuntime();
|
|
1758
3490
|
setActiveSessionRuntime(this.sessionRuntime);
|
|
1759
|
-
this.turnCyclePipeline = loadPipelineFromConfig(
|
|
3491
|
+
this.turnCyclePipeline = loadPipelineFromConfig(getRuntimePipelineConfig(), {
|
|
1760
3492
|
llm: this.llm,
|
|
1761
3493
|
conditions: getConditionRegistry(),
|
|
1762
3494
|
key: "turn_cycle"
|
|
@@ -1766,6 +3498,8 @@ var MainAgent = class extends CoreAgent {
|
|
|
1766
3498
|
async execute(userInput) {
|
|
1767
3499
|
this.userInput = "";
|
|
1768
3500
|
this.pendingInitialUserInput = userInput;
|
|
3501
|
+
this.delegatedTaskQueue.reset();
|
|
3502
|
+
resetDelegatedExecutionState(this.delegatedExecutionRuntime);
|
|
1769
3503
|
emitStart(this.events, userInput, this.state);
|
|
1770
3504
|
initializeTask(this.state);
|
|
1771
3505
|
try {
|
|
@@ -1774,7 +3508,7 @@ var MainAgent = class extends CoreAgent {
|
|
|
1774
3508
|
return result.output;
|
|
1775
3509
|
} finally {
|
|
1776
3510
|
try {
|
|
1777
|
-
|
|
3511
|
+
saveMainAgentSession(this.state);
|
|
1778
3512
|
} catch {
|
|
1779
3513
|
}
|
|
1780
3514
|
await cleanupAllProcesses();
|
|
@@ -1809,9 +3543,15 @@ var MainAgent = class extends CoreAgent {
|
|
|
1809
3543
|
};
|
|
1810
3544
|
}
|
|
1811
3545
|
async processUserInputTurn() {
|
|
1812
|
-
const messages = this.
|
|
3546
|
+
const messages = collectPendingUserInputs(this.userInputQueue, this.pendingInitialUserInput);
|
|
1813
3547
|
this.pendingInitialUserInput = null;
|
|
1814
3548
|
if (messages.length === 0) {
|
|
3549
|
+
const autoResume = buildAutoResumeResult(this.state, this.delegatedTaskQueue);
|
|
3550
|
+
if (autoResume) {
|
|
3551
|
+
this.updateObjectiveFromInputProcessor(autoResume);
|
|
3552
|
+
await this.maybeAutoExecuteDelegatedRequest();
|
|
3553
|
+
return autoResume;
|
|
3554
|
+
}
|
|
1815
3555
|
return { shouldForwardToMain: true };
|
|
1816
3556
|
}
|
|
1817
3557
|
const rawInput = messages.map((text, index) => `[${index + 1}] ${text}`).join("\n");
|
|
@@ -1821,11 +3561,11 @@ var MainAgent = class extends CoreAgent {
|
|
|
1821
3561
|
hasActiveEngagement: this.state.hasActiveEngagement(),
|
|
1822
3562
|
currentObjective: this.state.currentObjective || ""
|
|
1823
3563
|
});
|
|
1824
|
-
const normalized =
|
|
3564
|
+
const normalized = normalizeInputProcessorResult(result, rawInput);
|
|
1825
3565
|
this.updatePolicyFromInputProcessor(normalized);
|
|
1826
3566
|
this.updateObjectiveFromInputProcessor(normalized);
|
|
1827
3567
|
this.events.emit({ type: EVENT_TYPES.QUEUE_DRAINED, timestamp: Date.now() });
|
|
1828
|
-
this.
|
|
3568
|
+
emitMainAgentQueueUpdated(this.events, this.userInputQueue);
|
|
1829
3569
|
return normalized;
|
|
1830
3570
|
}
|
|
1831
3571
|
updatePolicyFromInputProcessor(result) {
|
|
@@ -1834,63 +3574,39 @@ var MainAgent = class extends CoreAgent {
|
|
|
1834
3574
|
}
|
|
1835
3575
|
}
|
|
1836
3576
|
updateObjectiveFromInputProcessor(result) {
|
|
3577
|
+
setDelegatedExecutionRequest(this.delegatedExecutionRuntime, result.delegatedExecutionRequest);
|
|
1837
3578
|
if (result.shouldForwardToMain && result.forwardedInput.trim()) {
|
|
1838
|
-
this.userInput =
|
|
3579
|
+
this.userInput = appendUserInput(this.userInput, result.forwardedInput);
|
|
1839
3580
|
if (!this.state.hasActiveEngagement()) {
|
|
1840
3581
|
this.state.currentObjective = result.forwardedInput;
|
|
1841
3582
|
}
|
|
1842
3583
|
}
|
|
1843
3584
|
}
|
|
1844
|
-
normalizeInputProcessorResult(result, fallbackInput) {
|
|
1845
|
-
if (!result.success) {
|
|
1846
|
-
return {
|
|
1847
|
-
shouldForwardToMain: true,
|
|
1848
|
-
forwardedInput: fallbackInput,
|
|
1849
|
-
directResponse: "",
|
|
1850
|
-
shouldWritePolicy: false,
|
|
1851
|
-
policyDocument: "",
|
|
1852
|
-
policyUpdateSummary: "",
|
|
1853
|
-
insightSummary: "Input processor fallback: forwarded raw input.",
|
|
1854
|
-
success: false
|
|
1855
|
-
};
|
|
1856
|
-
}
|
|
1857
|
-
return {
|
|
1858
|
-
...result,
|
|
1859
|
-
forwardedInput: result.forwardedInput.trim() || fallbackInput,
|
|
1860
|
-
directResponse: result.directResponse.trim(),
|
|
1861
|
-
policyDocument: result.policyDocument.trim(),
|
|
1862
|
-
policyUpdateSummary: result.policyUpdateSummary.trim(),
|
|
1863
|
-
insightSummary: result.insightSummary.trim()
|
|
1864
|
-
};
|
|
1865
|
-
}
|
|
1866
|
-
collectPendingUserInputs() {
|
|
1867
|
-
const drained = this.userInputQueue.drain();
|
|
1868
|
-
return [
|
|
1869
|
-
...this.pendingInitialUserInput ? [this.pendingInitialUserInput] : [],
|
|
1870
|
-
...drained.map((item) => item.text)
|
|
1871
|
-
].map((text) => text.trim()).filter(Boolean);
|
|
1872
|
-
}
|
|
1873
|
-
appendUserInput(existing, next) {
|
|
1874
|
-
return existing.trim() === "" ? next : `${existing}
|
|
1875
|
-
|
|
1876
|
-
${next}`;
|
|
1877
|
-
}
|
|
1878
3585
|
async buildDynamicPrompt(memory) {
|
|
1879
|
-
|
|
3586
|
+
const delegatedExecution = buildDelegatedPromptFragments(this.delegatedExecutionRuntime);
|
|
3587
|
+
return buildMainAgentPrompt(
|
|
3588
|
+
this.promptBuilder,
|
|
3589
|
+
this.state,
|
|
3590
|
+
this.userInput,
|
|
3591
|
+
delegatedExecution,
|
|
3592
|
+
memory
|
|
3593
|
+
);
|
|
1880
3594
|
}
|
|
1881
3595
|
// ─── Public API ────────────────────────────────────────────────────────────
|
|
1882
3596
|
loadPreviousSession() {
|
|
1883
|
-
return
|
|
3597
|
+
return loadMainAgentSession(this.state);
|
|
1884
3598
|
}
|
|
1885
3599
|
saveCurrentState() {
|
|
1886
|
-
return
|
|
3600
|
+
return saveMainAgentSession(this.state);
|
|
1887
3601
|
}
|
|
1888
3602
|
async resetSession() {
|
|
1889
|
-
const result = await
|
|
3603
|
+
const result = await resetMainAgentSession(this.state, this.userInputQueue);
|
|
1890
3604
|
this.userInput = "";
|
|
3605
|
+
this.delegatedTaskQueue.reset();
|
|
3606
|
+
resetDelegatedExecutionState(this.delegatedExecutionRuntime);
|
|
1891
3607
|
return result;
|
|
1892
3608
|
}
|
|
1893
|
-
|
|
3609
|
+
setToolAutoApprove(shouldEnable) {
|
|
1894
3610
|
this.approvalGate.setAutoApprove(shouldEnable);
|
|
1895
3611
|
}
|
|
1896
3612
|
getState() {
|
|
@@ -1905,63 +3621,63 @@ ${next}`;
|
|
|
1905
3621
|
getSessionRuntime() {
|
|
1906
3622
|
return this.sessionRuntime;
|
|
1907
3623
|
}
|
|
1908
|
-
|
|
1909
|
-
this.
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
3624
|
+
getLastDelegatedExecutionRequest() {
|
|
3625
|
+
return getDelegatedExecutionRequest(this.delegatedExecutionRuntime);
|
|
3626
|
+
}
|
|
3627
|
+
consumeDelegatedExecutionRequest() {
|
|
3628
|
+
return consumeDelegatedExecutionRequest(this.delegatedExecutionRuntime);
|
|
3629
|
+
}
|
|
3630
|
+
async executePendingDelegatedRequest() {
|
|
3631
|
+
return executePendingDelegatedRequest(
|
|
3632
|
+
this.delegatedAutoExecutor,
|
|
3633
|
+
this.delegatedExecutionRuntime,
|
|
3634
|
+
this.toolRegistry
|
|
3635
|
+
);
|
|
3636
|
+
}
|
|
3637
|
+
async maybeAutoExecuteDelegatedRequest() {
|
|
3638
|
+
return maybeAutoExecuteDelegatedRequest(
|
|
3639
|
+
this.delegatedAutoExecutor,
|
|
3640
|
+
this.delegatedExecutionRuntime,
|
|
3641
|
+
this.toolRegistry
|
|
3642
|
+
);
|
|
3643
|
+
}
|
|
3644
|
+
configureTarget(ip, exclusions = []) {
|
|
3645
|
+
addMainAgentTarget(this.state, this.events, ip);
|
|
3646
|
+
applyMainAgentScope(this.state, [ip], exclusions);
|
|
1916
3647
|
}
|
|
1917
3648
|
addTarget(ip) {
|
|
1918
|
-
this.state
|
|
1919
|
-
|
|
3649
|
+
addMainAgentTarget(this.state, this.events, ip);
|
|
3650
|
+
}
|
|
3651
|
+
setScope(allowed, exclusions = []) {
|
|
3652
|
+
applyMainAgentScope(this.state, allowed, exclusions);
|
|
1920
3653
|
}
|
|
1921
3654
|
enqueueUserInput(text) {
|
|
1922
|
-
this.userInputQueue.
|
|
1923
|
-
this.emitQueueUpdated();
|
|
3655
|
+
enqueueMainAgentUserInput(this.userInputQueue, this.events, text);
|
|
1924
3656
|
}
|
|
1925
3657
|
dequeueLastUserInput() {
|
|
1926
|
-
|
|
1927
|
-
this.emitQueueUpdated();
|
|
1928
|
-
return value;
|
|
3658
|
+
return dequeueMainAgentUserInput(this.userInputQueue, this.events);
|
|
1929
3659
|
}
|
|
1930
3660
|
hasPendingUserInput() {
|
|
1931
3661
|
return this.userInputQueue.hasPending();
|
|
1932
3662
|
}
|
|
1933
|
-
getPendingUserInputCount() {
|
|
1934
|
-
return this.userInputQueue.pendingCount();
|
|
1935
|
-
}
|
|
1936
3663
|
getPendingUserInputPreview() {
|
|
1937
|
-
return this.userInputQueue
|
|
1938
|
-
}
|
|
1939
|
-
emitQueueUpdated() {
|
|
1940
|
-
this.events.emit({
|
|
1941
|
-
type: EVENT_TYPES.QUEUE_UPDATED,
|
|
1942
|
-
timestamp: Date.now(),
|
|
1943
|
-
data: {
|
|
1944
|
-
count: this.getPendingUserInputCount(),
|
|
1945
|
-
preview: this.getPendingUserInputPreview()
|
|
1946
|
-
}
|
|
1947
|
-
});
|
|
3664
|
+
return getMainAgentPendingUserInputPreview(this.userInputQueue);
|
|
1948
3665
|
}
|
|
1949
3666
|
};
|
|
1950
3667
|
|
|
1951
|
-
// src/engine/
|
|
3668
|
+
// src/engine/agent-runtime.ts
|
|
1952
3669
|
import fs from "fs";
|
|
1953
|
-
import
|
|
1954
|
-
var
|
|
1955
|
-
static build(
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
return new MainAgent(state, events, toolRegistry, approvalGate, scopeGuard);
|
|
3670
|
+
import path from "path";
|
|
3671
|
+
var AgentRuntime = class _AgentRuntime {
|
|
3672
|
+
static build(options) {
|
|
3673
|
+
_AgentRuntime.setupWorkspace(getWorkspaceConfig());
|
|
3674
|
+
return new MainAgent(options);
|
|
1959
3675
|
}
|
|
1960
3676
|
static setupWorkspace(workspaceCfg) {
|
|
1961
3677
|
const directories = workspaceCfg?.directories ?? {};
|
|
1962
|
-
for (const
|
|
3678
|
+
for (const dirPath of Object.values(directories)) {
|
|
1963
3679
|
if (dirPath === "DEPRECATED") continue;
|
|
1964
|
-
const resolved =
|
|
3680
|
+
const resolved = path.resolve(process.cwd(), dirPath);
|
|
1965
3681
|
if (!fs.existsSync(resolved)) {
|
|
1966
3682
|
fs.mkdirSync(resolved, { recursive: true });
|
|
1967
3683
|
}
|
|
@@ -1970,11 +3686,48 @@ var YamlRuntime = class _YamlRuntime {
|
|
|
1970
3686
|
}
|
|
1971
3687
|
};
|
|
1972
3688
|
|
|
3689
|
+
// src/platform/tui/cli/command-runtime.ts
|
|
3690
|
+
import chalk from "chalk";
|
|
3691
|
+
function ignoreCleanupFailure() {
|
|
3692
|
+
}
|
|
3693
|
+
async function cleanupCliProcesses() {
|
|
3694
|
+
await cleanupAllProcesses().catch(ignoreCleanupFailure);
|
|
3695
|
+
}
|
|
3696
|
+
function printAutoApproveWarning(options = {}) {
|
|
3697
|
+
if (options.includeHeader) {
|
|
3698
|
+
console.log(chalk.hex(HEX.red)("[!] WARNING: Running with --dangerously-skip-permissions"));
|
|
3699
|
+
}
|
|
3700
|
+
console.log(chalk.hex(HEX.red)("[!] Strict approval mode will be bypassed by enabling tool auto-approve.\n"));
|
|
3701
|
+
}
|
|
3702
|
+
function registerShutdownSignals(shutdown) {
|
|
3703
|
+
process.on("SIGINT", () => shutdown(EXIT_CODES.SIGINT));
|
|
3704
|
+
process.on("SIGTERM", () => shutdown(EXIT_CODES.SIGTERM));
|
|
3705
|
+
}
|
|
3706
|
+
async function writeJsonReport(outputPath, result) {
|
|
3707
|
+
const fs2 = await import("fs/promises");
|
|
3708
|
+
const { dirname } = await import("path");
|
|
3709
|
+
const outputDir = dirname(outputPath);
|
|
3710
|
+
if (outputDir !== ".") {
|
|
3711
|
+
await fs2.mkdir(outputDir, { recursive: true }).catch(ignoreCleanupFailure);
|
|
3712
|
+
}
|
|
3713
|
+
await fs2.writeFile(outputPath, JSON.stringify({ result }, null, 2));
|
|
3714
|
+
}
|
|
3715
|
+
|
|
3716
|
+
// src/platform/tui/cli/interactive-runtime.tsx
|
|
3717
|
+
import React16 from "react";
|
|
3718
|
+
import { render } from "ink";
|
|
3719
|
+
|
|
3720
|
+
// src/platform/tui/app.tsx
|
|
3721
|
+
import { Box as Box18 } from "ink";
|
|
3722
|
+
|
|
3723
|
+
// src/platform/tui/hooks/useAgent.ts
|
|
3724
|
+
import { useState as useState6, useEffect as useEffect2, useCallback as useCallback6, useRef as useRef6 } from "react";
|
|
3725
|
+
|
|
1973
3726
|
// src/agents/factory.ts
|
|
1974
|
-
function createMainAgent(
|
|
3727
|
+
function createMainAgent(autoApproveTools = false) {
|
|
1975
3728
|
const state = new SharedState();
|
|
1976
3729
|
const events = new AgentEventEmitter();
|
|
1977
|
-
const approvalGate = new ApprovalGate(
|
|
3730
|
+
const approvalGate = new ApprovalGate(autoApproveTools);
|
|
1978
3731
|
const scopeGuard = new ScopeGuard(state);
|
|
1979
3732
|
const toolRegistry = new CategorizedToolRegistry(
|
|
1980
3733
|
state,
|
|
@@ -1982,11 +3735,11 @@ function createMainAgent(shouldAutoApprove = false) {
|
|
|
1982
3735
|
approvalGate,
|
|
1983
3736
|
events
|
|
1984
3737
|
);
|
|
1985
|
-
return
|
|
3738
|
+
return AgentRuntime.build({ state, events, toolRegistry, approvalGate, scopeGuard });
|
|
1986
3739
|
}
|
|
1987
3740
|
|
|
1988
3741
|
// src/platform/tui/hooks/useAgentState.ts
|
|
1989
|
-
import { useState, useRef, useCallback } from "react";
|
|
3742
|
+
import { useState as useState5, useRef as useRef4, useCallback as useCallback5 } from "react";
|
|
1990
3743
|
|
|
1991
3744
|
// src/platform/tui/constants/limits.ts
|
|
1992
3745
|
var TUI_DISPLAY_LIMITS = {
|
|
@@ -2054,27 +3807,6 @@ var TUI_DISPLAY_LIMITS = {
|
|
|
2054
3807
|
maxStoppedProcesses: 3
|
|
2055
3808
|
};
|
|
2056
3809
|
|
|
2057
|
-
// src/platform/tui/types.ts
|
|
2058
|
-
var DEFAULT_LIVE_PROGRESS = {
|
|
2059
|
-
mode: "idle",
|
|
2060
|
-
stage: "",
|
|
2061
|
-
detail: "",
|
|
2062
|
-
activeTool: "",
|
|
2063
|
-
reasoningPreview: "",
|
|
2064
|
-
responsePreview: ""
|
|
2065
|
-
};
|
|
2066
|
-
var DEFAULT_FOOTER_DISPLAY_CONFIG = {
|
|
2067
|
-
showTarget: true,
|
|
2068
|
-
showCounts: true,
|
|
2069
|
-
showHints: true,
|
|
2070
|
-
showQueue: true,
|
|
2071
|
-
showWindow: true,
|
|
2072
|
-
showTurn: true,
|
|
2073
|
-
showContext: true,
|
|
2074
|
-
showTokens: true,
|
|
2075
|
-
showRuntime: true
|
|
2076
|
-
};
|
|
2077
|
-
|
|
2078
3810
|
// src/platform/tui/utils/format.ts
|
|
2079
3811
|
function truncate(text, width) {
|
|
2080
3812
|
if (width <= 0) return "";
|
|
@@ -2126,6 +3858,47 @@ var formatInlineStatus = () => {
|
|
|
2126
3858
|
return JSON.stringify(statusData);
|
|
2127
3859
|
};
|
|
2128
3860
|
|
|
3861
|
+
// src/platform/tui/hooks/useAgentState/useMessages.ts
|
|
3862
|
+
import { useState, useCallback } from "react";
|
|
3863
|
+
function useMessages() {
|
|
3864
|
+
const [messages, setMessages] = useState([]);
|
|
3865
|
+
const addMessage = useCallback((type, content) => {
|
|
3866
|
+
const id = generateId();
|
|
3867
|
+
setMessages((prev) => {
|
|
3868
|
+
const next = [...prev, { id, type, content, timestamp: /* @__PURE__ */ new Date() }];
|
|
3869
|
+
if (next.length > TUI_DISPLAY_LIMITS.MAX_MESSAGES) {
|
|
3870
|
+
return next.slice(next.length - TUI_DISPLAY_LIMITS.MAX_MESSAGES);
|
|
3871
|
+
}
|
|
3872
|
+
return next;
|
|
3873
|
+
});
|
|
3874
|
+
}, []);
|
|
3875
|
+
return { messages, setMessages, addMessage };
|
|
3876
|
+
}
|
|
3877
|
+
|
|
3878
|
+
// src/platform/tui/hooks/useAgentState/useTimer.ts
|
|
3879
|
+
import { useState as useState2, useRef, useCallback as useCallback2 } from "react";
|
|
3880
|
+
function useTimer() {
|
|
3881
|
+
const [elapsedTime, setElapsedTime] = useState2(0);
|
|
3882
|
+
const startTimeRef = useRef(0);
|
|
3883
|
+
const manageTimer = useCallback2((action) => {
|
|
3884
|
+
if (action === "start") {
|
|
3885
|
+
startTimeRef.current = Date.now();
|
|
3886
|
+
setElapsedTime(0);
|
|
3887
|
+
} else {
|
|
3888
|
+
const startedAt = startTimeRef.current;
|
|
3889
|
+
if (startedAt > 0) {
|
|
3890
|
+
setElapsedTime(Math.max(0, Math.floor((Date.now() - startedAt) / 1e3)));
|
|
3891
|
+
} else {
|
|
3892
|
+
setElapsedTime(0);
|
|
3893
|
+
}
|
|
3894
|
+
}
|
|
3895
|
+
}, []);
|
|
3896
|
+
return { elapsedTime, setElapsedTime, manageTimer };
|
|
3897
|
+
}
|
|
3898
|
+
|
|
3899
|
+
// src/platform/tui/hooks/useAgentState/useInputBroker.ts
|
|
3900
|
+
import { useState as useState3, useCallback as useCallback3, useRef as useRef2 } from "react";
|
|
3901
|
+
|
|
2129
3902
|
// src/platform/tui/utils/input-request-broker.ts
|
|
2130
3903
|
function createInputRequestBrokerState() {
|
|
2131
3904
|
return {
|
|
@@ -2175,7 +3948,61 @@ function clearAllInputRequests(state) {
|
|
|
2175
3948
|
return pending;
|
|
2176
3949
|
}
|
|
2177
3950
|
|
|
2178
|
-
// src/platform/tui/hooks/useAgentState.ts
|
|
3951
|
+
// src/platform/tui/hooks/useAgentState/useInputBroker.ts
|
|
3952
|
+
function useInputBroker() {
|
|
3953
|
+
const [inputBroker, setInputBroker] = useState3(
|
|
3954
|
+
createInputRequestBrokerState()
|
|
3955
|
+
);
|
|
3956
|
+
const inputBrokerRef = useRef2(inputBroker);
|
|
3957
|
+
inputBrokerRef.current = inputBroker;
|
|
3958
|
+
const enqueueInputRequest2 = useCallback3((request) => {
|
|
3959
|
+
setInputBroker((prev) => enqueueInputRequest(prev, request));
|
|
3960
|
+
}, []);
|
|
3961
|
+
const settleActiveInputRequest = useCallback3((value) => {
|
|
3962
|
+
const current = inputBrokerRef.current;
|
|
3963
|
+
if (current.active.status !== "active") return;
|
|
3964
|
+
current.active.resolve(value);
|
|
3965
|
+
setInputBroker(resolveActiveInputRequest(current).nextState);
|
|
3966
|
+
}, []);
|
|
3967
|
+
const cancelAllInputRequests = useCallback3(() => {
|
|
3968
|
+
const current = inputBrokerRef.current;
|
|
3969
|
+
const pending = clearAllInputRequests(current);
|
|
3970
|
+
for (const request of pending) request.resolve(null);
|
|
3971
|
+
setInputBroker(createInputRequestBrokerState());
|
|
3972
|
+
}, []);
|
|
3973
|
+
return {
|
|
3974
|
+
inputBroker,
|
|
3975
|
+
enqueueInputRequest: enqueueInputRequest2,
|
|
3976
|
+
settleActiveInputRequest,
|
|
3977
|
+
cancelAllInputRequests
|
|
3978
|
+
};
|
|
3979
|
+
}
|
|
3980
|
+
|
|
3981
|
+
// src/platform/tui/hooks/useAgentState/useLiveProgress.ts
|
|
3982
|
+
import { useState as useState4, useCallback as useCallback4 } from "react";
|
|
3983
|
+
|
|
3984
|
+
// src/platform/tui/types.ts
|
|
3985
|
+
var DEFAULT_LIVE_PROGRESS = {
|
|
3986
|
+
mode: "idle",
|
|
3987
|
+
stage: "",
|
|
3988
|
+
detail: "",
|
|
3989
|
+
activeTool: "",
|
|
3990
|
+
reasoningPreview: "",
|
|
3991
|
+
responsePreview: ""
|
|
3992
|
+
};
|
|
3993
|
+
var DEFAULT_FOOTER_DISPLAY_CONFIG = {
|
|
3994
|
+
showTarget: true,
|
|
3995
|
+
showCounts: true,
|
|
3996
|
+
showHints: true,
|
|
3997
|
+
showQueue: true,
|
|
3998
|
+
showWindow: true,
|
|
3999
|
+
showTurn: true,
|
|
4000
|
+
showContext: true,
|
|
4001
|
+
showTokens: true,
|
|
4002
|
+
showRuntime: true
|
|
4003
|
+
};
|
|
4004
|
+
|
|
4005
|
+
// src/platform/tui/hooks/useAgentState/useLiveProgress.ts
|
|
2179
4006
|
function sanitizeLiveProgress(progress) {
|
|
2180
4007
|
return {
|
|
2181
4008
|
mode: progress.mode,
|
|
@@ -2186,87 +4013,57 @@ function sanitizeLiveProgress(progress) {
|
|
|
2186
4013
|
responsePreview: truncate(progress.responsePreview, TUI_DISPLAY_LIMITS.LIVE_PROGRESS_TEXT_MAX)
|
|
2187
4014
|
};
|
|
2188
4015
|
}
|
|
2189
|
-
|
|
2190
|
-
const [
|
|
2191
|
-
const
|
|
2192
|
-
const [currentStatusState, setCurrentStatusState] = useState("");
|
|
2193
|
-
const [elapsedTime, setElapsedTime] = useState(0);
|
|
2194
|
-
const [retryState, setRetryState] = useState({ status: "idle" });
|
|
2195
|
-
const [currentTokens, setCurrentTokens] = useState(0);
|
|
2196
|
-
const [inputBroker, setInputBroker] = useState(
|
|
2197
|
-
createInputRequestBrokerState()
|
|
2198
|
-
);
|
|
2199
|
-
const [stats, setStats] = useState({ phase: DEFAULTS.INIT_PHASE, targets: 0, findings: 0, todo: 0, targetLabel: "" });
|
|
2200
|
-
const [turnCount, setTurnCount] = useState(0);
|
|
2201
|
-
const [liveProgressState, setLiveProgressState] = useState(DEFAULT_LIVE_PROGRESS);
|
|
2202
|
-
const startTimeRef = useRef(0);
|
|
2203
|
-
const inputBrokerRef = useRef(createInputRequestBrokerState());
|
|
2204
|
-
inputBrokerRef.current = inputBroker;
|
|
2205
|
-
const retryCountdownRef = useRef(null);
|
|
2206
|
-
const retryCountRef = useRef(0);
|
|
2207
|
-
const tokenAccumRef = useRef(0);
|
|
2208
|
-
const lastStepTokensRef = useRef(0);
|
|
2209
|
-
const toolStartedAtRef = useRef(0);
|
|
2210
|
-
const addMessage = useCallback((type, content) => {
|
|
2211
|
-
const id = generateId();
|
|
2212
|
-
setMessages((prev) => {
|
|
2213
|
-
const next = [...prev, { id, type, content, timestamp: /* @__PURE__ */ new Date() }];
|
|
2214
|
-
if (next.length > TUI_DISPLAY_LIMITS.MAX_MESSAGES) {
|
|
2215
|
-
return next.slice(next.length - TUI_DISPLAY_LIMITS.MAX_MESSAGES);
|
|
2216
|
-
}
|
|
2217
|
-
return next;
|
|
2218
|
-
});
|
|
2219
|
-
}, []);
|
|
2220
|
-
const setCurrentStatus = useCallback((value) => {
|
|
2221
|
-
setCurrentStatusState(truncate(value, TUI_DISPLAY_LIMITS.STATUS_TEXT_MAX));
|
|
2222
|
-
}, []);
|
|
2223
|
-
const setLiveProgress = useCallback((value) => {
|
|
4016
|
+
function useLiveProgress() {
|
|
4017
|
+
const [liveProgress, setLiveProgressState] = useState4(DEFAULT_LIVE_PROGRESS);
|
|
4018
|
+
const setLiveProgress = useCallback4((value) => {
|
|
2224
4019
|
setLiveProgressState((prev) => {
|
|
2225
4020
|
const next = typeof value === "function" ? value(prev) : value;
|
|
2226
4021
|
return sanitizeLiveProgress(next);
|
|
2227
4022
|
});
|
|
2228
4023
|
}, []);
|
|
2229
|
-
|
|
4024
|
+
return { liveProgress, setLiveProgress };
|
|
4025
|
+
}
|
|
4026
|
+
|
|
4027
|
+
// src/platform/tui/hooks/useAgentState.ts
|
|
4028
|
+
var useAgentState = (isTyping = false) => {
|
|
4029
|
+
const { messages, setMessages, addMessage } = useMessages();
|
|
4030
|
+
const { elapsedTime, setElapsedTime, manageTimer } = useTimer();
|
|
4031
|
+
const { inputBroker, enqueueInputRequest: enqueueInputRequest2, settleActiveInputRequest, cancelAllInputRequests } = useInputBroker();
|
|
4032
|
+
const { liveProgress, setLiveProgress } = useLiveProgress();
|
|
4033
|
+
const [isProcessing, setIsProcessing] = useState5(false);
|
|
4034
|
+
const [currentStatusState, setCurrentStatusState] = useState5("");
|
|
4035
|
+
const [retryState, setRetryState] = useState5({ status: "idle" });
|
|
4036
|
+
const [currentTokens, setCurrentTokens] = useState5(0);
|
|
4037
|
+
const [stats, setStats] = useState5({ phase: DEFAULTS.INIT_PHASE, targets: 0, findings: 0, todo: 0, targetLabel: "" });
|
|
4038
|
+
const [turnCount, setTurnCount] = useState5(0);
|
|
4039
|
+
const retryCountdownRef = useRef4(null);
|
|
4040
|
+
const retryCountRef = useRef4(0);
|
|
4041
|
+
const tokenAccumRef = useRef4(0);
|
|
4042
|
+
const lastStepTokensRef = useRef4(0);
|
|
4043
|
+
const toolStartedAtRef = useRef4(0);
|
|
4044
|
+
const setCurrentStatus = useCallback5((value) => {
|
|
4045
|
+
setCurrentStatusState(truncate(value, TUI_DISPLAY_LIMITS.STATUS_TEXT_MAX));
|
|
4046
|
+
}, []);
|
|
4047
|
+
const resetCumulativeCounters = useCallback5(() => {
|
|
2230
4048
|
setCurrentTokens(0);
|
|
2231
4049
|
retryCountRef.current = 0;
|
|
2232
4050
|
tokenAccumRef.current = 0;
|
|
2233
4051
|
lastStepTokensRef.current = 0;
|
|
2234
|
-
setLiveProgress(
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
} else {
|
|
2245
|
-
setElapsedTime(0);
|
|
2246
|
-
}
|
|
2247
|
-
}
|
|
2248
|
-
}, []);
|
|
2249
|
-
const clearAllTimers = useCallback(() => {
|
|
4052
|
+
setLiveProgress({
|
|
4053
|
+
mode: "thinking",
|
|
4054
|
+
stage: "",
|
|
4055
|
+
detail: "",
|
|
4056
|
+
activeTool: "",
|
|
4057
|
+
reasoningPreview: "",
|
|
4058
|
+
responsePreview: ""
|
|
4059
|
+
});
|
|
4060
|
+
}, [setLiveProgress]);
|
|
4061
|
+
const clearAllTimers = useCallback5(() => {
|
|
2250
4062
|
if (retryCountdownRef.current) {
|
|
2251
4063
|
clearInterval(retryCountdownRef.current);
|
|
2252
4064
|
retryCountdownRef.current = null;
|
|
2253
4065
|
}
|
|
2254
4066
|
}, []);
|
|
2255
|
-
const enqueueInputRequest2 = useCallback((request) => {
|
|
2256
|
-
setInputBroker((prev) => enqueueInputRequest(prev, request));
|
|
2257
|
-
}, []);
|
|
2258
|
-
const settleActiveInputRequest = useCallback((value) => {
|
|
2259
|
-
const current = inputBrokerRef.current;
|
|
2260
|
-
if (current.active.status !== "active") return;
|
|
2261
|
-
current.active.resolve(value);
|
|
2262
|
-
setInputBroker(resolveActiveInputRequest(current).nextState);
|
|
2263
|
-
}, []);
|
|
2264
|
-
const cancelAllInputRequests = useCallback(() => {
|
|
2265
|
-
const current = inputBrokerRef.current;
|
|
2266
|
-
const pending = clearAllInputRequests(current);
|
|
2267
|
-
for (const request of pending) request.resolve(null);
|
|
2268
|
-
setInputBroker(createInputRequestBrokerState());
|
|
2269
|
-
}, []);
|
|
2270
4067
|
return {
|
|
2271
4068
|
// State
|
|
2272
4069
|
messages,
|
|
@@ -2286,7 +4083,7 @@ var useAgentState = (isTyping = false) => {
|
|
|
2286
4083
|
setStats,
|
|
2287
4084
|
turnCount,
|
|
2288
4085
|
setTurnCount,
|
|
2289
|
-
liveProgress
|
|
4086
|
+
liveProgress,
|
|
2290
4087
|
setLiveProgress,
|
|
2291
4088
|
// Refs (external consumers only)
|
|
2292
4089
|
retryCountdownRef,
|
|
@@ -2294,8 +4091,6 @@ var useAgentState = (isTyping = false) => {
|
|
|
2294
4091
|
tokenAccumRef,
|
|
2295
4092
|
lastStepTokensRef,
|
|
2296
4093
|
toolStartedAtRef,
|
|
2297
|
-
// WHY timerRef excluded: internal to manageTimer/clearAllTimers only,
|
|
2298
|
-
// no external consumer exists.
|
|
2299
4094
|
// Helpers
|
|
2300
4095
|
addMessage,
|
|
2301
4096
|
resetCumulativeCounters,
|
|
@@ -2308,7 +4103,7 @@ var useAgentState = (isTyping = false) => {
|
|
|
2308
4103
|
};
|
|
2309
4104
|
|
|
2310
4105
|
// src/platform/tui/hooks/useAgentEvents/index.ts
|
|
2311
|
-
import { useEffect, useRef as
|
|
4106
|
+
import { useEffect, useRef as useRef5 } from "react";
|
|
2312
4107
|
|
|
2313
4108
|
// src/platform/tui/constants/styles.ts
|
|
2314
4109
|
var MESSAGE_STYLES = {
|
|
@@ -2967,7 +4762,7 @@ var useAgentEvents = (agent, eventsRef, state, isTyping = false) => {
|
|
|
2967
4762
|
lastStepTokensRef,
|
|
2968
4763
|
setCurrentTokens
|
|
2969
4764
|
} = state;
|
|
2970
|
-
const reasoningBufferRef =
|
|
4765
|
+
const reasoningBufferRef = useRef5("");
|
|
2971
4766
|
const getTargetLabel = () => {
|
|
2972
4767
|
const firstTarget = agent.getState().getAllTargets()[0];
|
|
2973
4768
|
return firstTarget?.hostname || firstTarget?.ip || "";
|
|
@@ -3076,8 +4871,8 @@ function trimQueuedMessages(queue, maxLength, maxItems) {
|
|
|
3076
4871
|
return trimmed.slice(trimmed.length - maxItems);
|
|
3077
4872
|
}
|
|
3078
4873
|
var useAgent = (shouldAutoApprove, target, isTyping = false) => {
|
|
3079
|
-
const [agent] =
|
|
3080
|
-
const eventsRef =
|
|
4874
|
+
const [agent] = useState6(() => createMainAgent(shouldAutoApprove));
|
|
4875
|
+
const eventsRef = useRef6(agent.getEventEmitter());
|
|
3081
4876
|
const state = useAgentState(isTyping);
|
|
3082
4877
|
const {
|
|
3083
4878
|
messages,
|
|
@@ -3102,9 +4897,9 @@ var useAgent = (shouldAutoApprove, target, isTyping = false) => {
|
|
|
3102
4897
|
settleActiveInputRequest,
|
|
3103
4898
|
cancelAllInputRequests
|
|
3104
4899
|
} = state;
|
|
3105
|
-
const [messageQueue, setMessageQueue] =
|
|
3106
|
-
const messageQueueLengthRef =
|
|
3107
|
-
const setMessageQueueSafe =
|
|
4900
|
+
const [messageQueue, setMessageQueue] = useState6([]);
|
|
4901
|
+
const messageQueueLengthRef = useRef6(0);
|
|
4902
|
+
const setMessageQueueSafe = useCallback6((value) => {
|
|
3108
4903
|
setMessageQueue((prev) => {
|
|
3109
4904
|
const next = typeof value === "function" ? value(prev) : value;
|
|
3110
4905
|
return trimQueuedMessages(
|
|
@@ -3119,8 +4914,7 @@ var useAgent = (shouldAutoApprove, target, isTyping = false) => {
|
|
|
3119
4914
|
}, [messageQueue.length]);
|
|
3120
4915
|
useEffect2(() => {
|
|
3121
4916
|
if (target) {
|
|
3122
|
-
agent.
|
|
3123
|
-
agent.setScope([target]);
|
|
4917
|
+
agent.configureTarget(target);
|
|
3124
4918
|
}
|
|
3125
4919
|
}, [agent, target]);
|
|
3126
4920
|
useEffect2(() => {
|
|
@@ -3141,8 +4935,8 @@ var useAgent = (shouldAutoApprove, target, isTyping = false) => {
|
|
|
3141
4935
|
};
|
|
3142
4936
|
}, [agent, addMessage, setMessageQueueSafe]);
|
|
3143
4937
|
useAgentEvents(agent, eventsRef, state, isTyping);
|
|
3144
|
-
const abortedRef =
|
|
3145
|
-
const executeTask =
|
|
4938
|
+
const abortedRef = useRef6(false);
|
|
4939
|
+
const executeTask = useCallback6(async (task) => {
|
|
3146
4940
|
let currentTask = task;
|
|
3147
4941
|
while (true) {
|
|
3148
4942
|
abortedRef.current = false;
|
|
@@ -3188,7 +4982,7 @@ var useAgent = (shouldAutoApprove, target, isTyping = false) => {
|
|
|
3188
4982
|
}
|
|
3189
4983
|
}
|
|
3190
4984
|
}, [agent, addMessage, manageTimer, resetCumulativeCounters, setIsProcessing, setCurrentStatus, setTurnCount]);
|
|
3191
|
-
const abort =
|
|
4985
|
+
const abort = useCallback6(() => {
|
|
3192
4986
|
abortedRef.current = true;
|
|
3193
4987
|
agent.abort();
|
|
3194
4988
|
setIsProcessing(false);
|
|
@@ -3196,21 +4990,21 @@ var useAgent = (shouldAutoApprove, target, isTyping = false) => {
|
|
|
3196
4990
|
setCurrentStatus("");
|
|
3197
4991
|
addMessage("system", UI_MESSAGES.INTERRUPTED);
|
|
3198
4992
|
}, [agent, addMessage, manageTimer, setIsProcessing, setCurrentStatus]);
|
|
3199
|
-
const recallQueuedInput =
|
|
4993
|
+
const recallQueuedInput = useCallback6(() => {
|
|
3200
4994
|
const recalled = agent.dequeueLastUserInput();
|
|
3201
4995
|
if (!recalled) return null;
|
|
3202
4996
|
return recalled;
|
|
3203
4997
|
}, [agent]);
|
|
3204
|
-
const inputRequestRef =
|
|
4998
|
+
const inputRequestRef = useRef6(inputRequest);
|
|
3205
4999
|
inputRequestRef.current = inputRequest;
|
|
3206
|
-
const cancelInputRequest =
|
|
5000
|
+
const cancelInputRequest = useCallback6(() => {
|
|
3207
5001
|
const ir = inputRequestRef.current;
|
|
3208
5002
|
if (ir.status === "active") {
|
|
3209
5003
|
settleActiveInputRequest(null);
|
|
3210
5004
|
addMessage("system", UI_MESSAGES.INPUT_CANCELLED);
|
|
3211
5005
|
}
|
|
3212
5006
|
}, [settleActiveInputRequest, addMessage]);
|
|
3213
|
-
const refreshStats =
|
|
5007
|
+
const refreshStats = useCallback6(() => {
|
|
3214
5008
|
const s = agent.getState();
|
|
3215
5009
|
setStats({
|
|
3216
5010
|
phase: s.getPhase() || PHASES.RECON,
|
|
@@ -3248,7 +5042,7 @@ var useAgent = (shouldAutoApprove, target, isTyping = false) => {
|
|
|
3248
5042
|
};
|
|
3249
5043
|
|
|
3250
5044
|
// src/platform/tui/hooks/commands/index.ts
|
|
3251
|
-
import { useCallback as
|
|
5045
|
+
import { useCallback as useCallback7, useMemo } from "react";
|
|
3252
5046
|
|
|
3253
5047
|
// src/platform/tui/constants/commands.ts
|
|
3254
5048
|
var COMMAND_DEFINITIONS = [
|
|
@@ -3346,8 +5140,7 @@ var createTargetCommands = (ctx) => {
|
|
|
3346
5140
|
await ctx.agent.resetSession();
|
|
3347
5141
|
ctx.addMessage("system", UI_STATUS_MESSAGES.TARGET_CLEARED);
|
|
3348
5142
|
}
|
|
3349
|
-
ctx.agent.
|
|
3350
|
-
ctx.agent.setScope([args[0]]);
|
|
5143
|
+
ctx.agent.configureTarget(args[0]);
|
|
3351
5144
|
ctx.addMessage("system", `${UI_STATUS_MESSAGES.TARGET_SET_PREFIX}${args[0]}`);
|
|
3352
5145
|
};
|
|
3353
5146
|
const handleStart = async (args) => {
|
|
@@ -3357,7 +5150,7 @@ var createTargetCommands = (ctx) => {
|
|
|
3357
5150
|
}
|
|
3358
5151
|
if (!ctx.autoApproveModeRef.current) {
|
|
3359
5152
|
ctx.setAutoApproveMode(true);
|
|
3360
|
-
ctx.agent.
|
|
5153
|
+
ctx.agent.setToolAutoApprove(true);
|
|
3361
5154
|
ctx.addMessage("system", UI_STATUS_MESSAGES.AUTONOMOUS_MODE_ENABLED);
|
|
3362
5155
|
}
|
|
3363
5156
|
ctx.addMessage("system", UI_STATUS_MESSAGES.STARTING_PENTEST);
|
|
@@ -3757,7 +5550,7 @@ var createToggleCommands = (ctx) => ({
|
|
|
3757
5550
|
[UI_COMMANDS.AUTO]: () => {
|
|
3758
5551
|
ctx.setAutoApproveMode((prev) => {
|
|
3759
5552
|
const newVal = !prev;
|
|
3760
|
-
ctx.agent.
|
|
5553
|
+
ctx.agent.setToolAutoApprove(newVal);
|
|
3761
5554
|
ctx.addMessage("system", newVal ? UI_STATUS_MESSAGES.AUTO_APPROVE_ON : UI_STATUS_MESSAGES.AUTO_APPROVE_OFF);
|
|
3762
5555
|
return newVal;
|
|
3763
5556
|
});
|
|
@@ -3826,7 +5619,7 @@ var useCommands = (props) => {
|
|
|
3826
5619
|
...createDisplayCommands(ctx),
|
|
3827
5620
|
...createToggleCommands(ctx)
|
|
3828
5621
|
}), [ctx]);
|
|
3829
|
-
const handleCommand =
|
|
5622
|
+
const handleCommand = useCallback7(async (cmd, args) => {
|
|
3830
5623
|
const handler = handlers[cmd];
|
|
3831
5624
|
if (handler) {
|
|
3832
5625
|
await handler(args);
|
|
@@ -3838,15 +5631,15 @@ var useCommands = (props) => {
|
|
|
3838
5631
|
};
|
|
3839
5632
|
|
|
3840
5633
|
// src/platform/tui/hooks/useKeyboardShortcuts.ts
|
|
3841
|
-
import { useCallback as
|
|
5634
|
+
import { useCallback as useCallback10, useEffect as useEffect4 } from "react";
|
|
3842
5635
|
import { useInput } from "ink";
|
|
3843
5636
|
|
|
3844
5637
|
// src/platform/tui/hooks/keyboard/useDoubleTap.ts
|
|
3845
|
-
import { useRef as
|
|
5638
|
+
import { useRef as useRef7, useCallback as useCallback8, useEffect as useEffect3 } from "react";
|
|
3846
5639
|
var useDoubleTap = ({ onFirstTap, onSecondTap, windowMs }) => {
|
|
3847
|
-
const timerRef =
|
|
3848
|
-
const pressedRef =
|
|
3849
|
-
const trigger =
|
|
5640
|
+
const timerRef = useRef7(null);
|
|
5641
|
+
const pressedRef = useRef7(false);
|
|
5642
|
+
const trigger = useCallback8(() => {
|
|
3850
5643
|
if (pressedRef.current) {
|
|
3851
5644
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
3852
5645
|
pressedRef.current = false;
|
|
@@ -3861,7 +5654,7 @@ var useDoubleTap = ({ onFirstTap, onSecondTap, windowMs }) => {
|
|
|
3861
5654
|
timerRef.current = null;
|
|
3862
5655
|
}, windowMs);
|
|
3863
5656
|
}, [onFirstTap, onSecondTap, windowMs]);
|
|
3864
|
-
const reset =
|
|
5657
|
+
const reset = useCallback8(() => {
|
|
3865
5658
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
3866
5659
|
pressedRef.current = false;
|
|
3867
5660
|
timerRef.current = null;
|
|
@@ -3894,7 +5687,7 @@ var useExitHandler = ({
|
|
|
3894
5687
|
};
|
|
3895
5688
|
|
|
3896
5689
|
// src/platform/tui/hooks/keyboard/useEscHandler.ts
|
|
3897
|
-
import { useCallback as
|
|
5690
|
+
import { useCallback as useCallback9 } from "react";
|
|
3898
5691
|
var useEscHandler = ({
|
|
3899
5692
|
cancelInputRequest,
|
|
3900
5693
|
abort,
|
|
@@ -3917,7 +5710,7 @@ var useEscHandler = ({
|
|
|
3917
5710
|
},
|
|
3918
5711
|
windowMs
|
|
3919
5712
|
});
|
|
3920
|
-
const handleEsc =
|
|
5713
|
+
const handleEsc = useCallback9(() => {
|
|
3921
5714
|
if (inputRequestRef.current.status === "active") {
|
|
3922
5715
|
cancelInputRequest();
|
|
3923
5716
|
return;
|
|
@@ -3963,7 +5756,7 @@ var useKeyboardShortcuts = ({
|
|
|
3963
5756
|
inputRef,
|
|
3964
5757
|
windowMs: DOUBLE_TAP_WINDOW
|
|
3965
5758
|
});
|
|
3966
|
-
useInput(
|
|
5759
|
+
useInput(useCallback10((ch, key) => {
|
|
3967
5760
|
if (isModalOpenRef.current) return;
|
|
3968
5761
|
if (key.escape) handleEsc();
|
|
3969
5762
|
if (key.ctrl && ch === "c") handleCtrlC();
|
|
@@ -3981,7 +5774,7 @@ var useKeyboardShortcuts = ({
|
|
|
3981
5774
|
};
|
|
3982
5775
|
|
|
3983
5776
|
// src/platform/tui/hooks/useAppLogic.ts
|
|
3984
|
-
import { useState as
|
|
5777
|
+
import { useState as useState7, useCallback as useCallback11, useRef as useRef8 } from "react";
|
|
3985
5778
|
import { useApp } from "ink";
|
|
3986
5779
|
function sanitizeInput(value) {
|
|
3987
5780
|
return value.replace(/(?:\x1b)?\[<\d+;\d+;\d+[mM]/g, "").replace(/\x1b\[[0-9;]*[A-Za-z]/g, "").replace(/[\x00-\x08\x0B-\x1F\x7F-\x9F]/g, "").trim();
|
|
@@ -4005,11 +5798,11 @@ function parseSlashCommandInput(value) {
|
|
|
4005
5798
|
var useAppLogic = ({ autoApprove = false, target }) => {
|
|
4006
5799
|
const { exit } = useApp();
|
|
4007
5800
|
const { columns: terminalWidth, rows: terminalHeight } = useTerminalSize();
|
|
4008
|
-
const [input, setInput] =
|
|
4009
|
-
const [secretInput, setSecretInput] =
|
|
4010
|
-
const [autoApproveMode, setAutoApproveMode] =
|
|
4011
|
-
const [modal, setModal] =
|
|
4012
|
-
const [footerConfig, setFooterConfig] =
|
|
5801
|
+
const [input, setInput] = useState7("");
|
|
5802
|
+
const [secretInput, setSecretInput] = useState7("");
|
|
5803
|
+
const [autoApproveMode, setAutoApproveMode] = useState7(autoApprove);
|
|
5804
|
+
const [modal, setModal] = useState7({ type: null, content: "", scrollOffset: 0 });
|
|
5805
|
+
const [footerConfig, setFooterConfig] = useState7(DEFAULT_FOOTER_DISPLAY_CONFIG);
|
|
4013
5806
|
const isTyping = input.length > 0 || secretInput.length > 0;
|
|
4014
5807
|
const {
|
|
4015
5808
|
agent,
|
|
@@ -4035,26 +5828,26 @@ var useAppLogic = ({ autoApprove = false, target }) => {
|
|
|
4035
5828
|
addMessage,
|
|
4036
5829
|
refreshStats
|
|
4037
5830
|
} = useAgent(autoApproveMode, target, isTyping);
|
|
4038
|
-
const isProcessingRef =
|
|
5831
|
+
const isProcessingRef = useRef8(isProcessing);
|
|
4039
5832
|
isProcessingRef.current = isProcessing;
|
|
4040
|
-
const autoApproveModeRef =
|
|
5833
|
+
const autoApproveModeRef = useRef8(autoApproveMode);
|
|
4041
5834
|
autoApproveModeRef.current = autoApproveMode;
|
|
4042
|
-
const inputRequestRef =
|
|
5835
|
+
const inputRequestRef = useRef8(inputRequest);
|
|
4043
5836
|
inputRequestRef.current = inputRequest;
|
|
4044
|
-
const isModalOpenRef =
|
|
5837
|
+
const isModalOpenRef = useRef8(!!modal.type);
|
|
4045
5838
|
isModalOpenRef.current = !!modal.type;
|
|
4046
|
-
const inputRef =
|
|
5839
|
+
const inputRef = useRef8(input);
|
|
4047
5840
|
inputRef.current = input;
|
|
4048
|
-
const clearInput =
|
|
5841
|
+
const clearInput = useCallback11(() => {
|
|
4049
5842
|
setInput("");
|
|
4050
5843
|
}, []);
|
|
4051
|
-
const showModal =
|
|
5844
|
+
const showModal = useCallback11((type, content) => {
|
|
4052
5845
|
setModal({ type, content, scrollOffset: 0 });
|
|
4053
5846
|
}, []);
|
|
4054
|
-
const closeModal =
|
|
5847
|
+
const closeModal = useCallback11(() => {
|
|
4055
5848
|
setModal({ type: null, content: "", scrollOffset: 0 });
|
|
4056
5849
|
}, []);
|
|
4057
|
-
const handleModalScroll =
|
|
5850
|
+
const handleModalScroll = useCallback11((delta) => {
|
|
4058
5851
|
setModal((prev) => {
|
|
4059
5852
|
const lines = prev.content.split("\n");
|
|
4060
5853
|
const maxHeight = terminalHeight - TUI_DISPLAY_LIMITS.MODAL_CHROME_HEIGHT;
|
|
@@ -4063,12 +5856,19 @@ var useAppLogic = ({ autoApprove = false, target }) => {
|
|
|
4063
5856
|
return { ...prev, scrollOffset: newOffset };
|
|
4064
5857
|
});
|
|
4065
5858
|
}, [terminalHeight]);
|
|
4066
|
-
const handleExit =
|
|
5859
|
+
const handleExit = useCallback11(() => {
|
|
4067
5860
|
cancelAllInputRequests();
|
|
4068
5861
|
cleanupAllProcesses().catch(() => {
|
|
4069
5862
|
});
|
|
4070
5863
|
exit();
|
|
4071
|
-
setTimeout(() =>
|
|
5864
|
+
setTimeout(() => {
|
|
5865
|
+
const reqs = getGlobalRequestCount();
|
|
5866
|
+
const usage = getGlobalTokenUsage();
|
|
5867
|
+
console.log(`
|
|
5868
|
+
[Session Summary] Total LLM Requests: ${reqs} | Tokens In: ${usage.input_tokens} | Tokens Out: ${usage.output_tokens}
|
|
5869
|
+
`);
|
|
5870
|
+
process.exit(0);
|
|
5871
|
+
}, DISPLAY_LIMITS.EXIT_DELAY);
|
|
4072
5872
|
}, [exit, cancelAllInputRequests]);
|
|
4073
5873
|
const { handleCommand } = useCommands({
|
|
4074
5874
|
agent,
|
|
@@ -4084,10 +5884,10 @@ var useAppLogic = ({ autoApprove = false, target }) => {
|
|
|
4084
5884
|
isProcessingRef,
|
|
4085
5885
|
autoApproveModeRef
|
|
4086
5886
|
});
|
|
4087
|
-
const handleRecallQueuedInput =
|
|
5887
|
+
const handleRecallQueuedInput = useCallback11(() => {
|
|
4088
5888
|
return recallQueuedInput();
|
|
4089
5889
|
}, [recallQueuedInput]);
|
|
4090
|
-
const handleSecretSubmit =
|
|
5890
|
+
const handleSecretSubmit = useCallback11((value) => {
|
|
4091
5891
|
const ir = inputRequestRef.current;
|
|
4092
5892
|
if (ir.status !== "active") return;
|
|
4093
5893
|
const sanitized = sanitizeInput(value).slice(0, TUI_DISPLAY_LIMITS.MAX_INPUT_CHARS);
|
|
@@ -4096,7 +5896,7 @@ var useAppLogic = ({ autoApprove = false, target }) => {
|
|
|
4096
5896
|
settleActiveInputRequest(sanitized);
|
|
4097
5897
|
setSecretInput("");
|
|
4098
5898
|
}, [addMessage, settleActiveInputRequest]);
|
|
4099
|
-
const handleSubmit =
|
|
5899
|
+
const handleSubmit = useCallback11(async (value) => {
|
|
4100
5900
|
const trimmed = sanitizeInput(value);
|
|
4101
5901
|
if (!trimmed) return;
|
|
4102
5902
|
setInput("");
|
|
@@ -4159,11 +5959,11 @@ var useAppLogic = ({ autoApprove = false, target }) => {
|
|
|
4159
5959
|
};
|
|
4160
5960
|
|
|
4161
5961
|
// src/platform/tui/hooks/useTerminalSize.ts
|
|
4162
|
-
import { useState as
|
|
5962
|
+
import { useState as useState8, useEffect as useEffect5 } from "react";
|
|
4163
5963
|
import { useStdout } from "ink";
|
|
4164
5964
|
function useTerminalSize() {
|
|
4165
5965
|
const { stdout } = useStdout();
|
|
4166
|
-
const [size, setSize] =
|
|
5966
|
+
const [size, setSize] = useState8({
|
|
4167
5967
|
columns: stdout?.columns ?? TUI_DISPLAY_LIMITS.TERMINAL_DEFAULT_COLUMNS,
|
|
4168
5968
|
rows: stdout?.rows ?? TUI_DISPLAY_LIMITS.TERMINAL_DEFAULT_ROWS
|
|
4169
5969
|
});
|
|
@@ -4185,12 +5985,12 @@ function useTerminalSize() {
|
|
|
4185
5985
|
}
|
|
4186
5986
|
|
|
4187
5987
|
// src/platform/tui/hooks/useAnimationTick.tsx
|
|
4188
|
-
import { createContext, useContext, useState as
|
|
5988
|
+
import { createContext, useContext, useState as useState9, useEffect as useEffect6 } from "react";
|
|
4189
5989
|
import { jsx } from "react/jsx-runtime";
|
|
4190
5990
|
var ANIM_TICK_MS = 100;
|
|
4191
5991
|
var AnimationContext = createContext(0);
|
|
4192
5992
|
var AnimationProvider = ({ children }) => {
|
|
4193
|
-
const [tick, setTick] =
|
|
5993
|
+
const [tick, setTick] = useState9(0);
|
|
4194
5994
|
useEffect6(() => {
|
|
4195
5995
|
const timer = setInterval(() => {
|
|
4196
5996
|
setTick((t) => t + 1);
|
|
@@ -4780,10 +6580,10 @@ var MessageList = memo6(({
|
|
|
4780
6580
|
import { memo as memo9 } from "react";
|
|
4781
6581
|
|
|
4782
6582
|
// src/platform/tui/hooks/useStatusTimer.ts
|
|
4783
|
-
import { useState as
|
|
6583
|
+
import { useState as useState10, useEffect as useEffect7, useRef as useRef9 } from "react";
|
|
4784
6584
|
var useStatusTimer = (currentStatus, isProcessing) => {
|
|
4785
|
-
const [statusElapsed, setStatusElapsed] =
|
|
4786
|
-
const lastStatusRef =
|
|
6585
|
+
const [statusElapsed, setStatusElapsed] = useState10(0);
|
|
6586
|
+
const lastStatusRef = useRef9("");
|
|
4787
6587
|
useEffect7(() => {
|
|
4788
6588
|
if (!isProcessing || !currentStatus) {
|
|
4789
6589
|
lastStatusRef.current = "";
|
|
@@ -5038,7 +6838,7 @@ var StatusDisplay = memo9(({
|
|
|
5038
6838
|
});
|
|
5039
6839
|
|
|
5040
6840
|
// src/platform/tui/components/ChatInput.tsx
|
|
5041
|
-
import { useMemo as useMemo4, useCallback as
|
|
6841
|
+
import { useMemo as useMemo4, useCallback as useCallback12, useRef as useRef11, memo as memo10, useState as useState12 } from "react";
|
|
5042
6842
|
import { Box as Box14, Text as Text15 } from "ink";
|
|
5043
6843
|
|
|
5044
6844
|
// src/platform/tui/components/input/AutocompletePreview.tsx
|
|
@@ -5081,7 +6881,7 @@ var AutocompletePreview = ({
|
|
|
5081
6881
|
import { Box as Box12, Text as Text13 } from "ink";
|
|
5082
6882
|
|
|
5083
6883
|
// src/platform/tui/components/input/SimpleTextInput.tsx
|
|
5084
|
-
import { useState as
|
|
6884
|
+
import { useState as useState11, useEffect as useEffect8, useRef as useRef10, useMemo as useMemo3 } from "react";
|
|
5085
6885
|
import { Box as Box11, Text as Text12, useInput as useInput2, useStdout as useStdout2 } from "ink";
|
|
5086
6886
|
import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
5087
6887
|
var MOUSE_SEQUENCE_RE = /(?:\x1b)?\[<\d+;\d+;\d+[mM]/g;
|
|
@@ -5109,7 +6909,7 @@ var SimpleTextInput = ({
|
|
|
5109
6909
|
onSpecialKey
|
|
5110
6910
|
}) => {
|
|
5111
6911
|
const chars = useMemo3(() => Array.from(value || ""), [value]);
|
|
5112
|
-
const [cursor, setCursor] =
|
|
6912
|
+
const [cursor, setCursor] = useState11(chars.length);
|
|
5113
6913
|
const { write } = useStdout2();
|
|
5114
6914
|
useEffect8(() => {
|
|
5115
6915
|
write?.("\x1B[?25h");
|
|
@@ -5117,12 +6917,12 @@ var SimpleTextInput = ({
|
|
|
5117
6917
|
write?.("\x1B[?25l");
|
|
5118
6918
|
};
|
|
5119
6919
|
}, [write]);
|
|
5120
|
-
const lastInputRef =
|
|
5121
|
-
const lastInputTimeRef =
|
|
5122
|
-
const valueRef =
|
|
6920
|
+
const lastInputRef = useRef10("");
|
|
6921
|
+
const lastInputTimeRef = useRef10(0);
|
|
6922
|
+
const valueRef = useRef10(value || "");
|
|
5123
6923
|
valueRef.current = value || "";
|
|
5124
|
-
const mouseFragmentRef =
|
|
5125
|
-
const cursorRef =
|
|
6924
|
+
const mouseFragmentRef = useRef10("");
|
|
6925
|
+
const cursorRef = useRef10(cursor);
|
|
5126
6926
|
cursorRef.current = cursor;
|
|
5127
6927
|
useEffect8(() => {
|
|
5128
6928
|
setCursor((prev) => {
|
|
@@ -5313,40 +7113,40 @@ var ChatInput = memo10(({
|
|
|
5313
7113
|
}, [isSlashMode, partialCmd, hasArgs]);
|
|
5314
7114
|
const showPreview = isSlashMode && !hasArgs && suggestions.length > 0;
|
|
5315
7115
|
const showCommandHelp = isSlashMode && !hasArgs && suggestions.length === 0 && value.length > 1;
|
|
5316
|
-
const [selectedIdx, setSelectedIdx] =
|
|
7116
|
+
const [selectedIdx, setSelectedIdx] = useState12(0);
|
|
5317
7117
|
const clampedIdx = Math.min(selectedIdx, Math.max(0, suggestions.length - 1));
|
|
5318
|
-
const selectedIdxRef =
|
|
7118
|
+
const selectedIdxRef = useRef11(clampedIdx);
|
|
5319
7119
|
selectedIdxRef.current = clampedIdx;
|
|
5320
|
-
const suggestionsRef =
|
|
7120
|
+
const suggestionsRef = useRef11(suggestions);
|
|
5321
7121
|
suggestionsRef.current = suggestions;
|
|
5322
|
-
const isSlashModeRef =
|
|
7122
|
+
const isSlashModeRef = useRef11(isSlashMode);
|
|
5323
7123
|
isSlashModeRef.current = isSlashMode;
|
|
5324
|
-
const hasArgsRef =
|
|
7124
|
+
const hasArgsRef = useRef11(hasArgs);
|
|
5325
7125
|
hasArgsRef.current = hasArgs;
|
|
5326
|
-
const showPreviewRef =
|
|
7126
|
+
const showPreviewRef = useRef11(showPreview);
|
|
5327
7127
|
showPreviewRef.current = showPreview;
|
|
5328
|
-
const inputRequestRef =
|
|
7128
|
+
const inputRequestRef = useRef11(inputRequest);
|
|
5329
7129
|
inputRequestRef.current = inputRequest;
|
|
5330
|
-
const onChangeRef =
|
|
7130
|
+
const onChangeRef = useRef11(onChange);
|
|
5331
7131
|
onChangeRef.current = onChange;
|
|
5332
|
-
const onRecallQueuedInputRef =
|
|
7132
|
+
const onRecallQueuedInputRef = useRef11(onRecallQueuedInput);
|
|
5333
7133
|
onRecallQueuedInputRef.current = onRecallQueuedInput;
|
|
5334
|
-
const queuedCountRef =
|
|
7134
|
+
const queuedCountRef = useRef11(queuedCount);
|
|
5335
7135
|
queuedCountRef.current = queuedCount;
|
|
5336
|
-
const latestValueRef =
|
|
7136
|
+
const latestValueRef = useRef11(value);
|
|
5337
7137
|
latestValueRef.current = value;
|
|
5338
|
-
const handleLocalChange =
|
|
7138
|
+
const handleLocalChange = useCallback12((newVal) => {
|
|
5339
7139
|
latestValueRef.current = newVal;
|
|
5340
7140
|
onChange(newVal);
|
|
5341
7141
|
}, [onChange]);
|
|
5342
|
-
const latestSecretRef =
|
|
7142
|
+
const latestSecretRef = useRef11(secretInput);
|
|
5343
7143
|
latestSecretRef.current = secretInput;
|
|
5344
|
-
const handleSecretChange =
|
|
7144
|
+
const handleSecretChange = useCallback12((newVal) => {
|
|
5345
7145
|
latestSecretRef.current = newVal;
|
|
5346
7146
|
setSecretInput(newVal);
|
|
5347
7147
|
}, [setSecretInput]);
|
|
5348
|
-
const [inputKey, setInputKey] =
|
|
5349
|
-
const completeCommand =
|
|
7148
|
+
const [inputKey, setInputKey] = useState12(0);
|
|
7149
|
+
const completeCommand = useCallback12((idx) => {
|
|
5350
7150
|
const sug = suggestionsRef.current;
|
|
5351
7151
|
if (!sug.length) return;
|
|
5352
7152
|
const best = sug[Math.min(idx, sug.length - 1)];
|
|
@@ -5356,9 +7156,9 @@ var ChatInput = memo10(({
|
|
|
5356
7156
|
setSelectedIdx(0);
|
|
5357
7157
|
setInputKey((k) => k + 1);
|
|
5358
7158
|
}, []);
|
|
5359
|
-
const onSubmitRef =
|
|
7159
|
+
const onSubmitRef = useRef11(onSubmit);
|
|
5360
7160
|
onSubmitRef.current = onSubmit;
|
|
5361
|
-
const wrappedOnSubmit =
|
|
7161
|
+
const wrappedOnSubmit = useCallback12((_staleVal) => {
|
|
5362
7162
|
const finalValue = latestValueRef.current;
|
|
5363
7163
|
if (showPreviewRef.current) {
|
|
5364
7164
|
const sug = suggestionsRef.current;
|
|
@@ -5378,12 +7178,12 @@ var ChatInput = memo10(({
|
|
|
5378
7178
|
}
|
|
5379
7179
|
onSubmitRef.current(finalValue);
|
|
5380
7180
|
}, [completeCommand]);
|
|
5381
|
-
const onSecretSubmitRef =
|
|
7181
|
+
const onSecretSubmitRef = useRef11(onSecretSubmit);
|
|
5382
7182
|
onSecretSubmitRef.current = onSecretSubmit;
|
|
5383
|
-
const wrappedSecretSubmit =
|
|
7183
|
+
const wrappedSecretSubmit = useCallback12((_staleVal) => {
|
|
5384
7184
|
onSecretSubmitRef.current(latestSecretRef.current);
|
|
5385
7185
|
}, []);
|
|
5386
|
-
const handleSpecialKey =
|
|
7186
|
+
const handleSpecialKey = useCallback12((key) => {
|
|
5387
7187
|
if (inputRequestRef.current.status === "active") return false;
|
|
5388
7188
|
const sug = suggestionsRef.current;
|
|
5389
7189
|
const visible = showPreviewRef.current;
|
|
@@ -5454,7 +7254,7 @@ var ChatInput = memo10(({
|
|
|
5454
7254
|
});
|
|
5455
7255
|
|
|
5456
7256
|
// src/platform/tui/components/footer.tsx
|
|
5457
|
-
import { memo as memo11, useState as
|
|
7257
|
+
import { memo as memo11, useState as useState13, useEffect as useEffect9 } from "react";
|
|
5458
7258
|
import { Box as Box15, Text as Text16 } from "ink";
|
|
5459
7259
|
import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
5460
7260
|
var SECS_PER_HOUR = 3600;
|
|
@@ -5490,7 +7290,7 @@ var Footer = memo11(({
|
|
|
5490
7290
|
const { columns } = useTerminalSize();
|
|
5491
7291
|
const ctxPct = totalTokens > 0 ? Math.round(totalTokens / LLM_LIMITS.streamMaxTokens * 100) : 0;
|
|
5492
7292
|
const canShowHint = !isProcessing && inputText.length === 0;
|
|
5493
|
-
const [showHint, setShowHint] =
|
|
7293
|
+
const [showHint, setShowHint] = useState13(false);
|
|
5494
7294
|
useEffect9(() => {
|
|
5495
7295
|
if (!canShowHint) {
|
|
5496
7296
|
setShowHint(false);
|
|
@@ -5533,7 +7333,7 @@ var Footer = memo11(({
|
|
|
5533
7333
|
var footer_default = Footer;
|
|
5534
7334
|
|
|
5535
7335
|
// src/platform/tui/components/Modal.tsx
|
|
5536
|
-
import { useMemo as useMemo5, memo as memo12, useCallback as
|
|
7336
|
+
import { useMemo as useMemo5, memo as memo12, useCallback as useCallback13, useRef as useRef12 } from "react";
|
|
5537
7337
|
import { Box as Box16, Text as Text17, useInput as useInput3 } from "ink";
|
|
5538
7338
|
import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
5539
7339
|
var MODAL_TITLES = {
|
|
@@ -5548,9 +7348,9 @@ var Modal = memo12(({
|
|
|
5548
7348
|
onScroll,
|
|
5549
7349
|
onClose
|
|
5550
7350
|
}) => {
|
|
5551
|
-
const onScrollRef =
|
|
7351
|
+
const onScrollRef = useRef12(onScroll);
|
|
5552
7352
|
onScrollRef.current = onScroll;
|
|
5553
|
-
const onCloseRef =
|
|
7353
|
+
const onCloseRef = useRef12(onClose);
|
|
5554
7354
|
onCloseRef.current = onClose;
|
|
5555
7355
|
const { columns, rows } = useTerminalSize();
|
|
5556
7356
|
const terminalHeight = rows;
|
|
@@ -5562,7 +7362,7 @@ var Modal = memo12(({
|
|
|
5562
7362
|
() => lines.slice(scrollOffset, scrollOffset + maxHeight),
|
|
5563
7363
|
[lines, scrollOffset, maxHeight]
|
|
5564
7364
|
);
|
|
5565
|
-
useInput3(
|
|
7365
|
+
useInput3(useCallback13((input, key) => {
|
|
5566
7366
|
if (key.escape || input === "q") {
|
|
5567
7367
|
onCloseRef.current();
|
|
5568
7368
|
} else if (key.upArrow || input === "k") {
|
|
@@ -5828,7 +7628,7 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
5828
7628
|
var app_default = App;
|
|
5829
7629
|
|
|
5830
7630
|
// src/platform/tui/components/SplashScreen.tsx
|
|
5831
|
-
import { useEffect as useEffect11, useState as
|
|
7631
|
+
import { useEffect as useEffect11, useState as useState14 } from "react";
|
|
5832
7632
|
import { Box as Box19, Text as Text19 } from "ink";
|
|
5833
7633
|
|
|
5834
7634
|
// src/platform/tui/components/CoinFrames.ts
|
|
@@ -6527,7 +8327,7 @@ var SplashScreen = ({
|
|
|
6527
8327
|
}) => {
|
|
6528
8328
|
const { columns, rows } = useTerminalSize();
|
|
6529
8329
|
const tick = useAnimationTick();
|
|
6530
|
-
const [elapsed, setElapsed] =
|
|
8330
|
+
const [elapsed, setElapsed] = useState14(0);
|
|
6531
8331
|
useEffect11(() => {
|
|
6532
8332
|
const start = Date.now();
|
|
6533
8333
|
const interval = setInterval(() => {
|
|
@@ -6559,9 +8359,9 @@ var SplashScreen = ({
|
|
|
6559
8359
|
);
|
|
6560
8360
|
};
|
|
6561
8361
|
|
|
6562
|
-
// src/platform/tui/cli/
|
|
8362
|
+
// src/platform/tui/cli/interactive-runtime.tsx
|
|
6563
8363
|
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
6564
|
-
var
|
|
8364
|
+
var InteractiveRoot = ({ autoApprove, target }) => {
|
|
6565
8365
|
const [showSplash, setShowSplash] = React16.useState(true);
|
|
6566
8366
|
if (showSplash) {
|
|
6567
8367
|
return /* @__PURE__ */ jsx24(SplashScreen, { durationMs: 3e3, onComplete: () => setShowSplash(false) });
|
|
@@ -6569,61 +8369,62 @@ var Root = ({ skipPermissions, target }) => {
|
|
|
6569
8369
|
return /* @__PURE__ */ jsx24(
|
|
6570
8370
|
app_default,
|
|
6571
8371
|
{
|
|
6572
|
-
autoApprove
|
|
8372
|
+
autoApprove,
|
|
6573
8373
|
target
|
|
6574
8374
|
}
|
|
6575
8375
|
);
|
|
6576
8376
|
};
|
|
6577
|
-
async function
|
|
6578
|
-
const { dangerouslySkipPermissions: skipPermissions = false, target } = options;
|
|
6579
|
-
console.clear();
|
|
6580
|
-
if (skipPermissions) {
|
|
6581
|
-
console.log(chalk.hex(HEX.red)("[!] WARNING: Running with --dangerously-skip-permissions"));
|
|
6582
|
-
console.log(chalk.hex(HEX.red)("[!] All tool executions will be auto-approved!\n"));
|
|
6583
|
-
}
|
|
8377
|
+
async function renderInteractiveApp(props) {
|
|
6584
8378
|
const { waitUntilExit } = render(
|
|
6585
|
-
/* @__PURE__ */ jsx24(AnimationProvider, { children: /* @__PURE__ */ jsx24(
|
|
8379
|
+
/* @__PURE__ */ jsx24(AnimationProvider, { children: /* @__PURE__ */ jsx24(InteractiveRoot, { ...props }) })
|
|
6586
8380
|
);
|
|
6587
8381
|
await waitUntilExit();
|
|
6588
8382
|
}
|
|
6589
8383
|
|
|
8384
|
+
// src/platform/tui/cli/commands/interactive.tsx
|
|
8385
|
+
async function interactiveAction(options) {
|
|
8386
|
+
const { dangerouslySkipPermissions: autoApproveTools = false, target } = options;
|
|
8387
|
+
console.clear();
|
|
8388
|
+
if (autoApproveTools) {
|
|
8389
|
+
printAutoApproveWarning({ includeHeader: true });
|
|
8390
|
+
}
|
|
8391
|
+
await renderInteractiveApp({
|
|
8392
|
+
autoApprove: autoApproveTools,
|
|
8393
|
+
target
|
|
8394
|
+
});
|
|
8395
|
+
}
|
|
8396
|
+
|
|
6590
8397
|
// src/platform/tui/cli/commands/run.ts
|
|
6591
8398
|
import chalk2 from "chalk";
|
|
6592
8399
|
async function runAction(objective, options) {
|
|
6593
|
-
const
|
|
6594
|
-
if (
|
|
6595
|
-
|
|
8400
|
+
const autoApproveTools = options.dangerouslySkipPermissions || false;
|
|
8401
|
+
if (autoApproveTools) {
|
|
8402
|
+
printAutoApproveWarning({ includeHeader: true });
|
|
6596
8403
|
}
|
|
6597
8404
|
console.log(chalk2.hex(HEX.primary)(`[target] Objective: ${objective}
|
|
6598
8405
|
`));
|
|
6599
|
-
const agent = createMainAgent(
|
|
6600
|
-
if (
|
|
6601
|
-
agent.
|
|
8406
|
+
const agent = createMainAgent(autoApproveTools, options.maxSteps);
|
|
8407
|
+
if (autoApproveTools) {
|
|
8408
|
+
agent.setToolAutoApprove(true);
|
|
6602
8409
|
}
|
|
6603
8410
|
if (options.target) {
|
|
6604
|
-
agent.
|
|
6605
|
-
agent.setScope([options.target]);
|
|
8411
|
+
agent.configureTarget(options.target);
|
|
6606
8412
|
}
|
|
6607
8413
|
const shutdown = async (exitCode = 0) => {
|
|
6608
|
-
await
|
|
6609
|
-
|
|
8414
|
+
await cleanupCliProcesses();
|
|
8415
|
+
const reqs = getGlobalRequestCount();
|
|
8416
|
+
const usage = getGlobalTokenUsage();
|
|
8417
|
+
console.log(chalk2.hex(HEX.gray)(`
|
|
8418
|
+
[Session Summary] Requests: ${reqs} | In: ${usage.input_tokens} | Out: ${usage.output_tokens}`));
|
|
6610
8419
|
process.exit(exitCode);
|
|
6611
8420
|
};
|
|
6612
|
-
|
|
6613
|
-
process.on("SIGTERM", () => shutdown(EXIT_CODES.SIGTERM));
|
|
8421
|
+
registerShutdownSignals(shutdown);
|
|
6614
8422
|
try {
|
|
6615
8423
|
const result = await agent.execute(objective);
|
|
6616
8424
|
console.log(chalk2.hex(HEX.gray)("\n[+] Assessment complete!\n"));
|
|
6617
8425
|
console.log(result);
|
|
6618
8426
|
if (options.output) {
|
|
6619
|
-
|
|
6620
|
-
const { dirname } = await import("path");
|
|
6621
|
-
const outputDir = dirname(options.output);
|
|
6622
|
-
if (outputDir && outputDir !== ".") {
|
|
6623
|
-
await fs2.mkdir(outputDir, { recursive: true }).catch(() => {
|
|
6624
|
-
});
|
|
6625
|
-
}
|
|
6626
|
-
await fs2.writeFile(options.output, JSON.stringify({ result }, null, 2));
|
|
8427
|
+
await writeJsonReport(options.output, result);
|
|
6627
8428
|
console.log(chalk2.hex(HEX.primary)(`
|
|
6628
8429
|
[+] Report saved to: ${options.output}`));
|
|
6629
8430
|
}
|
|
@@ -6639,20 +8440,20 @@ async function runAction(objective, options) {
|
|
|
6639
8440
|
// src/platform/tui/cli/commands/scan.ts
|
|
6640
8441
|
import chalk3 from "chalk";
|
|
6641
8442
|
async function scanAction(target, options) {
|
|
6642
|
-
const
|
|
8443
|
+
const autoApproveTools = options.dangerouslySkipPermissions || false;
|
|
6643
8444
|
console.log(chalk3.hex(HEX.primary)(`
|
|
6644
8445
|
[scan] Target: ${target} (${options.scanType})
|
|
6645
8446
|
`));
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
8447
|
+
if (autoApproveTools) {
|
|
8448
|
+
printAutoApproveWarning();
|
|
8449
|
+
}
|
|
8450
|
+
const agent = createMainAgent(autoApproveTools);
|
|
8451
|
+
agent.configureTarget(target);
|
|
6649
8452
|
const shutdown = async (exitCode = 0) => {
|
|
6650
|
-
await
|
|
6651
|
-
});
|
|
8453
|
+
await cleanupCliProcesses();
|
|
6652
8454
|
process.exit(exitCode);
|
|
6653
8455
|
};
|
|
6654
|
-
|
|
6655
|
-
process.on("SIGTERM", () => shutdown(EXIT_CODES.SIGTERM));
|
|
8456
|
+
registerShutdownSignals(shutdown);
|
|
6656
8457
|
try {
|
|
6657
8458
|
await agent.execute(`Perform a ${options.scanType} scan on ${target}${options.ports ? ` focusing on ports ${options.ports}` : ""}. Analyze the results and identify potential vulnerabilities.`);
|
|
6658
8459
|
console.log(chalk3.hex(HEX.gray)("[+] Scan complete!"));
|
|
@@ -6674,7 +8475,7 @@ ${chalk4.hex(HEX.primary)(APP_NAME + " - Autonomous Penetration Testing AI")}
|
|
|
6674
8475
|
|
|
6675
8476
|
${chalk4.hex(HEX.gray)("$ pentesting")} Start interactive mode
|
|
6676
8477
|
${chalk4.hex(HEX.gray)("$ pentesting -t 192.168.1.1")} Start with target
|
|
6677
|
-
${chalk4.hex(HEX.gray)("$ pentesting --dangerously-skip-permissions")}
|
|
8478
|
+
${chalk4.hex(HEX.gray)("$ pentesting --dangerously-skip-permissions")} Enable tool auto-approve in strict mode
|
|
6678
8479
|
|
|
6679
8480
|
${chalk4.hex(HEX.yellow)("Commands:")}
|
|
6680
8481
|
|
|
@@ -6684,7 +8485,7 @@ ${chalk4.hex(HEX.yellow)("Commands:")}
|
|
|
6684
8485
|
|
|
6685
8486
|
${chalk4.hex(HEX.yellow)("Options:")}
|
|
6686
8487
|
|
|
6687
|
-
${chalk4.hex(HEX.primary)("--dangerously-skip-permissions")}
|
|
8488
|
+
${chalk4.hex(HEX.primary)("--dangerously-skip-permissions")} Bypass strict approval mode by auto-approving tools
|
|
6688
8489
|
${chalk4.hex(HEX.primary)("-t, --target <ip>")} Set target
|
|
6689
8490
|
${chalk4.hex(HEX.primary)("-o, --output <file>")} Save results to file
|
|
6690
8491
|
|
|
@@ -6715,10 +8516,20 @@ ${chalk4.hex(HEX.yellow)("Environment:")}
|
|
|
6715
8516
|
`);
|
|
6716
8517
|
}
|
|
6717
8518
|
|
|
8519
|
+
// src/platform/tui/cli/args.ts
|
|
8520
|
+
import { InvalidArgumentError } from "commander";
|
|
8521
|
+
function parsePositiveInt(value) {
|
|
8522
|
+
const parsed = Number.parseInt(value, 10);
|
|
8523
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
8524
|
+
throw new InvalidArgumentError("max-steps must be a positive integer");
|
|
8525
|
+
}
|
|
8526
|
+
return parsed;
|
|
8527
|
+
}
|
|
8528
|
+
|
|
6718
8529
|
// src/platform/tui/cli/program.ts
|
|
6719
8530
|
function createProgram() {
|
|
6720
8531
|
const program2 = new Command();
|
|
6721
|
-
program2.name("pentesting").version(APP_VERSION).description(APP_DESCRIPTION).option("--dangerously-skip-permissions", "
|
|
8532
|
+
program2.name("pentesting").version(APP_VERSION).description(APP_DESCRIPTION).option("--dangerously-skip-permissions", "Enable auto-approve for tool execution in strict approval mode (dangerous!)").option("-t, --target <target>", "Set initial target");
|
|
6722
8533
|
program2.command("interactive", { isDefault: true }).alias("i").description("Start interactive TUI mode").action(async () => {
|
|
6723
8534
|
const opts = program2.opts();
|
|
6724
8535
|
await interactiveAction({
|
|
@@ -6726,7 +8537,7 @@ function createProgram() {
|
|
|
6726
8537
|
target: opts.target
|
|
6727
8538
|
});
|
|
6728
8539
|
});
|
|
6729
|
-
program2.command("run <objective>").alias("r").description("Run a single objective and exit").option("-o, --output <file>", "Output file for results").option("--max-steps <n>", "Maximum number of steps",
|
|
8540
|
+
program2.command("run <objective>").alias("r").description("Run a single objective and exit").option("-o, --output <file>", "Output file for results").option("--max-steps <n>", "Maximum number of steps", parsePositiveInt, CLI_DEFAULT.MAX_STEPS).action(async (objective, options) => {
|
|
6730
8541
|
const opts = program2.opts();
|
|
6731
8542
|
await runAction(objective, {
|
|
6732
8543
|
...options,
|