pentesting 0.70.9 → 0.70.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/main.js +341 -163
- package/dist/prompts/base.md +11 -9
- package/dist/prompts/orchestrator.md +9 -0
- package/dist/prompts/strategist-system.md +22 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
## Purpose
|
|
32
32
|
|
|
33
|
-
Pentesting support tool
|
|
33
|
+
Pentesting support tool. Can autonomously execute network penetration tests or assist with generic Capture The Flag (CTF) challenges (such as Reverse Engineering, Cryptography, and binary analysis) without requiring a specific network target.
|
|
34
34
|
|
|
35
35
|
---
|
|
36
36
|
|
package/dist/main.js
CHANGED
|
@@ -759,7 +759,7 @@ var INPUT_PROMPT_PATTERNS = [
|
|
|
759
759
|
|
|
760
760
|
// src/shared/constants/agent.ts
|
|
761
761
|
var APP_NAME = "Pentest AI";
|
|
762
|
-
var APP_VERSION = "0.70.
|
|
762
|
+
var APP_VERSION = "0.70.10";
|
|
763
763
|
var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
|
|
764
764
|
var LLM_ROLES = {
|
|
765
765
|
SYSTEM: "system",
|
|
@@ -814,7 +814,7 @@ import { render } from "ink";
|
|
|
814
814
|
import chalk from "chalk";
|
|
815
815
|
|
|
816
816
|
// src/platform/tui/app.tsx
|
|
817
|
-
import { useState as
|
|
817
|
+
import { useState as useState7, useCallback as useCallback9, useRef as useRef9 } from "react";
|
|
818
818
|
import { Box as Box20, useApp, useStdout as useStdout4 } from "ink";
|
|
819
819
|
|
|
820
820
|
// src/platform/tui/hooks/useAgent.ts
|
|
@@ -1026,7 +1026,8 @@ var NOISE_CLASSIFICATION = {
|
|
|
1026
1026
|
"set_scope",
|
|
1027
1027
|
"bg_status",
|
|
1028
1028
|
"bg_cleanup",
|
|
1029
|
-
"health_check"
|
|
1029
|
+
"health_check",
|
|
1030
|
+
"ask_user"
|
|
1030
1031
|
]
|
|
1031
1032
|
};
|
|
1032
1033
|
|
|
@@ -1173,7 +1174,9 @@ var EVENT_TYPES = {
|
|
|
1173
1174
|
USAGE_UPDATE: "usage_update",
|
|
1174
1175
|
INPUT_REQUEST: "input_request",
|
|
1175
1176
|
FLAG_FOUND: "flag_found",
|
|
1176
|
-
NOTIFICATION: "notification"
|
|
1177
|
+
NOTIFICATION: "notification",
|
|
1178
|
+
AUXILIARY_WORK_START: "auxiliary_work_start",
|
|
1179
|
+
AUXILIARY_WORK_END: "auxiliary_work_end"
|
|
1177
1180
|
};
|
|
1178
1181
|
var COMMAND_EVENT_TYPES = {
|
|
1179
1182
|
TOOL_MISSING: "tool_missing",
|
|
@@ -3395,11 +3398,25 @@ var SharedState = class {
|
|
|
3395
3398
|
* 이 필드로 turn N의 reflection → turn N+1 prompt 레이어(epistemic-check)에 주입된다.
|
|
3396
3399
|
*/
|
|
3397
3400
|
lastReflection = null;
|
|
3401
|
+
/**
|
|
3402
|
+
* Artifacts for CTF / Non-Network tasks (e.g., .bin files, source code).
|
|
3403
|
+
*/
|
|
3404
|
+
artifacts = [];
|
|
3405
|
+
/**
|
|
3406
|
+
* Current objective for flexible task management.
|
|
3407
|
+
*/
|
|
3408
|
+
currentObjective = null;
|
|
3398
3409
|
constructor() {
|
|
3399
3410
|
this.targetState = new TargetState(this.attackGraph);
|
|
3400
3411
|
this.findingState = new FindingState();
|
|
3401
3412
|
this.lootState = new LootState();
|
|
3402
3413
|
}
|
|
3414
|
+
/**
|
|
3415
|
+
* Checks if the agent has a valid target, artifact, or general objective to work on.
|
|
3416
|
+
*/
|
|
3417
|
+
hasActiveEngagement() {
|
|
3418
|
+
return this.targetState.getAll().length > 0 || this.artifacts.length > 0 || this.currentObjective !== null;
|
|
3419
|
+
}
|
|
3403
3420
|
reset() {
|
|
3404
3421
|
this.targetState.clear();
|
|
3405
3422
|
this.findingState.clear();
|
|
@@ -3413,6 +3430,8 @@ var SharedState = class {
|
|
|
3413
3430
|
this.episodicMemory.clear();
|
|
3414
3431
|
this.dynamicTechniques.clear();
|
|
3415
3432
|
this.lastReflection = null;
|
|
3433
|
+
this.artifacts = [];
|
|
3434
|
+
this.currentObjective = null;
|
|
3416
3435
|
}
|
|
3417
3436
|
// Delegation to MissionState
|
|
3418
3437
|
setMissionSummary(summary) {
|
|
@@ -5108,6 +5127,7 @@ var StateSerializer = class {
|
|
|
5108
5127
|
static toPrompt(state) {
|
|
5109
5128
|
const lines = [];
|
|
5110
5129
|
this.formatContextAndMission(state, lines);
|
|
5130
|
+
this.formatArtifactsAndObjectives(state, lines);
|
|
5111
5131
|
this.formatTargets(state, lines);
|
|
5112
5132
|
this.formatFindings(state, lines);
|
|
5113
5133
|
this.formatLoot(state, lines);
|
|
@@ -5118,6 +5138,17 @@ var StateSerializer = class {
|
|
|
5118
5138
|
lines.push(`Phase: ${state.getPhase()}`);
|
|
5119
5139
|
return lines.join("\n");
|
|
5120
5140
|
}
|
|
5141
|
+
static formatArtifactsAndObjectives(state, lines) {
|
|
5142
|
+
if (state.currentObjective) {
|
|
5143
|
+
lines.push(`Current Objective: ${state.currentObjective}`);
|
|
5144
|
+
}
|
|
5145
|
+
if (state.artifacts && state.artifacts.length > 0) {
|
|
5146
|
+
lines.push(`Artifacts (${state.artifacts.length}):`);
|
|
5147
|
+
for (const a of state.artifacts) {
|
|
5148
|
+
lines.push(` [${a.type}] ${a.id}: ${a.description}`);
|
|
5149
|
+
}
|
|
5150
|
+
}
|
|
5151
|
+
}
|
|
5121
5152
|
static formatContextAndMission(state, lines) {
|
|
5122
5153
|
const engagement = state.getEngagement();
|
|
5123
5154
|
const scope = state.getScope();
|
|
@@ -14759,6 +14790,9 @@ var MainAgent = class extends CoreAgent {
|
|
|
14759
14790
|
}
|
|
14760
14791
|
async execute(userInput) {
|
|
14761
14792
|
this.userInput = userInput;
|
|
14793
|
+
if (!this.state.hasActiveEngagement() && userInput.trim().length > 0) {
|
|
14794
|
+
this.state.currentObjective = userInput.trim();
|
|
14795
|
+
}
|
|
14762
14796
|
emitStart(this.events, userInput, this.state);
|
|
14763
14797
|
initializeTask(this.state);
|
|
14764
14798
|
try {
|
|
@@ -14790,8 +14824,30 @@ var MainAgent = class extends CoreAgent {
|
|
|
14790
14824
|
const turnToolJournal = this.getTurnToolJournal();
|
|
14791
14825
|
const turnMemo = this.getTurnMemo();
|
|
14792
14826
|
const turnReflections = [];
|
|
14827
|
+
this.events.emit({
|
|
14828
|
+
type: "auxiliary_work_start",
|
|
14829
|
+
timestamp: Date.now(),
|
|
14830
|
+
data: { type: "context_extraction" }
|
|
14831
|
+
});
|
|
14832
|
+
const extStart = Date.now();
|
|
14793
14833
|
await processContextExtraction(messages, this.contextExtractor);
|
|
14834
|
+
this.events.emit({
|
|
14835
|
+
type: "auxiliary_work_end",
|
|
14836
|
+
timestamp: Date.now(),
|
|
14837
|
+
data: { type: "context_extraction", durationMs: Date.now() - extStart, success: true }
|
|
14838
|
+
});
|
|
14839
|
+
this.events.emit({
|
|
14840
|
+
type: "auxiliary_work_start",
|
|
14841
|
+
timestamp: Date.now(),
|
|
14842
|
+
data: { type: "reflection" }
|
|
14843
|
+
});
|
|
14844
|
+
const refStart = Date.now();
|
|
14794
14845
|
const reflection = await processReflection(turnToolJournal, turnMemo, this.state.getPhase(), this.reflector);
|
|
14846
|
+
this.events.emit({
|
|
14847
|
+
type: "auxiliary_work_end",
|
|
14848
|
+
timestamp: Date.now(),
|
|
14849
|
+
data: { type: "reflection", durationMs: Date.now() - refStart, success: !!reflection }
|
|
14850
|
+
});
|
|
14795
14851
|
if (reflection) {
|
|
14796
14852
|
turnReflections.push(reflection);
|
|
14797
14853
|
this.state.lastReflection = reflection;
|
|
@@ -15280,6 +15336,13 @@ function createLifecycleHandlers(agent, state, _reasoningBufferRef) {
|
|
|
15280
15336
|
const prefix = e.data.level === "error" ? "\u274C" : e.data.level === "warning" ? "\u26A0\uFE0F" : e.data.level === "success" ? "\u2705" : "\u{1F514}";
|
|
15281
15337
|
addMessage("system", `${prefix} [${e.data.title}] ${e.data.message}`);
|
|
15282
15338
|
};
|
|
15339
|
+
const onAuxiliaryWorkStart = (e) => {
|
|
15340
|
+
const msg = e.data.type === "context_extraction" ? "Compressing context..." : "Reflecting on actions...";
|
|
15341
|
+
setCurrentStatus(msg);
|
|
15342
|
+
};
|
|
15343
|
+
const onAuxiliaryWorkEnd = () => {
|
|
15344
|
+
setCurrentStatus("");
|
|
15345
|
+
};
|
|
15283
15346
|
return {
|
|
15284
15347
|
onComplete,
|
|
15285
15348
|
onRetry,
|
|
@@ -15289,7 +15352,9 @@ function createLifecycleHandlers(agent, state, _reasoningBufferRef) {
|
|
|
15289
15352
|
onUsageUpdate,
|
|
15290
15353
|
onFlagFound,
|
|
15291
15354
|
onPhaseChange,
|
|
15292
|
-
onNotification
|
|
15355
|
+
onNotification,
|
|
15356
|
+
onAuxiliaryWorkStart,
|
|
15357
|
+
onAuxiliaryWorkEnd
|
|
15293
15358
|
};
|
|
15294
15359
|
}
|
|
15295
15360
|
|
|
@@ -15455,6 +15520,8 @@ var useAgentEvents = (agent, eventsRef, state) => {
|
|
|
15455
15520
|
events.on(EVENT_TYPES.FLAG_FOUND, lifecycleHandlers.onFlagFound);
|
|
15456
15521
|
events.on(EVENT_TYPES.PHASE_CHANGE, lifecycleHandlers.onPhaseChange);
|
|
15457
15522
|
events.on(EVENT_TYPES.NOTIFICATION, lifecycleHandlers.onNotification);
|
|
15523
|
+
events.on(EVENT_TYPES.AUXILIARY_WORK_START, lifecycleHandlers.onAuxiliaryWorkStart);
|
|
15524
|
+
events.on(EVENT_TYPES.AUXILIARY_WORK_END, lifecycleHandlers.onAuxiliaryWorkEnd);
|
|
15458
15525
|
events.on(EVENT_TYPES.STATE_CHANGE, updateStats);
|
|
15459
15526
|
events.on(EVENT_TYPES.START, updateStats);
|
|
15460
15527
|
events.on(EVENT_TYPES.REASONING_START, reasoningHandlers.onStart);
|
|
@@ -15526,30 +15593,46 @@ var useAgent = (shouldAutoApprove, target) => {
|
|
|
15526
15593
|
useAgentEvents(agent, eventsRef, state);
|
|
15527
15594
|
const abortedRef = useRef3(false);
|
|
15528
15595
|
const executeTask = useCallback2(async (task) => {
|
|
15529
|
-
|
|
15530
|
-
|
|
15531
|
-
|
|
15532
|
-
|
|
15533
|
-
|
|
15534
|
-
|
|
15535
|
-
|
|
15536
|
-
|
|
15537
|
-
|
|
15538
|
-
|
|
15539
|
-
|
|
15540
|
-
|
|
15541
|
-
|
|
15542
|
-
|
|
15596
|
+
let currentTask = task;
|
|
15597
|
+
while (true) {
|
|
15598
|
+
abortedRef.current = false;
|
|
15599
|
+
setTurnCount((n) => n + 1);
|
|
15600
|
+
setIsProcessing(true);
|
|
15601
|
+
manageTimer("start");
|
|
15602
|
+
setCurrentStatus("Thinking");
|
|
15603
|
+
resetCumulativeCounters();
|
|
15604
|
+
try {
|
|
15605
|
+
const response = await agent.execute(currentTask);
|
|
15606
|
+
if (abortedRef.current) {
|
|
15607
|
+
if (agent.hasPendingUserInput()) {
|
|
15608
|
+
currentTask = "";
|
|
15609
|
+
continue;
|
|
15610
|
+
}
|
|
15611
|
+
return;
|
|
15612
|
+
}
|
|
15613
|
+
const meta = lastResponseMetaRef.current;
|
|
15614
|
+
const suffix = meta ? ` ${formatMeta(meta.durationMs || 0, (meta.tokens?.input || 0) + (meta.tokens?.output || 0))}` : "";
|
|
15615
|
+
addMessage("ai", response + suffix);
|
|
15616
|
+
break;
|
|
15617
|
+
} catch (e) {
|
|
15618
|
+
if (abortedRef.current) {
|
|
15619
|
+
if (agent.hasPendingUserInput()) {
|
|
15620
|
+
currentTask = "";
|
|
15621
|
+
continue;
|
|
15622
|
+
}
|
|
15623
|
+
return;
|
|
15624
|
+
}
|
|
15543
15625
|
addMessage("error", e instanceof Error ? e.message : String(e));
|
|
15544
|
-
|
|
15545
|
-
|
|
15546
|
-
|
|
15547
|
-
|
|
15548
|
-
|
|
15549
|
-
|
|
15626
|
+
break;
|
|
15627
|
+
} finally {
|
|
15628
|
+
if (!abortedRef.current) {
|
|
15629
|
+
manageTimer("stop");
|
|
15630
|
+
setIsProcessing(false);
|
|
15631
|
+
setCurrentStatus("");
|
|
15632
|
+
}
|
|
15550
15633
|
}
|
|
15551
15634
|
}
|
|
15552
|
-
}, [agent, addMessage, manageTimer, resetCumulativeCounters, setIsProcessing, lastResponseMetaRef, setCurrentStatus]);
|
|
15635
|
+
}, [agent, addMessage, manageTimer, resetCumulativeCounters, setIsProcessing, lastResponseMetaRef, setCurrentStatus, setTurnCount]);
|
|
15553
15636
|
const abort = useCallback2(() => {
|
|
15554
15637
|
abortedRef.current = true;
|
|
15555
15638
|
agent.abort();
|
|
@@ -15603,7 +15686,7 @@ var useAgent = (shouldAutoApprove, target) => {
|
|
|
15603
15686
|
};
|
|
15604
15687
|
|
|
15605
15688
|
// src/platform/tui/hooks/commands/index.ts
|
|
15606
|
-
import { useCallback as useCallback3 } from "react";
|
|
15689
|
+
import { useCallback as useCallback3, useMemo } from "react";
|
|
15607
15690
|
|
|
15608
15691
|
// src/platform/tui/constants/commands.ts
|
|
15609
15692
|
var COMMAND_DEFINITIONS = [
|
|
@@ -15710,7 +15793,7 @@ var createTargetCommands = (ctx) => ({
|
|
|
15710
15793
|
ctx.addMessage("error", "Usage: /target <ip>");
|
|
15711
15794
|
return;
|
|
15712
15795
|
}
|
|
15713
|
-
if (ctx.agent.getState().
|
|
15796
|
+
if (ctx.agent.getState().hasActiveEngagement()) {
|
|
15714
15797
|
await ctx.agent.resetSession();
|
|
15715
15798
|
ctx.addMessage("system", "Previous target data cleared. Starting fresh session for the new target.");
|
|
15716
15799
|
}
|
|
@@ -15732,8 +15815,8 @@ var createTargetCommands = (ctx) => ({
|
|
|
15732
15815
|
ctx.addMessage("system", `Target \u2192 ${args[0]}`);
|
|
15733
15816
|
},
|
|
15734
15817
|
[UI_COMMANDS.START]: async (args) => {
|
|
15735
|
-
if (!ctx.agent.getState().
|
|
15736
|
-
ctx.addMessage("error", "Set target first: /target <ip
|
|
15818
|
+
if (!ctx.agent.getState().hasActiveEngagement()) {
|
|
15819
|
+
ctx.addMessage("error", "Set a target first: /target <ip>, or ask me to perform a specific task.");
|
|
15737
15820
|
return;
|
|
15738
15821
|
}
|
|
15739
15822
|
if (!ctx.autoApproveModeRef.current) {
|
|
@@ -15747,8 +15830,8 @@ var createTargetCommands = (ctx) => ({
|
|
|
15747
15830
|
await ctx.executeTask(args.join(" ") || `Perform comprehensive penetration testing${targetInfo}`);
|
|
15748
15831
|
},
|
|
15749
15832
|
[UI_COMMANDS.START_SHORT]: async (args) => {
|
|
15750
|
-
if (!ctx.agent.getState().
|
|
15751
|
-
ctx.addMessage("error", "Set target first: /target <ip
|
|
15833
|
+
if (!ctx.agent.getState().hasActiveEngagement()) {
|
|
15834
|
+
ctx.addMessage("error", "Set a target first: /target <ip>, or ask me to perform a specific task.");
|
|
15752
15835
|
return;
|
|
15753
15836
|
}
|
|
15754
15837
|
if (!ctx.autoApproveModeRef.current) {
|
|
@@ -16090,7 +16173,7 @@ var createToggleCommands = (ctx) => ({
|
|
|
16090
16173
|
|
|
16091
16174
|
// src/platform/tui/hooks/commands/index.ts
|
|
16092
16175
|
var useCommands = (props) => {
|
|
16093
|
-
const ctx = {
|
|
16176
|
+
const ctx = useMemo(() => ({
|
|
16094
16177
|
agent: props.agent,
|
|
16095
16178
|
addMessage: props.addMessage,
|
|
16096
16179
|
showModal: props.showModal,
|
|
@@ -16101,13 +16184,24 @@ var useCommands = (props) => {
|
|
|
16101
16184
|
handleExit: props.handleExit,
|
|
16102
16185
|
isProcessingRef: props.isProcessingRef,
|
|
16103
16186
|
autoApproveModeRef: props.autoApproveModeRef
|
|
16104
|
-
}
|
|
16105
|
-
|
|
16187
|
+
}), [
|
|
16188
|
+
props.agent,
|
|
16189
|
+
props.addMessage,
|
|
16190
|
+
props.showModal,
|
|
16191
|
+
props.setMessages,
|
|
16192
|
+
props.executeTask,
|
|
16193
|
+
props.refreshStats,
|
|
16194
|
+
props.setAutoApproveMode,
|
|
16195
|
+
props.handleExit,
|
|
16196
|
+
props.isProcessingRef,
|
|
16197
|
+
props.autoApproveModeRef
|
|
16198
|
+
]);
|
|
16199
|
+
const handlers = useMemo(() => ({
|
|
16106
16200
|
...createSessionCommands(ctx),
|
|
16107
16201
|
...createTargetCommands(ctx),
|
|
16108
16202
|
...createDisplayCommands(ctx),
|
|
16109
16203
|
...createToggleCommands(ctx)
|
|
16110
|
-
};
|
|
16204
|
+
}), [ctx]);
|
|
16111
16205
|
const handleCommand = useCallback3(async (cmd, args) => {
|
|
16112
16206
|
const handler = handlers[cmd];
|
|
16113
16207
|
if (handler) {
|
|
@@ -16860,7 +16954,7 @@ var BrandingHeader = memo7(({ modelName, autoApproveMode, version, showGuide = f
|
|
|
16860
16954
|
/* @__PURE__ */ jsxs7(Box8, { gap: 2, children: [
|
|
16861
16955
|
/* @__PURE__ */ jsx9(Text8, { color: THEME.primary, children: "\u276F" }),
|
|
16862
16956
|
/* @__PURE__ */ jsx9(Text8, { color: THEME.white, children: "/target <ip>" }),
|
|
16863
|
-
/* @__PURE__ */ jsx9(Text8, { color: THEME.dimGray, children: "Set
|
|
16957
|
+
/* @__PURE__ */ jsx9(Text8, { color: THEME.dimGray, children: "Set a network target" })
|
|
16864
16958
|
] }),
|
|
16865
16959
|
/* @__PURE__ */ jsxs7(Box8, { gap: 2, children: [
|
|
16866
16960
|
/* @__PURE__ */ jsx9(Text8, { color: THEME.primary, children: "\u276F" }),
|
|
@@ -16872,7 +16966,7 @@ var BrandingHeader = memo7(({ modelName, autoApproveMode, version, showGuide = f
|
|
|
16872
16966
|
/* @__PURE__ */ jsx9(Text8, { color: THEME.white, children: "/help" }),
|
|
16873
16967
|
/* @__PURE__ */ jsx9(Text8, { color: THEME.dimGray, children: " Show all commands" })
|
|
16874
16968
|
] }),
|
|
16875
|
-
/* @__PURE__ */ jsx9(Box8, { paddingTop: 1, children: /* @__PURE__ */ jsx9(Text8, { color: THEME.
|
|
16969
|
+
/* @__PURE__ */ jsx9(Box8, { paddingTop: 1, children: /* @__PURE__ */ jsx9(Text8, { color: THEME.gray, children: 'Or type an objective (e.g. "Solve this python script").' }) })
|
|
16876
16970
|
] })
|
|
16877
16971
|
] })
|
|
16878
16972
|
] });
|
|
@@ -17093,8 +17187,8 @@ var StatusDisplay = memo11(({
|
|
|
17093
17187
|
});
|
|
17094
17188
|
|
|
17095
17189
|
// src/platform/tui/components/ChatInput.tsx
|
|
17096
|
-
import { useMemo, useCallback as useCallback7, useRef as
|
|
17097
|
-
import { Box as Box16, Text as
|
|
17190
|
+
import { useMemo as useMemo2, useCallback as useCallback7, useRef as useRef7, memo as memo12, useState as useState6, useEffect as useEffect8 } from "react";
|
|
17191
|
+
import { Box as Box16, Text as Text18, useInput as useInput3 } from "ink";
|
|
17098
17192
|
|
|
17099
17193
|
// src/platform/tui/components/input/AutocompletePreview.tsx
|
|
17100
17194
|
import { Box as Box13, Text as Text14 } from "ink";
|
|
@@ -17123,9 +17217,74 @@ var AutocompletePreview = ({
|
|
|
17123
17217
|
};
|
|
17124
17218
|
|
|
17125
17219
|
// src/platform/tui/components/input/SecretInputArea.tsx
|
|
17126
|
-
import { Box as Box14, Text as
|
|
17127
|
-
|
|
17220
|
+
import { Box as Box14, Text as Text16, useStdout } from "ink";
|
|
17221
|
+
|
|
17222
|
+
// src/platform/tui/components/input/SimpleTextInput.tsx
|
|
17223
|
+
import { useState as useState5, useEffect as useEffect7, useRef as useRef6 } from "react";
|
|
17224
|
+
import { Text as Text15, useInput as useInput2 } from "ink";
|
|
17128
17225
|
import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
17226
|
+
var SimpleTextInput = ({
|
|
17227
|
+
value,
|
|
17228
|
+
onChange,
|
|
17229
|
+
onSubmit,
|
|
17230
|
+
placeholder,
|
|
17231
|
+
isPassword
|
|
17232
|
+
}) => {
|
|
17233
|
+
const [cursor, setCursor] = useState5(Array.from(value || "").length);
|
|
17234
|
+
const valueRef = useRef6(value || "");
|
|
17235
|
+
valueRef.current = value || "";
|
|
17236
|
+
useEffect7(() => {
|
|
17237
|
+
const len = Array.from(valueRef.current).length;
|
|
17238
|
+
if (cursor > len) setCursor(len);
|
|
17239
|
+
}, [value, cursor]);
|
|
17240
|
+
useInput2((input, key) => {
|
|
17241
|
+
if (key.ctrl && input === "c") return;
|
|
17242
|
+
if (key.upArrow || key.downArrow || key.tab) return;
|
|
17243
|
+
if (key.return) {
|
|
17244
|
+
onSubmit?.(valueRef.current);
|
|
17245
|
+
return;
|
|
17246
|
+
}
|
|
17247
|
+
const chars2 = Array.from(valueRef.current);
|
|
17248
|
+
let c2 = cursor;
|
|
17249
|
+
if (key.leftArrow) {
|
|
17250
|
+
c2 = Math.max(0, c2 - 1);
|
|
17251
|
+
} else if (key.rightArrow) {
|
|
17252
|
+
c2 = Math.min(chars2.length, c2 + 1);
|
|
17253
|
+
} else if (key.backspace || key.delete) {
|
|
17254
|
+
if (c2 > 0) {
|
|
17255
|
+
chars2.splice(c2 - 1, 1);
|
|
17256
|
+
c2--;
|
|
17257
|
+
onChange(chars2.join(""));
|
|
17258
|
+
}
|
|
17259
|
+
} else if (input) {
|
|
17260
|
+
const inputChars = Array.from(input);
|
|
17261
|
+
chars2.splice(c2, 0, ...inputChars);
|
|
17262
|
+
c2 += inputChars.length;
|
|
17263
|
+
onChange(chars2.join(""));
|
|
17264
|
+
}
|
|
17265
|
+
setCursor(c2);
|
|
17266
|
+
}, { isActive: true });
|
|
17267
|
+
const displayValue = isPassword ? "\u2022".repeat(Array.from(valueRef.current).length) : valueRef.current;
|
|
17268
|
+
const chars = Array.from(displayValue);
|
|
17269
|
+
if (chars.length === 0) {
|
|
17270
|
+
return /* @__PURE__ */ jsxs12(Text15, { children: [
|
|
17271
|
+
/* @__PURE__ */ jsx17(Text15, { inverse: true, children: " " }),
|
|
17272
|
+
placeholder ? /* @__PURE__ */ jsx17(Text15, { color: "gray", children: placeholder }) : null
|
|
17273
|
+
] });
|
|
17274
|
+
}
|
|
17275
|
+
const c = Math.min(cursor, chars.length);
|
|
17276
|
+
const before = chars.slice(0, c).join("");
|
|
17277
|
+
const charAtCursor = c < chars.length ? chars[c] : " ";
|
|
17278
|
+
const after = chars.slice(c + 1).join("");
|
|
17279
|
+
return /* @__PURE__ */ jsxs12(Text15, { children: [
|
|
17280
|
+
before,
|
|
17281
|
+
/* @__PURE__ */ jsx17(Text15, { inverse: true, children: charAtCursor }),
|
|
17282
|
+
after
|
|
17283
|
+
] });
|
|
17284
|
+
};
|
|
17285
|
+
|
|
17286
|
+
// src/platform/tui/components/input/SecretInputArea.tsx
|
|
17287
|
+
import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
17129
17288
|
var OUTER_PADDING = 2;
|
|
17130
17289
|
var SecretInputArea = ({
|
|
17131
17290
|
inputRequest,
|
|
@@ -17136,29 +17295,28 @@ var SecretInputArea = ({
|
|
|
17136
17295
|
const { stdout } = useStdout();
|
|
17137
17296
|
const borderWidth = Math.max(10, (stdout?.columns ?? 80) - OUTER_PADDING);
|
|
17138
17297
|
const borderLine = "\u2501".repeat(borderWidth);
|
|
17139
|
-
return /* @__PURE__ */
|
|
17140
|
-
/* @__PURE__ */
|
|
17141
|
-
/* @__PURE__ */
|
|
17142
|
-
/* @__PURE__ */
|
|
17143
|
-
/* @__PURE__ */
|
|
17144
|
-
|
|
17298
|
+
return /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", children: [
|
|
17299
|
+
/* @__PURE__ */ jsx18(Box14, { children: /* @__PURE__ */ jsx18(Text16, { color: THEME.yellow, children: borderLine }) }),
|
|
17300
|
+
/* @__PURE__ */ jsxs13(Box14, { paddingX: 1, children: [
|
|
17301
|
+
/* @__PURE__ */ jsx18(Text16, { color: THEME.yellow, bold: true, children: "\u25B8 " }),
|
|
17302
|
+
/* @__PURE__ */ jsx18(
|
|
17303
|
+
SimpleTextInput,
|
|
17145
17304
|
{
|
|
17146
17305
|
value: secretInput,
|
|
17147
17306
|
onChange: setSecretInput,
|
|
17148
17307
|
onSubmit: onSecretSubmit,
|
|
17149
17308
|
placeholder: inputRequest.isPassword ? "(hidden \xB7 press Enter)" : "(type and press Enter)",
|
|
17150
|
-
|
|
17309
|
+
isPassword: inputRequest.isPassword
|
|
17151
17310
|
}
|
|
17152
17311
|
)
|
|
17153
17312
|
] }),
|
|
17154
|
-
/* @__PURE__ */
|
|
17313
|
+
/* @__PURE__ */ jsx18(Box14, { children: /* @__PURE__ */ jsx18(Text16, { color: THEME.yellow, children: borderLine }) })
|
|
17155
17314
|
] });
|
|
17156
17315
|
};
|
|
17157
17316
|
|
|
17158
17317
|
// src/platform/tui/components/input/NormalInputArea.tsx
|
|
17159
|
-
import { Box as Box15, Text as
|
|
17160
|
-
import
|
|
17161
|
-
import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
17318
|
+
import { Box as Box15, Text as Text17, useStdout as useStdout2 } from "ink";
|
|
17319
|
+
import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
17162
17320
|
var OUTER_PADDING2 = 2;
|
|
17163
17321
|
var NormalInputArea = ({
|
|
17164
17322
|
inputKey,
|
|
@@ -17170,12 +17328,12 @@ var NormalInputArea = ({
|
|
|
17170
17328
|
const { stdout } = useStdout2();
|
|
17171
17329
|
const borderWidth = Math.max(10, (stdout?.columns ?? 80) - OUTER_PADDING2);
|
|
17172
17330
|
const borderLine = "\u2500".repeat(borderWidth);
|
|
17173
|
-
return /* @__PURE__ */
|
|
17174
|
-
/* @__PURE__ */
|
|
17175
|
-
/* @__PURE__ */
|
|
17176
|
-
/* @__PURE__ */
|
|
17177
|
-
/* @__PURE__ */
|
|
17178
|
-
|
|
17331
|
+
return /* @__PURE__ */ jsxs14(Box15, { flexDirection: "column", children: [
|
|
17332
|
+
/* @__PURE__ */ jsx19(Box15, { children: /* @__PURE__ */ jsx19(Text17, { dimColor: true, color: THEME.dimGray, children: borderLine }) }),
|
|
17333
|
+
/* @__PURE__ */ jsxs14(Box15, { paddingX: 1, children: [
|
|
17334
|
+
/* @__PURE__ */ jsx19(Text17, { color: THEME.primary, children: "\u276F " }),
|
|
17335
|
+
/* @__PURE__ */ jsx19(
|
|
17336
|
+
SimpleTextInput,
|
|
17179
17337
|
{
|
|
17180
17338
|
value,
|
|
17181
17339
|
onChange,
|
|
@@ -17185,12 +17343,12 @@ var NormalInputArea = ({
|
|
|
17185
17343
|
inputKey
|
|
17186
17344
|
)
|
|
17187
17345
|
] }),
|
|
17188
|
-
/* @__PURE__ */
|
|
17346
|
+
/* @__PURE__ */ jsx19(Box15, { children: /* @__PURE__ */ jsx19(Text17, { dimColor: true, color: THEME.dimGray, children: borderLine }) })
|
|
17189
17347
|
] });
|
|
17190
17348
|
};
|
|
17191
17349
|
|
|
17192
17350
|
// src/platform/tui/components/ChatInput.tsx
|
|
17193
|
-
import { jsx as
|
|
17351
|
+
import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
17194
17352
|
var MAX_SUGGESTIONS = 6;
|
|
17195
17353
|
var ChatInput = memo12(({
|
|
17196
17354
|
value,
|
|
@@ -17205,31 +17363,43 @@ var ChatInput = memo12(({
|
|
|
17205
17363
|
const isSlashMode = value.startsWith("/");
|
|
17206
17364
|
const partialCmd = isSlashMode ? value.slice(1).split(" ")[0] : "";
|
|
17207
17365
|
const hasArgs = isSlashMode && value.includes(" ");
|
|
17208
|
-
const suggestions =
|
|
17366
|
+
const suggestions = useMemo2(() => {
|
|
17209
17367
|
if (!isSlashMode || hasArgs) return [];
|
|
17210
17368
|
return getMatchingCommands(partialCmd).slice(0, MAX_SUGGESTIONS);
|
|
17211
17369
|
}, [isSlashMode, partialCmd, hasArgs]);
|
|
17212
17370
|
const showPreview = isSlashMode && !hasArgs && suggestions.length > 0;
|
|
17213
|
-
const [selectedIdx, setSelectedIdx] =
|
|
17371
|
+
const [selectedIdx, setSelectedIdx] = useState6(0);
|
|
17214
17372
|
const clampedIdx = Math.min(selectedIdx, Math.max(0, suggestions.length - 1));
|
|
17215
|
-
const selectedIdxRef =
|
|
17373
|
+
const selectedIdxRef = useRef7(clampedIdx);
|
|
17216
17374
|
selectedIdxRef.current = clampedIdx;
|
|
17217
|
-
const suggestionsRef =
|
|
17375
|
+
const suggestionsRef = useRef7(suggestions);
|
|
17218
17376
|
suggestionsRef.current = suggestions;
|
|
17219
|
-
const isSlashModeRef =
|
|
17377
|
+
const isSlashModeRef = useRef7(isSlashMode);
|
|
17220
17378
|
isSlashModeRef.current = isSlashMode;
|
|
17221
|
-
const hasArgsRef =
|
|
17379
|
+
const hasArgsRef = useRef7(hasArgs);
|
|
17222
17380
|
hasArgsRef.current = hasArgs;
|
|
17223
|
-
const showPreviewRef =
|
|
17381
|
+
const showPreviewRef = useRef7(showPreview);
|
|
17224
17382
|
showPreviewRef.current = showPreview;
|
|
17225
|
-
const inputRequestRef =
|
|
17383
|
+
const inputRequestRef = useRef7(inputRequest);
|
|
17226
17384
|
inputRequestRef.current = inputRequest;
|
|
17227
|
-
const onChangeRef =
|
|
17385
|
+
const onChangeRef = useRef7(onChange);
|
|
17228
17386
|
onChangeRef.current = onChange;
|
|
17229
|
-
const
|
|
17230
|
-
|
|
17231
|
-
const
|
|
17232
|
-
|
|
17387
|
+
const latestValueRef = useRef7(value);
|
|
17388
|
+
latestValueRef.current = value;
|
|
17389
|
+
const handleLocalChange = useCallback7((newVal) => {
|
|
17390
|
+
latestValueRef.current = newVal;
|
|
17391
|
+
onChange(newVal);
|
|
17392
|
+
}, [onChange]);
|
|
17393
|
+
const latestSecretRef = useRef7(secretInput);
|
|
17394
|
+
latestSecretRef.current = secretInput;
|
|
17395
|
+
const handleSecretChange = useCallback7((newVal) => {
|
|
17396
|
+
latestSecretRef.current = newVal;
|
|
17397
|
+
setSecretInput(newVal);
|
|
17398
|
+
}, [setSecretInput]);
|
|
17399
|
+
const [pastedHint, setPastedHint] = useState6(null);
|
|
17400
|
+
const prevValueRef = useRef7(value);
|
|
17401
|
+
const pasteTimerRef = useRef7(null);
|
|
17402
|
+
useEffect8(() => {
|
|
17233
17403
|
const diff = value.length - prevValueRef.current.length;
|
|
17234
17404
|
if (diff > 20) {
|
|
17235
17405
|
if (pasteTimerRef.current) clearTimeout(pasteTimerRef.current);
|
|
@@ -17241,7 +17411,7 @@ var ChatInput = memo12(({
|
|
|
17241
17411
|
if (pasteTimerRef.current) clearTimeout(pasteTimerRef.current);
|
|
17242
17412
|
};
|
|
17243
17413
|
}, [value]);
|
|
17244
|
-
const [inputKey, setInputKey] =
|
|
17414
|
+
const [inputKey, setInputKey] = useState6(0);
|
|
17245
17415
|
const completeCommand = useCallback7((idx) => {
|
|
17246
17416
|
const sug = suggestionsRef.current;
|
|
17247
17417
|
if (!sug.length) return;
|
|
@@ -17252,13 +17422,14 @@ var ChatInput = memo12(({
|
|
|
17252
17422
|
setSelectedIdx(0);
|
|
17253
17423
|
setInputKey((k) => k + 1);
|
|
17254
17424
|
}, []);
|
|
17255
|
-
const onSubmitRef =
|
|
17425
|
+
const onSubmitRef = useRef7(onSubmit);
|
|
17256
17426
|
onSubmitRef.current = onSubmit;
|
|
17257
|
-
const wrappedOnSubmit = useCallback7((
|
|
17427
|
+
const wrappedOnSubmit = useCallback7((_staleVal) => {
|
|
17428
|
+
const finalValue = latestValueRef.current;
|
|
17258
17429
|
if (showPreviewRef.current) {
|
|
17259
17430
|
const sug = suggestionsRef.current;
|
|
17260
17431
|
if (!sug.length) {
|
|
17261
|
-
onSubmitRef.current(
|
|
17432
|
+
onSubmitRef.current(finalValue);
|
|
17262
17433
|
return;
|
|
17263
17434
|
}
|
|
17264
17435
|
const best = sug[Math.min(selectedIdxRef.current, sug.length - 1)];
|
|
@@ -17271,9 +17442,14 @@ var ChatInput = memo12(({
|
|
|
17271
17442
|
}
|
|
17272
17443
|
return;
|
|
17273
17444
|
}
|
|
17274
|
-
onSubmitRef.current(
|
|
17445
|
+
onSubmitRef.current(finalValue);
|
|
17275
17446
|
}, [completeCommand]);
|
|
17276
|
-
|
|
17447
|
+
const onSecretSubmitRef = useRef7(onSecretSubmit);
|
|
17448
|
+
onSecretSubmitRef.current = onSecretSubmit;
|
|
17449
|
+
const wrappedSecretSubmit = useCallback7((_staleVal) => {
|
|
17450
|
+
onSecretSubmitRef.current(latestSecretRef.current);
|
|
17451
|
+
}, []);
|
|
17452
|
+
useInput3(useCallback7((_input, key) => {
|
|
17277
17453
|
if (inputRequestRef.current.status === "active") return;
|
|
17278
17454
|
const sug = suggestionsRef.current;
|
|
17279
17455
|
const visible = showPreviewRef.current;
|
|
@@ -17289,8 +17465,8 @@ var ChatInput = memo12(({
|
|
|
17289
17465
|
completeCommand(selectedIdxRef.current);
|
|
17290
17466
|
}
|
|
17291
17467
|
}, [completeCommand]));
|
|
17292
|
-
return /* @__PURE__ */
|
|
17293
|
-
showPreview && /* @__PURE__ */
|
|
17468
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
|
|
17469
|
+
showPreview && /* @__PURE__ */ jsx20(
|
|
17294
17470
|
AutocompletePreview,
|
|
17295
17471
|
{
|
|
17296
17472
|
suggestions,
|
|
@@ -17299,36 +17475,36 @@ var ChatInput = memo12(({
|
|
|
17299
17475
|
),
|
|
17300
17476
|
inputRequest.status === "active" ? (
|
|
17301
17477
|
/* Active input request — yellow top/bottom border */
|
|
17302
|
-
/* @__PURE__ */
|
|
17478
|
+
/* @__PURE__ */ jsx20(
|
|
17303
17479
|
SecretInputArea,
|
|
17304
17480
|
{
|
|
17305
17481
|
inputRequest,
|
|
17306
17482
|
secretInput,
|
|
17307
|
-
setSecretInput,
|
|
17308
|
-
onSecretSubmit
|
|
17483
|
+
setSecretInput: handleSecretChange,
|
|
17484
|
+
onSecretSubmit: wrappedSecretSubmit
|
|
17309
17485
|
}
|
|
17310
17486
|
)
|
|
17311
17487
|
) : (
|
|
17312
17488
|
/* Normal input — dim top/bottom border */
|
|
17313
|
-
/* @__PURE__ */
|
|
17489
|
+
/* @__PURE__ */ jsx20(
|
|
17314
17490
|
NormalInputArea,
|
|
17315
17491
|
{
|
|
17316
17492
|
inputKey,
|
|
17317
17493
|
value,
|
|
17318
|
-
onChange,
|
|
17494
|
+
onChange: handleLocalChange,
|
|
17319
17495
|
onSubmit: wrappedOnSubmit,
|
|
17320
17496
|
placeholder
|
|
17321
17497
|
}
|
|
17322
17498
|
)
|
|
17323
17499
|
),
|
|
17324
|
-
pastedHint && /* @__PURE__ */
|
|
17500
|
+
pastedHint && /* @__PURE__ */ jsx20(Box16, { paddingX: 2, children: /* @__PURE__ */ jsx20(Text18, { dimColor: true, color: THEME.dimGray, children: pastedHint }) })
|
|
17325
17501
|
] });
|
|
17326
17502
|
});
|
|
17327
17503
|
|
|
17328
17504
|
// src/platform/tui/components/footer.tsx
|
|
17329
17505
|
import { memo as memo13 } from "react";
|
|
17330
|
-
import { Box as Box17, Text as
|
|
17331
|
-
import { jsx as
|
|
17506
|
+
import { Box as Box17, Text as Text19 } from "ink";
|
|
17507
|
+
import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
17332
17508
|
var CTX_WARN_THRESHOLD = 0.8;
|
|
17333
17509
|
var MAX_CONTEXT_TOKENS = LLM_LIMITS.streamMaxTokens;
|
|
17334
17510
|
var formatElapsed = (totalSeconds) => {
|
|
@@ -17344,7 +17520,7 @@ var formatElapsed = (totalSeconds) => {
|
|
|
17344
17520
|
var Footer = memo13(({ phase, targets, findings, todo, elapsedTime, isProcessing, totalTokens, turnCount }) => {
|
|
17345
17521
|
const ctxPct = totalTokens > 0 ? Math.round(totalTokens / MAX_CONTEXT_TOKENS * 100) : 0;
|
|
17346
17522
|
const ctxColor = ctxPct >= CTX_WARN_THRESHOLD * 100 ? THEME.yellow : THEME.dimGray;
|
|
17347
|
-
return /* @__PURE__ */
|
|
17523
|
+
return /* @__PURE__ */ jsxs16(
|
|
17348
17524
|
Box17,
|
|
17349
17525
|
{
|
|
17350
17526
|
width: "100%",
|
|
@@ -17352,44 +17528,44 @@ var Footer = memo13(({ phase, targets, findings, todo, elapsedTime, isProcessing
|
|
|
17352
17528
|
justifyContent: "space-between",
|
|
17353
17529
|
overflow: "hidden",
|
|
17354
17530
|
children: [
|
|
17355
|
-
/* @__PURE__ */
|
|
17356
|
-
/* @__PURE__ */
|
|
17531
|
+
/* @__PURE__ */ jsxs16(Box17, { gap: 2, children: [
|
|
17532
|
+
/* @__PURE__ */ jsxs16(Text19, { color: THEME.gray, children: [
|
|
17357
17533
|
"Phase: ",
|
|
17358
|
-
/* @__PURE__ */
|
|
17534
|
+
/* @__PURE__ */ jsx21(Text19, { color: THEME.white, children: phase })
|
|
17359
17535
|
] }),
|
|
17360
|
-
/* @__PURE__ */
|
|
17536
|
+
/* @__PURE__ */ jsxs16(Text19, { color: THEME.gray, children: [
|
|
17361
17537
|
"Targets: ",
|
|
17362
|
-
/* @__PURE__ */
|
|
17538
|
+
/* @__PURE__ */ jsx21(Text19, { color: THEME.white, children: targets })
|
|
17363
17539
|
] }),
|
|
17364
|
-
/* @__PURE__ */
|
|
17540
|
+
/* @__PURE__ */ jsxs16(Text19, { color: THEME.gray, children: [
|
|
17365
17541
|
"Findings: ",
|
|
17366
|
-
/* @__PURE__ */
|
|
17542
|
+
/* @__PURE__ */ jsx21(Text19, { color: THEME.white, children: findings })
|
|
17367
17543
|
] }),
|
|
17368
|
-
/* @__PURE__ */
|
|
17544
|
+
/* @__PURE__ */ jsxs16(Text19, { color: THEME.gray, children: [
|
|
17369
17545
|
"Tasks: ",
|
|
17370
|
-
/* @__PURE__ */
|
|
17546
|
+
/* @__PURE__ */ jsx21(Text19, { color: THEME.white, children: todo })
|
|
17371
17547
|
] })
|
|
17372
17548
|
] }),
|
|
17373
|
-
/* @__PURE__ */
|
|
17374
|
-
isProcessing ? /* @__PURE__ */
|
|
17375
|
-
/* @__PURE__ */
|
|
17376
|
-
/* @__PURE__ */
|
|
17377
|
-
] }) : /* @__PURE__ */
|
|
17378
|
-
turnCount > 0 && /* @__PURE__ */
|
|
17549
|
+
/* @__PURE__ */ jsxs16(Box17, { gap: 2, children: [
|
|
17550
|
+
isProcessing ? /* @__PURE__ */ jsxs16(Box17, { gap: 1, children: [
|
|
17551
|
+
/* @__PURE__ */ jsx21(Text19, { dimColor: true, color: THEME.dimGray, children: "[ESC] abort" }),
|
|
17552
|
+
/* @__PURE__ */ jsx21(Text19, { dimColor: true, color: THEME.dimGray, children: "[^C\xD72] exit" })
|
|
17553
|
+
] }) : /* @__PURE__ */ jsx21(Text19, { dimColor: true, color: THEME.dimGray, children: "[/help] commands" }),
|
|
17554
|
+
turnCount > 0 && /* @__PURE__ */ jsxs16(Text19, { dimColor: true, color: THEME.dimGray, children: [
|
|
17379
17555
|
"turn:",
|
|
17380
17556
|
turnCount
|
|
17381
17557
|
] }),
|
|
17382
|
-
totalTokens > 0 && /* @__PURE__ */
|
|
17558
|
+
totalTokens > 0 && /* @__PURE__ */ jsxs16(Text19, { dimColor: true, color: ctxColor, children: [
|
|
17383
17559
|
"ctx:",
|
|
17384
17560
|
ctxPct,
|
|
17385
17561
|
"%"
|
|
17386
17562
|
] }),
|
|
17387
|
-
totalTokens > 0 && /* @__PURE__ */
|
|
17563
|
+
totalTokens > 0 && /* @__PURE__ */ jsxs16(Text19, { dimColor: true, color: THEME.dimGray, children: [
|
|
17388
17564
|
"\u2191",
|
|
17389
17565
|
formatTokens(totalTokens)
|
|
17390
17566
|
] }),
|
|
17391
|
-
/* @__PURE__ */
|
|
17392
|
-
/* @__PURE__ */
|
|
17567
|
+
/* @__PURE__ */ jsx21(Text19, { color: isProcessing ? THEME.primary : THEME.gray, children: isProcessing ? "Running " : "Idle " }),
|
|
17568
|
+
/* @__PURE__ */ jsx21(Text19, { color: THEME.white, children: formatElapsed(elapsedTime) })
|
|
17393
17569
|
] })
|
|
17394
17570
|
]
|
|
17395
17571
|
}
|
|
@@ -17398,9 +17574,9 @@ var Footer = memo13(({ phase, targets, findings, todo, elapsedTime, isProcessing
|
|
|
17398
17574
|
var footer_default = Footer;
|
|
17399
17575
|
|
|
17400
17576
|
// src/platform/tui/components/Modal.tsx
|
|
17401
|
-
import { useMemo as
|
|
17402
|
-
import { Box as Box18, Text as
|
|
17403
|
-
import { jsx as
|
|
17577
|
+
import { useMemo as useMemo3, memo as memo14, useCallback as useCallback8, useRef as useRef8 } from "react";
|
|
17578
|
+
import { Box as Box18, Text as Text20, useStdout as useStdout3, useInput as useInput4 } from "ink";
|
|
17579
|
+
import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
17404
17580
|
var MODAL_TITLES = {
|
|
17405
17581
|
findings: "\u25C8 FINDINGS \u25C8",
|
|
17406
17582
|
graph: "\u25C8 ATTACK GRAPH \u25C8",
|
|
@@ -17413,21 +17589,21 @@ var Modal = memo14(({
|
|
|
17413
17589
|
onScroll,
|
|
17414
17590
|
onClose
|
|
17415
17591
|
}) => {
|
|
17416
|
-
const onScrollRef =
|
|
17592
|
+
const onScrollRef = useRef8(onScroll);
|
|
17417
17593
|
onScrollRef.current = onScroll;
|
|
17418
|
-
const onCloseRef =
|
|
17594
|
+
const onCloseRef = useRef8(onClose);
|
|
17419
17595
|
onCloseRef.current = onClose;
|
|
17420
17596
|
const { stdout } = useStdout3();
|
|
17421
17597
|
const terminalHeight = stdout?.rows ?? 24;
|
|
17422
17598
|
const terminalWidth = (stdout?.columns ?? 80) - 4;
|
|
17423
17599
|
const maxHeight = Math.max(1, terminalHeight - 6);
|
|
17424
|
-
const lines =
|
|
17600
|
+
const lines = useMemo3(() => content.split("\n"), [content]);
|
|
17425
17601
|
const totalLines = lines.length;
|
|
17426
|
-
const visibleLines =
|
|
17602
|
+
const visibleLines = useMemo3(
|
|
17427
17603
|
() => lines.slice(scrollOffset, scrollOffset + maxHeight),
|
|
17428
17604
|
[lines, scrollOffset, maxHeight]
|
|
17429
17605
|
);
|
|
17430
|
-
|
|
17606
|
+
useInput4(useCallback8((input, key) => {
|
|
17431
17607
|
if (key.escape || input === "q") {
|
|
17432
17608
|
onCloseRef.current();
|
|
17433
17609
|
} else if (key.upArrow || input === "k") {
|
|
@@ -17444,19 +17620,19 @@ var Modal = memo14(({
|
|
|
17444
17620
|
const endLine = Math.min(scrollOffset + maxHeight, totalLines);
|
|
17445
17621
|
const scrollbarHeight = Math.max(1, Math.floor(maxHeight * (maxHeight / Math.max(totalLines, 1))));
|
|
17446
17622
|
const scrollbarPosition = totalLines > maxHeight ? Math.floor(scrollOffset / (totalLines - maxHeight) * (maxHeight - scrollbarHeight)) : 0;
|
|
17447
|
-
return /* @__PURE__ */
|
|
17623
|
+
return /* @__PURE__ */ jsxs17(
|
|
17448
17624
|
Box18,
|
|
17449
17625
|
{
|
|
17450
17626
|
flexDirection: "column",
|
|
17451
17627
|
width: terminalWidth,
|
|
17452
17628
|
height: terminalHeight,
|
|
17453
17629
|
children: [
|
|
17454
|
-
/* @__PURE__ */
|
|
17630
|
+
/* @__PURE__ */ jsx22(Box18, { justifyContent: "center", marginBottom: 0, children: /* @__PURE__ */ jsx22(Text20, { color: THEME.cyan, bold: true, children: (() => {
|
|
17455
17631
|
const title = MODAL_TITLES[type];
|
|
17456
17632
|
const sideWidth = Math.max(3, Math.floor((terminalWidth - title.length - 2) / 2));
|
|
17457
17633
|
return `${"\u2500".repeat(sideWidth)} ${title} ${"\u2500".repeat(sideWidth)}`;
|
|
17458
17634
|
})() }) }),
|
|
17459
|
-
/* @__PURE__ */
|
|
17635
|
+
/* @__PURE__ */ jsx22(
|
|
17460
17636
|
Box18,
|
|
17461
17637
|
{
|
|
17462
17638
|
flexDirection: "column",
|
|
@@ -17466,17 +17642,17 @@ var Modal = memo14(({
|
|
|
17466
17642
|
flexGrow: 1,
|
|
17467
17643
|
children: visibleLines.map((line, i) => {
|
|
17468
17644
|
const showScrollbar = totalLines > maxHeight && i >= scrollbarPosition && i < scrollbarPosition + scrollbarHeight;
|
|
17469
|
-
return /* @__PURE__ */
|
|
17470
|
-
/* @__PURE__ */
|
|
17471
|
-
/* @__PURE__ */
|
|
17472
|
-
totalLines > maxHeight && /* @__PURE__ */
|
|
17645
|
+
return /* @__PURE__ */ jsxs17(Box18, { children: [
|
|
17646
|
+
/* @__PURE__ */ jsx22(Text20, { color: THEME.white, wrap: "truncate", children: line }),
|
|
17647
|
+
/* @__PURE__ */ jsx22(Box18, { flexGrow: 1 }),
|
|
17648
|
+
totalLines > maxHeight && /* @__PURE__ */ jsx22(Text20, { color: showScrollbar ? THEME.cyan : THEME.dimGray, children: showScrollbar ? "\u2588" : "\u2502" })
|
|
17473
17649
|
] }, i);
|
|
17474
17650
|
})
|
|
17475
17651
|
}
|
|
17476
17652
|
),
|
|
17477
|
-
/* @__PURE__ */
|
|
17478
|
-
/* @__PURE__ */
|
|
17479
|
-
/* @__PURE__ */
|
|
17653
|
+
/* @__PURE__ */ jsxs17(Box18, { justifyContent: "space-between", paddingX: 1, children: [
|
|
17654
|
+
/* @__PURE__ */ jsx22(Text20, { dimColor: true, color: THEME.gray, children: "\u2191\u2193/jk: scroll | g/G: top/bottom | ESC/q: close" }),
|
|
17655
|
+
/* @__PURE__ */ jsxs17(Text20, { dimColor: true, color: THEME.cyan, children: [
|
|
17480
17656
|
startLine,
|
|
17481
17657
|
"-",
|
|
17482
17658
|
endLine,
|
|
@@ -17491,7 +17667,7 @@ var Modal = memo14(({
|
|
|
17491
17667
|
|
|
17492
17668
|
// src/platform/tui/components/app/bottom-region.tsx
|
|
17493
17669
|
import { Box as Box19 } from "ink";
|
|
17494
|
-
import { jsx as
|
|
17670
|
+
import { jsx as jsx23, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
17495
17671
|
var BottomRegion = ({
|
|
17496
17672
|
input,
|
|
17497
17673
|
setInput,
|
|
@@ -17515,8 +17691,8 @@ var BottomRegion = ({
|
|
|
17515
17691
|
const suggestionCount = isSlashMode && !hasArgs && inputRequest.status !== "active" ? Math.min(getMatchingCommands(partialCmd).length, MAX_SUGGESTIONS) : 0;
|
|
17516
17692
|
const previewHeight = suggestionCount;
|
|
17517
17693
|
const bottomMinHeight = 7;
|
|
17518
|
-
return /* @__PURE__ */
|
|
17519
|
-
/* @__PURE__ */
|
|
17694
|
+
return /* @__PURE__ */ jsxs18(Box19, { flexDirection: "column", minHeight: bottomMinHeight, children: [
|
|
17695
|
+
/* @__PURE__ */ jsx23(
|
|
17520
17696
|
StatusDisplay,
|
|
17521
17697
|
{
|
|
17522
17698
|
retryState,
|
|
@@ -17526,7 +17702,7 @@ var BottomRegion = ({
|
|
|
17526
17702
|
inputRequest
|
|
17527
17703
|
}
|
|
17528
17704
|
),
|
|
17529
|
-
/* @__PURE__ */
|
|
17705
|
+
/* @__PURE__ */ jsx23(
|
|
17530
17706
|
ChatInput,
|
|
17531
17707
|
{
|
|
17532
17708
|
value: input,
|
|
@@ -17539,7 +17715,7 @@ var BottomRegion = ({
|
|
|
17539
17715
|
onSecretSubmit: handleSecretSubmit
|
|
17540
17716
|
}
|
|
17541
17717
|
),
|
|
17542
|
-
/* @__PURE__ */
|
|
17718
|
+
/* @__PURE__ */ jsx23(Box19, { marginTop: 1, children: /* @__PURE__ */ jsx23(
|
|
17543
17719
|
footer_default,
|
|
17544
17720
|
{
|
|
17545
17721
|
phase: stats.phase,
|
|
@@ -17556,16 +17732,16 @@ var BottomRegion = ({
|
|
|
17556
17732
|
};
|
|
17557
17733
|
|
|
17558
17734
|
// src/platform/tui/app.tsx
|
|
17559
|
-
import { jsx as
|
|
17735
|
+
import { jsx as jsx24, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
17560
17736
|
var MODEL_NAME = getModel() || DEFAULT_MODEL;
|
|
17561
17737
|
var App = ({ autoApprove = false, target }) => {
|
|
17562
17738
|
const { exit } = useApp();
|
|
17563
17739
|
const { stdout } = useStdout4();
|
|
17564
17740
|
const terminalWidth = stdout?.columns ?? 80;
|
|
17565
|
-
const [input, setInput] =
|
|
17566
|
-
const [secretInput, setSecretInput] =
|
|
17567
|
-
const [autoApproveMode, setAutoApproveMode] =
|
|
17568
|
-
const [modal, setModal] =
|
|
17741
|
+
const [input, setInput] = useState7("");
|
|
17742
|
+
const [secretInput, setSecretInput] = useState7("");
|
|
17743
|
+
const [autoApproveMode, setAutoApproveMode] = useState7(autoApprove);
|
|
17744
|
+
const [modal, setModal] = useState7({ type: null, content: "", scrollOffset: 0 });
|
|
17569
17745
|
const {
|
|
17570
17746
|
agent,
|
|
17571
17747
|
messages,
|
|
@@ -17587,15 +17763,15 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
17587
17763
|
liveReasoning,
|
|
17588
17764
|
isReasoning
|
|
17589
17765
|
} = useAgent(autoApproveMode, target);
|
|
17590
|
-
const isProcessingRef =
|
|
17766
|
+
const isProcessingRef = useRef9(isProcessing);
|
|
17591
17767
|
isProcessingRef.current = isProcessing;
|
|
17592
|
-
const autoApproveModeRef =
|
|
17768
|
+
const autoApproveModeRef = useRef9(autoApproveMode);
|
|
17593
17769
|
autoApproveModeRef.current = autoApproveMode;
|
|
17594
|
-
const inputRequestRef =
|
|
17770
|
+
const inputRequestRef = useRef9(inputRequest);
|
|
17595
17771
|
inputRequestRef.current = inputRequest;
|
|
17596
|
-
const isModalOpenRef =
|
|
17772
|
+
const isModalOpenRef = useRef9(!!modal.type);
|
|
17597
17773
|
isModalOpenRef.current = !!modal.type;
|
|
17598
|
-
const inputRef =
|
|
17774
|
+
const inputRef = useRef9(input);
|
|
17599
17775
|
inputRef.current = input;
|
|
17600
17776
|
const clearInput = useCallback9(() => {
|
|
17601
17777
|
setInput("");
|
|
@@ -17638,6 +17814,15 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
17638
17814
|
isProcessingRef,
|
|
17639
17815
|
autoApproveModeRef
|
|
17640
17816
|
});
|
|
17817
|
+
const handleSecretSubmit = useCallback9((value) => {
|
|
17818
|
+
const ir = inputRequestRef.current;
|
|
17819
|
+
if (ir.status !== "active") return;
|
|
17820
|
+
const displayText = ir.isPassword ? "\u2022".repeat(Math.min(value.length, TUI_DISPLAY_LIMITS.PASSWORD_MASK_MAX)) : value;
|
|
17821
|
+
addMessage("system", `${UI_MESSAGES.SECRET_PREFIX}${displayText}`);
|
|
17822
|
+
ir.resolve(value);
|
|
17823
|
+
setInputRequest({ status: "inactive" });
|
|
17824
|
+
setSecretInput("");
|
|
17825
|
+
}, [addMessage, setInputRequest]);
|
|
17641
17826
|
const handleSubmit = useCallback9(async (value) => {
|
|
17642
17827
|
const trimmed = value.trim();
|
|
17643
17828
|
if (!trimmed) return;
|
|
@@ -17646,22 +17831,15 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
17646
17831
|
if (trimmed.startsWith("/")) {
|
|
17647
17832
|
const [cmd, ...args] = trimmed.slice(1).split(" ");
|
|
17648
17833
|
await handleCommand(cmd, args);
|
|
17834
|
+
} else if (inputRequestRef.current.status === "active") {
|
|
17835
|
+
handleSecretSubmit(trimmed);
|
|
17649
17836
|
} else if (isProcessingRef.current) {
|
|
17650
17837
|
agent.enqueueUserInput(trimmed);
|
|
17651
|
-
|
|
17838
|
+
agent.abort();
|
|
17652
17839
|
} else {
|
|
17653
17840
|
await executeTask(trimmed);
|
|
17654
17841
|
}
|
|
17655
|
-
}, [agent, addMessage, executeTask, handleCommand]);
|
|
17656
|
-
const handleSecretSubmit = useCallback9((value) => {
|
|
17657
|
-
const ir = inputRequestRef.current;
|
|
17658
|
-
if (ir.status !== "active") return;
|
|
17659
|
-
const displayText = ir.isPassword ? "\u2022".repeat(Math.min(value.length, TUI_DISPLAY_LIMITS.PASSWORD_MASK_MAX)) : value;
|
|
17660
|
-
addMessage("system", `${UI_MESSAGES.SECRET_PREFIX}${displayText}`);
|
|
17661
|
-
ir.resolve(value);
|
|
17662
|
-
setInputRequest({ status: "inactive" });
|
|
17663
|
-
setSecretInput("");
|
|
17664
|
-
}, [addMessage, setInputRequest]);
|
|
17842
|
+
}, [agent, addMessage, executeTask, handleCommand, handleSecretSubmit]);
|
|
17665
17843
|
useKeyboardShortcuts({
|
|
17666
17844
|
addMessage,
|
|
17667
17845
|
handleExit,
|
|
@@ -17674,7 +17852,7 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
17674
17852
|
clearInput
|
|
17675
17853
|
});
|
|
17676
17854
|
if (modal.type) {
|
|
17677
|
-
return /* @__PURE__ */
|
|
17855
|
+
return /* @__PURE__ */ jsx24(Box20, { flexDirection: "column", paddingX: 1, width: terminalWidth, children: /* @__PURE__ */ jsx24(
|
|
17678
17856
|
Modal,
|
|
17679
17857
|
{
|
|
17680
17858
|
type: modal.type,
|
|
@@ -17685,8 +17863,8 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
17685
17863
|
}
|
|
17686
17864
|
) });
|
|
17687
17865
|
}
|
|
17688
|
-
return /* @__PURE__ */
|
|
17689
|
-
/* @__PURE__ */
|
|
17866
|
+
return /* @__PURE__ */ jsxs19(Box20, { flexDirection: "column", width: terminalWidth, children: [
|
|
17867
|
+
/* @__PURE__ */ jsx24(Box20, { flexDirection: "column", children: /* @__PURE__ */ jsx24(
|
|
17690
17868
|
MessageList,
|
|
17691
17869
|
{
|
|
17692
17870
|
messages,
|
|
@@ -17695,14 +17873,14 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
17695
17873
|
version: APP_VERSION
|
|
17696
17874
|
}
|
|
17697
17875
|
) }),
|
|
17698
|
-
/* @__PURE__ */
|
|
17876
|
+
/* @__PURE__ */ jsx24(
|
|
17699
17877
|
LiveReasoningPanel,
|
|
17700
17878
|
{
|
|
17701
17879
|
content: liveReasoning,
|
|
17702
17880
|
isReasoning
|
|
17703
17881
|
}
|
|
17704
17882
|
),
|
|
17705
|
-
/* @__PURE__ */
|
|
17883
|
+
/* @__PURE__ */ jsx24(AnimationProvider, { children: /* @__PURE__ */ jsx24(
|
|
17706
17884
|
BottomRegion,
|
|
17707
17885
|
{
|
|
17708
17886
|
input,
|
|
@@ -17721,13 +17899,13 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
17721
17899
|
totalTokens: currentTokens,
|
|
17722
17900
|
turnCount
|
|
17723
17901
|
}
|
|
17724
|
-
)
|
|
17902
|
+
) })
|
|
17725
17903
|
] });
|
|
17726
17904
|
};
|
|
17727
17905
|
var app_default = App;
|
|
17728
17906
|
|
|
17729
17907
|
// src/platform/tui/cli/commands/interactive.tsx
|
|
17730
|
-
import { jsx as
|
|
17908
|
+
import { jsx as jsx25 } from "react/jsx-runtime";
|
|
17731
17909
|
async function interactiveAction(options) {
|
|
17732
17910
|
const { dangerouslySkipPermissions: skipPermissions = false, target } = options;
|
|
17733
17911
|
console.clear();
|
|
@@ -17736,13 +17914,13 @@ async function interactiveAction(options) {
|
|
|
17736
17914
|
console.log(chalk.hex(HEX.red)("[!] All tool executions will be auto-approved!\n"));
|
|
17737
17915
|
}
|
|
17738
17916
|
const { waitUntilExit } = render(
|
|
17739
|
-
/* @__PURE__ */
|
|
17917
|
+
/* @__PURE__ */ jsx25(
|
|
17740
17918
|
app_default,
|
|
17741
17919
|
{
|
|
17742
17920
|
autoApprove: skipPermissions,
|
|
17743
17921
|
target
|
|
17744
17922
|
}
|
|
17745
|
-
)
|
|
17923
|
+
)
|
|
17746
17924
|
);
|
|
17747
17925
|
await waitUntilExit();
|
|
17748
17926
|
}
|
package/dist/prompts/base.md
CHANGED
|
@@ -8,10 +8,11 @@ You have direct access to all tools. **If a tool or PoC doesn't exist, build it
|
|
|
8
8
|
|
|
9
9
|
**On the first turn, classify intent BEFORE any action:**
|
|
10
10
|
|
|
11
|
-
1. **
|
|
12
|
-
2. **
|
|
13
|
-
3. **
|
|
14
|
-
4. **
|
|
11
|
+
1. **Network Pentest** (IP/domain) → Execute reconnaissance immediately.
|
|
12
|
+
2. **Artifact / CTF Task** (file, code snippet, math problem, reversing/crypto task) → Treat the provided input as the Engagement Objective. Start local static analysis, write solver scripts, or use tools immediately. **Do NOT ask for a target IP.**
|
|
13
|
+
3. **Greeting/Small Talk** → `ask_user` to greet and ask for the objective. No other tools.
|
|
14
|
+
4. **Question/Help** → Answer via `ask_user`.
|
|
15
|
+
5. **Unclear input** → `ask_user` to clarify. Do not assume it's a network target.
|
|
15
16
|
|
|
16
17
|
## Subsequent Turns: Every Turn Must Produce Tool Calls
|
|
17
18
|
|
|
@@ -134,14 +135,15 @@ Self-check every turn: Did I find a vuln but not call `add_finding`? Call it now
|
|
|
134
135
|
|
|
135
136
|
### 2.5. Phase Transition Signals — When to Call `update_phase`
|
|
136
137
|
```
|
|
137
|
-
RECON → vuln_analysis: 1+ service identified
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
RECON → vuln_analysis: [Network] 1+ service identified — ATTACK IMMEDIATELY
|
|
139
|
+
[Artifact] File type identified, strings/static analysis complete
|
|
140
|
+
vuln_analysis → exploit: [Network] Exploit path identified OR brute-force ready
|
|
141
|
+
[Artifact] Logic understood (e.g. crypto flaw, reverse engineering logic mapped) — ready to write solver
|
|
140
142
|
exploit → post_exploitation: Shell obtained AND promoted (active_shell process active)
|
|
141
143
|
post_exploitation → lateral: root/SYSTEM achieved on current host
|
|
142
|
-
ANY_PHASE → report: All targets compromised OR time is up
|
|
144
|
+
ANY_PHASE → report: All targets compromised, flag obtained, OR time is up
|
|
143
145
|
```
|
|
144
|
-
**ATTACK OVER RECON: Transition to vuln_analysis as soon as ANY
|
|
146
|
+
**ATTACK OVER RECON: Transition to vuln_analysis as soon as ANY attack surface or file property is found.**
|
|
145
147
|
**NEVER transition away from a phase while HIGH-priority vectors remain untested.**
|
|
146
148
|
|
|
147
149
|
### 3. ask_user Rules
|
|
@@ -26,11 +26,20 @@ Your thought process must be visible. Before each tool call: OBSERVE what change
|
|
|
26
26
|
|
|
27
27
|
## Kill Chain Position — Know Where You Are
|
|
28
28
|
|
|
29
|
+
Determine your engagement type and track your position:
|
|
30
|
+
|
|
31
|
+
**[Network Pentest Chain]**
|
|
29
32
|
```
|
|
30
33
|
External Recon → Service Discovery → Vuln ID → Initial Access → Shell Stabilization
|
|
31
34
|
→ Situational Awareness → Privilege Escalation → Credential Harvest → Lateral Movement → Objective
|
|
32
35
|
```
|
|
33
36
|
|
|
37
|
+
**[Artifact / CTF Chain (Rev, Crypto, Forensics)]**
|
|
38
|
+
```
|
|
39
|
+
File/Input ID (file, strings) → Static Analysis (Code Review, Decompilation) → Logic Mapping
|
|
40
|
+
→ Dynamic Analysis (Debugger, Interaction) → Exploit/Solver Script Generation → Flag Capture
|
|
41
|
+
```
|
|
42
|
+
|
|
34
43
|
Know your position before every turn. Act accordingly.
|
|
35
44
|
|
|
36
45
|
## After First Shell — See base.md "Shell Lifecycle" + post.md pipeline
|
|
@@ -62,11 +62,28 @@ STAGE 5 — FORK PLAN: If STAGE 4 fails, which PATH becomes Priority 2? Decla
|
|
|
62
62
|
├─ Initial access granted but no obvious privesc → hidden connector exists
|
|
63
63
|
├─ AD environment → lateral chain required before final objective
|
|
64
64
|
├─ Multiple hops needed (pivot → internal host → target)
|
|
65
|
-
|
|
65
|
+
├─ Standard tools all return clean/negative (custom path required)
|
|
66
|
+
└─ Complex Cryptography/Reverse Engineering logic is encountered (requires solver script)
|
|
66
67
|
```
|
|
67
68
|
|
|
68
69
|
After 3 consecutive failures on the current path → **re-derive STAGE 3 entirely** with new hypotheses.
|
|
69
70
|
|
|
71
|
+
## MISSION FLEXIBILITY & INTENT ADAPTATION
|
|
72
|
+
|
|
73
|
+
You must be hypersensitive to changes in user intent. If new user input appears in the snapshot, analyze it immediately.
|
|
74
|
+
|
|
75
|
+
### 1. MISSION ABANDONMENT / PIVOT
|
|
76
|
+
If the user explicitly changes the topic (e.g., "Stop hacking, help me with development", "Explain this code", "Let's just chat"):
|
|
77
|
+
├─ IMMEDIATE PIVOT: Abandon current pentesting priorities.
|
|
78
|
+
├─ RE-CLASSIFY: Transition to CONVERSATION or DEVELOPMENT mode.
|
|
79
|
+
└─ DO NOT: Do not demand a pentesting target if the user wants to do something else.
|
|
80
|
+
|
|
81
|
+
### 2. INTERACTIVE INTERVENTION
|
|
82
|
+
If the user provides feedback during an active attack (e.g., "Try this payload instead", "Don't scan that port"):
|
|
83
|
+
├─ SUPERCEDE: User instructions supercede your previous tactical plan.
|
|
84
|
+
├─ ACKNOWLEDGE: Incorporate the user's specific hint into PRIORITY 1.
|
|
85
|
+
└─ ADAPT: Explain how the user's input changes the current attack chain.
|
|
86
|
+
|
|
70
87
|
---
|
|
71
88
|
|
|
72
89
|
## STRATEGIC REASONING FRAMEWORK
|
|
@@ -324,13 +341,15 @@ ORDER update_phase when these conditions are met:
|
|
|
324
341
|
recon → vuln_analysis:
|
|
325
342
|
├─ 1+ service identified (version optional) — ATTACK IMMEDIATELY, refine during exploitation
|
|
326
343
|
├─ OSINT complete (shodan/github/crt.sh checked)
|
|
327
|
-
|
|
344
|
+
├─ Web surface mapped (get_web_attack_surface called if HTTP found)
|
|
345
|
+
└─ [Artifact] File type identified, strings/static analysis complete
|
|
328
346
|
|
|
329
347
|
vuln_analysis → exploit:
|
|
330
348
|
├─ 1+ finding with confidence ≥ 50 AND a concrete exploit path identified
|
|
331
349
|
├─ Specific CVE confirmed applicable (version matches, PoC available)
|
|
332
350
|
├─ Or: critical misconfiguration found (default creds, exposed .env, anon access)
|
|
333
|
-
|
|
351
|
+
├─ Or: brute-force/credential testing ready on identified service
|
|
352
|
+
└─ [Artifact] Logic understood (e.g. crypto flaw, reverse engineering logic mapped) — ready to write solver
|
|
334
353
|
|
|
335
354
|
exploit → post_exploitation:
|
|
336
355
|
├─ Shell obtained AND promoted (active_shell process is running)
|