pentesting 0.73.2 → 0.73.4

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