pentesting 0.73.3 → 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-JEFUBDZE.js → agent-tool-MP274HWD.js} +3 -3
- package/dist/{chunk-BKWCGMSV.js → chunk-3KWJPPYB.js} +46 -11
- package/dist/{chunk-UB7RW6LM.js → chunk-7E2VUIFU.js} +194 -63
- package/dist/chunk-I52SWXYV.js +1122 -0
- package/dist/main.js +1635 -1005
- package/dist/{persistence-2WKQHGOL.js → persistence-BNVN3WW6.js} +2 -2
- package/dist/{process-registry-QIW7ZIUT.js → process-registry-BI7BKPHN.js} +1 -1
- package/package.json +3 -4
- package/dist/chunk-GLO6TOJN.js +0 -333
package/dist/main.js
CHANGED
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
rotateTurnRecords,
|
|
43
43
|
setCurrentTurn,
|
|
44
44
|
writePolicyDocument
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-3KWJPPYB.js";
|
|
46
46
|
import {
|
|
47
47
|
AGENT_ROLES,
|
|
48
48
|
APP_DESCRIPTION,
|
|
@@ -75,20 +75,22 @@ import {
|
|
|
75
75
|
generateId,
|
|
76
76
|
getErrorMessage,
|
|
77
77
|
getProcessOutput,
|
|
78
|
+
getWorkspaceConfig,
|
|
78
79
|
initDebugLogger,
|
|
79
80
|
listBackgroundProcesses,
|
|
80
81
|
loadState,
|
|
82
|
+
readRuntimeAssetFile,
|
|
83
|
+
resolveRuntimeAssetPath,
|
|
81
84
|
saveState,
|
|
82
85
|
setActiveSessionRuntime,
|
|
83
86
|
setTorEnabled,
|
|
84
87
|
snapshotToPrompt
|
|
85
|
-
} from "./chunk-
|
|
88
|
+
} from "./chunk-7E2VUIFU.js";
|
|
86
89
|
import {
|
|
87
90
|
EXIT_CODES,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
} from "./chunk-GLO6TOJN.js";
|
|
91
|
+
getOptionalRuntimeSection,
|
|
92
|
+
getRequiredRuntimeSection
|
|
93
|
+
} from "./chunk-I52SWXYV.js";
|
|
92
94
|
|
|
93
95
|
// src/platform/tui/main.tsx
|
|
94
96
|
import chalk5 from "chalk";
|
|
@@ -294,17 +296,6 @@ var CLI_SCAN_TYPES = Object.freeze([
|
|
|
294
296
|
CLI_SCAN_TYPE.VULN
|
|
295
297
|
]);
|
|
296
298
|
|
|
297
|
-
// src/platform/tui/cli/commands/interactive.tsx
|
|
298
|
-
import React16 from "react";
|
|
299
|
-
import { render } from "ink";
|
|
300
|
-
import chalk from "chalk";
|
|
301
|
-
|
|
302
|
-
// src/platform/tui/app.tsx
|
|
303
|
-
import { Box as Box18 } from "ink";
|
|
304
|
-
|
|
305
|
-
// src/platform/tui/hooks/useAgent.ts
|
|
306
|
-
import { useState as useState6, useEffect as useEffect2, useCallback as useCallback6, useRef as useRef6 } from "react";
|
|
307
|
-
|
|
308
299
|
// src/engine/events.ts
|
|
309
300
|
var AgentEventEmitter = class {
|
|
310
301
|
listeners = /* @__PURE__ */ new Map();
|
|
@@ -591,13 +582,25 @@ var ApprovalGate = class {
|
|
|
591
582
|
return this.shouldAutoApprove;
|
|
592
583
|
}
|
|
593
584
|
/**
|
|
594
|
-
*
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
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.
|
|
598
601
|
*/
|
|
599
602
|
async request(toolCall) {
|
|
600
|
-
if (this.
|
|
603
|
+
if (this.requiresAutoApprove() && !this.shouldAutoApprove) {
|
|
601
604
|
return {
|
|
602
605
|
isApproved: false,
|
|
603
606
|
reason: AUTO_APPROVE_REQUIRED_REASON(toolCall.name)
|
|
@@ -608,34 +611,67 @@ var ApprovalGate = class {
|
|
|
608
611
|
};
|
|
609
612
|
|
|
610
613
|
// src/agents/prompt-builder/prompt-loader.ts
|
|
611
|
-
import { readFileSync as readFileSync2
|
|
614
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
612
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
|
|
613
644
|
var _promptsDir = null;
|
|
614
645
|
var _techniquesDir = null;
|
|
615
646
|
function getPromptsDir() {
|
|
616
647
|
if (!_promptsDir) {
|
|
617
|
-
const
|
|
618
|
-
_promptsDir = join(process.cwd(),
|
|
648
|
+
const baseDir = getPromptBaseDir();
|
|
649
|
+
_promptsDir = resolveRuntimeAssetPath(baseDir) ?? join(process.cwd(), baseDir);
|
|
619
650
|
}
|
|
620
651
|
return _promptsDir;
|
|
621
652
|
}
|
|
622
653
|
function getTechniquesDir() {
|
|
623
654
|
if (!_techniquesDir) {
|
|
624
|
-
const
|
|
625
|
-
_techniquesDir =
|
|
655
|
+
const techniquesDir = getTechniqueBaseDir();
|
|
656
|
+
_techniquesDir = techniquesDir ? resolveRuntimeAssetPath(techniquesDir) ?? join(process.cwd(), techniquesDir) : join(getPromptsDir(), PROMPT_PATHS.TECHNIQUES_DIR);
|
|
626
657
|
}
|
|
627
658
|
return _techniquesDir;
|
|
628
659
|
}
|
|
629
660
|
function loadPromptFile(filename) {
|
|
630
|
-
const
|
|
631
|
-
|
|
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
|
+
}
|
|
632
668
|
}
|
|
633
669
|
function loadTechniqueFile(techniqueName) {
|
|
634
670
|
const filename = techniqueName.endsWith(".md") ? techniqueName : `${techniqueName}.md`;
|
|
635
671
|
const filePath = join(getTechniquesDir(), filename);
|
|
636
672
|
try {
|
|
637
|
-
|
|
638
|
-
return readFileSync2(
|
|
673
|
+
const resolved = resolveRuntimeAssetPath(filePath) ?? filePath;
|
|
674
|
+
return readFileSync2(resolved, PROMPT_CONFIG.ENCODING);
|
|
639
675
|
} catch {
|
|
640
676
|
return "";
|
|
641
677
|
}
|
|
@@ -794,68 +830,32 @@ function buildUserContextFragment(userInput) {
|
|
|
794
830
|
return PROMPT_DEFAULTS.USER_CONTEXT(userInput);
|
|
795
831
|
}
|
|
796
832
|
|
|
797
|
-
// src/agents/prompt-builder/
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
"state.scope": (ctx) => buildScopeFragment(ctx.state),
|
|
802
|
-
"state.todo": (ctx) => buildTodoFragment(ctx.state),
|
|
803
|
-
"state.time": (ctx) => buildTimeFragment(ctx.state),
|
|
804
|
-
"state.serialized": (ctx) => buildStateFragment(ctx.state, StateSerializer.toPrompt(ctx.state)),
|
|
805
|
-
"state.workingMemory": (ctx) => buildWorkingMemoryFragment(ctx.state),
|
|
806
|
-
"state.challengeAnalysis": (ctx) => buildChallengeAnalysisFragment(ctx.state),
|
|
807
|
-
"state.attackGraph": (ctx) => buildAttackGraphFragment(ctx.state),
|
|
808
|
-
"state.episodicMemory": (ctx) => buildEpisodicMemoryFragment(ctx.state),
|
|
809
|
-
"state.dynamicTechniques": (ctx) => buildDynamicTechniquesFragment(ctx.state),
|
|
810
|
-
"state.lastReflection": (ctx) => buildLastReflectionFragment(ctx.state.lastReflection),
|
|
811
|
-
"state.targets_ports": (ctx) => buildAttackIntelligenceFragment(ctx.state),
|
|
812
|
-
// persistent memory는 별도 처리 (import 순서 문제 방지)
|
|
813
|
-
"state.persistentMemory": (ctx) => buildPersistentMemoryFragment(ctx.state)
|
|
814
|
-
};
|
|
815
|
-
async function executeLayer(layer, ctx) {
|
|
816
|
-
try {
|
|
817
|
-
switch (layer.type) {
|
|
818
|
-
case "file":
|
|
819
|
-
return executeFileLayer(layer);
|
|
820
|
-
case "file_phase_mapped":
|
|
821
|
-
return executePhaseFileLayer(ctx.phase);
|
|
822
|
-
case "files":
|
|
823
|
-
return executeFilesLayer(layer, ctx.phase);
|
|
824
|
-
case "files_phase_mapped":
|
|
825
|
-
return executePhaseFilesLayer(layer, ctx.phase);
|
|
826
|
-
case "state":
|
|
827
|
-
return executeStateLayer(layer, ctx);
|
|
828
|
-
case "static":
|
|
829
|
-
return layer.content?.trim() ?? null;
|
|
830
|
-
case "file_read":
|
|
831
|
-
return executeFileReadLayer(layer) || null;
|
|
832
|
-
case "user_input":
|
|
833
|
-
return buildUserContextFragment(ctx.userInput) || null;
|
|
834
|
-
case "memory": {
|
|
835
|
-
const key = layer.source?.replace(/^memory\./, "") || "";
|
|
836
|
-
return ctx.memory?.[key] || null;
|
|
837
|
-
}
|
|
838
|
-
default:
|
|
839
|
-
return null;
|
|
840
|
-
}
|
|
841
|
-
} catch {
|
|
842
|
-
return null;
|
|
833
|
+
// src/agents/prompt-builder/layer-wrapping.ts
|
|
834
|
+
function wrapLayerContent(template, content, tokens = {}) {
|
|
835
|
+
if (!template) {
|
|
836
|
+
return content;
|
|
843
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>`;
|
|
844
847
|
}
|
|
848
|
+
|
|
849
|
+
// src/agents/prompt-builder/file-layer-executors.ts
|
|
845
850
|
function executeFileReadLayer(layer) {
|
|
846
851
|
const source = layer.source?.trim();
|
|
847
852
|
if (!source) return null;
|
|
848
853
|
if (source.includes("{N-1}")) {
|
|
849
854
|
return buildJournalFragment() || null;
|
|
850
855
|
}
|
|
851
|
-
const
|
|
852
|
-
if (!existsSync3(absolutePath)) return null;
|
|
853
|
-
const content = readFileSync3(absolutePath, "utf-8").trim();
|
|
856
|
+
const content = readRuntimeAssetFile(source, "utf-8")?.trim();
|
|
854
857
|
if (!content) return null;
|
|
855
|
-
|
|
856
|
-
return layer.wrap.replace("{content}", content);
|
|
857
|
-
}
|
|
858
|
-
return content;
|
|
858
|
+
return wrapLayerContent(layer.wrap, content);
|
|
859
859
|
}
|
|
860
860
|
function executeFileLayer(layer) {
|
|
861
861
|
if (!layer.source) return null;
|
|
@@ -864,54 +864,149 @@ function executeFileLayer(layer) {
|
|
|
864
864
|
return loadPromptFile(filename) || null;
|
|
865
865
|
}
|
|
866
866
|
function executePhaseFileLayer(phase) {
|
|
867
|
-
const
|
|
868
|
-
const file = sources.phase_prompts?.[phase];
|
|
867
|
+
const file = getPhasePromptSource(phase);
|
|
869
868
|
if (!file) return null;
|
|
870
869
|
return loadPromptFile(file) || null;
|
|
871
870
|
}
|
|
872
871
|
function executeFilesLayer(layer, phase) {
|
|
873
|
-
const
|
|
874
|
-
const
|
|
875
|
-
const phaseFile = sources.phase_prompts?.[phase];
|
|
872
|
+
const files = getCoreKnowledgeSources();
|
|
873
|
+
const phaseFile = getPhasePromptSource(phase);
|
|
876
874
|
const filtered = layer.exclude_current_phase_file && phaseFile ? files.filter((f) => f !== phaseFile) : files;
|
|
877
|
-
const parts = filtered.map((
|
|
878
|
-
const content = loadPromptFile(
|
|
875
|
+
const parts = filtered.map((file) => {
|
|
876
|
+
const content = loadPromptFile(file);
|
|
879
877
|
if (!content) return "";
|
|
880
|
-
const label =
|
|
881
|
-
|
|
882
|
-
return layer.wrap.replace("{label}", label).replace("{content}", content);
|
|
883
|
-
}
|
|
884
|
-
return content;
|
|
878
|
+
const label = file.replace(".md", "");
|
|
879
|
+
return wrapLayerContent(layer.wrap, content, { label });
|
|
885
880
|
});
|
|
886
881
|
return parts.filter(Boolean).join("\n\n") || null;
|
|
887
882
|
}
|
|
888
883
|
function executePhaseFilesLayer(layer, phase) {
|
|
889
|
-
const
|
|
890
|
-
const files = sources.phase_techniques?.[phase] ?? [];
|
|
884
|
+
const files = getPhaseTechniqueSources(phase);
|
|
891
885
|
if (files.length === 0) return null;
|
|
892
886
|
const parts = files.map((name) => {
|
|
893
887
|
const content = loadTechniqueFile(name);
|
|
894
888
|
if (!content) return "";
|
|
895
|
-
if (layer.wrap) {
|
|
896
|
-
|
|
897
|
-
}
|
|
898
|
-
return `<technique-reference category="${name}">
|
|
899
|
-
${content}
|
|
900
|
-
</technique-reference>`;
|
|
889
|
+
if (layer.wrap) return wrapLayerContent(layer.wrap, content, { name });
|
|
890
|
+
return wrapTechniqueReference(name, content);
|
|
901
891
|
});
|
|
902
892
|
return parts.filter(Boolean).join("\n\n") || null;
|
|
903
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
|
|
904
912
|
function executeStateLayer(layer, ctx) {
|
|
905
913
|
const source = layer.source ?? "";
|
|
906
914
|
if (source === "state.blind_spots") {
|
|
907
915
|
return buildBlindSpotsCheckFragment() || null;
|
|
908
916
|
}
|
|
909
|
-
const fn =
|
|
917
|
+
const fn = STATE_LAYER_REGISTRY[source];
|
|
910
918
|
if (!fn) return null;
|
|
911
919
|
const fragment = fn(ctx);
|
|
912
920
|
if (!fragment && layer.on_empty) return layer.on_empty;
|
|
913
921
|
return fragment || null;
|
|
914
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
|
+
}
|
|
915
1010
|
|
|
916
1011
|
// src/agents/prompt-builder/prompt-builder.ts
|
|
917
1012
|
var DEFAULT_MAX_CHARS = 4e5;
|
|
@@ -921,51 +1016,23 @@ var PromptBuilder = class {
|
|
|
921
1016
|
this.state = state;
|
|
922
1017
|
}
|
|
923
1018
|
/**
|
|
924
|
-
* Build the system prompt by executing
|
|
1019
|
+
* Build the system prompt by executing declarative prompt layers in order.
|
|
925
1020
|
*
|
|
926
|
-
* Layer order is determined by `id` in
|
|
927
|
-
* To add/remove/reorder layers: edit pipeline.yaml only.
|
|
1021
|
+
* Layer order is determined by `id` in runtime config prompt_builder.layers.
|
|
928
1022
|
*/
|
|
929
1023
|
async build(userInput, phase, memory) {
|
|
930
1024
|
const config = getPromptBuilderConfig();
|
|
931
1025
|
const maxChars = config.max_chars ?? DEFAULT_MAX_CHARS;
|
|
932
|
-
const layers =
|
|
933
|
-
const alwaysIncluded = layers.filter((l) => l.always_included);
|
|
934
|
-
const regularLayers = layers.filter((l) => !l.always_included);
|
|
1026
|
+
const layers = getPromptBuilderLayers();
|
|
935
1027
|
const ctx = {
|
|
936
1028
|
state: this.state,
|
|
937
1029
|
phase,
|
|
938
1030
|
userInput,
|
|
939
1031
|
memory: memory ?? {}
|
|
940
1032
|
};
|
|
941
|
-
const
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
if (fragment) regularSections.push(fragment);
|
|
945
|
-
}
|
|
946
|
-
const alwaysSections = [];
|
|
947
|
-
for (const layer of alwaysIncluded) {
|
|
948
|
-
const fragment = await executeLayer(layer, ctx);
|
|
949
|
-
if (fragment) alwaysSections.push(fragment);
|
|
950
|
-
}
|
|
951
|
-
const alwaysText = alwaysSections.filter(Boolean).join("\n\n");
|
|
952
|
-
const fullBody = regularSections.filter(Boolean).join("\n\n");
|
|
953
|
-
const full = alwaysText ? `${fullBody}
|
|
954
|
-
|
|
955
|
-
${alwaysText}` : fullBody;
|
|
956
|
-
if (full.length <= maxChars) return full;
|
|
957
|
-
const reservedLen = alwaysText.length + 100;
|
|
958
|
-
const truncated = fullBody.slice(0, maxChars - reservedLen);
|
|
959
|
-
const notice = "... [PROMPT TRUNCATED: content exceeded context limit] ...";
|
|
960
|
-
return alwaysText ? `${truncated}
|
|
961
|
-
|
|
962
|
-
${notice}
|
|
963
|
-
|
|
964
|
-
${alwaysText}` : `${truncated}
|
|
965
|
-
|
|
966
|
-
${notice}
|
|
967
|
-
|
|
968
|
-
${buildUserContextFragment(userInput)}`;
|
|
1033
|
+
const sections = await buildPromptSections(layers, ctx);
|
|
1034
|
+
const { fullBody, alwaysText } = joinPromptSections(sections);
|
|
1035
|
+
return applyPromptTruncation(fullBody, alwaysText, maxChars, userInput);
|
|
969
1036
|
}
|
|
970
1037
|
};
|
|
971
1038
|
|
|
@@ -1091,9 +1158,6 @@ var ActionNode = class {
|
|
|
1091
1158
|
// src/engine/pipeline/builder.ts
|
|
1092
1159
|
var flagsCaptured = (ctx) => ctx.flagsAfter > ctx.flagsBefore;
|
|
1093
1160
|
|
|
1094
|
-
// src/engine/pipeline/loader.ts
|
|
1095
|
-
import { parse as yamlParse } from "yaml";
|
|
1096
|
-
|
|
1097
1161
|
// src/engine/pipeline/io-registry.ts
|
|
1098
1162
|
var SOURCE_REGISTRY = {
|
|
1099
1163
|
// ── Reflector input fields (ReflectorInput) ───────────────────────────────
|
|
@@ -1412,7 +1476,7 @@ var makeRotateTurns = (_llm, _opts) => async (_ctx) => {
|
|
|
1412
1476
|
};
|
|
1413
1477
|
var makeSaveSession = (_llm, _opts) => async (ctx) => {
|
|
1414
1478
|
try {
|
|
1415
|
-
const { saveState: saveState2 } = await import("./persistence-
|
|
1479
|
+
const { saveState: saveState2 } = await import("./persistence-BNVN3WW6.js");
|
|
1416
1480
|
saveState2(ctx.state);
|
|
1417
1481
|
} catch {
|
|
1418
1482
|
}
|
|
@@ -1737,6 +1801,21 @@ var session = {
|
|
|
1737
1801
|
load: (state) => loadState(state)
|
|
1738
1802
|
};
|
|
1739
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
|
+
|
|
1740
1819
|
// src/agents/main-agent/delegated-task-scheduler.ts
|
|
1741
1820
|
function getDelegatedTaskPriority(task) {
|
|
1742
1821
|
const workerPriority = task.nextWorkerType || task.workerType;
|
|
@@ -1828,7 +1907,126 @@ var DelegatedTaskQueue = class {
|
|
|
1828
1907
|
}
|
|
1829
1908
|
};
|
|
1830
1909
|
|
|
1831
|
-
// src/agents/main-agent/
|
|
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
|
+
}
|
|
1832
2030
|
function getPhaseFromContext(context, key) {
|
|
1833
2031
|
if (!context) {
|
|
1834
2032
|
return null;
|
|
@@ -1836,50 +2034,150 @@ function getPhaseFromContext(context, key) {
|
|
|
1836
2034
|
const match = context.match(new RegExp(`${key}=([a-z_]+)`));
|
|
1837
2035
|
return match?.[1] ?? null;
|
|
1838
2036
|
}
|
|
1839
|
-
function
|
|
1840
|
-
if (request
|
|
1841
|
-
const phase = getPhaseFromContext(request.context, "shell_phase");
|
|
1842
|
-
if (phase === "listener_waiting") return "listener_status";
|
|
1843
|
-
if (phase === "listener_callback_detected") return "shell_promote";
|
|
1844
|
-
if (phase === "active_shell_stabilizing") return "shell_upgrade";
|
|
1845
|
-
if (phase === "active_shell_stabilized" || phase === "post_exploitation_active") return "shell_check";
|
|
1846
|
-
return null;
|
|
1847
|
-
}
|
|
1848
|
-
if (request.workerType === "exploit") {
|
|
1849
|
-
const phase = getPhaseFromContext(request.context, "exploit_phase");
|
|
1850
|
-
if (phase === "foothold_active") return "exploit_foothold_check";
|
|
1851
|
-
if (phase === "credential_followup") return "exploit_credential_check";
|
|
1852
|
-
if (phase === "artifact_ready") return "exploit_artifact_check";
|
|
2037
|
+
function analyzeDelegatedExecutionResult(request, result) {
|
|
2038
|
+
if (!request || !result) {
|
|
1853
2039
|
return null;
|
|
1854
2040
|
}
|
|
1855
|
-
if (
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
return null;
|
|
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
|
+
};
|
|
1861
2046
|
}
|
|
1862
|
-
return null;
|
|
1863
|
-
}
|
|
1864
|
-
function buildDelegatedExecutionPlan(request) {
|
|
1865
2047
|
if (request.workerType === "shell-supervisor") {
|
|
1866
2048
|
const phase = getPhaseFromContext(request.context, "shell_phase");
|
|
1867
|
-
if (phase === "listener_waiting")
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
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
|
+
}
|
|
1873
2067
|
}
|
|
1874
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
|
+
}
|
|
1875
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");
|
|
1876
2174
|
if (phase === "foothold_active") return "Reuse the existing foothold and prefer exploit_foothold_check for bounded access validation.";
|
|
1877
2175
|
if (phase === "credential_followup") return "Validate and reuse the obtained credentials or tokens, preferring exploit_credential_check for bounded probes.";
|
|
1878
2176
|
if (phase === "artifact_ready") return "Validate the current exploit artifact, preferring exploit_artifact_check before building a replacement.";
|
|
1879
2177
|
return "Continue the strongest surviving exploit vector and preserve branch evidence.";
|
|
1880
2178
|
}
|
|
1881
2179
|
if (request.workerType === "pwn") {
|
|
1882
|
-
const phase =
|
|
2180
|
+
const phase = getPhaseFromContext2(request.context, "pwn_phase");
|
|
1883
2181
|
if (phase === "foothold_active") return "Reuse the achieved execution foothold and continue from there.";
|
|
1884
2182
|
if (phase === "offset_known") return "Resume from the known offset and latest payload revision, preferring pwn_offset_check for bounded verification.";
|
|
1885
2183
|
if (phase === "crash_iteration") return "Reproduce the latest crash and continue debugging from the preserved crash state, preferring pwn_crash_repro.";
|
|
@@ -1888,520 +2186,235 @@ function buildDelegatedExecutionPlan(request) {
|
|
|
1888
2186
|
return "Continue the selected delegated chain before starting unrelated work.";
|
|
1889
2187
|
}
|
|
1890
2188
|
|
|
1891
|
-
// src/agents/main-agent/
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
)
|
|
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);
|
|
1897
2195
|
}
|
|
1898
|
-
function
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
return
|
|
1902
|
-
}
|
|
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)}...`;
|
|
1903
2205
|
}
|
|
1904
|
-
function
|
|
1905
|
-
return
|
|
1906
|
-
const normalized = asset.toLowerCase();
|
|
1907
|
-
return normalized.includes("shell:") || normalized.includes("stabilized_shell:") || normalized.includes("post_exploitation_shell:") || normalized.includes("listener:") || normalized.includes("session:") || normalized.includes("foothold");
|
|
1908
|
-
});
|
|
2206
|
+
function summarizeDelegatedExecutionOutput(output) {
|
|
2207
|
+
return summarizeDelegatedExecutionField(output, OUTPUT_PREVIEW_LIMIT);
|
|
1909
2208
|
}
|
|
1910
|
-
function
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
const artifactMatch = trimmed.match(/^artifact:(.+)$/i);
|
|
1914
|
-
if (artifactMatch?.[1]) {
|
|
1915
|
-
return artifactMatch[1].trim();
|
|
1916
|
-
}
|
|
1917
|
-
if (trimmed.startsWith("/") || trimmed.startsWith("./")) {
|
|
1918
|
-
return trimmed;
|
|
1919
|
-
}
|
|
2209
|
+
function buildDelegatedExecutionRequestFragment(request) {
|
|
2210
|
+
if (!request) {
|
|
2211
|
+
return "";
|
|
1920
2212
|
}
|
|
1921
|
-
|
|
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");
|
|
1922
2229
|
}
|
|
1923
|
-
function
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
return {
|
|
1927
|
-
phase: "foothold_active",
|
|
1928
|
-
recommendation: "A foothold already exists. Reuse the current access path and continue the exploit chain from that foothold.",
|
|
1929
|
-
boundedTool: "exploit_foothold_check",
|
|
1930
|
-
boundedCommand: replayableCommand || void 0,
|
|
1931
|
-
boundedTimeout: 4e3,
|
|
1932
|
-
boundedInstruction: "Run one narrow foothold validation step before expanding the exploit chain."
|
|
1933
|
-
};
|
|
1934
|
-
}
|
|
1935
|
-
if (hasCredentialLoot(task)) {
|
|
1936
|
-
return {
|
|
1937
|
-
phase: "credential_followup",
|
|
1938
|
-
recommendation: "Credentials or tokens exist. Prioritize validation, reuse, and pivot from the current exploit chain.",
|
|
1939
|
-
boundedTool: "exploit_credential_check",
|
|
1940
|
-
boundedCommand: replayableCommand || void 0,
|
|
1941
|
-
boundedTimeout: 5e3,
|
|
1942
|
-
boundedInstruction: "Run one bounded credential or token validation step before changing exploit vectors."
|
|
1943
|
-
};
|
|
1944
|
-
}
|
|
1945
|
-
if (task.assets.length > 0) {
|
|
1946
|
-
const artifactPath = getArtifactPath(task);
|
|
1947
|
-
return {
|
|
1948
|
-
phase: "artifact_ready",
|
|
1949
|
-
recommendation: "The exploit chain produced reusable artifacts. Validate and advance the current artifact before changing vectors.",
|
|
1950
|
-
boundedTool: "exploit_artifact_check",
|
|
1951
|
-
boundedCommand: artifactPath ? `test -e ${artifactPath} && ls -l ${artifactPath} && file ${artifactPath} 2>/dev/null` : void 0,
|
|
1952
|
-
boundedTimeout: 3e3,
|
|
1953
|
-
boundedInstruction: "Run one bounded artifact validation step before replacing the current artifact."
|
|
1954
|
-
};
|
|
2230
|
+
function buildDelegatedExecutionResultFragment(result, analysis) {
|
|
2231
|
+
if (!result) {
|
|
2232
|
+
return "";
|
|
1955
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() {
|
|
1956
2249
|
return {
|
|
1957
|
-
|
|
1958
|
-
|
|
2250
|
+
pendingRequest: null,
|
|
2251
|
+
lastAttemptedSignature: null
|
|
1959
2252
|
};
|
|
1960
2253
|
}
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
].filter(Boolean).join(" ").toLowerCase();
|
|
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
|
+
};
|
|
1972
2264
|
}
|
|
1973
|
-
function
|
|
1974
|
-
const
|
|
1975
|
-
return
|
|
1976
|
-
(candidate) => /^(?:\.\/|\/|python(?:3)?\b|gdb\b|qemu(?:-[\w-]+)?\b|nc\b)/i.test(candidate) || candidate.includes("pwntools")
|
|
1977
|
-
) || null;
|
|
2265
|
+
function canAutoExecuteDelegatedState(state) {
|
|
2266
|
+
const pendingSignature = getDelegatedExecutionSignature(state.pendingRequest);
|
|
2267
|
+
return pendingSignature !== null && pendingSignature !== state.lastAttemptedSignature;
|
|
1978
2268
|
}
|
|
1979
|
-
function
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
if (normalized.startsWith("./") || normalized.startsWith("/")) {
|
|
1985
|
-
return normalized;
|
|
1986
|
-
}
|
|
1987
|
-
const embeddedPath = normalized.match(/(\.\/[A-Za-z0-9_./-]+|\/[A-Za-z0-9_./-]+)/);
|
|
1988
|
-
if (embeddedPath?.[1]) {
|
|
1989
|
-
return embeddedPath[1];
|
|
1990
|
-
}
|
|
1991
|
-
}
|
|
1992
|
-
return null;
|
|
2269
|
+
function markDelegatedExecutionAttempt(state) {
|
|
2270
|
+
return {
|
|
2271
|
+
...state,
|
|
2272
|
+
lastAttemptedSignature: getDelegatedExecutionSignature(state.pendingRequest)
|
|
2273
|
+
};
|
|
1993
2274
|
}
|
|
1994
|
-
function
|
|
1995
|
-
const evidence = joinedEvidence(task);
|
|
1996
|
-
const replayableCommand = getReplayableCommand(task);
|
|
1997
|
-
const binaryPath = getCandidateBinaryPath(task);
|
|
1998
|
-
if (task.sessions.length > 0 || task.assets.some((asset) => {
|
|
1999
|
-
const normalized = asset.toLowerCase();
|
|
2000
|
-
return normalized.includes("shell:") || normalized.includes("stabilized_shell:") || normalized.includes("post_exploitation_shell:");
|
|
2001
|
-
})) {
|
|
2002
|
-
return {
|
|
2003
|
-
phase: "foothold_active",
|
|
2004
|
-
recommendation: "The pwn chain already produced execution. Reuse the existing foothold instead of restarting the exploit loop."
|
|
2005
|
-
};
|
|
2006
|
-
}
|
|
2007
|
-
if (evidence.includes("offset") || evidence.includes("pattern") || evidence.includes("eip") || evidence.includes("rip")) {
|
|
2008
|
-
return {
|
|
2009
|
-
phase: "offset_known",
|
|
2010
|
-
recommendation: "Offsets or control primitives are known. Resume from the latest payload iteration instead of rediscovering the crash.",
|
|
2011
|
-
boundedTool: "pwn_offset_check",
|
|
2012
|
-
boundedCommand: replayableCommand || (binaryPath ? `test -x ${binaryPath} && timeout 5 ${binaryPath} </dev/null` : void 0),
|
|
2013
|
-
boundedTimeout: 5e3,
|
|
2014
|
-
boundedInstruction: "Run one bounded offset verification step against the latest exploit revision."
|
|
2015
|
-
};
|
|
2016
|
-
}
|
|
2017
|
-
if (evidence.includes("crash") || evidence.includes("segfault") || evidence.includes("core")) {
|
|
2018
|
-
return {
|
|
2019
|
-
phase: "crash_iteration",
|
|
2020
|
-
recommendation: "A crash state exists. Preserve it and continue the debug loop from the latest observed crash.",
|
|
2021
|
-
boundedTool: "pwn_crash_repro",
|
|
2022
|
-
boundedCommand: replayableCommand || (binaryPath ? `test -x ${binaryPath} && timeout 5 ${binaryPath} </dev/null` : void 0),
|
|
2023
|
-
boundedTimeout: 1e4,
|
|
2024
|
-
boundedInstruction: "Reproduce the latest crash once and preserve the exact crash state."
|
|
2025
|
-
};
|
|
2026
|
-
}
|
|
2275
|
+
function consumeDelegatedExecutionState(state) {
|
|
2027
2276
|
return {
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
boundedTool: "pwn_payload_smoke",
|
|
2031
|
-
boundedCommand: replayableCommand || (binaryPath ? `test -x ${binaryPath} && timeout 5 ${binaryPath} </dev/null` : void 0),
|
|
2032
|
-
boundedTimeout: 5e3,
|
|
2033
|
-
boundedInstruction: "Run one bounded payload smoke test from the latest payload revision."
|
|
2277
|
+
consumedRequest: state.pendingRequest,
|
|
2278
|
+
nextState: createDelegatedExecutionState()
|
|
2034
2279
|
};
|
|
2035
2280
|
}
|
|
2036
2281
|
|
|
2037
|
-
// src/agents/main-agent/delegated-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
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;
|
|
2043
2301
|
}
|
|
2044
|
-
if (decision.preferredWorkerType === "pwn") {
|
|
2045
|
-
return getPwnLifecycleSnapshot(task);
|
|
2046
|
-
}
|
|
2047
|
-
return null;
|
|
2048
|
-
}
|
|
2049
|
-
return getShellSupervisorLifecycleSnapshot();
|
|
2050
|
-
}
|
|
2051
|
-
function buildDelegatedRequestContext(decision, lifecycleSnapshot) {
|
|
2052
|
-
const task = decision.task;
|
|
2053
|
-
if (!lifecycleSnapshot) {
|
|
2054
|
-
return task.summary;
|
|
2055
2302
|
}
|
|
2056
|
-
|
|
2057
|
-
return
|
|
2058
|
-
task.summary,
|
|
2059
|
-
`exploit_phase=${lifecycleSnapshot.phase}`,
|
|
2060
|
-
`recommendation=${lifecycleSnapshot.recommendation}`
|
|
2061
|
-
].filter(Boolean).join(" | ");
|
|
2303
|
+
peek() {
|
|
2304
|
+
return this.pendingRequest;
|
|
2062
2305
|
}
|
|
2063
|
-
|
|
2064
|
-
return
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
].filter(Boolean).join(" | ");
|
|
2306
|
+
canAutoExecute() {
|
|
2307
|
+
return canAutoExecuteDelegatedState({
|
|
2308
|
+
pendingRequest: this.pendingRequest,
|
|
2309
|
+
lastAttemptedSignature: this.lastAttemptedSignature
|
|
2310
|
+
});
|
|
2069
2311
|
}
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
`recommendation=${shellSnapshot.recommendation}`
|
|
2077
|
-
].filter(Boolean).join(" | ");
|
|
2078
|
-
}
|
|
2079
|
-
function buildDelegatedExecutionRequest(decision, context, lifecycleSnapshot) {
|
|
2080
|
-
const task = decision.task;
|
|
2081
|
-
const preferredWorkerType = decision.preferredWorkerType;
|
|
2082
|
-
const requestTask = task.resumeHint || task.task;
|
|
2083
|
-
const boundedLifecycle = preferredWorkerType === "exploit" || preferredWorkerType === "pwn" ? lifecycleSnapshot : null;
|
|
2084
|
-
return {
|
|
2085
|
-
task: requestTask,
|
|
2086
|
-
workerType: preferredWorkerType,
|
|
2087
|
-
resumeTaskId: task.id,
|
|
2088
|
-
target: task.target,
|
|
2089
|
-
context,
|
|
2090
|
-
boundedTool: preferredWorkerType ? getRecommendedBoundedTool({
|
|
2091
|
-
task: requestTask,
|
|
2092
|
-
workerType: preferredWorkerType,
|
|
2093
|
-
target: task.target,
|
|
2094
|
-
context
|
|
2095
|
-
}) ?? void 0 : void 0,
|
|
2096
|
-
boundedCommand: boundedLifecycle?.boundedCommand,
|
|
2097
|
-
boundedTimeout: boundedLifecycle?.boundedTimeout,
|
|
2098
|
-
boundedInstruction: preferredWorkerType ? boundedLifecycle?.boundedInstruction || buildDelegatedExecutionPlan({
|
|
2099
|
-
task: requestTask,
|
|
2100
|
-
workerType: preferredWorkerType,
|
|
2101
|
-
target: task.target,
|
|
2102
|
-
context
|
|
2103
|
-
}) : void 0
|
|
2104
|
-
};
|
|
2105
|
-
}
|
|
2106
|
-
function buildDelegatedTaskHandoff(decision) {
|
|
2107
|
-
const task = decision.task;
|
|
2108
|
-
const preferredWorkerType = decision.preferredWorkerType;
|
|
2109
|
-
const lifecycleSnapshot = buildLifecycleSnapshot(decision);
|
|
2110
|
-
const context = buildDelegatedRequestContext(decision, lifecycleSnapshot);
|
|
2111
|
-
const delegatedExecutionRequest = buildDelegatedExecutionRequest(decision, context, lifecycleSnapshot);
|
|
2112
|
-
const executionPlan = buildDelegatedExecutionPlan(delegatedExecutionRequest);
|
|
2113
|
-
const details = [
|
|
2114
|
-
`Resume delegated task: ${task.task}.`,
|
|
2115
|
-
`Delegated task ID: ${task.id}.`,
|
|
2116
|
-
`Scheduler reason: ${decision.reason}`,
|
|
2117
|
-
`Scheduler cycle count: ${decision.resumeCount + 1}.`,
|
|
2118
|
-
`Current status: ${task.status}.`,
|
|
2119
|
-
task.waitingOn ? `Waiting on: ${task.waitingOn}.` : "",
|
|
2120
|
-
task.assets.length > 0 ? `Reuse assets: ${task.assets.join(", ")}.` : "",
|
|
2121
|
-
task.sessions.length > 0 ? `Reuse sessions: ${task.sessions.join(", ")}.` : "",
|
|
2122
|
-
task.resumeHint ? `Resume guidance: ${task.resumeHint}.` : "",
|
|
2123
|
-
preferredWorkerType ? `Preferred worker: ${preferredWorkerType}.` : "",
|
|
2124
|
-
delegatedExecutionRequest.boundedTool ? `Bounded tool: ${delegatedExecutionRequest.boundedTool}.` : "",
|
|
2125
|
-
delegatedExecutionRequest.boundedCommand ? `Bounded command candidate: ${delegatedExecutionRequest.boundedCommand}.` : "",
|
|
2126
|
-
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.` : "",
|
|
2127
|
-
`Execution hint: ${executionPlan}`,
|
|
2128
|
-
"Execution policy: continue the selected delegated chain first. Do not switch to a different active delegated task until this one is advanced or invalidated.",
|
|
2129
|
-
"Goal: continue the existing chain instead of creating a duplicate operation."
|
|
2130
|
-
].filter(Boolean);
|
|
2131
|
-
return {
|
|
2132
|
-
shouldForwardToMain: true,
|
|
2133
|
-
forwardedInput: details.join(" "),
|
|
2134
|
-
directResponse: "",
|
|
2135
|
-
shouldWritePolicy: false,
|
|
2136
|
-
policyDocument: "",
|
|
2137
|
-
policyUpdateSummary: "",
|
|
2138
|
-
insightSummary: `Auto-resume delegated task ${task.id} via handoff builder.`,
|
|
2139
|
-
delegatedExecutionRequest,
|
|
2140
|
-
success: true
|
|
2141
|
-
};
|
|
2142
|
-
}
|
|
2143
|
-
|
|
2144
|
-
// src/agents/main-agent/exploit-runtime-signals.ts
|
|
2145
|
-
function outputContainsAny(output, patterns) {
|
|
2146
|
-
return patterns.some((pattern) => output.includes(pattern.toLowerCase()));
|
|
2147
|
-
}
|
|
2148
|
-
function getExploitResultSignals(result) {
|
|
2149
|
-
const output = (result.output || "").toLowerCase();
|
|
2150
|
-
const hasSession = outputContainsAny(output, ["[sessions]", "shell", "session"]);
|
|
2151
|
-
const hasLoot = outputContainsAny(output, ["[loot]", "password", "credential", "token", "hash"]);
|
|
2152
|
-
const hasAsset = outputContainsAny(output, ["[assets]", "artifact", "file", "payload"]);
|
|
2153
|
-
return {
|
|
2154
|
-
hasSession,
|
|
2155
|
-
hasLoot,
|
|
2156
|
-
hasAsset,
|
|
2157
|
-
indicatesProgress: hasSession || hasLoot || hasAsset || outputContainsAny(output, ["[status] running", "[status] success"])
|
|
2158
|
-
};
|
|
2159
|
-
}
|
|
2160
|
-
function buildExploitExecutionAnalysis(phase, signals) {
|
|
2161
|
-
if (signals.hasSession) {
|
|
2162
|
-
return {
|
|
2163
|
-
phaseObservation: "exploit chain produced a foothold or authenticated session",
|
|
2164
|
-
nextHint: "reuse the current foothold and continue from the confirmed access path"
|
|
2165
|
-
};
|
|
2312
|
+
markAutoExecutionAttempt() {
|
|
2313
|
+
const nextState = markDelegatedExecutionAttempt({
|
|
2314
|
+
pendingRequest: this.pendingRequest,
|
|
2315
|
+
lastAttemptedSignature: this.lastAttemptedSignature
|
|
2316
|
+
});
|
|
2317
|
+
this.lastAttemptedSignature = nextState.lastAttemptedSignature;
|
|
2166
2318
|
}
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
nextHint: "reuse validated credentials before changing exploit vectors"
|
|
2171
|
-
};
|
|
2319
|
+
setLastExecutionResult(result) {
|
|
2320
|
+
this.lastExecutionResult = result;
|
|
2321
|
+
this.lastExecutionAnalysis = analyzeDelegatedExecutionResult(this.pendingRequest, result);
|
|
2172
2322
|
}
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
};
|
|
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;
|
|
2178
2332
|
}
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
// src/agents/main-agent/pwn-runtime-signals.ts
|
|
2186
|
-
function outputContainsAny2(output, patterns) {
|
|
2187
|
-
return patterns.some((pattern) => output.includes(pattern.toLowerCase()));
|
|
2188
|
-
}
|
|
2189
|
-
function getPwnResultSignals(result) {
|
|
2190
|
-
const output = (result.output || "").toLowerCase();
|
|
2191
|
-
const hasFoothold2 = outputContainsAny2(output, ["[sessions]", "shell", "uid=", "got shell"]);
|
|
2192
|
-
const stillCrashing = outputContainsAny2(output, ["crash", "segfault", "core"]);
|
|
2193
|
-
const hasFindings = outputContainsAny2(output, ["[findings]", "rip controlled", "offset", "eip", "pc controlled"]);
|
|
2194
|
-
return {
|
|
2195
|
-
hasFoothold: hasFoothold2,
|
|
2196
|
-
stillCrashing,
|
|
2197
|
-
hasFindings,
|
|
2198
|
-
indicatesProgress: hasFoothold2 || hasFindings || outputContainsAny2(output, ["[status] running", "[status] success", "[nextworker] pwn"])
|
|
2199
|
-
};
|
|
2200
|
-
}
|
|
2201
|
-
function buildPwnExecutionAnalysis(phase, signals) {
|
|
2202
|
-
if (signals.hasFoothold) {
|
|
2203
|
-
return {
|
|
2204
|
-
phaseObservation: "pwn chain achieved code execution or an interactive foothold",
|
|
2205
|
-
nextHint: "reuse the achieved foothold and continue from the working exploit state"
|
|
2206
|
-
};
|
|
2333
|
+
reset() {
|
|
2334
|
+
const resetState = createDelegatedExecutionState();
|
|
2335
|
+
this.pendingRequest = resetState.pendingRequest;
|
|
2336
|
+
this.lastAttemptedSignature = resetState.lastAttemptedSignature;
|
|
2337
|
+
this.lastExecutionResult = null;
|
|
2338
|
+
this.lastExecutionAnalysis = null;
|
|
2207
2339
|
}
|
|
2208
|
-
|
|
2209
|
-
return
|
|
2210
|
-
phaseObservation: signals.hasFindings ? "offset-guided pwn iteration confirmed control signals" : "offset-guided pwn iteration executed",
|
|
2211
|
-
nextHint: "continue from the known offset and latest payload revision"
|
|
2212
|
-
};
|
|
2340
|
+
toPromptFragment() {
|
|
2341
|
+
return buildDelegatedExecutionRequestFragment(this.pendingRequest);
|
|
2213
2342
|
}
|
|
2214
|
-
|
|
2215
|
-
return
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2343
|
+
toResultFragment() {
|
|
2344
|
+
return buildDelegatedExecutionResultFragment(
|
|
2345
|
+
this.lastExecutionResult,
|
|
2346
|
+
this.lastExecutionAnalysis
|
|
2347
|
+
);
|
|
2219
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);
|
|
2220
2354
|
return {
|
|
2221
|
-
|
|
2222
|
-
|
|
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
|
+
}
|
|
2223
2361
|
};
|
|
2224
2362
|
}
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
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) {
|
|
2228
2369
|
if (!context) {
|
|
2229
2370
|
return null;
|
|
2230
2371
|
}
|
|
2231
2372
|
const match = context.match(new RegExp(`${key}=([a-z_]+)`));
|
|
2232
2373
|
return match?.[1] ?? null;
|
|
2233
2374
|
}
|
|
2234
|
-
function
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
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;
|
|
2243
2390
|
}
|
|
2244
2391
|
if (request.workerType === "shell-supervisor") {
|
|
2245
|
-
|
|
2246
|
-
if (phase === "listener_waiting") {
|
|
2247
|
-
return {
|
|
2248
|
-
phaseObservation: "listener supervision step executed",
|
|
2249
|
-
nextHint: "if callback evidence appeared, promote and validate the shell immediately"
|
|
2250
|
-
};
|
|
2251
|
-
}
|
|
2252
|
-
if (phase === "active_shell_stabilizing") {
|
|
2253
|
-
return {
|
|
2254
|
-
phaseObservation: "shell stabilization step executed",
|
|
2255
|
-
nextHint: "finish PTY stabilization before broader enumeration"
|
|
2256
|
-
};
|
|
2257
|
-
}
|
|
2258
|
-
if (phase === "post_exploitation_active") {
|
|
2259
|
-
return {
|
|
2260
|
-
phaseObservation: "post-exploitation enumeration step executed",
|
|
2261
|
-
nextHint: "continue controlled enumeration through the same shell"
|
|
2262
|
-
};
|
|
2263
|
-
}
|
|
2392
|
+
return true;
|
|
2264
2393
|
}
|
|
2265
2394
|
if (request.workerType === "exploit") {
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
phaseObservation: "bounded credential validation executed for the exploit chain",
|
|
2269
|
-
nextHint: "reuse the validated credentials or convert the access path into a foothold before changing vectors"
|
|
2270
|
-
};
|
|
2271
|
-
}
|
|
2272
|
-
if (request.boundedTool === "exploit_artifact_check") {
|
|
2273
|
-
return {
|
|
2274
|
-
phaseObservation: "bounded artifact validation executed for the exploit chain",
|
|
2275
|
-
nextHint: "advance the validated artifact into data access, authenticated use, or foothold confirmation"
|
|
2276
|
-
};
|
|
2277
|
-
}
|
|
2278
|
-
if (request.boundedTool === "exploit_foothold_check") {
|
|
2279
|
-
return {
|
|
2280
|
-
phaseObservation: "bounded foothold validation executed for the exploit chain",
|
|
2281
|
-
nextHint: "reuse the foothold and continue from the confirmed access path instead of restarting delivery"
|
|
2282
|
-
};
|
|
2283
|
-
}
|
|
2284
|
-
const phase = getPhaseFromContext2(request.context, "exploit_phase");
|
|
2285
|
-
return buildExploitExecutionAnalysis(phase, getExploitResultSignals(result));
|
|
2395
|
+
const phase = getPhaseFromContext3(request.context, "exploit_phase");
|
|
2396
|
+
return phase === "artifact_ready" || phase === "credential_followup" || phase === "foothold_active";
|
|
2286
2397
|
}
|
|
2287
2398
|
if (request.workerType === "pwn") {
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
phaseObservation: "bounded offset verification executed for the pwn loop",
|
|
2291
|
-
nextHint: "continue from the known offset and narrow the next payload iteration"
|
|
2292
|
-
};
|
|
2293
|
-
}
|
|
2294
|
-
if (request.boundedTool === "pwn_crash_repro") {
|
|
2295
|
-
return {
|
|
2296
|
-
phaseObservation: "bounded crash reproduction executed for the pwn loop",
|
|
2297
|
-
nextHint: "preserve the crash state and apply the narrowest next debugging or payload mutation step"
|
|
2298
|
-
};
|
|
2299
|
-
}
|
|
2300
|
-
if (request.boundedTool === "pwn_payload_smoke") {
|
|
2301
|
-
return {
|
|
2302
|
-
phaseObservation: "bounded payload smoke test executed for the pwn loop",
|
|
2303
|
-
nextHint: "preserve the last payload revision and continue narrowing the next test"
|
|
2304
|
-
};
|
|
2305
|
-
}
|
|
2306
|
-
const phase = getPhaseFromContext2(request.context, "pwn_phase");
|
|
2307
|
-
return buildPwnExecutionAnalysis(phase, getPwnResultSignals(result));
|
|
2399
|
+
const phase = getPhaseFromContext3(request.context, "pwn_phase");
|
|
2400
|
+
return phase === "offset_known" || phase === "crash_iteration" || phase === "foothold_active" || phase === "payload_iteration";
|
|
2308
2401
|
}
|
|
2309
|
-
return
|
|
2310
|
-
phaseObservation: "delegated step executed",
|
|
2311
|
-
nextHint: "continue the selected delegated chain before switching focus"
|
|
2312
|
-
};
|
|
2402
|
+
return false;
|
|
2313
2403
|
}
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
lastAttemptedSignature = null;
|
|
2319
|
-
lastExecutionResult = null;
|
|
2320
|
-
lastExecutionAnalysis = null;
|
|
2321
|
-
getSignature(request) {
|
|
2322
|
-
if (!request) return null;
|
|
2323
|
-
return JSON.stringify(request);
|
|
2324
|
-
}
|
|
2325
|
-
set(request) {
|
|
2326
|
-
const nextRequest = request || null;
|
|
2327
|
-
const nextSignature = this.getSignature(nextRequest);
|
|
2328
|
-
const currentSignature = this.getSignature(this.pendingRequest);
|
|
2329
|
-
if (nextSignature !== currentSignature) {
|
|
2330
|
-
this.lastAttemptedSignature = null;
|
|
2331
|
-
}
|
|
2332
|
-
this.pendingRequest = nextRequest;
|
|
2333
|
-
if (nextSignature !== currentSignature) {
|
|
2334
|
-
this.lastExecutionResult = null;
|
|
2335
|
-
this.lastExecutionAnalysis = null;
|
|
2336
|
-
}
|
|
2337
|
-
}
|
|
2338
|
-
peek() {
|
|
2339
|
-
return this.pendingRequest;
|
|
2340
|
-
}
|
|
2341
|
-
canAutoExecute() {
|
|
2342
|
-
const pendingSignature = this.getSignature(this.pendingRequest);
|
|
2343
|
-
return pendingSignature !== null && pendingSignature !== this.lastAttemptedSignature;
|
|
2344
|
-
}
|
|
2345
|
-
markAutoExecutionAttempt() {
|
|
2346
|
-
this.lastAttemptedSignature = this.getSignature(this.pendingRequest);
|
|
2347
|
-
}
|
|
2348
|
-
setLastExecutionResult(result) {
|
|
2349
|
-
this.lastExecutionResult = result;
|
|
2350
|
-
this.lastExecutionAnalysis = analyzeDelegatedExecutionResult(this.pendingRequest, result);
|
|
2351
|
-
}
|
|
2352
|
-
consume() {
|
|
2353
|
-
const request = this.pendingRequest;
|
|
2354
|
-
this.pendingRequest = null;
|
|
2355
|
-
this.lastAttemptedSignature = null;
|
|
2356
|
-
this.lastExecutionAnalysis = null;
|
|
2357
|
-
return request;
|
|
2358
|
-
}
|
|
2359
|
-
reset() {
|
|
2360
|
-
this.pendingRequest = null;
|
|
2361
|
-
this.lastAttemptedSignature = null;
|
|
2362
|
-
this.lastExecutionResult = null;
|
|
2363
|
-
this.lastExecutionAnalysis = null;
|
|
2364
|
-
}
|
|
2365
|
-
toPromptFragment() {
|
|
2366
|
-
if (!this.pendingRequest) {
|
|
2367
|
-
return "";
|
|
2368
|
-
}
|
|
2369
|
-
const request = this.pendingRequest;
|
|
2370
|
-
const executionPlan = buildDelegatedExecutionPlan(request);
|
|
2371
|
-
const lines = [
|
|
2372
|
-
"<delegated-execution-request>",
|
|
2373
|
-
`task: ${request.task}`,
|
|
2374
|
-
request.workerType ? `worker_type: ${request.workerType}` : "",
|
|
2375
|
-
request.resumeTaskId ? `resume_task_id: ${request.resumeTaskId}` : "",
|
|
2376
|
-
request.target ? `target: ${request.target}` : "",
|
|
2377
|
-
request.context ? `context: ${request.context}` : "",
|
|
2378
|
-
request.boundedTool ? `bounded_tool: ${request.boundedTool}` : "",
|
|
2379
|
-
request.boundedCommand ? `bounded_command: ${request.boundedCommand}` : "",
|
|
2380
|
-
request.boundedTimeout ? `bounded_timeout: ${request.boundedTimeout}` : "",
|
|
2381
|
-
request.boundedInstruction ? `bounded_instruction: ${request.boundedInstruction}` : "",
|
|
2382
|
-
executionPlan ? `execution_plan: ${executionPlan}` : "",
|
|
2383
|
-
"</delegated-execution-request>"
|
|
2384
|
-
].filter(Boolean);
|
|
2385
|
-
return lines.join("\n");
|
|
2404
|
+
async function executeTwoStepLoop(execute, firstCall, buildFollowupCall, getSignals) {
|
|
2405
|
+
const firstResult = await execute(firstCall);
|
|
2406
|
+
if (!firstResult.success) {
|
|
2407
|
+
return firstResult;
|
|
2386
2408
|
}
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
}
|
|
2391
|
-
const lines = [
|
|
2392
|
-
"<delegated-execution-result>",
|
|
2393
|
-
`success: ${this.lastExecutionResult.success ? "true" : "false"}`,
|
|
2394
|
-
this.lastExecutionResult.error ? `error: ${this.lastExecutionResult.error}` : "",
|
|
2395
|
-
this.lastExecutionResult.output ? `output: ${this.lastExecutionResult.output}` : "",
|
|
2396
|
-
this.lastExecutionAnalysis?.phaseObservation ? `phase_observation: ${this.lastExecutionAnalysis.phaseObservation}` : "",
|
|
2397
|
-
this.lastExecutionAnalysis?.nextHint ? `next_hint: ${this.lastExecutionAnalysis.nextHint}` : "",
|
|
2398
|
-
"</delegated-execution-result>"
|
|
2399
|
-
].filter(Boolean);
|
|
2400
|
-
return lines.join("\n");
|
|
2409
|
+
const followupCall = buildFollowupCall(firstResult);
|
|
2410
|
+
if (!followupCall || !getSignals(firstResult).indicatesProgress) {
|
|
2411
|
+
return firstResult;
|
|
2401
2412
|
}
|
|
2402
|
-
|
|
2413
|
+
const followupResult = await execute(followupCall);
|
|
2414
|
+
return combineToolResults([firstResult, followupResult]);
|
|
2415
|
+
}
|
|
2403
2416
|
|
|
2404
|
-
// src/agents/main-agent/delegated-auto-executor.ts
|
|
2417
|
+
// src/agents/main-agent/delegated-auto-executor-shell.ts
|
|
2405
2418
|
function didStatusDetectConnection(result) {
|
|
2406
2419
|
const output = result.output || "";
|
|
2407
2420
|
return output.includes("CONNECTION DETECTED") || output.includes("Promote to shell");
|
|
@@ -2460,142 +2473,6 @@ async function executeShellUpgradeSequence(toolRegistry, processId) {
|
|
|
2460
2473
|
});
|
|
2461
2474
|
return combineToolResults([initialCheck, pythonUpgrade, postPythonCheck, scriptUpgrade, finalCheck]);
|
|
2462
2475
|
}
|
|
2463
|
-
function combineToolResults(results) {
|
|
2464
|
-
const success = results.every((result) => result.success);
|
|
2465
|
-
return {
|
|
2466
|
-
success,
|
|
2467
|
-
output: results.map((result, index) => `Step ${index + 1}:
|
|
2468
|
-
${result.output}`).join("\n\n"),
|
|
2469
|
-
...success ? {} : {
|
|
2470
|
-
error: results.find((result) => !result.success)?.error || "delegated auto-execution failed"
|
|
2471
|
-
}
|
|
2472
|
-
};
|
|
2473
|
-
}
|
|
2474
|
-
function buildPatchedContext(context, key, value) {
|
|
2475
|
-
const parts = (context || "").split("|").map((part) => part.trim()).filter(Boolean).filter((part) => !part.startsWith(`${key}=`));
|
|
2476
|
-
parts.push(`${key}=${value}`);
|
|
2477
|
-
return parts.join(" | ");
|
|
2478
|
-
}
|
|
2479
|
-
function getPhaseFromContext3(context, key) {
|
|
2480
|
-
if (!context) {
|
|
2481
|
-
return null;
|
|
2482
|
-
}
|
|
2483
|
-
const match = context.match(new RegExp(`${key}=([a-z_]+)`));
|
|
2484
|
-
return match?.[1] ?? null;
|
|
2485
|
-
}
|
|
2486
|
-
function buildRunTaskInput(request, task, workerType, context) {
|
|
2487
|
-
return {
|
|
2488
|
-
name: TOOL_NAMES.RUN_TASK,
|
|
2489
|
-
input: {
|
|
2490
|
-
task,
|
|
2491
|
-
worker_type: workerType,
|
|
2492
|
-
...request.resumeTaskId ? { resume_task_id: request.resumeTaskId } : {},
|
|
2493
|
-
...request.target ? { target: request.target } : {},
|
|
2494
|
-
...context ? { context } : {}
|
|
2495
|
-
}
|
|
2496
|
-
};
|
|
2497
|
-
}
|
|
2498
|
-
function shouldAutoExecuteDelegatedRequest(request) {
|
|
2499
|
-
if (!request || typeof request.resumeTaskId !== "string") {
|
|
2500
|
-
return false;
|
|
2501
|
-
}
|
|
2502
|
-
if (request.workerType === "shell-supervisor") {
|
|
2503
|
-
return true;
|
|
2504
|
-
}
|
|
2505
|
-
if (request.workerType === "exploit") {
|
|
2506
|
-
const phase = getPhaseFromContext3(request.context, "exploit_phase");
|
|
2507
|
-
return phase === "artifact_ready" || phase === "credential_followup" || phase === "foothold_active";
|
|
2508
|
-
}
|
|
2509
|
-
if (request.workerType === "pwn") {
|
|
2510
|
-
const phase = getPhaseFromContext3(request.context, "pwn_phase");
|
|
2511
|
-
return phase === "offset_known" || phase === "crash_iteration" || phase === "foothold_active" || phase === "payload_iteration";
|
|
2512
|
-
}
|
|
2513
|
-
return false;
|
|
2514
|
-
}
|
|
2515
|
-
function buildDelegatedRunTaskCall(request) {
|
|
2516
|
-
return {
|
|
2517
|
-
name: TOOL_NAMES.RUN_TASK,
|
|
2518
|
-
input: {
|
|
2519
|
-
task: request.task,
|
|
2520
|
-
...request.workerType ? { worker_type: request.workerType } : {},
|
|
2521
|
-
...request.resumeTaskId ? { resume_task_id: request.resumeTaskId } : {},
|
|
2522
|
-
...request.target ? { target: request.target } : {},
|
|
2523
|
-
...request.context ? { context: request.context } : {}
|
|
2524
|
-
}
|
|
2525
|
-
};
|
|
2526
|
-
}
|
|
2527
|
-
function buildBoundedDelegatedToolCall(request) {
|
|
2528
|
-
if (!request.boundedTool || !request.boundedCommand) {
|
|
2529
|
-
return null;
|
|
2530
|
-
}
|
|
2531
|
-
return {
|
|
2532
|
-
name: request.boundedTool,
|
|
2533
|
-
input: {
|
|
2534
|
-
command: request.boundedCommand,
|
|
2535
|
-
...request.boundedTimeout ? { timeout: request.boundedTimeout } : {}
|
|
2536
|
-
}
|
|
2537
|
-
};
|
|
2538
|
-
}
|
|
2539
|
-
function buildExploitPhaseRunTaskCall(request) {
|
|
2540
|
-
const phase = getPhaseFromContext3(request.context, "exploit_phase");
|
|
2541
|
-
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;
|
|
2542
|
-
return buildRunTaskInput(request, phaseTask, "exploit", request.context);
|
|
2543
|
-
}
|
|
2544
|
-
function buildExploitFollowupRunTaskCall(request, previousResult) {
|
|
2545
|
-
const phase = getPhaseFromContext3(request.context, "exploit_phase");
|
|
2546
|
-
const signals = previousResult ? getExploitResultSignals(previousResult) : null;
|
|
2547
|
-
const hasSession = signals?.hasSession ?? false;
|
|
2548
|
-
const hasLoot = signals?.hasLoot ?? false;
|
|
2549
|
-
if (phase === "credential_followup" || hasLoot) {
|
|
2550
|
-
return buildRunTaskInput(
|
|
2551
|
-
request,
|
|
2552
|
-
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.",
|
|
2553
|
-
"exploit",
|
|
2554
|
-
buildPatchedContext(request.context, "exploit_phase", hasSession ? "foothold_active" : "credential_followup")
|
|
2555
|
-
);
|
|
2556
|
-
}
|
|
2557
|
-
if (phase === "artifact_ready") {
|
|
2558
|
-
return buildRunTaskInput(
|
|
2559
|
-
request,
|
|
2560
|
-
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.",
|
|
2561
|
-
"exploit",
|
|
2562
|
-
buildPatchedContext(request.context, "exploit_phase", hasSession ? "foothold_active" : "artifact_ready")
|
|
2563
|
-
);
|
|
2564
|
-
}
|
|
2565
|
-
return null;
|
|
2566
|
-
}
|
|
2567
|
-
function buildPwnPhaseRunTaskCall(request) {
|
|
2568
|
-
const phase = getPhaseFromContext3(request.context, "pwn_phase");
|
|
2569
|
-
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;
|
|
2570
|
-
return buildRunTaskInput(request, phaseTask, "pwn", request.context);
|
|
2571
|
-
}
|
|
2572
|
-
function buildPwnFollowupRunTaskCall(request, previousResult) {
|
|
2573
|
-
const phase = getPhaseFromContext3(request.context, "pwn_phase");
|
|
2574
|
-
const signals = previousResult ? getPwnResultSignals(previousResult) : null;
|
|
2575
|
-
const hasFoothold2 = signals?.hasFoothold ?? false;
|
|
2576
|
-
const stillCrashing = signals?.stillCrashing ?? false;
|
|
2577
|
-
if (phase === "offset_known") {
|
|
2578
|
-
return buildRunTaskInput(
|
|
2579
|
-
request,
|
|
2580
|
-
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.",
|
|
2581
|
-
"pwn",
|
|
2582
|
-
buildPatchedContext(request.context, "pwn_phase", hasFoothold2 ? "foothold_active" : "payload_iteration")
|
|
2583
|
-
);
|
|
2584
|
-
}
|
|
2585
|
-
if (phase === "crash_iteration") {
|
|
2586
|
-
return buildRunTaskInput(
|
|
2587
|
-
request,
|
|
2588
|
-
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.",
|
|
2589
|
-
"pwn",
|
|
2590
|
-
buildPatchedContext(
|
|
2591
|
-
request.context,
|
|
2592
|
-
"pwn_phase",
|
|
2593
|
-
hasFoothold2 ? "foothold_active" : stillCrashing ? "crash_iteration" : "payload_iteration"
|
|
2594
|
-
)
|
|
2595
|
-
);
|
|
2596
|
-
}
|
|
2597
|
-
return null;
|
|
2598
|
-
}
|
|
2599
2476
|
function buildShellSupervisorFastPathCall() {
|
|
2600
2477
|
const snapshot = getShellSupervisorLifecycleSnapshot();
|
|
2601
2478
|
switch (snapshot.phase) {
|
|
@@ -2731,85 +2608,855 @@ async function executeShellSupervisorMiniLoop(toolRegistry) {
|
|
|
2731
2608
|
});
|
|
2732
2609
|
return combineToolResults([promoteResult, interactResult]);
|
|
2733
2610
|
}
|
|
2734
|
-
const singleCall = buildShellSupervisorFastPathCall();
|
|
2735
|
-
if (!singleCall) {
|
|
2736
|
-
return null;
|
|
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(" | ");
|
|
2737
3250
|
}
|
|
2738
|
-
|
|
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(" | ");
|
|
2739
3266
|
}
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
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
|
+
};
|
|
2747
3293
|
}
|
|
2748
|
-
|
|
2749
|
-
const
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
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
|
+
};
|
|
2759
3330
|
}
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
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);
|
|
2767
3339
|
}
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
}
|
|
2773
|
-
return executeTwoStepLoop(
|
|
2774
|
-
toolRegistry,
|
|
2775
|
-
boundedCall,
|
|
2776
|
-
(firstResult) => buildPwnFollowupRunTaskCall(request, firstResult),
|
|
2777
|
-
getPwnResultSignals
|
|
2778
|
-
);
|
|
3340
|
+
function appendUserInput(existing, next) {
|
|
3341
|
+
return existing.trim() === "" ? next : `${existing}
|
|
3342
|
+
|
|
3343
|
+
${next}`;
|
|
2779
3344
|
}
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
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
|
+
};
|
|
2788
3357
|
}
|
|
2789
|
-
|
|
2790
|
-
|
|
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
|
+
};
|
|
2791
3366
|
}
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
3367
|
+
function buildAutoResumeResult(state, delegatedTaskQueue) {
|
|
3368
|
+
const activeTasks = state.getActiveDelegatedTasks();
|
|
3369
|
+
const decision = delegatedTaskQueue.next(activeTasks);
|
|
3370
|
+
if (!decision) {
|
|
3371
|
+
return null;
|
|
2795
3372
|
}
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
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
|
|
3421
|
+
});
|
|
3422
|
+
}
|
|
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;
|
|
2811
3453
|
}
|
|
2812
|
-
|
|
3454
|
+
return executePendingDelegatedRequest(
|
|
3455
|
+
delegatedAutoExecutor,
|
|
3456
|
+
delegatedExecutionRuntime,
|
|
3457
|
+
toolRegistry
|
|
3458
|
+
);
|
|
3459
|
+
}
|
|
2813
3460
|
|
|
2814
3461
|
// src/agents/main-agent/main-agent.ts
|
|
2815
3462
|
var MainAgent = class extends CoreAgent {
|
|
@@ -2841,7 +3488,7 @@ var MainAgent = class extends CoreAgent {
|
|
|
2841
3488
|
this.inputProcessor = createInputProcessor(this.llm);
|
|
2842
3489
|
this.sessionRuntime = createSessionRuntime();
|
|
2843
3490
|
setActiveSessionRuntime(this.sessionRuntime);
|
|
2844
|
-
this.turnCyclePipeline = loadPipelineFromConfig(
|
|
3491
|
+
this.turnCyclePipeline = loadPipelineFromConfig(getRuntimePipelineConfig(), {
|
|
2845
3492
|
llm: this.llm,
|
|
2846
3493
|
conditions: getConditionRegistry(),
|
|
2847
3494
|
key: "turn_cycle"
|
|
@@ -2852,7 +3499,7 @@ var MainAgent = class extends CoreAgent {
|
|
|
2852
3499
|
this.userInput = "";
|
|
2853
3500
|
this.pendingInitialUserInput = userInput;
|
|
2854
3501
|
this.delegatedTaskQueue.reset();
|
|
2855
|
-
this.delegatedExecutionRuntime
|
|
3502
|
+
resetDelegatedExecutionState(this.delegatedExecutionRuntime);
|
|
2856
3503
|
emitStart(this.events, userInput, this.state);
|
|
2857
3504
|
initializeTask(this.state);
|
|
2858
3505
|
try {
|
|
@@ -2861,7 +3508,7 @@ var MainAgent = class extends CoreAgent {
|
|
|
2861
3508
|
return result.output;
|
|
2862
3509
|
} finally {
|
|
2863
3510
|
try {
|
|
2864
|
-
|
|
3511
|
+
saveMainAgentSession(this.state);
|
|
2865
3512
|
} catch {
|
|
2866
3513
|
}
|
|
2867
3514
|
await cleanupAllProcesses();
|
|
@@ -2896,10 +3543,10 @@ var MainAgent = class extends CoreAgent {
|
|
|
2896
3543
|
};
|
|
2897
3544
|
}
|
|
2898
3545
|
async processUserInputTurn() {
|
|
2899
|
-
const messages = this.
|
|
3546
|
+
const messages = collectPendingUserInputs(this.userInputQueue, this.pendingInitialUserInput);
|
|
2900
3547
|
this.pendingInitialUserInput = null;
|
|
2901
3548
|
if (messages.length === 0) {
|
|
2902
|
-
const autoResume = this.
|
|
3549
|
+
const autoResume = buildAutoResumeResult(this.state, this.delegatedTaskQueue);
|
|
2903
3550
|
if (autoResume) {
|
|
2904
3551
|
this.updateObjectiveFromInputProcessor(autoResume);
|
|
2905
3552
|
await this.maybeAutoExecuteDelegatedRequest();
|
|
@@ -2914,11 +3561,11 @@ var MainAgent = class extends CoreAgent {
|
|
|
2914
3561
|
hasActiveEngagement: this.state.hasActiveEngagement(),
|
|
2915
3562
|
currentObjective: this.state.currentObjective || ""
|
|
2916
3563
|
});
|
|
2917
|
-
const normalized =
|
|
3564
|
+
const normalized = normalizeInputProcessorResult(result, rawInput);
|
|
2918
3565
|
this.updatePolicyFromInputProcessor(normalized);
|
|
2919
3566
|
this.updateObjectiveFromInputProcessor(normalized);
|
|
2920
3567
|
this.events.emit({ type: EVENT_TYPES.QUEUE_DRAINED, timestamp: Date.now() });
|
|
2921
|
-
this.
|
|
3568
|
+
emitMainAgentQueueUpdated(this.events, this.userInputQueue);
|
|
2922
3569
|
return normalized;
|
|
2923
3570
|
}
|
|
2924
3571
|
updatePolicyFromInputProcessor(result) {
|
|
@@ -2927,82 +3574,39 @@ var MainAgent = class extends CoreAgent {
|
|
|
2927
3574
|
}
|
|
2928
3575
|
}
|
|
2929
3576
|
updateObjectiveFromInputProcessor(result) {
|
|
2930
|
-
this.delegatedExecutionRuntime
|
|
3577
|
+
setDelegatedExecutionRequest(this.delegatedExecutionRuntime, result.delegatedExecutionRequest);
|
|
2931
3578
|
if (result.shouldForwardToMain && result.forwardedInput.trim()) {
|
|
2932
|
-
this.userInput =
|
|
3579
|
+
this.userInput = appendUserInput(this.userInput, result.forwardedInput);
|
|
2933
3580
|
if (!this.state.hasActiveEngagement()) {
|
|
2934
3581
|
this.state.currentObjective = result.forwardedInput;
|
|
2935
3582
|
}
|
|
2936
3583
|
}
|
|
2937
3584
|
}
|
|
2938
|
-
normalizeInputProcessorResult(result, fallbackInput) {
|
|
2939
|
-
if (!result.success) {
|
|
2940
|
-
return {
|
|
2941
|
-
shouldForwardToMain: true,
|
|
2942
|
-
forwardedInput: fallbackInput,
|
|
2943
|
-
directResponse: "",
|
|
2944
|
-
shouldWritePolicy: false,
|
|
2945
|
-
policyDocument: "",
|
|
2946
|
-
policyUpdateSummary: "",
|
|
2947
|
-
insightSummary: "Input processor fallback: forwarded raw input.",
|
|
2948
|
-
success: false
|
|
2949
|
-
};
|
|
2950
|
-
}
|
|
2951
|
-
return {
|
|
2952
|
-
...result,
|
|
2953
|
-
forwardedInput: result.forwardedInput.trim() || fallbackInput,
|
|
2954
|
-
directResponse: result.directResponse.trim(),
|
|
2955
|
-
policyDocument: result.policyDocument.trim(),
|
|
2956
|
-
policyUpdateSummary: result.policyUpdateSummary.trim(),
|
|
2957
|
-
insightSummary: result.insightSummary.trim()
|
|
2958
|
-
};
|
|
2959
|
-
}
|
|
2960
|
-
collectPendingUserInputs() {
|
|
2961
|
-
const drained = this.userInputQueue.drain();
|
|
2962
|
-
return [
|
|
2963
|
-
...this.pendingInitialUserInput ? [this.pendingInitialUserInput] : [],
|
|
2964
|
-
...drained.map((item) => item.text)
|
|
2965
|
-
].map((text) => text.trim()).filter(Boolean);
|
|
2966
|
-
}
|
|
2967
|
-
appendUserInput(existing, next) {
|
|
2968
|
-
return existing.trim() === "" ? next : `${existing}
|
|
2969
|
-
|
|
2970
|
-
${next}`;
|
|
2971
|
-
}
|
|
2972
|
-
buildAutoResumeResult() {
|
|
2973
|
-
const activeTasks = this.state.getActiveDelegatedTasks();
|
|
2974
|
-
const decision = this.delegatedTaskQueue.next(activeTasks);
|
|
2975
|
-
if (!decision) {
|
|
2976
|
-
return null;
|
|
2977
|
-
}
|
|
2978
|
-
this.delegatedTaskQueue.acknowledge(decision.task.id);
|
|
2979
|
-
return buildDelegatedTaskHandoff(decision);
|
|
2980
|
-
}
|
|
2981
3585
|
async buildDynamicPrompt(memory) {
|
|
2982
|
-
const
|
|
3586
|
+
const delegatedExecution = buildDelegatedPromptFragments(this.delegatedExecutionRuntime);
|
|
3587
|
+
return buildMainAgentPrompt(
|
|
3588
|
+
this.promptBuilder,
|
|
3589
|
+
this.state,
|
|
2983
3590
|
this.userInput,
|
|
2984
|
-
|
|
3591
|
+
delegatedExecution,
|
|
2985
3592
|
memory
|
|
2986
3593
|
);
|
|
2987
|
-
const delegatedExecutionRequest = this.delegatedExecutionRuntime.toPromptFragment();
|
|
2988
|
-
const delegatedExecutionResult = this.delegatedExecutionRuntime.toResultFragment();
|
|
2989
|
-
return [basePrompt, delegatedExecutionRequest, delegatedExecutionResult].filter(Boolean).join("\n\n");
|
|
2990
3594
|
}
|
|
2991
3595
|
// ─── Public API ────────────────────────────────────────────────────────────
|
|
2992
3596
|
loadPreviousSession() {
|
|
2993
|
-
return
|
|
3597
|
+
return loadMainAgentSession(this.state);
|
|
2994
3598
|
}
|
|
2995
3599
|
saveCurrentState() {
|
|
2996
|
-
return
|
|
3600
|
+
return saveMainAgentSession(this.state);
|
|
2997
3601
|
}
|
|
2998
3602
|
async resetSession() {
|
|
2999
|
-
const result = await
|
|
3603
|
+
const result = await resetMainAgentSession(this.state, this.userInputQueue);
|
|
3000
3604
|
this.userInput = "";
|
|
3001
3605
|
this.delegatedTaskQueue.reset();
|
|
3002
|
-
this.delegatedExecutionRuntime
|
|
3606
|
+
resetDelegatedExecutionState(this.delegatedExecutionRuntime);
|
|
3003
3607
|
return result;
|
|
3004
3608
|
}
|
|
3005
|
-
|
|
3609
|
+
setToolAutoApprove(shouldEnable) {
|
|
3006
3610
|
this.approvalGate.setAutoApprove(shouldEnable);
|
|
3007
3611
|
}
|
|
3008
3612
|
getState() {
|
|
@@ -3018,78 +3622,62 @@ ${next}`;
|
|
|
3018
3622
|
return this.sessionRuntime;
|
|
3019
3623
|
}
|
|
3020
3624
|
getLastDelegatedExecutionRequest() {
|
|
3021
|
-
return this.delegatedExecutionRuntime
|
|
3625
|
+
return getDelegatedExecutionRequest(this.delegatedExecutionRuntime);
|
|
3022
3626
|
}
|
|
3023
3627
|
consumeDelegatedExecutionRequest() {
|
|
3024
|
-
return this.delegatedExecutionRuntime
|
|
3628
|
+
return consumeDelegatedExecutionRequest(this.delegatedExecutionRuntime);
|
|
3025
3629
|
}
|
|
3026
3630
|
async executePendingDelegatedRequest() {
|
|
3027
|
-
return
|
|
3631
|
+
return executePendingDelegatedRequest(
|
|
3632
|
+
this.delegatedAutoExecutor,
|
|
3633
|
+
this.delegatedExecutionRuntime,
|
|
3634
|
+
this.toolRegistry
|
|
3635
|
+
);
|
|
3028
3636
|
}
|
|
3029
3637
|
async maybeAutoExecuteDelegatedRequest() {
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3638
|
+
return maybeAutoExecuteDelegatedRequest(
|
|
3639
|
+
this.delegatedAutoExecutor,
|
|
3640
|
+
this.delegatedExecutionRuntime,
|
|
3641
|
+
this.toolRegistry
|
|
3642
|
+
);
|
|
3035
3643
|
}
|
|
3036
|
-
|
|
3037
|
-
this.state.
|
|
3038
|
-
|
|
3039
|
-
allowedDomains: allowed.filter((a) => !a.includes("/")),
|
|
3040
|
-
exclusions,
|
|
3041
|
-
isDOSAllowed: false,
|
|
3042
|
-
isSocialAllowed: false
|
|
3043
|
-
});
|
|
3644
|
+
configureTarget(ip, exclusions = []) {
|
|
3645
|
+
addMainAgentTarget(this.state, this.events, ip);
|
|
3646
|
+
applyMainAgentScope(this.state, [ip], exclusions);
|
|
3044
3647
|
}
|
|
3045
3648
|
addTarget(ip) {
|
|
3046
|
-
this.state
|
|
3047
|
-
|
|
3649
|
+
addMainAgentTarget(this.state, this.events, ip);
|
|
3650
|
+
}
|
|
3651
|
+
setScope(allowed, exclusions = []) {
|
|
3652
|
+
applyMainAgentScope(this.state, allowed, exclusions);
|
|
3048
3653
|
}
|
|
3049
3654
|
enqueueUserInput(text) {
|
|
3050
|
-
this.userInputQueue.
|
|
3051
|
-
this.emitQueueUpdated();
|
|
3655
|
+
enqueueMainAgentUserInput(this.userInputQueue, this.events, text);
|
|
3052
3656
|
}
|
|
3053
3657
|
dequeueLastUserInput() {
|
|
3054
|
-
|
|
3055
|
-
this.emitQueueUpdated();
|
|
3056
|
-
return value;
|
|
3658
|
+
return dequeueMainAgentUserInput(this.userInputQueue, this.events);
|
|
3057
3659
|
}
|
|
3058
3660
|
hasPendingUserInput() {
|
|
3059
3661
|
return this.userInputQueue.hasPending();
|
|
3060
3662
|
}
|
|
3061
|
-
getPendingUserInputCount() {
|
|
3062
|
-
return this.userInputQueue.pendingCount();
|
|
3063
|
-
}
|
|
3064
3663
|
getPendingUserInputPreview() {
|
|
3065
|
-
return this.userInputQueue
|
|
3066
|
-
}
|
|
3067
|
-
emitQueueUpdated() {
|
|
3068
|
-
this.events.emit({
|
|
3069
|
-
type: EVENT_TYPES.QUEUE_UPDATED,
|
|
3070
|
-
timestamp: Date.now(),
|
|
3071
|
-
data: {
|
|
3072
|
-
count: this.getPendingUserInputCount(),
|
|
3073
|
-
preview: this.getPendingUserInputPreview()
|
|
3074
|
-
}
|
|
3075
|
-
});
|
|
3664
|
+
return getMainAgentPendingUserInputPreview(this.userInputQueue);
|
|
3076
3665
|
}
|
|
3077
3666
|
};
|
|
3078
3667
|
|
|
3079
|
-
// src/engine/
|
|
3668
|
+
// src/engine/agent-runtime.ts
|
|
3080
3669
|
import fs from "fs";
|
|
3081
|
-
import
|
|
3082
|
-
var
|
|
3670
|
+
import path from "path";
|
|
3671
|
+
var AgentRuntime = class _AgentRuntime {
|
|
3083
3672
|
static build(options) {
|
|
3084
|
-
|
|
3085
|
-
_YamlRuntime.setupWorkspace(cfg.workspace);
|
|
3673
|
+
_AgentRuntime.setupWorkspace(getWorkspaceConfig());
|
|
3086
3674
|
return new MainAgent(options);
|
|
3087
3675
|
}
|
|
3088
3676
|
static setupWorkspace(workspaceCfg) {
|
|
3089
3677
|
const directories = workspaceCfg?.directories ?? {};
|
|
3090
|
-
for (const
|
|
3678
|
+
for (const dirPath of Object.values(directories)) {
|
|
3091
3679
|
if (dirPath === "DEPRECATED") continue;
|
|
3092
|
-
const resolved =
|
|
3680
|
+
const resolved = path.resolve(process.cwd(), dirPath);
|
|
3093
3681
|
if (!fs.existsSync(resolved)) {
|
|
3094
3682
|
fs.mkdirSync(resolved, { recursive: true });
|
|
3095
3683
|
}
|
|
@@ -3098,11 +3686,48 @@ var YamlRuntime = class _YamlRuntime {
|
|
|
3098
3686
|
}
|
|
3099
3687
|
};
|
|
3100
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
|
+
|
|
3101
3726
|
// src/agents/factory.ts
|
|
3102
|
-
function createMainAgent(
|
|
3727
|
+
function createMainAgent(autoApproveTools = false) {
|
|
3103
3728
|
const state = new SharedState();
|
|
3104
3729
|
const events = new AgentEventEmitter();
|
|
3105
|
-
const approvalGate = new ApprovalGate(
|
|
3730
|
+
const approvalGate = new ApprovalGate(autoApproveTools);
|
|
3106
3731
|
const scopeGuard = new ScopeGuard(state);
|
|
3107
3732
|
const toolRegistry = new CategorizedToolRegistry(
|
|
3108
3733
|
state,
|
|
@@ -3110,7 +3735,7 @@ function createMainAgent(shouldAutoApprove = false) {
|
|
|
3110
3735
|
approvalGate,
|
|
3111
3736
|
events
|
|
3112
3737
|
);
|
|
3113
|
-
return
|
|
3738
|
+
return AgentRuntime.build({ state, events, toolRegistry, approvalGate, scopeGuard });
|
|
3114
3739
|
}
|
|
3115
3740
|
|
|
3116
3741
|
// src/platform/tui/hooks/useAgentState.ts
|
|
@@ -4289,8 +4914,7 @@ var useAgent = (shouldAutoApprove, target, isTyping = false) => {
|
|
|
4289
4914
|
}, [messageQueue.length]);
|
|
4290
4915
|
useEffect2(() => {
|
|
4291
4916
|
if (target) {
|
|
4292
|
-
agent.
|
|
4293
|
-
agent.setScope([target]);
|
|
4917
|
+
agent.configureTarget(target);
|
|
4294
4918
|
}
|
|
4295
4919
|
}, [agent, target]);
|
|
4296
4920
|
useEffect2(() => {
|
|
@@ -4516,8 +5140,7 @@ var createTargetCommands = (ctx) => {
|
|
|
4516
5140
|
await ctx.agent.resetSession();
|
|
4517
5141
|
ctx.addMessage("system", UI_STATUS_MESSAGES.TARGET_CLEARED);
|
|
4518
5142
|
}
|
|
4519
|
-
ctx.agent.
|
|
4520
|
-
ctx.agent.setScope([args[0]]);
|
|
5143
|
+
ctx.agent.configureTarget(args[0]);
|
|
4521
5144
|
ctx.addMessage("system", `${UI_STATUS_MESSAGES.TARGET_SET_PREFIX}${args[0]}`);
|
|
4522
5145
|
};
|
|
4523
5146
|
const handleStart = async (args) => {
|
|
@@ -4527,7 +5150,7 @@ var createTargetCommands = (ctx) => {
|
|
|
4527
5150
|
}
|
|
4528
5151
|
if (!ctx.autoApproveModeRef.current) {
|
|
4529
5152
|
ctx.setAutoApproveMode(true);
|
|
4530
|
-
ctx.agent.
|
|
5153
|
+
ctx.agent.setToolAutoApprove(true);
|
|
4531
5154
|
ctx.addMessage("system", UI_STATUS_MESSAGES.AUTONOMOUS_MODE_ENABLED);
|
|
4532
5155
|
}
|
|
4533
5156
|
ctx.addMessage("system", UI_STATUS_MESSAGES.STARTING_PENTEST);
|
|
@@ -4927,7 +5550,7 @@ var createToggleCommands = (ctx) => ({
|
|
|
4927
5550
|
[UI_COMMANDS.AUTO]: () => {
|
|
4928
5551
|
ctx.setAutoApproveMode((prev) => {
|
|
4929
5552
|
const newVal = !prev;
|
|
4930
|
-
ctx.agent.
|
|
5553
|
+
ctx.agent.setToolAutoApprove(newVal);
|
|
4931
5554
|
ctx.addMessage("system", newVal ? UI_STATUS_MESSAGES.AUTO_APPROVE_ON : UI_STATUS_MESSAGES.AUTO_APPROVE_OFF);
|
|
4932
5555
|
return newVal;
|
|
4933
5556
|
});
|
|
@@ -7736,9 +8359,9 @@ var SplashScreen = ({
|
|
|
7736
8359
|
);
|
|
7737
8360
|
};
|
|
7738
8361
|
|
|
7739
|
-
// src/platform/tui/cli/
|
|
8362
|
+
// src/platform/tui/cli/interactive-runtime.tsx
|
|
7740
8363
|
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
7741
|
-
var
|
|
8364
|
+
var InteractiveRoot = ({ autoApprove, target }) => {
|
|
7742
8365
|
const [showSplash, setShowSplash] = React16.useState(true);
|
|
7743
8366
|
if (showSplash) {
|
|
7744
8367
|
return /* @__PURE__ */ jsx24(SplashScreen, { durationMs: 3e3, onComplete: () => setShowSplash(false) });
|
|
@@ -7746,65 +8369,62 @@ var Root = ({ skipPermissions, target }) => {
|
|
|
7746
8369
|
return /* @__PURE__ */ jsx24(
|
|
7747
8370
|
app_default,
|
|
7748
8371
|
{
|
|
7749
|
-
autoApprove
|
|
8372
|
+
autoApprove,
|
|
7750
8373
|
target
|
|
7751
8374
|
}
|
|
7752
8375
|
);
|
|
7753
8376
|
};
|
|
7754
|
-
async function
|
|
7755
|
-
const { dangerouslySkipPermissions: skipPermissions = false, target } = options;
|
|
7756
|
-
console.clear();
|
|
7757
|
-
if (skipPermissions) {
|
|
7758
|
-
console.log(chalk.hex(HEX.red)("[!] WARNING: Running with --dangerously-skip-permissions"));
|
|
7759
|
-
console.log(chalk.hex(HEX.red)("[!] All tool executions will be auto-approved!\n"));
|
|
7760
|
-
}
|
|
8377
|
+
async function renderInteractiveApp(props) {
|
|
7761
8378
|
const { waitUntilExit } = render(
|
|
7762
|
-
/* @__PURE__ */ jsx24(AnimationProvider, { children: /* @__PURE__ */ jsx24(
|
|
8379
|
+
/* @__PURE__ */ jsx24(AnimationProvider, { children: /* @__PURE__ */ jsx24(InteractiveRoot, { ...props }) })
|
|
7763
8380
|
);
|
|
7764
8381
|
await waitUntilExit();
|
|
7765
8382
|
}
|
|
7766
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
|
+
|
|
7767
8397
|
// src/platform/tui/cli/commands/run.ts
|
|
7768
8398
|
import chalk2 from "chalk";
|
|
7769
8399
|
async function runAction(objective, options) {
|
|
7770
|
-
const
|
|
7771
|
-
if (
|
|
7772
|
-
|
|
8400
|
+
const autoApproveTools = options.dangerouslySkipPermissions || false;
|
|
8401
|
+
if (autoApproveTools) {
|
|
8402
|
+
printAutoApproveWarning({ includeHeader: true });
|
|
7773
8403
|
}
|
|
7774
8404
|
console.log(chalk2.hex(HEX.primary)(`[target] Objective: ${objective}
|
|
7775
8405
|
`));
|
|
7776
|
-
const agent = createMainAgent(
|
|
7777
|
-
if (
|
|
7778
|
-
agent.
|
|
8406
|
+
const agent = createMainAgent(autoApproveTools, options.maxSteps);
|
|
8407
|
+
if (autoApproveTools) {
|
|
8408
|
+
agent.setToolAutoApprove(true);
|
|
7779
8409
|
}
|
|
7780
8410
|
if (options.target) {
|
|
7781
|
-
agent.
|
|
7782
|
-
agent.setScope([options.target]);
|
|
8411
|
+
agent.configureTarget(options.target);
|
|
7783
8412
|
}
|
|
7784
8413
|
const shutdown = async (exitCode = 0) => {
|
|
7785
|
-
await
|
|
7786
|
-
});
|
|
8414
|
+
await cleanupCliProcesses();
|
|
7787
8415
|
const reqs = getGlobalRequestCount();
|
|
7788
8416
|
const usage = getGlobalTokenUsage();
|
|
7789
8417
|
console.log(chalk2.hex(HEX.gray)(`
|
|
7790
8418
|
[Session Summary] Requests: ${reqs} | In: ${usage.input_tokens} | Out: ${usage.output_tokens}`));
|
|
7791
8419
|
process.exit(exitCode);
|
|
7792
8420
|
};
|
|
7793
|
-
|
|
7794
|
-
process.on("SIGTERM", () => shutdown(EXIT_CODES.SIGTERM));
|
|
8421
|
+
registerShutdownSignals(shutdown);
|
|
7795
8422
|
try {
|
|
7796
8423
|
const result = await agent.execute(objective);
|
|
7797
8424
|
console.log(chalk2.hex(HEX.gray)("\n[+] Assessment complete!\n"));
|
|
7798
8425
|
console.log(result);
|
|
7799
8426
|
if (options.output) {
|
|
7800
|
-
|
|
7801
|
-
const { dirname } = await import("path");
|
|
7802
|
-
const outputDir = dirname(options.output);
|
|
7803
|
-
if (outputDir && outputDir !== ".") {
|
|
7804
|
-
await fs2.mkdir(outputDir, { recursive: true }).catch(() => {
|
|
7805
|
-
});
|
|
7806
|
-
}
|
|
7807
|
-
await fs2.writeFile(options.output, JSON.stringify({ result }, null, 2));
|
|
8427
|
+
await writeJsonReport(options.output, result);
|
|
7808
8428
|
console.log(chalk2.hex(HEX.primary)(`
|
|
7809
8429
|
[+] Report saved to: ${options.output}`));
|
|
7810
8430
|
}
|
|
@@ -7820,20 +8440,20 @@ async function runAction(objective, options) {
|
|
|
7820
8440
|
// src/platform/tui/cli/commands/scan.ts
|
|
7821
8441
|
import chalk3 from "chalk";
|
|
7822
8442
|
async function scanAction(target, options) {
|
|
7823
|
-
const
|
|
8443
|
+
const autoApproveTools = options.dangerouslySkipPermissions || false;
|
|
7824
8444
|
console.log(chalk3.hex(HEX.primary)(`
|
|
7825
8445
|
[scan] Target: ${target} (${options.scanType})
|
|
7826
8446
|
`));
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
|
|
8447
|
+
if (autoApproveTools) {
|
|
8448
|
+
printAutoApproveWarning();
|
|
8449
|
+
}
|
|
8450
|
+
const agent = createMainAgent(autoApproveTools);
|
|
8451
|
+
agent.configureTarget(target);
|
|
7830
8452
|
const shutdown = async (exitCode = 0) => {
|
|
7831
|
-
await
|
|
7832
|
-
});
|
|
8453
|
+
await cleanupCliProcesses();
|
|
7833
8454
|
process.exit(exitCode);
|
|
7834
8455
|
};
|
|
7835
|
-
|
|
7836
|
-
process.on("SIGTERM", () => shutdown(EXIT_CODES.SIGTERM));
|
|
8456
|
+
registerShutdownSignals(shutdown);
|
|
7837
8457
|
try {
|
|
7838
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.`);
|
|
7839
8459
|
console.log(chalk3.hex(HEX.gray)("[+] Scan complete!"));
|
|
@@ -7855,7 +8475,7 @@ ${chalk4.hex(HEX.primary)(APP_NAME + " - Autonomous Penetration Testing AI")}
|
|
|
7855
8475
|
|
|
7856
8476
|
${chalk4.hex(HEX.gray)("$ pentesting")} Start interactive mode
|
|
7857
8477
|
${chalk4.hex(HEX.gray)("$ pentesting -t 192.168.1.1")} Start with target
|
|
7858
|
-
${chalk4.hex(HEX.gray)("$ pentesting --dangerously-skip-permissions")}
|
|
8478
|
+
${chalk4.hex(HEX.gray)("$ pentesting --dangerously-skip-permissions")} Enable tool auto-approve in strict mode
|
|
7859
8479
|
|
|
7860
8480
|
${chalk4.hex(HEX.yellow)("Commands:")}
|
|
7861
8481
|
|
|
@@ -7865,7 +8485,7 @@ ${chalk4.hex(HEX.yellow)("Commands:")}
|
|
|
7865
8485
|
|
|
7866
8486
|
${chalk4.hex(HEX.yellow)("Options:")}
|
|
7867
8487
|
|
|
7868
|
-
${chalk4.hex(HEX.primary)("--dangerously-skip-permissions")}
|
|
8488
|
+
${chalk4.hex(HEX.primary)("--dangerously-skip-permissions")} Bypass strict approval mode by auto-approving tools
|
|
7869
8489
|
${chalk4.hex(HEX.primary)("-t, --target <ip>")} Set target
|
|
7870
8490
|
${chalk4.hex(HEX.primary)("-o, --output <file>")} Save results to file
|
|
7871
8491
|
|
|
@@ -7896,10 +8516,20 @@ ${chalk4.hex(HEX.yellow)("Environment:")}
|
|
|
7896
8516
|
`);
|
|
7897
8517
|
}
|
|
7898
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
|
+
|
|
7899
8529
|
// src/platform/tui/cli/program.ts
|
|
7900
8530
|
function createProgram() {
|
|
7901
8531
|
const program2 = new Command();
|
|
7902
|
-
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");
|
|
7903
8533
|
program2.command("interactive", { isDefault: true }).alias("i").description("Start interactive TUI mode").action(async () => {
|
|
7904
8534
|
const opts = program2.opts();
|
|
7905
8535
|
await interactiveAction({
|
|
@@ -7907,7 +8537,7 @@ function createProgram() {
|
|
|
7907
8537
|
target: opts.target
|
|
7908
8538
|
});
|
|
7909
8539
|
});
|
|
7910
|
-
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) => {
|
|
7911
8541
|
const opts = program2.opts();
|
|
7912
8542
|
await runAction(objective, {
|
|
7913
8543
|
...options,
|