pentesting 0.73.2 → 0.73.3
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/agent-tool-JEFUBDZE.js +989 -0
- package/dist/{chunk-BGEXGHPB.js → chunk-BKWCGMSV.js} +879 -427
- package/dist/{chunk-YFDJI3GO.js → chunk-GLO6TOJN.js} +2 -0
- package/dist/{chunk-KBJPZDIL.js → chunk-UB7RW6LM.js} +267 -153
- package/dist/main.js +1377 -196
- package/dist/{persistence-VFIOGTRC.js → persistence-2WKQHGOL.js} +2 -2
- package/dist/{process-registry-GSHEX2LT.js → process-registry-QIW7ZIUT.js} +1 -1
- package/dist/prompts/main-agent.md +35 -1
- package/dist/prompts/strategist-system.md +34 -0
- package/package.json +1 -1
- package/dist/agent-tool-HYQGTZC4.js +0 -256
package/dist/main.js
CHANGED
|
@@ -28,9 +28,12 @@ import {
|
|
|
28
28
|
formatChallengeAnalysis,
|
|
29
29
|
formatTurnRecord,
|
|
30
30
|
getApiKey,
|
|
31
|
+
getGlobalRequestCount,
|
|
32
|
+
getGlobalTokenUsage,
|
|
31
33
|
getModel,
|
|
32
34
|
getNextTurnNumber,
|
|
33
35
|
getServiceContext,
|
|
36
|
+
getShellSupervisorLifecycleSnapshot,
|
|
34
37
|
getTimeAdaptiveStrategy,
|
|
35
38
|
parseTurnNumbers,
|
|
36
39
|
readJournalSummary,
|
|
@@ -39,7 +42,7 @@ import {
|
|
|
39
42
|
rotateTurnRecords,
|
|
40
43
|
setCurrentTurn,
|
|
41
44
|
writePolicyDocument
|
|
42
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-BKWCGMSV.js";
|
|
43
46
|
import {
|
|
44
47
|
AGENT_ROLES,
|
|
45
48
|
APP_DESCRIPTION,
|
|
@@ -79,13 +82,13 @@ import {
|
|
|
79
82
|
setActiveSessionRuntime,
|
|
80
83
|
setTorEnabled,
|
|
81
84
|
snapshotToPrompt
|
|
82
|
-
} from "./chunk-
|
|
85
|
+
} from "./chunk-UB7RW6LM.js";
|
|
83
86
|
import {
|
|
84
87
|
EXIT_CODES,
|
|
85
88
|
getPipelineConfig,
|
|
86
89
|
getPromptBuilderConfig,
|
|
87
90
|
getPromptSources
|
|
88
|
-
} from "./chunk-
|
|
91
|
+
} from "./chunk-GLO6TOJN.js";
|
|
89
92
|
|
|
90
93
|
// src/platform/tui/main.tsx
|
|
91
94
|
import chalk5 from "chalk";
|
|
@@ -300,7 +303,7 @@ import chalk from "chalk";
|
|
|
300
303
|
import { Box as Box18 } from "ink";
|
|
301
304
|
|
|
302
305
|
// src/platform/tui/hooks/useAgent.ts
|
|
303
|
-
import { useState as
|
|
306
|
+
import { useState as useState6, useEffect as useEffect2, useCallback as useCallback6, useRef as useRef6 } from "react";
|
|
304
307
|
|
|
305
308
|
// src/engine/events.ts
|
|
306
309
|
var AgentEventEmitter = class {
|
|
@@ -1409,7 +1412,7 @@ var makeRotateTurns = (_llm, _opts) => async (_ctx) => {
|
|
|
1409
1412
|
};
|
|
1410
1413
|
var makeSaveSession = (_llm, _opts) => async (ctx) => {
|
|
1411
1414
|
try {
|
|
1412
|
-
const { saveState: saveState2 } = await import("./persistence-
|
|
1415
|
+
const { saveState: saveState2 } = await import("./persistence-2WKQHGOL.js");
|
|
1413
1416
|
saveState2(ctx.state);
|
|
1414
1417
|
} catch {
|
|
1415
1418
|
}
|
|
@@ -1734,6 +1737,1080 @@ var session = {
|
|
|
1734
1737
|
load: (state) => loadState(state)
|
|
1735
1738
|
};
|
|
1736
1739
|
|
|
1740
|
+
// src/agents/main-agent/delegated-task-scheduler.ts
|
|
1741
|
+
function getDelegatedTaskPriority(task) {
|
|
1742
|
+
const workerPriority = task.nextWorkerType || task.workerType;
|
|
1743
|
+
if (workerPriority === "shell-supervisor" && task.status === "waiting") return 0;
|
|
1744
|
+
if (workerPriority === "shell-supervisor" && task.status === "running") return 1;
|
|
1745
|
+
if (task.status === "waiting") return 2;
|
|
1746
|
+
return 3;
|
|
1747
|
+
}
|
|
1748
|
+
function buildSelectionReason(task) {
|
|
1749
|
+
const preferredWorkerType = task.nextWorkerType || task.workerType;
|
|
1750
|
+
if (preferredWorkerType === "shell-supervisor" && task.status === "waiting") {
|
|
1751
|
+
return "Selected first because shell supervision is waiting on an external callback and should be polled before duplicate chains start.";
|
|
1752
|
+
}
|
|
1753
|
+
if (preferredWorkerType === "shell-supervisor" && task.status === "running") {
|
|
1754
|
+
return "Selected first because shell supervision is already active and should stay attached to the existing access path.";
|
|
1755
|
+
}
|
|
1756
|
+
if (task.status === "waiting") {
|
|
1757
|
+
return "Selected because this delegated task is blocked on an external event and needs explicit supervision.";
|
|
1758
|
+
}
|
|
1759
|
+
return "Selected because this delegated task is already running and should progress before opening a fresh chain.";
|
|
1760
|
+
}
|
|
1761
|
+
function selectDelegatedTaskForResume(activeTasks, autoResumeCounts, lastAutoResumeTaskId) {
|
|
1762
|
+
if (activeTasks.length === 0) {
|
|
1763
|
+
return null;
|
|
1764
|
+
}
|
|
1765
|
+
const rankedTasks = [...activeTasks].sort((left, right) => {
|
|
1766
|
+
const leftCount = autoResumeCounts.get(left.id) || 0;
|
|
1767
|
+
const rightCount = autoResumeCounts.get(right.id) || 0;
|
|
1768
|
+
if (leftCount !== rightCount) {
|
|
1769
|
+
return leftCount - rightCount;
|
|
1770
|
+
}
|
|
1771
|
+
const leftPriority = getDelegatedTaskPriority(left);
|
|
1772
|
+
const rightPriority = getDelegatedTaskPriority(right);
|
|
1773
|
+
if (leftPriority !== rightPriority) {
|
|
1774
|
+
return leftPriority - rightPriority;
|
|
1775
|
+
}
|
|
1776
|
+
if (left.updatedAt !== right.updatedAt) {
|
|
1777
|
+
return left.updatedAt - right.updatedAt;
|
|
1778
|
+
}
|
|
1779
|
+
return left.createdAt - right.createdAt;
|
|
1780
|
+
});
|
|
1781
|
+
const selectedTask = rankedTasks.length > 1 ? rankedTasks.find((task) => task.id !== lastAutoResumeTaskId) || rankedTasks[0] : rankedTasks[0];
|
|
1782
|
+
if (!selectedTask || selectedTask.id === lastAutoResumeTaskId) {
|
|
1783
|
+
return null;
|
|
1784
|
+
}
|
|
1785
|
+
return {
|
|
1786
|
+
task: selectedTask,
|
|
1787
|
+
preferredWorkerType: selectedTask.nextWorkerType || selectedTask.workerType,
|
|
1788
|
+
reason: buildSelectionReason(selectedTask),
|
|
1789
|
+
resumeCount: autoResumeCounts.get(selectedTask.id) || 0
|
|
1790
|
+
};
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
// src/agents/main-agent/delegated-task-queue.ts
|
|
1794
|
+
var DelegatedTaskQueue = class {
|
|
1795
|
+
lastTaskId = null;
|
|
1796
|
+
resumeCounts = /* @__PURE__ */ new Map();
|
|
1797
|
+
reset() {
|
|
1798
|
+
this.lastTaskId = null;
|
|
1799
|
+
this.resumeCounts.clear();
|
|
1800
|
+
}
|
|
1801
|
+
sync(activeTasks) {
|
|
1802
|
+
const activeIds = new Set(activeTasks.map((task) => task.id));
|
|
1803
|
+
if (activeTasks.length === 0) {
|
|
1804
|
+
this.reset();
|
|
1805
|
+
return;
|
|
1806
|
+
}
|
|
1807
|
+
for (const taskId of this.resumeCounts.keys()) {
|
|
1808
|
+
if (!activeIds.has(taskId)) {
|
|
1809
|
+
this.resumeCounts.delete(taskId);
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
if (this.lastTaskId && !activeIds.has(this.lastTaskId)) {
|
|
1813
|
+
this.lastTaskId = null;
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
next(activeTasks) {
|
|
1817
|
+
this.sync(activeTasks);
|
|
1818
|
+
return selectDelegatedTaskForResume(activeTasks, this.resumeCounts, this.lastTaskId);
|
|
1819
|
+
}
|
|
1820
|
+
acknowledge(taskId) {
|
|
1821
|
+
this.lastTaskId = taskId;
|
|
1822
|
+
const nextCount = (this.resumeCounts.get(taskId) || 0) + 1;
|
|
1823
|
+
this.resumeCounts.set(taskId, nextCount);
|
|
1824
|
+
return nextCount;
|
|
1825
|
+
}
|
|
1826
|
+
getLastTaskId() {
|
|
1827
|
+
return this.lastTaskId;
|
|
1828
|
+
}
|
|
1829
|
+
};
|
|
1830
|
+
|
|
1831
|
+
// src/agents/main-agent/delegated-execution-plan.ts
|
|
1832
|
+
function getPhaseFromContext(context, key) {
|
|
1833
|
+
if (!context) {
|
|
1834
|
+
return null;
|
|
1835
|
+
}
|
|
1836
|
+
const match = context.match(new RegExp(`${key}=([a-z_]+)`));
|
|
1837
|
+
return match?.[1] ?? null;
|
|
1838
|
+
}
|
|
1839
|
+
function getRecommendedBoundedTool(request) {
|
|
1840
|
+
if (request.workerType === "shell-supervisor") {
|
|
1841
|
+
const phase = getPhaseFromContext(request.context, "shell_phase");
|
|
1842
|
+
if (phase === "listener_waiting") return "listener_status";
|
|
1843
|
+
if (phase === "listener_callback_detected") return "shell_promote";
|
|
1844
|
+
if (phase === "active_shell_stabilizing") return "shell_upgrade";
|
|
1845
|
+
if (phase === "active_shell_stabilized" || phase === "post_exploitation_active") return "shell_check";
|
|
1846
|
+
return null;
|
|
1847
|
+
}
|
|
1848
|
+
if (request.workerType === "exploit") {
|
|
1849
|
+
const phase = getPhaseFromContext(request.context, "exploit_phase");
|
|
1850
|
+
if (phase === "foothold_active") return "exploit_foothold_check";
|
|
1851
|
+
if (phase === "credential_followup") return "exploit_credential_check";
|
|
1852
|
+
if (phase === "artifact_ready") return "exploit_artifact_check";
|
|
1853
|
+
return null;
|
|
1854
|
+
}
|
|
1855
|
+
if (request.workerType === "pwn") {
|
|
1856
|
+
const phase = getPhaseFromContext(request.context, "pwn_phase");
|
|
1857
|
+
if (phase === "offset_known") return "pwn_offset_check";
|
|
1858
|
+
if (phase === "crash_iteration") return "pwn_crash_repro";
|
|
1859
|
+
if (phase === "payload_iteration") return "pwn_payload_smoke";
|
|
1860
|
+
return null;
|
|
1861
|
+
}
|
|
1862
|
+
return null;
|
|
1863
|
+
}
|
|
1864
|
+
function buildDelegatedExecutionPlan(request) {
|
|
1865
|
+
if (request.workerType === "shell-supervisor") {
|
|
1866
|
+
const phase = getPhaseFromContext(request.context, "shell_phase");
|
|
1867
|
+
if (phase === "listener_waiting") return "Poll the listener and promote immediately if a callback appears.";
|
|
1868
|
+
if (phase === "listener_callback_detected") return "Promote the callback path and validate the resulting shell.";
|
|
1869
|
+
if (phase === "active_shell_stabilizing") return "Finish PTY stabilization before broader post-exploitation.";
|
|
1870
|
+
if (phase === "active_shell_stabilized") return "Reuse the stabilized shell for controlled enumeration.";
|
|
1871
|
+
if (phase === "post_exploitation_active") return "Continue post-exploitation through the existing shell rather than opening a new chain.";
|
|
1872
|
+
return "Continue supervising the current shell/listener chain first.";
|
|
1873
|
+
}
|
|
1874
|
+
if (request.workerType === "exploit") {
|
|
1875
|
+
const phase = getPhaseFromContext(request.context, "exploit_phase");
|
|
1876
|
+
if (phase === "foothold_active") return "Reuse the existing foothold and prefer exploit_foothold_check for bounded access validation.";
|
|
1877
|
+
if (phase === "credential_followup") return "Validate and reuse the obtained credentials or tokens, preferring exploit_credential_check for bounded probes.";
|
|
1878
|
+
if (phase === "artifact_ready") return "Validate the current exploit artifact, preferring exploit_artifact_check before building a replacement.";
|
|
1879
|
+
return "Continue the strongest surviving exploit vector and preserve branch evidence.";
|
|
1880
|
+
}
|
|
1881
|
+
if (request.workerType === "pwn") {
|
|
1882
|
+
const phase = getPhaseFromContext(request.context, "pwn_phase");
|
|
1883
|
+
if (phase === "foothold_active") return "Reuse the achieved execution foothold and continue from there.";
|
|
1884
|
+
if (phase === "offset_known") return "Resume from the known offset and latest payload revision, preferring pwn_offset_check for bounded verification.";
|
|
1885
|
+
if (phase === "crash_iteration") return "Reproduce the latest crash and continue debugging from the preserved crash state, preferring pwn_crash_repro.";
|
|
1886
|
+
return "Continue the active payload-design loop with narrower iterations, preferring pwn_payload_smoke for the next bounded test.";
|
|
1887
|
+
}
|
|
1888
|
+
return "Continue the selected delegated chain before starting unrelated work.";
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
// src/agents/main-agent/exploit-lifecycle.ts
|
|
1892
|
+
function getReplayableExploitCommand(task) {
|
|
1893
|
+
const candidates = [task.resumeHint || "", ...task.tried, ...task.findings, task.summary].map((value) => value.trim()).filter(Boolean);
|
|
1894
|
+
return candidates.find(
|
|
1895
|
+
(candidate) => candidate.includes("curl ") || candidate.includes("hydra ") || candidate.includes("sqlmap ") || candidate.includes("psql ") || candidate.includes("mysql ") || candidate.includes("ssh ") || candidate.includes("ftp ") || candidate.includes("smbclient ") || candidate.includes("xfreerdp ") || candidate.includes("evil-winrm ")
|
|
1896
|
+
) || null;
|
|
1897
|
+
}
|
|
1898
|
+
function hasCredentialLoot(task) {
|
|
1899
|
+
return task.loot.some((item) => {
|
|
1900
|
+
const normalized = item.toLowerCase();
|
|
1901
|
+
return normalized.includes("password") || normalized.includes("credential") || normalized.includes("hash") || normalized.includes("token") || normalized.includes("key");
|
|
1902
|
+
});
|
|
1903
|
+
}
|
|
1904
|
+
function hasFoothold(task) {
|
|
1905
|
+
return task.sessions.length > 0 || task.assets.some((asset) => {
|
|
1906
|
+
const normalized = asset.toLowerCase();
|
|
1907
|
+
return normalized.includes("shell:") || normalized.includes("stabilized_shell:") || normalized.includes("post_exploitation_shell:") || normalized.includes("listener:") || normalized.includes("session:") || normalized.includes("foothold");
|
|
1908
|
+
});
|
|
1909
|
+
}
|
|
1910
|
+
function getArtifactPath(task) {
|
|
1911
|
+
for (const asset of task.assets) {
|
|
1912
|
+
const trimmed = asset.trim();
|
|
1913
|
+
const artifactMatch = trimmed.match(/^artifact:(.+)$/i);
|
|
1914
|
+
if (artifactMatch?.[1]) {
|
|
1915
|
+
return artifactMatch[1].trim();
|
|
1916
|
+
}
|
|
1917
|
+
if (trimmed.startsWith("/") || trimmed.startsWith("./")) {
|
|
1918
|
+
return trimmed;
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
return null;
|
|
1922
|
+
}
|
|
1923
|
+
function getExploitLifecycleSnapshot(task) {
|
|
1924
|
+
const replayableCommand = getReplayableExploitCommand(task);
|
|
1925
|
+
if (hasFoothold(task)) {
|
|
1926
|
+
return {
|
|
1927
|
+
phase: "foothold_active",
|
|
1928
|
+
recommendation: "A foothold already exists. Reuse the current access path and continue the exploit chain from that foothold.",
|
|
1929
|
+
boundedTool: "exploit_foothold_check",
|
|
1930
|
+
boundedCommand: replayableCommand || void 0,
|
|
1931
|
+
boundedTimeout: 4e3,
|
|
1932
|
+
boundedInstruction: "Run one narrow foothold validation step before expanding the exploit chain."
|
|
1933
|
+
};
|
|
1934
|
+
}
|
|
1935
|
+
if (hasCredentialLoot(task)) {
|
|
1936
|
+
return {
|
|
1937
|
+
phase: "credential_followup",
|
|
1938
|
+
recommendation: "Credentials or tokens exist. Prioritize validation, reuse, and pivot from the current exploit chain.",
|
|
1939
|
+
boundedTool: "exploit_credential_check",
|
|
1940
|
+
boundedCommand: replayableCommand || void 0,
|
|
1941
|
+
boundedTimeout: 5e3,
|
|
1942
|
+
boundedInstruction: "Run one bounded credential or token validation step before changing exploit vectors."
|
|
1943
|
+
};
|
|
1944
|
+
}
|
|
1945
|
+
if (task.assets.length > 0) {
|
|
1946
|
+
const artifactPath = getArtifactPath(task);
|
|
1947
|
+
return {
|
|
1948
|
+
phase: "artifact_ready",
|
|
1949
|
+
recommendation: "The exploit chain produced reusable artifacts. Validate and advance the current artifact before changing vectors.",
|
|
1950
|
+
boundedTool: "exploit_artifact_check",
|
|
1951
|
+
boundedCommand: artifactPath ? `test -e ${artifactPath} && ls -l ${artifactPath} && file ${artifactPath} 2>/dev/null` : void 0,
|
|
1952
|
+
boundedTimeout: 3e3,
|
|
1953
|
+
boundedInstruction: "Run one bounded artifact validation step before replacing the current artifact."
|
|
1954
|
+
};
|
|
1955
|
+
}
|
|
1956
|
+
return {
|
|
1957
|
+
phase: "vector_active",
|
|
1958
|
+
recommendation: "The current exploit vector is still active. Continue adapting it before switching to a new chain."
|
|
1959
|
+
};
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
// src/agents/main-agent/pwn-lifecycle.ts
|
|
1963
|
+
function joinedEvidence(task) {
|
|
1964
|
+
return [
|
|
1965
|
+
task.summary,
|
|
1966
|
+
task.resumeHint,
|
|
1967
|
+
task.waitingOn,
|
|
1968
|
+
...task.tried,
|
|
1969
|
+
...task.findings,
|
|
1970
|
+
...task.assets
|
|
1971
|
+
].filter(Boolean).join(" ").toLowerCase();
|
|
1972
|
+
}
|
|
1973
|
+
function getReplayableCommand(task) {
|
|
1974
|
+
const candidates = [...task.tried, task.resumeHint || "", task.summary].map((value) => value.trim()).filter(Boolean);
|
|
1975
|
+
return candidates.find(
|
|
1976
|
+
(candidate) => /^(?:\.\/|\/|python(?:3)?\b|gdb\b|qemu(?:-[\w-]+)?\b|nc\b)/i.test(candidate) || candidate.includes("pwntools")
|
|
1977
|
+
) || null;
|
|
1978
|
+
}
|
|
1979
|
+
function getCandidateBinaryPath(task) {
|
|
1980
|
+
const candidates = [...task.assets, ...task.findings, task.summary].map((value) => value.trim()).filter(Boolean);
|
|
1981
|
+
for (const candidate of candidates) {
|
|
1982
|
+
const artifactMatch = candidate.match(/^artifact:(.+)$/i);
|
|
1983
|
+
const normalized = artifactMatch?.[1]?.trim() || candidate;
|
|
1984
|
+
if (normalized.startsWith("./") || normalized.startsWith("/")) {
|
|
1985
|
+
return normalized;
|
|
1986
|
+
}
|
|
1987
|
+
const embeddedPath = normalized.match(/(\.\/[A-Za-z0-9_./-]+|\/[A-Za-z0-9_./-]+)/);
|
|
1988
|
+
if (embeddedPath?.[1]) {
|
|
1989
|
+
return embeddedPath[1];
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
return null;
|
|
1993
|
+
}
|
|
1994
|
+
function getPwnLifecycleSnapshot(task) {
|
|
1995
|
+
const evidence = joinedEvidence(task);
|
|
1996
|
+
const replayableCommand = getReplayableCommand(task);
|
|
1997
|
+
const binaryPath = getCandidateBinaryPath(task);
|
|
1998
|
+
if (task.sessions.length > 0 || task.assets.some((asset) => {
|
|
1999
|
+
const normalized = asset.toLowerCase();
|
|
2000
|
+
return normalized.includes("shell:") || normalized.includes("stabilized_shell:") || normalized.includes("post_exploitation_shell:");
|
|
2001
|
+
})) {
|
|
2002
|
+
return {
|
|
2003
|
+
phase: "foothold_active",
|
|
2004
|
+
recommendation: "The pwn chain already produced execution. Reuse the existing foothold instead of restarting the exploit loop."
|
|
2005
|
+
};
|
|
2006
|
+
}
|
|
2007
|
+
if (evidence.includes("offset") || evidence.includes("pattern") || evidence.includes("eip") || evidence.includes("rip")) {
|
|
2008
|
+
return {
|
|
2009
|
+
phase: "offset_known",
|
|
2010
|
+
recommendation: "Offsets or control primitives are known. Resume from the latest payload iteration instead of rediscovering the crash.",
|
|
2011
|
+
boundedTool: "pwn_offset_check",
|
|
2012
|
+
boundedCommand: replayableCommand || (binaryPath ? `test -x ${binaryPath} && timeout 5 ${binaryPath} </dev/null` : void 0),
|
|
2013
|
+
boundedTimeout: 5e3,
|
|
2014
|
+
boundedInstruction: "Run one bounded offset verification step against the latest exploit revision."
|
|
2015
|
+
};
|
|
2016
|
+
}
|
|
2017
|
+
if (evidence.includes("crash") || evidence.includes("segfault") || evidence.includes("core")) {
|
|
2018
|
+
return {
|
|
2019
|
+
phase: "crash_iteration",
|
|
2020
|
+
recommendation: "A crash state exists. Preserve it and continue the debug loop from the latest observed crash.",
|
|
2021
|
+
boundedTool: "pwn_crash_repro",
|
|
2022
|
+
boundedCommand: replayableCommand || (binaryPath ? `test -x ${binaryPath} && timeout 5 ${binaryPath} </dev/null` : void 0),
|
|
2023
|
+
boundedTimeout: 1e4,
|
|
2024
|
+
boundedInstruction: "Reproduce the latest crash once and preserve the exact crash state."
|
|
2025
|
+
};
|
|
2026
|
+
}
|
|
2027
|
+
return {
|
|
2028
|
+
phase: "payload_iteration",
|
|
2029
|
+
recommendation: "The pwn loop is still iterating payload design. Continue from the latest payload revision and observations.",
|
|
2030
|
+
boundedTool: "pwn_payload_smoke",
|
|
2031
|
+
boundedCommand: replayableCommand || (binaryPath ? `test -x ${binaryPath} && timeout 5 ${binaryPath} </dev/null` : void 0),
|
|
2032
|
+
boundedTimeout: 5e3,
|
|
2033
|
+
boundedInstruction: "Run one bounded payload smoke test from the latest payload revision."
|
|
2034
|
+
};
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
// src/agents/main-agent/delegated-task-handoff.ts
|
|
2038
|
+
function buildLifecycleSnapshot(decision) {
|
|
2039
|
+
const task = decision.task;
|
|
2040
|
+
if (decision.preferredWorkerType !== "shell-supervisor") {
|
|
2041
|
+
if (decision.preferredWorkerType === "exploit") {
|
|
2042
|
+
return getExploitLifecycleSnapshot(task);
|
|
2043
|
+
}
|
|
2044
|
+
if (decision.preferredWorkerType === "pwn") {
|
|
2045
|
+
return getPwnLifecycleSnapshot(task);
|
|
2046
|
+
}
|
|
2047
|
+
return null;
|
|
2048
|
+
}
|
|
2049
|
+
return getShellSupervisorLifecycleSnapshot();
|
|
2050
|
+
}
|
|
2051
|
+
function buildDelegatedRequestContext(decision, lifecycleSnapshot) {
|
|
2052
|
+
const task = decision.task;
|
|
2053
|
+
if (!lifecycleSnapshot) {
|
|
2054
|
+
return task.summary;
|
|
2055
|
+
}
|
|
2056
|
+
if (decision.preferredWorkerType === "exploit") {
|
|
2057
|
+
return [
|
|
2058
|
+
task.summary,
|
|
2059
|
+
`exploit_phase=${lifecycleSnapshot.phase}`,
|
|
2060
|
+
`recommendation=${lifecycleSnapshot.recommendation}`
|
|
2061
|
+
].filter(Boolean).join(" | ");
|
|
2062
|
+
}
|
|
2063
|
+
if (decision.preferredWorkerType === "pwn") {
|
|
2064
|
+
return [
|
|
2065
|
+
task.summary,
|
|
2066
|
+
`pwn_phase=${lifecycleSnapshot.phase}`,
|
|
2067
|
+
`recommendation=${lifecycleSnapshot.recommendation}`
|
|
2068
|
+
].filter(Boolean).join(" | ");
|
|
2069
|
+
}
|
|
2070
|
+
const shellSnapshot = lifecycleSnapshot;
|
|
2071
|
+
return [
|
|
2072
|
+
task.summary,
|
|
2073
|
+
`shell_phase=${shellSnapshot.phase}`,
|
|
2074
|
+
shellSnapshot.listenerId ? `listener=${shellSnapshot.listenerId}` : "",
|
|
2075
|
+
shellSnapshot.activeShellId ? `active_shell=${shellSnapshot.activeShellId}` : "",
|
|
2076
|
+
`recommendation=${shellSnapshot.recommendation}`
|
|
2077
|
+
].filter(Boolean).join(" | ");
|
|
2078
|
+
}
|
|
2079
|
+
function buildDelegatedExecutionRequest(decision, context, lifecycleSnapshot) {
|
|
2080
|
+
const task = decision.task;
|
|
2081
|
+
const preferredWorkerType = decision.preferredWorkerType;
|
|
2082
|
+
const requestTask = task.resumeHint || task.task;
|
|
2083
|
+
const boundedLifecycle = preferredWorkerType === "exploit" || preferredWorkerType === "pwn" ? lifecycleSnapshot : null;
|
|
2084
|
+
return {
|
|
2085
|
+
task: requestTask,
|
|
2086
|
+
workerType: preferredWorkerType,
|
|
2087
|
+
resumeTaskId: task.id,
|
|
2088
|
+
target: task.target,
|
|
2089
|
+
context,
|
|
2090
|
+
boundedTool: preferredWorkerType ? getRecommendedBoundedTool({
|
|
2091
|
+
task: requestTask,
|
|
2092
|
+
workerType: preferredWorkerType,
|
|
2093
|
+
target: task.target,
|
|
2094
|
+
context
|
|
2095
|
+
}) ?? void 0 : void 0,
|
|
2096
|
+
boundedCommand: boundedLifecycle?.boundedCommand,
|
|
2097
|
+
boundedTimeout: boundedLifecycle?.boundedTimeout,
|
|
2098
|
+
boundedInstruction: preferredWorkerType ? boundedLifecycle?.boundedInstruction || buildDelegatedExecutionPlan({
|
|
2099
|
+
task: requestTask,
|
|
2100
|
+
workerType: preferredWorkerType,
|
|
2101
|
+
target: task.target,
|
|
2102
|
+
context
|
|
2103
|
+
}) : void 0
|
|
2104
|
+
};
|
|
2105
|
+
}
|
|
2106
|
+
function buildDelegatedTaskHandoff(decision) {
|
|
2107
|
+
const task = decision.task;
|
|
2108
|
+
const preferredWorkerType = decision.preferredWorkerType;
|
|
2109
|
+
const lifecycleSnapshot = buildLifecycleSnapshot(decision);
|
|
2110
|
+
const context = buildDelegatedRequestContext(decision, lifecycleSnapshot);
|
|
2111
|
+
const delegatedExecutionRequest = buildDelegatedExecutionRequest(decision, context, lifecycleSnapshot);
|
|
2112
|
+
const executionPlan = buildDelegatedExecutionPlan(delegatedExecutionRequest);
|
|
2113
|
+
const details = [
|
|
2114
|
+
`Resume delegated task: ${task.task}.`,
|
|
2115
|
+
`Delegated task ID: ${task.id}.`,
|
|
2116
|
+
`Scheduler reason: ${decision.reason}`,
|
|
2117
|
+
`Scheduler cycle count: ${decision.resumeCount + 1}.`,
|
|
2118
|
+
`Current status: ${task.status}.`,
|
|
2119
|
+
task.waitingOn ? `Waiting on: ${task.waitingOn}.` : "",
|
|
2120
|
+
task.assets.length > 0 ? `Reuse assets: ${task.assets.join(", ")}.` : "",
|
|
2121
|
+
task.sessions.length > 0 ? `Reuse sessions: ${task.sessions.join(", ")}.` : "",
|
|
2122
|
+
task.resumeHint ? `Resume guidance: ${task.resumeHint}.` : "",
|
|
2123
|
+
preferredWorkerType ? `Preferred worker: ${preferredWorkerType}.` : "",
|
|
2124
|
+
delegatedExecutionRequest.boundedTool ? `Bounded tool: ${delegatedExecutionRequest.boundedTool}.` : "",
|
|
2125
|
+
delegatedExecutionRequest.boundedCommand ? `Bounded command candidate: ${delegatedExecutionRequest.boundedCommand}.` : "",
|
|
2126
|
+
preferredWorkerType ? `Delegation policy: if this requires multi-step follow-up, call run_task with worker_type="${preferredWorkerType}" and resume_task_id="${task.id}" for this selected task before creating any unrelated delegated chain.` : "",
|
|
2127
|
+
`Execution hint: ${executionPlan}`,
|
|
2128
|
+
"Execution policy: continue the selected delegated chain first. Do not switch to a different active delegated task until this one is advanced or invalidated.",
|
|
2129
|
+
"Goal: continue the existing chain instead of creating a duplicate operation."
|
|
2130
|
+
].filter(Boolean);
|
|
2131
|
+
return {
|
|
2132
|
+
shouldForwardToMain: true,
|
|
2133
|
+
forwardedInput: details.join(" "),
|
|
2134
|
+
directResponse: "",
|
|
2135
|
+
shouldWritePolicy: false,
|
|
2136
|
+
policyDocument: "",
|
|
2137
|
+
policyUpdateSummary: "",
|
|
2138
|
+
insightSummary: `Auto-resume delegated task ${task.id} via handoff builder.`,
|
|
2139
|
+
delegatedExecutionRequest,
|
|
2140
|
+
success: true
|
|
2141
|
+
};
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
// src/agents/main-agent/exploit-runtime-signals.ts
|
|
2145
|
+
function outputContainsAny(output, patterns) {
|
|
2146
|
+
return patterns.some((pattern) => output.includes(pattern.toLowerCase()));
|
|
2147
|
+
}
|
|
2148
|
+
function getExploitResultSignals(result) {
|
|
2149
|
+
const output = (result.output || "").toLowerCase();
|
|
2150
|
+
const hasSession = outputContainsAny(output, ["[sessions]", "shell", "session"]);
|
|
2151
|
+
const hasLoot = outputContainsAny(output, ["[loot]", "password", "credential", "token", "hash"]);
|
|
2152
|
+
const hasAsset = outputContainsAny(output, ["[assets]", "artifact", "file", "payload"]);
|
|
2153
|
+
return {
|
|
2154
|
+
hasSession,
|
|
2155
|
+
hasLoot,
|
|
2156
|
+
hasAsset,
|
|
2157
|
+
indicatesProgress: hasSession || hasLoot || hasAsset || outputContainsAny(output, ["[status] running", "[status] success"])
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
function buildExploitExecutionAnalysis(phase, signals) {
|
|
2161
|
+
if (signals.hasSession) {
|
|
2162
|
+
return {
|
|
2163
|
+
phaseObservation: "exploit chain produced a foothold or authenticated session",
|
|
2164
|
+
nextHint: "reuse the current foothold and continue from the confirmed access path"
|
|
2165
|
+
};
|
|
2166
|
+
}
|
|
2167
|
+
if (phase === "credential_followup") {
|
|
2168
|
+
return {
|
|
2169
|
+
phaseObservation: signals.hasLoot ? "credential follow-up produced reusable credential material" : "credential follow-up step executed",
|
|
2170
|
+
nextHint: "reuse validated credentials before changing exploit vectors"
|
|
2171
|
+
};
|
|
2172
|
+
}
|
|
2173
|
+
if (phase === "artifact_ready") {
|
|
2174
|
+
return {
|
|
2175
|
+
phaseObservation: signals.hasAsset ? "artifact validation produced a reusable artifact or asset" : "artifact validation step executed",
|
|
2176
|
+
nextHint: "advance the current artifact before replacing it"
|
|
2177
|
+
};
|
|
2178
|
+
}
|
|
2179
|
+
return {
|
|
2180
|
+
phaseObservation: signals.indicatesProgress ? "exploit chain advanced and produced follow-up signals" : "delegated exploit step executed",
|
|
2181
|
+
nextHint: "continue the selected exploit chain before switching vectors"
|
|
2182
|
+
};
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
// src/agents/main-agent/pwn-runtime-signals.ts
|
|
2186
|
+
function outputContainsAny2(output, patterns) {
|
|
2187
|
+
return patterns.some((pattern) => output.includes(pattern.toLowerCase()));
|
|
2188
|
+
}
|
|
2189
|
+
function getPwnResultSignals(result) {
|
|
2190
|
+
const output = (result.output || "").toLowerCase();
|
|
2191
|
+
const hasFoothold2 = outputContainsAny2(output, ["[sessions]", "shell", "uid=", "got shell"]);
|
|
2192
|
+
const stillCrashing = outputContainsAny2(output, ["crash", "segfault", "core"]);
|
|
2193
|
+
const hasFindings = outputContainsAny2(output, ["[findings]", "rip controlled", "offset", "eip", "pc controlled"]);
|
|
2194
|
+
return {
|
|
2195
|
+
hasFoothold: hasFoothold2,
|
|
2196
|
+
stillCrashing,
|
|
2197
|
+
hasFindings,
|
|
2198
|
+
indicatesProgress: hasFoothold2 || hasFindings || outputContainsAny2(output, ["[status] running", "[status] success", "[nextworker] pwn"])
|
|
2199
|
+
};
|
|
2200
|
+
}
|
|
2201
|
+
function buildPwnExecutionAnalysis(phase, signals) {
|
|
2202
|
+
if (signals.hasFoothold) {
|
|
2203
|
+
return {
|
|
2204
|
+
phaseObservation: "pwn chain achieved code execution or an interactive foothold",
|
|
2205
|
+
nextHint: "reuse the achieved foothold and continue from the working exploit state"
|
|
2206
|
+
};
|
|
2207
|
+
}
|
|
2208
|
+
if (phase === "offset_known") {
|
|
2209
|
+
return {
|
|
2210
|
+
phaseObservation: signals.hasFindings ? "offset-guided pwn iteration confirmed control signals" : "offset-guided pwn iteration executed",
|
|
2211
|
+
nextHint: "continue from the known offset and latest payload revision"
|
|
2212
|
+
};
|
|
2213
|
+
}
|
|
2214
|
+
if (phase === "crash_iteration") {
|
|
2215
|
+
return {
|
|
2216
|
+
phaseObservation: signals.stillCrashing ? "crash-driven pwn iteration preserved the crash state" : "crash-driven pwn iteration executed",
|
|
2217
|
+
nextHint: "preserve crash evidence and narrow the next payload iteration"
|
|
2218
|
+
};
|
|
2219
|
+
}
|
|
2220
|
+
return {
|
|
2221
|
+
phaseObservation: signals.indicatesProgress ? "pwn chain advanced and produced follow-up signals" : "delegated pwn step executed",
|
|
2222
|
+
nextHint: "continue the selected exploit-development loop before broadening scope"
|
|
2223
|
+
};
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
// src/agents/main-agent/delegated-execution-analysis.ts
|
|
2227
|
+
function getPhaseFromContext2(context, key) {
|
|
2228
|
+
if (!context) {
|
|
2229
|
+
return null;
|
|
2230
|
+
}
|
|
2231
|
+
const match = context.match(new RegExp(`${key}=([a-z_]+)`));
|
|
2232
|
+
return match?.[1] ?? null;
|
|
2233
|
+
}
|
|
2234
|
+
function analyzeDelegatedExecutionResult(request, result) {
|
|
2235
|
+
if (!request || !result) {
|
|
2236
|
+
return null;
|
|
2237
|
+
}
|
|
2238
|
+
if (!result.success) {
|
|
2239
|
+
return {
|
|
2240
|
+
phaseObservation: "delegated step failed",
|
|
2241
|
+
nextHint: "retry the same chain only after using the error to narrow the next step"
|
|
2242
|
+
};
|
|
2243
|
+
}
|
|
2244
|
+
if (request.workerType === "shell-supervisor") {
|
|
2245
|
+
const phase = getPhaseFromContext2(request.context, "shell_phase");
|
|
2246
|
+
if (phase === "listener_waiting") {
|
|
2247
|
+
return {
|
|
2248
|
+
phaseObservation: "listener supervision step executed",
|
|
2249
|
+
nextHint: "if callback evidence appeared, promote and validate the shell immediately"
|
|
2250
|
+
};
|
|
2251
|
+
}
|
|
2252
|
+
if (phase === "active_shell_stabilizing") {
|
|
2253
|
+
return {
|
|
2254
|
+
phaseObservation: "shell stabilization step executed",
|
|
2255
|
+
nextHint: "finish PTY stabilization before broader enumeration"
|
|
2256
|
+
};
|
|
2257
|
+
}
|
|
2258
|
+
if (phase === "post_exploitation_active") {
|
|
2259
|
+
return {
|
|
2260
|
+
phaseObservation: "post-exploitation enumeration step executed",
|
|
2261
|
+
nextHint: "continue controlled enumeration through the same shell"
|
|
2262
|
+
};
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
if (request.workerType === "exploit") {
|
|
2266
|
+
if (request.boundedTool === "exploit_credential_check") {
|
|
2267
|
+
return {
|
|
2268
|
+
phaseObservation: "bounded credential validation executed for the exploit chain",
|
|
2269
|
+
nextHint: "reuse the validated credentials or convert the access path into a foothold before changing vectors"
|
|
2270
|
+
};
|
|
2271
|
+
}
|
|
2272
|
+
if (request.boundedTool === "exploit_artifact_check") {
|
|
2273
|
+
return {
|
|
2274
|
+
phaseObservation: "bounded artifact validation executed for the exploit chain",
|
|
2275
|
+
nextHint: "advance the validated artifact into data access, authenticated use, or foothold confirmation"
|
|
2276
|
+
};
|
|
2277
|
+
}
|
|
2278
|
+
if (request.boundedTool === "exploit_foothold_check") {
|
|
2279
|
+
return {
|
|
2280
|
+
phaseObservation: "bounded foothold validation executed for the exploit chain",
|
|
2281
|
+
nextHint: "reuse the foothold and continue from the confirmed access path instead of restarting delivery"
|
|
2282
|
+
};
|
|
2283
|
+
}
|
|
2284
|
+
const phase = getPhaseFromContext2(request.context, "exploit_phase");
|
|
2285
|
+
return buildExploitExecutionAnalysis(phase, getExploitResultSignals(result));
|
|
2286
|
+
}
|
|
2287
|
+
if (request.workerType === "pwn") {
|
|
2288
|
+
if (request.boundedTool === "pwn_offset_check") {
|
|
2289
|
+
return {
|
|
2290
|
+
phaseObservation: "bounded offset verification executed for the pwn loop",
|
|
2291
|
+
nextHint: "continue from the known offset and narrow the next payload iteration"
|
|
2292
|
+
};
|
|
2293
|
+
}
|
|
2294
|
+
if (request.boundedTool === "pwn_crash_repro") {
|
|
2295
|
+
return {
|
|
2296
|
+
phaseObservation: "bounded crash reproduction executed for the pwn loop",
|
|
2297
|
+
nextHint: "preserve the crash state and apply the narrowest next debugging or payload mutation step"
|
|
2298
|
+
};
|
|
2299
|
+
}
|
|
2300
|
+
if (request.boundedTool === "pwn_payload_smoke") {
|
|
2301
|
+
return {
|
|
2302
|
+
phaseObservation: "bounded payload smoke test executed for the pwn loop",
|
|
2303
|
+
nextHint: "preserve the last payload revision and continue narrowing the next test"
|
|
2304
|
+
};
|
|
2305
|
+
}
|
|
2306
|
+
const phase = getPhaseFromContext2(request.context, "pwn_phase");
|
|
2307
|
+
return buildPwnExecutionAnalysis(phase, getPwnResultSignals(result));
|
|
2308
|
+
}
|
|
2309
|
+
return {
|
|
2310
|
+
phaseObservation: "delegated step executed",
|
|
2311
|
+
nextHint: "continue the selected delegated chain before switching focus"
|
|
2312
|
+
};
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
// src/agents/main-agent/delegated-execution-runtime.ts
|
|
2316
|
+
var DelegatedExecutionRuntime = class {
|
|
2317
|
+
pendingRequest = null;
|
|
2318
|
+
lastAttemptedSignature = null;
|
|
2319
|
+
lastExecutionResult = null;
|
|
2320
|
+
lastExecutionAnalysis = null;
|
|
2321
|
+
getSignature(request) {
|
|
2322
|
+
if (!request) return null;
|
|
2323
|
+
return JSON.stringify(request);
|
|
2324
|
+
}
|
|
2325
|
+
set(request) {
|
|
2326
|
+
const nextRequest = request || null;
|
|
2327
|
+
const nextSignature = this.getSignature(nextRequest);
|
|
2328
|
+
const currentSignature = this.getSignature(this.pendingRequest);
|
|
2329
|
+
if (nextSignature !== currentSignature) {
|
|
2330
|
+
this.lastAttemptedSignature = null;
|
|
2331
|
+
}
|
|
2332
|
+
this.pendingRequest = nextRequest;
|
|
2333
|
+
if (nextSignature !== currentSignature) {
|
|
2334
|
+
this.lastExecutionResult = null;
|
|
2335
|
+
this.lastExecutionAnalysis = null;
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
peek() {
|
|
2339
|
+
return this.pendingRequest;
|
|
2340
|
+
}
|
|
2341
|
+
canAutoExecute() {
|
|
2342
|
+
const pendingSignature = this.getSignature(this.pendingRequest);
|
|
2343
|
+
return pendingSignature !== null && pendingSignature !== this.lastAttemptedSignature;
|
|
2344
|
+
}
|
|
2345
|
+
markAutoExecutionAttempt() {
|
|
2346
|
+
this.lastAttemptedSignature = this.getSignature(this.pendingRequest);
|
|
2347
|
+
}
|
|
2348
|
+
setLastExecutionResult(result) {
|
|
2349
|
+
this.lastExecutionResult = result;
|
|
2350
|
+
this.lastExecutionAnalysis = analyzeDelegatedExecutionResult(this.pendingRequest, result);
|
|
2351
|
+
}
|
|
2352
|
+
consume() {
|
|
2353
|
+
const request = this.pendingRequest;
|
|
2354
|
+
this.pendingRequest = null;
|
|
2355
|
+
this.lastAttemptedSignature = null;
|
|
2356
|
+
this.lastExecutionAnalysis = null;
|
|
2357
|
+
return request;
|
|
2358
|
+
}
|
|
2359
|
+
reset() {
|
|
2360
|
+
this.pendingRequest = null;
|
|
2361
|
+
this.lastAttemptedSignature = null;
|
|
2362
|
+
this.lastExecutionResult = null;
|
|
2363
|
+
this.lastExecutionAnalysis = null;
|
|
2364
|
+
}
|
|
2365
|
+
toPromptFragment() {
|
|
2366
|
+
if (!this.pendingRequest) {
|
|
2367
|
+
return "";
|
|
2368
|
+
}
|
|
2369
|
+
const request = this.pendingRequest;
|
|
2370
|
+
const executionPlan = buildDelegatedExecutionPlan(request);
|
|
2371
|
+
const lines = [
|
|
2372
|
+
"<delegated-execution-request>",
|
|
2373
|
+
`task: ${request.task}`,
|
|
2374
|
+
request.workerType ? `worker_type: ${request.workerType}` : "",
|
|
2375
|
+
request.resumeTaskId ? `resume_task_id: ${request.resumeTaskId}` : "",
|
|
2376
|
+
request.target ? `target: ${request.target}` : "",
|
|
2377
|
+
request.context ? `context: ${request.context}` : "",
|
|
2378
|
+
request.boundedTool ? `bounded_tool: ${request.boundedTool}` : "",
|
|
2379
|
+
request.boundedCommand ? `bounded_command: ${request.boundedCommand}` : "",
|
|
2380
|
+
request.boundedTimeout ? `bounded_timeout: ${request.boundedTimeout}` : "",
|
|
2381
|
+
request.boundedInstruction ? `bounded_instruction: ${request.boundedInstruction}` : "",
|
|
2382
|
+
executionPlan ? `execution_plan: ${executionPlan}` : "",
|
|
2383
|
+
"</delegated-execution-request>"
|
|
2384
|
+
].filter(Boolean);
|
|
2385
|
+
return lines.join("\n");
|
|
2386
|
+
}
|
|
2387
|
+
toResultFragment() {
|
|
2388
|
+
if (!this.lastExecutionResult) {
|
|
2389
|
+
return "";
|
|
2390
|
+
}
|
|
2391
|
+
const lines = [
|
|
2392
|
+
"<delegated-execution-result>",
|
|
2393
|
+
`success: ${this.lastExecutionResult.success ? "true" : "false"}`,
|
|
2394
|
+
this.lastExecutionResult.error ? `error: ${this.lastExecutionResult.error}` : "",
|
|
2395
|
+
this.lastExecutionResult.output ? `output: ${this.lastExecutionResult.output}` : "",
|
|
2396
|
+
this.lastExecutionAnalysis?.phaseObservation ? `phase_observation: ${this.lastExecutionAnalysis.phaseObservation}` : "",
|
|
2397
|
+
this.lastExecutionAnalysis?.nextHint ? `next_hint: ${this.lastExecutionAnalysis.nextHint}` : "",
|
|
2398
|
+
"</delegated-execution-result>"
|
|
2399
|
+
].filter(Boolean);
|
|
2400
|
+
return lines.join("\n");
|
|
2401
|
+
}
|
|
2402
|
+
};
|
|
2403
|
+
|
|
2404
|
+
// src/agents/main-agent/delegated-auto-executor.ts
|
|
2405
|
+
function didStatusDetectConnection(result) {
|
|
2406
|
+
const output = result.output || "";
|
|
2407
|
+
return output.includes("CONNECTION DETECTED") || output.includes("Promote to shell");
|
|
2408
|
+
}
|
|
2409
|
+
function didShellCheckShowStability(result) {
|
|
2410
|
+
const output = (result.output || "").toLowerCase();
|
|
2411
|
+
return output.includes("xterm") || output.includes("rows") || output.includes("columns") || output.includes("/dev/pts/");
|
|
2412
|
+
}
|
|
2413
|
+
async function executeShellUpgradeSequence(toolRegistry, processId) {
|
|
2414
|
+
const initialCheck = await toolRegistry.execute({
|
|
2415
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2416
|
+
input: {
|
|
2417
|
+
process_id: processId,
|
|
2418
|
+
profile: "stability"
|
|
2419
|
+
}
|
|
2420
|
+
});
|
|
2421
|
+
if (!initialCheck.success || didShellCheckShowStability(initialCheck)) {
|
|
2422
|
+
return initialCheck;
|
|
2423
|
+
}
|
|
2424
|
+
const pythonUpgrade = await toolRegistry.execute({
|
|
2425
|
+
name: TOOL_NAMES.SHELL_UPGRADE,
|
|
2426
|
+
input: {
|
|
2427
|
+
process_id: processId,
|
|
2428
|
+
method: "python_pty"
|
|
2429
|
+
}
|
|
2430
|
+
});
|
|
2431
|
+
if (!pythonUpgrade.success) {
|
|
2432
|
+
return combineToolResults([initialCheck, pythonUpgrade]);
|
|
2433
|
+
}
|
|
2434
|
+
const postPythonCheck = await toolRegistry.execute({
|
|
2435
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2436
|
+
input: {
|
|
2437
|
+
process_id: processId,
|
|
2438
|
+
profile: "stability"
|
|
2439
|
+
}
|
|
2440
|
+
});
|
|
2441
|
+
if (!postPythonCheck.success || didShellCheckShowStability(postPythonCheck)) {
|
|
2442
|
+
return combineToolResults([initialCheck, pythonUpgrade, postPythonCheck]);
|
|
2443
|
+
}
|
|
2444
|
+
const scriptUpgrade = await toolRegistry.execute({
|
|
2445
|
+
name: TOOL_NAMES.SHELL_UPGRADE,
|
|
2446
|
+
input: {
|
|
2447
|
+
process_id: processId,
|
|
2448
|
+
method: "script"
|
|
2449
|
+
}
|
|
2450
|
+
});
|
|
2451
|
+
if (!scriptUpgrade.success) {
|
|
2452
|
+
return combineToolResults([initialCheck, pythonUpgrade, postPythonCheck, scriptUpgrade]);
|
|
2453
|
+
}
|
|
2454
|
+
const finalCheck = await toolRegistry.execute({
|
|
2455
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2456
|
+
input: {
|
|
2457
|
+
process_id: processId,
|
|
2458
|
+
profile: "stability"
|
|
2459
|
+
}
|
|
2460
|
+
});
|
|
2461
|
+
return combineToolResults([initialCheck, pythonUpgrade, postPythonCheck, scriptUpgrade, finalCheck]);
|
|
2462
|
+
}
|
|
2463
|
+
function combineToolResults(results) {
|
|
2464
|
+
const success = results.every((result) => result.success);
|
|
2465
|
+
return {
|
|
2466
|
+
success,
|
|
2467
|
+
output: results.map((result, index) => `Step ${index + 1}:
|
|
2468
|
+
${result.output}`).join("\n\n"),
|
|
2469
|
+
...success ? {} : {
|
|
2470
|
+
error: results.find((result) => !result.success)?.error || "delegated auto-execution failed"
|
|
2471
|
+
}
|
|
2472
|
+
};
|
|
2473
|
+
}
|
|
2474
|
+
function buildPatchedContext(context, key, value) {
|
|
2475
|
+
const parts = (context || "").split("|").map((part) => part.trim()).filter(Boolean).filter((part) => !part.startsWith(`${key}=`));
|
|
2476
|
+
parts.push(`${key}=${value}`);
|
|
2477
|
+
return parts.join(" | ");
|
|
2478
|
+
}
|
|
2479
|
+
function getPhaseFromContext3(context, key) {
|
|
2480
|
+
if (!context) {
|
|
2481
|
+
return null;
|
|
2482
|
+
}
|
|
2483
|
+
const match = context.match(new RegExp(`${key}=([a-z_]+)`));
|
|
2484
|
+
return match?.[1] ?? null;
|
|
2485
|
+
}
|
|
2486
|
+
function buildRunTaskInput(request, task, workerType, context) {
|
|
2487
|
+
return {
|
|
2488
|
+
name: TOOL_NAMES.RUN_TASK,
|
|
2489
|
+
input: {
|
|
2490
|
+
task,
|
|
2491
|
+
worker_type: workerType,
|
|
2492
|
+
...request.resumeTaskId ? { resume_task_id: request.resumeTaskId } : {},
|
|
2493
|
+
...request.target ? { target: request.target } : {},
|
|
2494
|
+
...context ? { context } : {}
|
|
2495
|
+
}
|
|
2496
|
+
};
|
|
2497
|
+
}
|
|
2498
|
+
function shouldAutoExecuteDelegatedRequest(request) {
|
|
2499
|
+
if (!request || typeof request.resumeTaskId !== "string") {
|
|
2500
|
+
return false;
|
|
2501
|
+
}
|
|
2502
|
+
if (request.workerType === "shell-supervisor") {
|
|
2503
|
+
return true;
|
|
2504
|
+
}
|
|
2505
|
+
if (request.workerType === "exploit") {
|
|
2506
|
+
const phase = getPhaseFromContext3(request.context, "exploit_phase");
|
|
2507
|
+
return phase === "artifact_ready" || phase === "credential_followup" || phase === "foothold_active";
|
|
2508
|
+
}
|
|
2509
|
+
if (request.workerType === "pwn") {
|
|
2510
|
+
const phase = getPhaseFromContext3(request.context, "pwn_phase");
|
|
2511
|
+
return phase === "offset_known" || phase === "crash_iteration" || phase === "foothold_active" || phase === "payload_iteration";
|
|
2512
|
+
}
|
|
2513
|
+
return false;
|
|
2514
|
+
}
|
|
2515
|
+
function buildDelegatedRunTaskCall(request) {
|
|
2516
|
+
return {
|
|
2517
|
+
name: TOOL_NAMES.RUN_TASK,
|
|
2518
|
+
input: {
|
|
2519
|
+
task: request.task,
|
|
2520
|
+
...request.workerType ? { worker_type: request.workerType } : {},
|
|
2521
|
+
...request.resumeTaskId ? { resume_task_id: request.resumeTaskId } : {},
|
|
2522
|
+
...request.target ? { target: request.target } : {},
|
|
2523
|
+
...request.context ? { context: request.context } : {}
|
|
2524
|
+
}
|
|
2525
|
+
};
|
|
2526
|
+
}
|
|
2527
|
+
function buildBoundedDelegatedToolCall(request) {
|
|
2528
|
+
if (!request.boundedTool || !request.boundedCommand) {
|
|
2529
|
+
return null;
|
|
2530
|
+
}
|
|
2531
|
+
return {
|
|
2532
|
+
name: request.boundedTool,
|
|
2533
|
+
input: {
|
|
2534
|
+
command: request.boundedCommand,
|
|
2535
|
+
...request.boundedTimeout ? { timeout: request.boundedTimeout } : {}
|
|
2536
|
+
}
|
|
2537
|
+
};
|
|
2538
|
+
}
|
|
2539
|
+
function buildExploitPhaseRunTaskCall(request) {
|
|
2540
|
+
const phase = getPhaseFromContext3(request.context, "exploit_phase");
|
|
2541
|
+
const phaseTask = phase === "foothold_active" ? "Reuse the existing foothold and continue the exploit chain from the current access path." : phase === "credential_followup" ? "Validate and reuse the obtained credentials or tokens before changing exploit vectors." : phase === "artifact_ready" ? "Validate and advance the current exploit artifact before switching to a different vector." : request.task;
|
|
2542
|
+
return buildRunTaskInput(request, phaseTask, "exploit", request.context);
|
|
2543
|
+
}
|
|
2544
|
+
function buildExploitFollowupRunTaskCall(request, previousResult) {
|
|
2545
|
+
const phase = getPhaseFromContext3(request.context, "exploit_phase");
|
|
2546
|
+
const signals = previousResult ? getExploitResultSignals(previousResult) : null;
|
|
2547
|
+
const hasSession = signals?.hasSession ?? false;
|
|
2548
|
+
const hasLoot = signals?.hasLoot ?? false;
|
|
2549
|
+
if (phase === "credential_followup" || hasLoot) {
|
|
2550
|
+
return buildRunTaskInput(
|
|
2551
|
+
request,
|
|
2552
|
+
hasSession ? "Reuse the confirmed foothold or authenticated path and continue the exploit chain without restarting delivery." : "If credential reuse succeeded, continue from the confirmed foothold or authenticated path without restarting the exploit chain.",
|
|
2553
|
+
"exploit",
|
|
2554
|
+
buildPatchedContext(request.context, "exploit_phase", hasSession ? "foothold_active" : "credential_followup")
|
|
2555
|
+
);
|
|
2556
|
+
}
|
|
2557
|
+
if (phase === "artifact_ready") {
|
|
2558
|
+
return buildRunTaskInput(
|
|
2559
|
+
request,
|
|
2560
|
+
hasSession ? "A foothold exists from the validated artifact. Reuse it and continue the chain from that access path." : "If the artifact validated successfully, turn it into data extraction, authenticated access, or foothold confirmation before changing vectors.",
|
|
2561
|
+
"exploit",
|
|
2562
|
+
buildPatchedContext(request.context, "exploit_phase", hasSession ? "foothold_active" : "artifact_ready")
|
|
2563
|
+
);
|
|
2564
|
+
}
|
|
2565
|
+
return null;
|
|
2566
|
+
}
|
|
2567
|
+
function buildPwnPhaseRunTaskCall(request) {
|
|
2568
|
+
const phase = getPhaseFromContext3(request.context, "pwn_phase");
|
|
2569
|
+
const phaseTask = phase === "foothold_active" ? "Reuse the existing execution foothold and continue post-exploitation or objective completion." : phase === "offset_known" ? "Resume the exploit loop from the known offset and latest payload revision." : phase === "crash_iteration" ? "Reproduce the latest crash and continue debugging from the preserved crash state." : request.task;
|
|
2570
|
+
return buildRunTaskInput(request, phaseTask, "pwn", request.context);
|
|
2571
|
+
}
|
|
2572
|
+
function buildPwnFollowupRunTaskCall(request, previousResult) {
|
|
2573
|
+
const phase = getPhaseFromContext3(request.context, "pwn_phase");
|
|
2574
|
+
const signals = previousResult ? getPwnResultSignals(previousResult) : null;
|
|
2575
|
+
const hasFoothold2 = signals?.hasFoothold ?? false;
|
|
2576
|
+
const stillCrashing = signals?.stillCrashing ?? false;
|
|
2577
|
+
if (phase === "offset_known") {
|
|
2578
|
+
return buildRunTaskInput(
|
|
2579
|
+
request,
|
|
2580
|
+
hasFoothold2 ? "Execution is confirmed. Reuse the achieved foothold and continue from the working exploit state." : "Use the known offset to perform the narrowest next payload smoke test and confirm progress toward execution.",
|
|
2581
|
+
"pwn",
|
|
2582
|
+
buildPatchedContext(request.context, "pwn_phase", hasFoothold2 ? "foothold_active" : "payload_iteration")
|
|
2583
|
+
);
|
|
2584
|
+
}
|
|
2585
|
+
if (phase === "crash_iteration") {
|
|
2586
|
+
return buildRunTaskInput(
|
|
2587
|
+
request,
|
|
2588
|
+
stillCrashing ? "The crash state persists. Apply the narrowest next debugging or payload mutation step from the preserved crash state." : hasFoothold2 ? "Execution is confirmed. Reuse the achieved foothold and continue from the working exploit state." : "Apply the narrowest next debugging or payload mutation step from the preserved crash state.",
|
|
2589
|
+
"pwn",
|
|
2590
|
+
buildPatchedContext(
|
|
2591
|
+
request.context,
|
|
2592
|
+
"pwn_phase",
|
|
2593
|
+
hasFoothold2 ? "foothold_active" : stillCrashing ? "crash_iteration" : "payload_iteration"
|
|
2594
|
+
)
|
|
2595
|
+
);
|
|
2596
|
+
}
|
|
2597
|
+
return null;
|
|
2598
|
+
}
|
|
2599
|
+
function buildShellSupervisorFastPathCall() {
|
|
2600
|
+
const snapshot = getShellSupervisorLifecycleSnapshot();
|
|
2601
|
+
switch (snapshot.phase) {
|
|
2602
|
+
case "post_exploitation_active":
|
|
2603
|
+
return snapshot.activeShellId ? {
|
|
2604
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2605
|
+
input: {
|
|
2606
|
+
process_id: snapshot.activeShellId,
|
|
2607
|
+
profile: "post"
|
|
2608
|
+
}
|
|
2609
|
+
} : null;
|
|
2610
|
+
case "active_shell_stabilized":
|
|
2611
|
+
return snapshot.activeShellId ? {
|
|
2612
|
+
name: TOOL_NAMES.SHELL_EXEC,
|
|
2613
|
+
input: {
|
|
2614
|
+
process_id: snapshot.activeShellId,
|
|
2615
|
+
command: "pwd && uname -a"
|
|
2616
|
+
}
|
|
2617
|
+
} : null;
|
|
2618
|
+
case "active_shell_stabilizing":
|
|
2619
|
+
return snapshot.activeShellId ? {
|
|
2620
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2621
|
+
input: {
|
|
2622
|
+
process_id: snapshot.activeShellId,
|
|
2623
|
+
profile: "stability"
|
|
2624
|
+
}
|
|
2625
|
+
} : null;
|
|
2626
|
+
case "listener_waiting":
|
|
2627
|
+
return snapshot.listenerId ? {
|
|
2628
|
+
name: TOOL_NAMES.LISTENER_STATUS,
|
|
2629
|
+
input: {
|
|
2630
|
+
process_id: snapshot.listenerId
|
|
2631
|
+
}
|
|
2632
|
+
} : null;
|
|
2633
|
+
case "listener_callback_detected":
|
|
2634
|
+
return snapshot.listenerId ? {
|
|
2635
|
+
name: TOOL_NAMES.SHELL_PROMOTE,
|
|
2636
|
+
input: {
|
|
2637
|
+
process_id: snapshot.listenerId
|
|
2638
|
+
}
|
|
2639
|
+
} : null;
|
|
2640
|
+
case "active_shell_ready":
|
|
2641
|
+
return snapshot.activeShellId ? {
|
|
2642
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2643
|
+
input: {
|
|
2644
|
+
process_id: snapshot.activeShellId,
|
|
2645
|
+
profile: "identity"
|
|
2646
|
+
}
|
|
2647
|
+
} : null;
|
|
2648
|
+
default:
|
|
2649
|
+
return null;
|
|
2650
|
+
}
|
|
2651
|
+
}
|
|
2652
|
+
async function executeShellSupervisorMiniLoop(toolRegistry) {
|
|
2653
|
+
const snapshot = getShellSupervisorLifecycleSnapshot();
|
|
2654
|
+
if (snapshot.phase === "post_exploitation_active" && snapshot.activeShellId) {
|
|
2655
|
+
const identityResult = await toolRegistry.execute({
|
|
2656
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2657
|
+
input: {
|
|
2658
|
+
process_id: snapshot.activeShellId,
|
|
2659
|
+
profile: "identity"
|
|
2660
|
+
}
|
|
2661
|
+
});
|
|
2662
|
+
if (!identityResult.success) {
|
|
2663
|
+
return identityResult;
|
|
2664
|
+
}
|
|
2665
|
+
const environmentResult = await toolRegistry.execute({
|
|
2666
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2667
|
+
input: {
|
|
2668
|
+
process_id: snapshot.activeShellId,
|
|
2669
|
+
profile: "environment"
|
|
2670
|
+
}
|
|
2671
|
+
});
|
|
2672
|
+
if (!environmentResult.success) {
|
|
2673
|
+
return combineToolResults([identityResult, environmentResult]);
|
|
2674
|
+
}
|
|
2675
|
+
const networkResult = await toolRegistry.execute({
|
|
2676
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2677
|
+
input: {
|
|
2678
|
+
process_id: snapshot.activeShellId,
|
|
2679
|
+
profile: "post"
|
|
2680
|
+
}
|
|
2681
|
+
});
|
|
2682
|
+
return combineToolResults([identityResult, environmentResult, networkResult]);
|
|
2683
|
+
}
|
|
2684
|
+
if (snapshot.phase === "active_shell_stabilizing" && snapshot.activeShellId) {
|
|
2685
|
+
return executeShellUpgradeSequence(toolRegistry, snapshot.activeShellId);
|
|
2686
|
+
}
|
|
2687
|
+
if (snapshot.phase === "listener_waiting" && snapshot.listenerId) {
|
|
2688
|
+
const statusResult = await toolRegistry.execute({
|
|
2689
|
+
name: TOOL_NAMES.LISTENER_STATUS,
|
|
2690
|
+
input: {
|
|
2691
|
+
process_id: snapshot.listenerId
|
|
2692
|
+
}
|
|
2693
|
+
});
|
|
2694
|
+
if (!statusResult.success || !didStatusDetectConnection(statusResult)) {
|
|
2695
|
+
return statusResult;
|
|
2696
|
+
}
|
|
2697
|
+
const promoteResult = await toolRegistry.execute({
|
|
2698
|
+
name: TOOL_NAMES.SHELL_PROMOTE,
|
|
2699
|
+
input: {
|
|
2700
|
+
process_id: snapshot.listenerId
|
|
2701
|
+
}
|
|
2702
|
+
});
|
|
2703
|
+
if (!promoteResult.success) {
|
|
2704
|
+
return combineToolResults([statusResult, promoteResult]);
|
|
2705
|
+
}
|
|
2706
|
+
const interactResult = await toolRegistry.execute({
|
|
2707
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2708
|
+
input: {
|
|
2709
|
+
process_id: snapshot.listenerId,
|
|
2710
|
+
profile: "identity"
|
|
2711
|
+
}
|
|
2712
|
+
});
|
|
2713
|
+
return combineToolResults([statusResult, promoteResult, interactResult]);
|
|
2714
|
+
}
|
|
2715
|
+
if (snapshot.phase === "listener_callback_detected" && snapshot.listenerId) {
|
|
2716
|
+
const promoteResult = await toolRegistry.execute({
|
|
2717
|
+
name: TOOL_NAMES.SHELL_PROMOTE,
|
|
2718
|
+
input: {
|
|
2719
|
+
process_id: snapshot.listenerId
|
|
2720
|
+
}
|
|
2721
|
+
});
|
|
2722
|
+
if (!promoteResult.success) {
|
|
2723
|
+
return promoteResult;
|
|
2724
|
+
}
|
|
2725
|
+
const interactResult = await toolRegistry.execute({
|
|
2726
|
+
name: TOOL_NAMES.SHELL_CHECK,
|
|
2727
|
+
input: {
|
|
2728
|
+
process_id: snapshot.listenerId,
|
|
2729
|
+
profile: "identity"
|
|
2730
|
+
}
|
|
2731
|
+
});
|
|
2732
|
+
return combineToolResults([promoteResult, interactResult]);
|
|
2733
|
+
}
|
|
2734
|
+
const singleCall = buildShellSupervisorFastPathCall();
|
|
2735
|
+
if (!singleCall) {
|
|
2736
|
+
return null;
|
|
2737
|
+
}
|
|
2738
|
+
return toolRegistry.execute(singleCall);
|
|
2739
|
+
}
|
|
2740
|
+
async function executeExploitMiniLoop(toolRegistry, request) {
|
|
2741
|
+
return executeTwoStepLoop(
|
|
2742
|
+
toolRegistry,
|
|
2743
|
+
buildExploitPhaseRunTaskCall(request),
|
|
2744
|
+
(firstResult) => buildExploitFollowupRunTaskCall(request, firstResult),
|
|
2745
|
+
getExploitResultSignals
|
|
2746
|
+
);
|
|
2747
|
+
}
|
|
2748
|
+
async function executeExploitDirectBoundedLoop(toolRegistry, request) {
|
|
2749
|
+
const boundedCall = buildBoundedDelegatedToolCall(request);
|
|
2750
|
+
if (!boundedCall) {
|
|
2751
|
+
return executeExploitMiniLoop(toolRegistry, request);
|
|
2752
|
+
}
|
|
2753
|
+
return executeTwoStepLoop(
|
|
2754
|
+
toolRegistry,
|
|
2755
|
+
boundedCall,
|
|
2756
|
+
(firstResult) => buildExploitFollowupRunTaskCall(request, firstResult),
|
|
2757
|
+
getExploitResultSignals
|
|
2758
|
+
);
|
|
2759
|
+
}
|
|
2760
|
+
async function executePwnMiniLoop(toolRegistry, request) {
|
|
2761
|
+
return executeTwoStepLoop(
|
|
2762
|
+
toolRegistry,
|
|
2763
|
+
buildPwnPhaseRunTaskCall(request),
|
|
2764
|
+
(firstResult) => buildPwnFollowupRunTaskCall(request, firstResult),
|
|
2765
|
+
getPwnResultSignals
|
|
2766
|
+
);
|
|
2767
|
+
}
|
|
2768
|
+
async function executePwnDirectBoundedLoop(toolRegistry, request) {
|
|
2769
|
+
const boundedCall = buildBoundedDelegatedToolCall(request);
|
|
2770
|
+
if (!boundedCall) {
|
|
2771
|
+
return executePwnMiniLoop(toolRegistry, request);
|
|
2772
|
+
}
|
|
2773
|
+
return executeTwoStepLoop(
|
|
2774
|
+
toolRegistry,
|
|
2775
|
+
boundedCall,
|
|
2776
|
+
(firstResult) => buildPwnFollowupRunTaskCall(request, firstResult),
|
|
2777
|
+
getPwnResultSignals
|
|
2778
|
+
);
|
|
2779
|
+
}
|
|
2780
|
+
async function executeTwoStepLoop(toolRegistry, firstCall, buildFollowupCall, getSignals) {
|
|
2781
|
+
const firstResult = await toolRegistry.execute(firstCall);
|
|
2782
|
+
if (!firstResult.success) {
|
|
2783
|
+
return firstResult;
|
|
2784
|
+
}
|
|
2785
|
+
const followupCall = buildFollowupCall(firstResult);
|
|
2786
|
+
if (!followupCall || !getSignals(firstResult).indicatesProgress) {
|
|
2787
|
+
return firstResult;
|
|
2788
|
+
}
|
|
2789
|
+
const followupResult = await toolRegistry.execute(followupCall);
|
|
2790
|
+
return combineToolResults([firstResult, followupResult]);
|
|
2791
|
+
}
|
|
2792
|
+
var DelegatedAutoExecutor = class {
|
|
2793
|
+
canExecutePending(runtime) {
|
|
2794
|
+
return runtime.canAutoExecute() && shouldAutoExecuteDelegatedRequest(runtime.peek());
|
|
2795
|
+
}
|
|
2796
|
+
async executePending(runtime, toolRegistry) {
|
|
2797
|
+
if (!toolRegistry) {
|
|
2798
|
+
return null;
|
|
2799
|
+
}
|
|
2800
|
+
const request = runtime.peek();
|
|
2801
|
+
if (!request || !this.canExecutePending(runtime)) {
|
|
2802
|
+
return null;
|
|
2803
|
+
}
|
|
2804
|
+
runtime.markAutoExecutionAttempt();
|
|
2805
|
+
const result = request.workerType === "shell-supervisor" ? await executeShellSupervisorMiniLoop(toolRegistry) || await toolRegistry.execute(buildDelegatedRunTaskCall(request)) : request.workerType === "exploit" ? await executeExploitDirectBoundedLoop(toolRegistry, request) : request.workerType === "pwn" ? await executePwnDirectBoundedLoop(toolRegistry, request) : await toolRegistry.execute(buildDelegatedRunTaskCall(request));
|
|
2806
|
+
runtime.setLastExecutionResult(result);
|
|
2807
|
+
if (result.success) {
|
|
2808
|
+
runtime.consume();
|
|
2809
|
+
}
|
|
2810
|
+
return result;
|
|
2811
|
+
}
|
|
2812
|
+
};
|
|
2813
|
+
|
|
1737
2814
|
// src/agents/main-agent/main-agent.ts
|
|
1738
2815
|
var MainAgent = class extends CoreAgent {
|
|
1739
2816
|
promptBuilder;
|
|
@@ -1743,15 +2820,23 @@ var MainAgent = class extends CoreAgent {
|
|
|
1743
2820
|
turnCounter = 0;
|
|
1744
2821
|
userInputQueue = new UserInputQueue();
|
|
1745
2822
|
pendingInitialUserInput = null;
|
|
2823
|
+
delegatedTaskQueue = new DelegatedTaskQueue();
|
|
2824
|
+
delegatedExecutionRuntime = new DelegatedExecutionRuntime();
|
|
2825
|
+
delegatedAutoExecutor = new DelegatedAutoExecutor();
|
|
1746
2826
|
pipelineRunner;
|
|
1747
2827
|
turnCyclePipeline;
|
|
1748
2828
|
inputProcessor;
|
|
1749
2829
|
sessionRuntime;
|
|
1750
|
-
constructor(
|
|
1751
|
-
super(
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
2830
|
+
constructor(options) {
|
|
2831
|
+
super({
|
|
2832
|
+
agentType: AGENT_ROLES.ORCHESTRATOR,
|
|
2833
|
+
state: options.state,
|
|
2834
|
+
events: options.events,
|
|
2835
|
+
toolRegistry: options.toolRegistry
|
|
2836
|
+
});
|
|
2837
|
+
this.approvalGate = options.approvalGate;
|
|
2838
|
+
this.scopeGuard = options.scopeGuard;
|
|
2839
|
+
this.promptBuilder = new PromptBuilder(options.state);
|
|
1755
2840
|
this.pipelineRunner = new PipelineRunner();
|
|
1756
2841
|
this.inputProcessor = createInputProcessor(this.llm);
|
|
1757
2842
|
this.sessionRuntime = createSessionRuntime();
|
|
@@ -1766,6 +2851,8 @@ var MainAgent = class extends CoreAgent {
|
|
|
1766
2851
|
async execute(userInput) {
|
|
1767
2852
|
this.userInput = "";
|
|
1768
2853
|
this.pendingInitialUserInput = userInput;
|
|
2854
|
+
this.delegatedTaskQueue.reset();
|
|
2855
|
+
this.delegatedExecutionRuntime.reset();
|
|
1769
2856
|
emitStart(this.events, userInput, this.state);
|
|
1770
2857
|
initializeTask(this.state);
|
|
1771
2858
|
try {
|
|
@@ -1812,6 +2899,12 @@ var MainAgent = class extends CoreAgent {
|
|
|
1812
2899
|
const messages = this.collectPendingUserInputs();
|
|
1813
2900
|
this.pendingInitialUserInput = null;
|
|
1814
2901
|
if (messages.length === 0) {
|
|
2902
|
+
const autoResume = this.buildAutoResumeResult();
|
|
2903
|
+
if (autoResume) {
|
|
2904
|
+
this.updateObjectiveFromInputProcessor(autoResume);
|
|
2905
|
+
await this.maybeAutoExecuteDelegatedRequest();
|
|
2906
|
+
return autoResume;
|
|
2907
|
+
}
|
|
1815
2908
|
return { shouldForwardToMain: true };
|
|
1816
2909
|
}
|
|
1817
2910
|
const rawInput = messages.map((text, index) => `[${index + 1}] ${text}`).join("\n");
|
|
@@ -1834,6 +2927,7 @@ var MainAgent = class extends CoreAgent {
|
|
|
1834
2927
|
}
|
|
1835
2928
|
}
|
|
1836
2929
|
updateObjectiveFromInputProcessor(result) {
|
|
2930
|
+
this.delegatedExecutionRuntime.set(result.delegatedExecutionRequest);
|
|
1837
2931
|
if (result.shouldForwardToMain && result.forwardedInput.trim()) {
|
|
1838
2932
|
this.userInput = this.appendUserInput(this.userInput, result.forwardedInput);
|
|
1839
2933
|
if (!this.state.hasActiveEngagement()) {
|
|
@@ -1875,8 +2969,24 @@ var MainAgent = class extends CoreAgent {
|
|
|
1875
2969
|
|
|
1876
2970
|
${next}`;
|
|
1877
2971
|
}
|
|
2972
|
+
buildAutoResumeResult() {
|
|
2973
|
+
const activeTasks = this.state.getActiveDelegatedTasks();
|
|
2974
|
+
const decision = this.delegatedTaskQueue.next(activeTasks);
|
|
2975
|
+
if (!decision) {
|
|
2976
|
+
return null;
|
|
2977
|
+
}
|
|
2978
|
+
this.delegatedTaskQueue.acknowledge(decision.task.id);
|
|
2979
|
+
return buildDelegatedTaskHandoff(decision);
|
|
2980
|
+
}
|
|
1878
2981
|
async buildDynamicPrompt(memory) {
|
|
1879
|
-
|
|
2982
|
+
const basePrompt = await this.promptBuilder.build(
|
|
2983
|
+
this.userInput,
|
|
2984
|
+
this.state.getPhase() || PHASES.RECON,
|
|
2985
|
+
memory
|
|
2986
|
+
);
|
|
2987
|
+
const delegatedExecutionRequest = this.delegatedExecutionRuntime.toPromptFragment();
|
|
2988
|
+
const delegatedExecutionResult = this.delegatedExecutionRuntime.toResultFragment();
|
|
2989
|
+
return [basePrompt, delegatedExecutionRequest, delegatedExecutionResult].filter(Boolean).join("\n\n");
|
|
1880
2990
|
}
|
|
1881
2991
|
// ─── Public API ────────────────────────────────────────────────────────────
|
|
1882
2992
|
loadPreviousSession() {
|
|
@@ -1888,6 +2998,8 @@ ${next}`;
|
|
|
1888
2998
|
async resetSession() {
|
|
1889
2999
|
const result = await resetSessionAction(this.state, this.userInputQueue);
|
|
1890
3000
|
this.userInput = "";
|
|
3001
|
+
this.delegatedTaskQueue.reset();
|
|
3002
|
+
this.delegatedExecutionRuntime.reset();
|
|
1891
3003
|
return result;
|
|
1892
3004
|
}
|
|
1893
3005
|
setAutoApprove(shouldEnable) {
|
|
@@ -1905,6 +3017,22 @@ ${next}`;
|
|
|
1905
3017
|
getSessionRuntime() {
|
|
1906
3018
|
return this.sessionRuntime;
|
|
1907
3019
|
}
|
|
3020
|
+
getLastDelegatedExecutionRequest() {
|
|
3021
|
+
return this.delegatedExecutionRuntime.peek();
|
|
3022
|
+
}
|
|
3023
|
+
consumeDelegatedExecutionRequest() {
|
|
3024
|
+
return this.delegatedExecutionRuntime.consume();
|
|
3025
|
+
}
|
|
3026
|
+
async executePendingDelegatedRequest() {
|
|
3027
|
+
return this.delegatedAutoExecutor.executePending(this.delegatedExecutionRuntime, this.toolRegistry);
|
|
3028
|
+
}
|
|
3029
|
+
async maybeAutoExecuteDelegatedRequest() {
|
|
3030
|
+
if (!this.delegatedAutoExecutor.canExecutePending(this.delegatedExecutionRuntime)) {
|
|
3031
|
+
return null;
|
|
3032
|
+
}
|
|
3033
|
+
const result = await this.executePendingDelegatedRequest();
|
|
3034
|
+
return result;
|
|
3035
|
+
}
|
|
1908
3036
|
setScope(allowed, exclusions = []) {
|
|
1909
3037
|
this.state.setScope({
|
|
1910
3038
|
allowedCidrs: allowed.filter((a) => a.includes("/")),
|
|
@@ -1952,10 +3080,10 @@ ${next}`;
|
|
|
1952
3080
|
import fs from "fs";
|
|
1953
3081
|
import path2 from "path";
|
|
1954
3082
|
var YamlRuntime = class _YamlRuntime {
|
|
1955
|
-
static build(
|
|
3083
|
+
static build(options) {
|
|
1956
3084
|
const cfg = getPipelineConfig();
|
|
1957
3085
|
_YamlRuntime.setupWorkspace(cfg.workspace);
|
|
1958
|
-
return new MainAgent(
|
|
3086
|
+
return new MainAgent(options);
|
|
1959
3087
|
}
|
|
1960
3088
|
static setupWorkspace(workspaceCfg) {
|
|
1961
3089
|
const directories = workspaceCfg?.directories ?? {};
|
|
@@ -1982,11 +3110,11 @@ function createMainAgent(shouldAutoApprove = false) {
|
|
|
1982
3110
|
approvalGate,
|
|
1983
3111
|
events
|
|
1984
3112
|
);
|
|
1985
|
-
return YamlRuntime.build(state, events, toolRegistry, approvalGate, scopeGuard);
|
|
3113
|
+
return YamlRuntime.build({ state, events, toolRegistry, approvalGate, scopeGuard });
|
|
1986
3114
|
}
|
|
1987
3115
|
|
|
1988
3116
|
// src/platform/tui/hooks/useAgentState.ts
|
|
1989
|
-
import { useState, useRef, useCallback } from "react";
|
|
3117
|
+
import { useState as useState5, useRef as useRef4, useCallback as useCallback5 } from "react";
|
|
1990
3118
|
|
|
1991
3119
|
// src/platform/tui/constants/limits.ts
|
|
1992
3120
|
var TUI_DISPLAY_LIMITS = {
|
|
@@ -2054,27 +3182,6 @@ var TUI_DISPLAY_LIMITS = {
|
|
|
2054
3182
|
maxStoppedProcesses: 3
|
|
2055
3183
|
};
|
|
2056
3184
|
|
|
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
3185
|
// src/platform/tui/utils/format.ts
|
|
2079
3186
|
function truncate(text, width) {
|
|
2080
3187
|
if (width <= 0) return "";
|
|
@@ -2126,6 +3233,47 @@ var formatInlineStatus = () => {
|
|
|
2126
3233
|
return JSON.stringify(statusData);
|
|
2127
3234
|
};
|
|
2128
3235
|
|
|
3236
|
+
// src/platform/tui/hooks/useAgentState/useMessages.ts
|
|
3237
|
+
import { useState, useCallback } from "react";
|
|
3238
|
+
function useMessages() {
|
|
3239
|
+
const [messages, setMessages] = useState([]);
|
|
3240
|
+
const addMessage = useCallback((type, content) => {
|
|
3241
|
+
const id = generateId();
|
|
3242
|
+
setMessages((prev) => {
|
|
3243
|
+
const next = [...prev, { id, type, content, timestamp: /* @__PURE__ */ new Date() }];
|
|
3244
|
+
if (next.length > TUI_DISPLAY_LIMITS.MAX_MESSAGES) {
|
|
3245
|
+
return next.slice(next.length - TUI_DISPLAY_LIMITS.MAX_MESSAGES);
|
|
3246
|
+
}
|
|
3247
|
+
return next;
|
|
3248
|
+
});
|
|
3249
|
+
}, []);
|
|
3250
|
+
return { messages, setMessages, addMessage };
|
|
3251
|
+
}
|
|
3252
|
+
|
|
3253
|
+
// src/platform/tui/hooks/useAgentState/useTimer.ts
|
|
3254
|
+
import { useState as useState2, useRef, useCallback as useCallback2 } from "react";
|
|
3255
|
+
function useTimer() {
|
|
3256
|
+
const [elapsedTime, setElapsedTime] = useState2(0);
|
|
3257
|
+
const startTimeRef = useRef(0);
|
|
3258
|
+
const manageTimer = useCallback2((action) => {
|
|
3259
|
+
if (action === "start") {
|
|
3260
|
+
startTimeRef.current = Date.now();
|
|
3261
|
+
setElapsedTime(0);
|
|
3262
|
+
} else {
|
|
3263
|
+
const startedAt = startTimeRef.current;
|
|
3264
|
+
if (startedAt > 0) {
|
|
3265
|
+
setElapsedTime(Math.max(0, Math.floor((Date.now() - startedAt) / 1e3)));
|
|
3266
|
+
} else {
|
|
3267
|
+
setElapsedTime(0);
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
}, []);
|
|
3271
|
+
return { elapsedTime, setElapsedTime, manageTimer };
|
|
3272
|
+
}
|
|
3273
|
+
|
|
3274
|
+
// src/platform/tui/hooks/useAgentState/useInputBroker.ts
|
|
3275
|
+
import { useState as useState3, useCallback as useCallback3, useRef as useRef2 } from "react";
|
|
3276
|
+
|
|
2129
3277
|
// src/platform/tui/utils/input-request-broker.ts
|
|
2130
3278
|
function createInputRequestBrokerState() {
|
|
2131
3279
|
return {
|
|
@@ -2175,7 +3323,61 @@ function clearAllInputRequests(state) {
|
|
|
2175
3323
|
return pending;
|
|
2176
3324
|
}
|
|
2177
3325
|
|
|
2178
|
-
// src/platform/tui/hooks/useAgentState.ts
|
|
3326
|
+
// src/platform/tui/hooks/useAgentState/useInputBroker.ts
|
|
3327
|
+
function useInputBroker() {
|
|
3328
|
+
const [inputBroker, setInputBroker] = useState3(
|
|
3329
|
+
createInputRequestBrokerState()
|
|
3330
|
+
);
|
|
3331
|
+
const inputBrokerRef = useRef2(inputBroker);
|
|
3332
|
+
inputBrokerRef.current = inputBroker;
|
|
3333
|
+
const enqueueInputRequest2 = useCallback3((request) => {
|
|
3334
|
+
setInputBroker((prev) => enqueueInputRequest(prev, request));
|
|
3335
|
+
}, []);
|
|
3336
|
+
const settleActiveInputRequest = useCallback3((value) => {
|
|
3337
|
+
const current = inputBrokerRef.current;
|
|
3338
|
+
if (current.active.status !== "active") return;
|
|
3339
|
+
current.active.resolve(value);
|
|
3340
|
+
setInputBroker(resolveActiveInputRequest(current).nextState);
|
|
3341
|
+
}, []);
|
|
3342
|
+
const cancelAllInputRequests = useCallback3(() => {
|
|
3343
|
+
const current = inputBrokerRef.current;
|
|
3344
|
+
const pending = clearAllInputRequests(current);
|
|
3345
|
+
for (const request of pending) request.resolve(null);
|
|
3346
|
+
setInputBroker(createInputRequestBrokerState());
|
|
3347
|
+
}, []);
|
|
3348
|
+
return {
|
|
3349
|
+
inputBroker,
|
|
3350
|
+
enqueueInputRequest: enqueueInputRequest2,
|
|
3351
|
+
settleActiveInputRequest,
|
|
3352
|
+
cancelAllInputRequests
|
|
3353
|
+
};
|
|
3354
|
+
}
|
|
3355
|
+
|
|
3356
|
+
// src/platform/tui/hooks/useAgentState/useLiveProgress.ts
|
|
3357
|
+
import { useState as useState4, useCallback as useCallback4 } from "react";
|
|
3358
|
+
|
|
3359
|
+
// src/platform/tui/types.ts
|
|
3360
|
+
var DEFAULT_LIVE_PROGRESS = {
|
|
3361
|
+
mode: "idle",
|
|
3362
|
+
stage: "",
|
|
3363
|
+
detail: "",
|
|
3364
|
+
activeTool: "",
|
|
3365
|
+
reasoningPreview: "",
|
|
3366
|
+
responsePreview: ""
|
|
3367
|
+
};
|
|
3368
|
+
var DEFAULT_FOOTER_DISPLAY_CONFIG = {
|
|
3369
|
+
showTarget: true,
|
|
3370
|
+
showCounts: true,
|
|
3371
|
+
showHints: true,
|
|
3372
|
+
showQueue: true,
|
|
3373
|
+
showWindow: true,
|
|
3374
|
+
showTurn: true,
|
|
3375
|
+
showContext: true,
|
|
3376
|
+
showTokens: true,
|
|
3377
|
+
showRuntime: true
|
|
3378
|
+
};
|
|
3379
|
+
|
|
3380
|
+
// src/platform/tui/hooks/useAgentState/useLiveProgress.ts
|
|
2179
3381
|
function sanitizeLiveProgress(progress) {
|
|
2180
3382
|
return {
|
|
2181
3383
|
mode: progress.mode,
|
|
@@ -2186,87 +3388,57 @@ function sanitizeLiveProgress(progress) {
|
|
|
2186
3388
|
responsePreview: truncate(progress.responsePreview, TUI_DISPLAY_LIMITS.LIVE_PROGRESS_TEXT_MAX)
|
|
2187
3389
|
};
|
|
2188
3390
|
}
|
|
2189
|
-
|
|
2190
|
-
const [
|
|
2191
|
-
const
|
|
2192
|
-
const [currentStatusState, setCurrentStatusState] = useState("");
|
|
2193
|
-
const [elapsedTime, setElapsedTime] = useState(0);
|
|
2194
|
-
const [retryState, setRetryState] = useState({ status: "idle" });
|
|
2195
|
-
const [currentTokens, setCurrentTokens] = useState(0);
|
|
2196
|
-
const [inputBroker, setInputBroker] = useState(
|
|
2197
|
-
createInputRequestBrokerState()
|
|
2198
|
-
);
|
|
2199
|
-
const [stats, setStats] = useState({ phase: DEFAULTS.INIT_PHASE, targets: 0, findings: 0, todo: 0, targetLabel: "" });
|
|
2200
|
-
const [turnCount, setTurnCount] = useState(0);
|
|
2201
|
-
const [liveProgressState, setLiveProgressState] = useState(DEFAULT_LIVE_PROGRESS);
|
|
2202
|
-
const startTimeRef = useRef(0);
|
|
2203
|
-
const inputBrokerRef = useRef(createInputRequestBrokerState());
|
|
2204
|
-
inputBrokerRef.current = inputBroker;
|
|
2205
|
-
const retryCountdownRef = useRef(null);
|
|
2206
|
-
const retryCountRef = useRef(0);
|
|
2207
|
-
const tokenAccumRef = useRef(0);
|
|
2208
|
-
const lastStepTokensRef = useRef(0);
|
|
2209
|
-
const toolStartedAtRef = useRef(0);
|
|
2210
|
-
const addMessage = useCallback((type, content) => {
|
|
2211
|
-
const id = generateId();
|
|
2212
|
-
setMessages((prev) => {
|
|
2213
|
-
const next = [...prev, { id, type, content, timestamp: /* @__PURE__ */ new Date() }];
|
|
2214
|
-
if (next.length > TUI_DISPLAY_LIMITS.MAX_MESSAGES) {
|
|
2215
|
-
return next.slice(next.length - TUI_DISPLAY_LIMITS.MAX_MESSAGES);
|
|
2216
|
-
}
|
|
2217
|
-
return next;
|
|
2218
|
-
});
|
|
2219
|
-
}, []);
|
|
2220
|
-
const setCurrentStatus = useCallback((value) => {
|
|
2221
|
-
setCurrentStatusState(truncate(value, TUI_DISPLAY_LIMITS.STATUS_TEXT_MAX));
|
|
2222
|
-
}, []);
|
|
2223
|
-
const setLiveProgress = useCallback((value) => {
|
|
3391
|
+
function useLiveProgress() {
|
|
3392
|
+
const [liveProgress, setLiveProgressState] = useState4(DEFAULT_LIVE_PROGRESS);
|
|
3393
|
+
const setLiveProgress = useCallback4((value) => {
|
|
2224
3394
|
setLiveProgressState((prev) => {
|
|
2225
3395
|
const next = typeof value === "function" ? value(prev) : value;
|
|
2226
3396
|
return sanitizeLiveProgress(next);
|
|
2227
3397
|
});
|
|
2228
3398
|
}, []);
|
|
2229
|
-
|
|
3399
|
+
return { liveProgress, setLiveProgress };
|
|
3400
|
+
}
|
|
3401
|
+
|
|
3402
|
+
// src/platform/tui/hooks/useAgentState.ts
|
|
3403
|
+
var useAgentState = (isTyping = false) => {
|
|
3404
|
+
const { messages, setMessages, addMessage } = useMessages();
|
|
3405
|
+
const { elapsedTime, setElapsedTime, manageTimer } = useTimer();
|
|
3406
|
+
const { inputBroker, enqueueInputRequest: enqueueInputRequest2, settleActiveInputRequest, cancelAllInputRequests } = useInputBroker();
|
|
3407
|
+
const { liveProgress, setLiveProgress } = useLiveProgress();
|
|
3408
|
+
const [isProcessing, setIsProcessing] = useState5(false);
|
|
3409
|
+
const [currentStatusState, setCurrentStatusState] = useState5("");
|
|
3410
|
+
const [retryState, setRetryState] = useState5({ status: "idle" });
|
|
3411
|
+
const [currentTokens, setCurrentTokens] = useState5(0);
|
|
3412
|
+
const [stats, setStats] = useState5({ phase: DEFAULTS.INIT_PHASE, targets: 0, findings: 0, todo: 0, targetLabel: "" });
|
|
3413
|
+
const [turnCount, setTurnCount] = useState5(0);
|
|
3414
|
+
const retryCountdownRef = useRef4(null);
|
|
3415
|
+
const retryCountRef = useRef4(0);
|
|
3416
|
+
const tokenAccumRef = useRef4(0);
|
|
3417
|
+
const lastStepTokensRef = useRef4(0);
|
|
3418
|
+
const toolStartedAtRef = useRef4(0);
|
|
3419
|
+
const setCurrentStatus = useCallback5((value) => {
|
|
3420
|
+
setCurrentStatusState(truncate(value, TUI_DISPLAY_LIMITS.STATUS_TEXT_MAX));
|
|
3421
|
+
}, []);
|
|
3422
|
+
const resetCumulativeCounters = useCallback5(() => {
|
|
2230
3423
|
setCurrentTokens(0);
|
|
2231
3424
|
retryCountRef.current = 0;
|
|
2232
3425
|
tokenAccumRef.current = 0;
|
|
2233
3426
|
lastStepTokensRef.current = 0;
|
|
2234
|
-
setLiveProgress(
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
} else {
|
|
2245
|
-
setElapsedTime(0);
|
|
2246
|
-
}
|
|
2247
|
-
}
|
|
2248
|
-
}, []);
|
|
2249
|
-
const clearAllTimers = useCallback(() => {
|
|
3427
|
+
setLiveProgress({
|
|
3428
|
+
mode: "thinking",
|
|
3429
|
+
stage: "",
|
|
3430
|
+
detail: "",
|
|
3431
|
+
activeTool: "",
|
|
3432
|
+
reasoningPreview: "",
|
|
3433
|
+
responsePreview: ""
|
|
3434
|
+
});
|
|
3435
|
+
}, [setLiveProgress]);
|
|
3436
|
+
const clearAllTimers = useCallback5(() => {
|
|
2250
3437
|
if (retryCountdownRef.current) {
|
|
2251
3438
|
clearInterval(retryCountdownRef.current);
|
|
2252
3439
|
retryCountdownRef.current = null;
|
|
2253
3440
|
}
|
|
2254
3441
|
}, []);
|
|
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
3442
|
return {
|
|
2271
3443
|
// State
|
|
2272
3444
|
messages,
|
|
@@ -2286,7 +3458,7 @@ var useAgentState = (isTyping = false) => {
|
|
|
2286
3458
|
setStats,
|
|
2287
3459
|
turnCount,
|
|
2288
3460
|
setTurnCount,
|
|
2289
|
-
liveProgress
|
|
3461
|
+
liveProgress,
|
|
2290
3462
|
setLiveProgress,
|
|
2291
3463
|
// Refs (external consumers only)
|
|
2292
3464
|
retryCountdownRef,
|
|
@@ -2294,8 +3466,6 @@ var useAgentState = (isTyping = false) => {
|
|
|
2294
3466
|
tokenAccumRef,
|
|
2295
3467
|
lastStepTokensRef,
|
|
2296
3468
|
toolStartedAtRef,
|
|
2297
|
-
// WHY timerRef excluded: internal to manageTimer/clearAllTimers only,
|
|
2298
|
-
// no external consumer exists.
|
|
2299
3469
|
// Helpers
|
|
2300
3470
|
addMessage,
|
|
2301
3471
|
resetCumulativeCounters,
|
|
@@ -2308,7 +3478,7 @@ var useAgentState = (isTyping = false) => {
|
|
|
2308
3478
|
};
|
|
2309
3479
|
|
|
2310
3480
|
// src/platform/tui/hooks/useAgentEvents/index.ts
|
|
2311
|
-
import { useEffect, useRef as
|
|
3481
|
+
import { useEffect, useRef as useRef5 } from "react";
|
|
2312
3482
|
|
|
2313
3483
|
// src/platform/tui/constants/styles.ts
|
|
2314
3484
|
var MESSAGE_STYLES = {
|
|
@@ -2967,7 +4137,7 @@ var useAgentEvents = (agent, eventsRef, state, isTyping = false) => {
|
|
|
2967
4137
|
lastStepTokensRef,
|
|
2968
4138
|
setCurrentTokens
|
|
2969
4139
|
} = state;
|
|
2970
|
-
const reasoningBufferRef =
|
|
4140
|
+
const reasoningBufferRef = useRef5("");
|
|
2971
4141
|
const getTargetLabel = () => {
|
|
2972
4142
|
const firstTarget = agent.getState().getAllTargets()[0];
|
|
2973
4143
|
return firstTarget?.hostname || firstTarget?.ip || "";
|
|
@@ -3076,8 +4246,8 @@ function trimQueuedMessages(queue, maxLength, maxItems) {
|
|
|
3076
4246
|
return trimmed.slice(trimmed.length - maxItems);
|
|
3077
4247
|
}
|
|
3078
4248
|
var useAgent = (shouldAutoApprove, target, isTyping = false) => {
|
|
3079
|
-
const [agent] =
|
|
3080
|
-
const eventsRef =
|
|
4249
|
+
const [agent] = useState6(() => createMainAgent(shouldAutoApprove));
|
|
4250
|
+
const eventsRef = useRef6(agent.getEventEmitter());
|
|
3081
4251
|
const state = useAgentState(isTyping);
|
|
3082
4252
|
const {
|
|
3083
4253
|
messages,
|
|
@@ -3102,9 +4272,9 @@ var useAgent = (shouldAutoApprove, target, isTyping = false) => {
|
|
|
3102
4272
|
settleActiveInputRequest,
|
|
3103
4273
|
cancelAllInputRequests
|
|
3104
4274
|
} = state;
|
|
3105
|
-
const [messageQueue, setMessageQueue] =
|
|
3106
|
-
const messageQueueLengthRef =
|
|
3107
|
-
const setMessageQueueSafe =
|
|
4275
|
+
const [messageQueue, setMessageQueue] = useState6([]);
|
|
4276
|
+
const messageQueueLengthRef = useRef6(0);
|
|
4277
|
+
const setMessageQueueSafe = useCallback6((value) => {
|
|
3108
4278
|
setMessageQueue((prev) => {
|
|
3109
4279
|
const next = typeof value === "function" ? value(prev) : value;
|
|
3110
4280
|
return trimQueuedMessages(
|
|
@@ -3141,8 +4311,8 @@ var useAgent = (shouldAutoApprove, target, isTyping = false) => {
|
|
|
3141
4311
|
};
|
|
3142
4312
|
}, [agent, addMessage, setMessageQueueSafe]);
|
|
3143
4313
|
useAgentEvents(agent, eventsRef, state, isTyping);
|
|
3144
|
-
const abortedRef =
|
|
3145
|
-
const executeTask =
|
|
4314
|
+
const abortedRef = useRef6(false);
|
|
4315
|
+
const executeTask = useCallback6(async (task) => {
|
|
3146
4316
|
let currentTask = task;
|
|
3147
4317
|
while (true) {
|
|
3148
4318
|
abortedRef.current = false;
|
|
@@ -3188,7 +4358,7 @@ var useAgent = (shouldAutoApprove, target, isTyping = false) => {
|
|
|
3188
4358
|
}
|
|
3189
4359
|
}
|
|
3190
4360
|
}, [agent, addMessage, manageTimer, resetCumulativeCounters, setIsProcessing, setCurrentStatus, setTurnCount]);
|
|
3191
|
-
const abort =
|
|
4361
|
+
const abort = useCallback6(() => {
|
|
3192
4362
|
abortedRef.current = true;
|
|
3193
4363
|
agent.abort();
|
|
3194
4364
|
setIsProcessing(false);
|
|
@@ -3196,21 +4366,21 @@ var useAgent = (shouldAutoApprove, target, isTyping = false) => {
|
|
|
3196
4366
|
setCurrentStatus("");
|
|
3197
4367
|
addMessage("system", UI_MESSAGES.INTERRUPTED);
|
|
3198
4368
|
}, [agent, addMessage, manageTimer, setIsProcessing, setCurrentStatus]);
|
|
3199
|
-
const recallQueuedInput =
|
|
4369
|
+
const recallQueuedInput = useCallback6(() => {
|
|
3200
4370
|
const recalled = agent.dequeueLastUserInput();
|
|
3201
4371
|
if (!recalled) return null;
|
|
3202
4372
|
return recalled;
|
|
3203
4373
|
}, [agent]);
|
|
3204
|
-
const inputRequestRef =
|
|
4374
|
+
const inputRequestRef = useRef6(inputRequest);
|
|
3205
4375
|
inputRequestRef.current = inputRequest;
|
|
3206
|
-
const cancelInputRequest =
|
|
4376
|
+
const cancelInputRequest = useCallback6(() => {
|
|
3207
4377
|
const ir = inputRequestRef.current;
|
|
3208
4378
|
if (ir.status === "active") {
|
|
3209
4379
|
settleActiveInputRequest(null);
|
|
3210
4380
|
addMessage("system", UI_MESSAGES.INPUT_CANCELLED);
|
|
3211
4381
|
}
|
|
3212
4382
|
}, [settleActiveInputRequest, addMessage]);
|
|
3213
|
-
const refreshStats =
|
|
4383
|
+
const refreshStats = useCallback6(() => {
|
|
3214
4384
|
const s = agent.getState();
|
|
3215
4385
|
setStats({
|
|
3216
4386
|
phase: s.getPhase() || PHASES.RECON,
|
|
@@ -3248,7 +4418,7 @@ var useAgent = (shouldAutoApprove, target, isTyping = false) => {
|
|
|
3248
4418
|
};
|
|
3249
4419
|
|
|
3250
4420
|
// src/platform/tui/hooks/commands/index.ts
|
|
3251
|
-
import { useCallback as
|
|
4421
|
+
import { useCallback as useCallback7, useMemo } from "react";
|
|
3252
4422
|
|
|
3253
4423
|
// src/platform/tui/constants/commands.ts
|
|
3254
4424
|
var COMMAND_DEFINITIONS = [
|
|
@@ -3826,7 +4996,7 @@ var useCommands = (props) => {
|
|
|
3826
4996
|
...createDisplayCommands(ctx),
|
|
3827
4997
|
...createToggleCommands(ctx)
|
|
3828
4998
|
}), [ctx]);
|
|
3829
|
-
const handleCommand =
|
|
4999
|
+
const handleCommand = useCallback7(async (cmd, args) => {
|
|
3830
5000
|
const handler = handlers[cmd];
|
|
3831
5001
|
if (handler) {
|
|
3832
5002
|
await handler(args);
|
|
@@ -3838,15 +5008,15 @@ var useCommands = (props) => {
|
|
|
3838
5008
|
};
|
|
3839
5009
|
|
|
3840
5010
|
// src/platform/tui/hooks/useKeyboardShortcuts.ts
|
|
3841
|
-
import { useCallback as
|
|
5011
|
+
import { useCallback as useCallback10, useEffect as useEffect4 } from "react";
|
|
3842
5012
|
import { useInput } from "ink";
|
|
3843
5013
|
|
|
3844
5014
|
// src/platform/tui/hooks/keyboard/useDoubleTap.ts
|
|
3845
|
-
import { useRef as
|
|
5015
|
+
import { useRef as useRef7, useCallback as useCallback8, useEffect as useEffect3 } from "react";
|
|
3846
5016
|
var useDoubleTap = ({ onFirstTap, onSecondTap, windowMs }) => {
|
|
3847
|
-
const timerRef =
|
|
3848
|
-
const pressedRef =
|
|
3849
|
-
const trigger =
|
|
5017
|
+
const timerRef = useRef7(null);
|
|
5018
|
+
const pressedRef = useRef7(false);
|
|
5019
|
+
const trigger = useCallback8(() => {
|
|
3850
5020
|
if (pressedRef.current) {
|
|
3851
5021
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
3852
5022
|
pressedRef.current = false;
|
|
@@ -3861,7 +5031,7 @@ var useDoubleTap = ({ onFirstTap, onSecondTap, windowMs }) => {
|
|
|
3861
5031
|
timerRef.current = null;
|
|
3862
5032
|
}, windowMs);
|
|
3863
5033
|
}, [onFirstTap, onSecondTap, windowMs]);
|
|
3864
|
-
const reset =
|
|
5034
|
+
const reset = useCallback8(() => {
|
|
3865
5035
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
3866
5036
|
pressedRef.current = false;
|
|
3867
5037
|
timerRef.current = null;
|
|
@@ -3894,7 +5064,7 @@ var useExitHandler = ({
|
|
|
3894
5064
|
};
|
|
3895
5065
|
|
|
3896
5066
|
// src/platform/tui/hooks/keyboard/useEscHandler.ts
|
|
3897
|
-
import { useCallback as
|
|
5067
|
+
import { useCallback as useCallback9 } from "react";
|
|
3898
5068
|
var useEscHandler = ({
|
|
3899
5069
|
cancelInputRequest,
|
|
3900
5070
|
abort,
|
|
@@ -3917,7 +5087,7 @@ var useEscHandler = ({
|
|
|
3917
5087
|
},
|
|
3918
5088
|
windowMs
|
|
3919
5089
|
});
|
|
3920
|
-
const handleEsc =
|
|
5090
|
+
const handleEsc = useCallback9(() => {
|
|
3921
5091
|
if (inputRequestRef.current.status === "active") {
|
|
3922
5092
|
cancelInputRequest();
|
|
3923
5093
|
return;
|
|
@@ -3963,7 +5133,7 @@ var useKeyboardShortcuts = ({
|
|
|
3963
5133
|
inputRef,
|
|
3964
5134
|
windowMs: DOUBLE_TAP_WINDOW
|
|
3965
5135
|
});
|
|
3966
|
-
useInput(
|
|
5136
|
+
useInput(useCallback10((ch, key) => {
|
|
3967
5137
|
if (isModalOpenRef.current) return;
|
|
3968
5138
|
if (key.escape) handleEsc();
|
|
3969
5139
|
if (key.ctrl && ch === "c") handleCtrlC();
|
|
@@ -3981,7 +5151,7 @@ var useKeyboardShortcuts = ({
|
|
|
3981
5151
|
};
|
|
3982
5152
|
|
|
3983
5153
|
// src/platform/tui/hooks/useAppLogic.ts
|
|
3984
|
-
import { useState as
|
|
5154
|
+
import { useState as useState7, useCallback as useCallback11, useRef as useRef8 } from "react";
|
|
3985
5155
|
import { useApp } from "ink";
|
|
3986
5156
|
function sanitizeInput(value) {
|
|
3987
5157
|
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 +5175,11 @@ function parseSlashCommandInput(value) {
|
|
|
4005
5175
|
var useAppLogic = ({ autoApprove = false, target }) => {
|
|
4006
5176
|
const { exit } = useApp();
|
|
4007
5177
|
const { columns: terminalWidth, rows: terminalHeight } = useTerminalSize();
|
|
4008
|
-
const [input, setInput] =
|
|
4009
|
-
const [secretInput, setSecretInput] =
|
|
4010
|
-
const [autoApproveMode, setAutoApproveMode] =
|
|
4011
|
-
const [modal, setModal] =
|
|
4012
|
-
const [footerConfig, setFooterConfig] =
|
|
5178
|
+
const [input, setInput] = useState7("");
|
|
5179
|
+
const [secretInput, setSecretInput] = useState7("");
|
|
5180
|
+
const [autoApproveMode, setAutoApproveMode] = useState7(autoApprove);
|
|
5181
|
+
const [modal, setModal] = useState7({ type: null, content: "", scrollOffset: 0 });
|
|
5182
|
+
const [footerConfig, setFooterConfig] = useState7(DEFAULT_FOOTER_DISPLAY_CONFIG);
|
|
4013
5183
|
const isTyping = input.length > 0 || secretInput.length > 0;
|
|
4014
5184
|
const {
|
|
4015
5185
|
agent,
|
|
@@ -4035,26 +5205,26 @@ var useAppLogic = ({ autoApprove = false, target }) => {
|
|
|
4035
5205
|
addMessage,
|
|
4036
5206
|
refreshStats
|
|
4037
5207
|
} = useAgent(autoApproveMode, target, isTyping);
|
|
4038
|
-
const isProcessingRef =
|
|
5208
|
+
const isProcessingRef = useRef8(isProcessing);
|
|
4039
5209
|
isProcessingRef.current = isProcessing;
|
|
4040
|
-
const autoApproveModeRef =
|
|
5210
|
+
const autoApproveModeRef = useRef8(autoApproveMode);
|
|
4041
5211
|
autoApproveModeRef.current = autoApproveMode;
|
|
4042
|
-
const inputRequestRef =
|
|
5212
|
+
const inputRequestRef = useRef8(inputRequest);
|
|
4043
5213
|
inputRequestRef.current = inputRequest;
|
|
4044
|
-
const isModalOpenRef =
|
|
5214
|
+
const isModalOpenRef = useRef8(!!modal.type);
|
|
4045
5215
|
isModalOpenRef.current = !!modal.type;
|
|
4046
|
-
const inputRef =
|
|
5216
|
+
const inputRef = useRef8(input);
|
|
4047
5217
|
inputRef.current = input;
|
|
4048
|
-
const clearInput =
|
|
5218
|
+
const clearInput = useCallback11(() => {
|
|
4049
5219
|
setInput("");
|
|
4050
5220
|
}, []);
|
|
4051
|
-
const showModal =
|
|
5221
|
+
const showModal = useCallback11((type, content) => {
|
|
4052
5222
|
setModal({ type, content, scrollOffset: 0 });
|
|
4053
5223
|
}, []);
|
|
4054
|
-
const closeModal =
|
|
5224
|
+
const closeModal = useCallback11(() => {
|
|
4055
5225
|
setModal({ type: null, content: "", scrollOffset: 0 });
|
|
4056
5226
|
}, []);
|
|
4057
|
-
const handleModalScroll =
|
|
5227
|
+
const handleModalScroll = useCallback11((delta) => {
|
|
4058
5228
|
setModal((prev) => {
|
|
4059
5229
|
const lines = prev.content.split("\n");
|
|
4060
5230
|
const maxHeight = terminalHeight - TUI_DISPLAY_LIMITS.MODAL_CHROME_HEIGHT;
|
|
@@ -4063,12 +5233,19 @@ var useAppLogic = ({ autoApprove = false, target }) => {
|
|
|
4063
5233
|
return { ...prev, scrollOffset: newOffset };
|
|
4064
5234
|
});
|
|
4065
5235
|
}, [terminalHeight]);
|
|
4066
|
-
const handleExit =
|
|
5236
|
+
const handleExit = useCallback11(() => {
|
|
4067
5237
|
cancelAllInputRequests();
|
|
4068
5238
|
cleanupAllProcesses().catch(() => {
|
|
4069
5239
|
});
|
|
4070
5240
|
exit();
|
|
4071
|
-
setTimeout(() =>
|
|
5241
|
+
setTimeout(() => {
|
|
5242
|
+
const reqs = getGlobalRequestCount();
|
|
5243
|
+
const usage = getGlobalTokenUsage();
|
|
5244
|
+
console.log(`
|
|
5245
|
+
[Session Summary] Total LLM Requests: ${reqs} | Tokens In: ${usage.input_tokens} | Tokens Out: ${usage.output_tokens}
|
|
5246
|
+
`);
|
|
5247
|
+
process.exit(0);
|
|
5248
|
+
}, DISPLAY_LIMITS.EXIT_DELAY);
|
|
4072
5249
|
}, [exit, cancelAllInputRequests]);
|
|
4073
5250
|
const { handleCommand } = useCommands({
|
|
4074
5251
|
agent,
|
|
@@ -4084,10 +5261,10 @@ var useAppLogic = ({ autoApprove = false, target }) => {
|
|
|
4084
5261
|
isProcessingRef,
|
|
4085
5262
|
autoApproveModeRef
|
|
4086
5263
|
});
|
|
4087
|
-
const handleRecallQueuedInput =
|
|
5264
|
+
const handleRecallQueuedInput = useCallback11(() => {
|
|
4088
5265
|
return recallQueuedInput();
|
|
4089
5266
|
}, [recallQueuedInput]);
|
|
4090
|
-
const handleSecretSubmit =
|
|
5267
|
+
const handleSecretSubmit = useCallback11((value) => {
|
|
4091
5268
|
const ir = inputRequestRef.current;
|
|
4092
5269
|
if (ir.status !== "active") return;
|
|
4093
5270
|
const sanitized = sanitizeInput(value).slice(0, TUI_DISPLAY_LIMITS.MAX_INPUT_CHARS);
|
|
@@ -4096,7 +5273,7 @@ var useAppLogic = ({ autoApprove = false, target }) => {
|
|
|
4096
5273
|
settleActiveInputRequest(sanitized);
|
|
4097
5274
|
setSecretInput("");
|
|
4098
5275
|
}, [addMessage, settleActiveInputRequest]);
|
|
4099
|
-
const handleSubmit =
|
|
5276
|
+
const handleSubmit = useCallback11(async (value) => {
|
|
4100
5277
|
const trimmed = sanitizeInput(value);
|
|
4101
5278
|
if (!trimmed) return;
|
|
4102
5279
|
setInput("");
|
|
@@ -4159,11 +5336,11 @@ var useAppLogic = ({ autoApprove = false, target }) => {
|
|
|
4159
5336
|
};
|
|
4160
5337
|
|
|
4161
5338
|
// src/platform/tui/hooks/useTerminalSize.ts
|
|
4162
|
-
import { useState as
|
|
5339
|
+
import { useState as useState8, useEffect as useEffect5 } from "react";
|
|
4163
5340
|
import { useStdout } from "ink";
|
|
4164
5341
|
function useTerminalSize() {
|
|
4165
5342
|
const { stdout } = useStdout();
|
|
4166
|
-
const [size, setSize] =
|
|
5343
|
+
const [size, setSize] = useState8({
|
|
4167
5344
|
columns: stdout?.columns ?? TUI_DISPLAY_LIMITS.TERMINAL_DEFAULT_COLUMNS,
|
|
4168
5345
|
rows: stdout?.rows ?? TUI_DISPLAY_LIMITS.TERMINAL_DEFAULT_ROWS
|
|
4169
5346
|
});
|
|
@@ -4185,12 +5362,12 @@ function useTerminalSize() {
|
|
|
4185
5362
|
}
|
|
4186
5363
|
|
|
4187
5364
|
// src/platform/tui/hooks/useAnimationTick.tsx
|
|
4188
|
-
import { createContext, useContext, useState as
|
|
5365
|
+
import { createContext, useContext, useState as useState9, useEffect as useEffect6 } from "react";
|
|
4189
5366
|
import { jsx } from "react/jsx-runtime";
|
|
4190
5367
|
var ANIM_TICK_MS = 100;
|
|
4191
5368
|
var AnimationContext = createContext(0);
|
|
4192
5369
|
var AnimationProvider = ({ children }) => {
|
|
4193
|
-
const [tick, setTick] =
|
|
5370
|
+
const [tick, setTick] = useState9(0);
|
|
4194
5371
|
useEffect6(() => {
|
|
4195
5372
|
const timer = setInterval(() => {
|
|
4196
5373
|
setTick((t) => t + 1);
|
|
@@ -4780,10 +5957,10 @@ var MessageList = memo6(({
|
|
|
4780
5957
|
import { memo as memo9 } from "react";
|
|
4781
5958
|
|
|
4782
5959
|
// src/platform/tui/hooks/useStatusTimer.ts
|
|
4783
|
-
import { useState as
|
|
5960
|
+
import { useState as useState10, useEffect as useEffect7, useRef as useRef9 } from "react";
|
|
4784
5961
|
var useStatusTimer = (currentStatus, isProcessing) => {
|
|
4785
|
-
const [statusElapsed, setStatusElapsed] =
|
|
4786
|
-
const lastStatusRef =
|
|
5962
|
+
const [statusElapsed, setStatusElapsed] = useState10(0);
|
|
5963
|
+
const lastStatusRef = useRef9("");
|
|
4787
5964
|
useEffect7(() => {
|
|
4788
5965
|
if (!isProcessing || !currentStatus) {
|
|
4789
5966
|
lastStatusRef.current = "";
|
|
@@ -5038,7 +6215,7 @@ var StatusDisplay = memo9(({
|
|
|
5038
6215
|
});
|
|
5039
6216
|
|
|
5040
6217
|
// src/platform/tui/components/ChatInput.tsx
|
|
5041
|
-
import { useMemo as useMemo4, useCallback as
|
|
6218
|
+
import { useMemo as useMemo4, useCallback as useCallback12, useRef as useRef11, memo as memo10, useState as useState12 } from "react";
|
|
5042
6219
|
import { Box as Box14, Text as Text15 } from "ink";
|
|
5043
6220
|
|
|
5044
6221
|
// src/platform/tui/components/input/AutocompletePreview.tsx
|
|
@@ -5081,7 +6258,7 @@ var AutocompletePreview = ({
|
|
|
5081
6258
|
import { Box as Box12, Text as Text13 } from "ink";
|
|
5082
6259
|
|
|
5083
6260
|
// src/platform/tui/components/input/SimpleTextInput.tsx
|
|
5084
|
-
import { useState as
|
|
6261
|
+
import { useState as useState11, useEffect as useEffect8, useRef as useRef10, useMemo as useMemo3 } from "react";
|
|
5085
6262
|
import { Box as Box11, Text as Text12, useInput as useInput2, useStdout as useStdout2 } from "ink";
|
|
5086
6263
|
import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
5087
6264
|
var MOUSE_SEQUENCE_RE = /(?:\x1b)?\[<\d+;\d+;\d+[mM]/g;
|
|
@@ -5109,7 +6286,7 @@ var SimpleTextInput = ({
|
|
|
5109
6286
|
onSpecialKey
|
|
5110
6287
|
}) => {
|
|
5111
6288
|
const chars = useMemo3(() => Array.from(value || ""), [value]);
|
|
5112
|
-
const [cursor, setCursor] =
|
|
6289
|
+
const [cursor, setCursor] = useState11(chars.length);
|
|
5113
6290
|
const { write } = useStdout2();
|
|
5114
6291
|
useEffect8(() => {
|
|
5115
6292
|
write?.("\x1B[?25h");
|
|
@@ -5117,12 +6294,12 @@ var SimpleTextInput = ({
|
|
|
5117
6294
|
write?.("\x1B[?25l");
|
|
5118
6295
|
};
|
|
5119
6296
|
}, [write]);
|
|
5120
|
-
const lastInputRef =
|
|
5121
|
-
const lastInputTimeRef =
|
|
5122
|
-
const valueRef =
|
|
6297
|
+
const lastInputRef = useRef10("");
|
|
6298
|
+
const lastInputTimeRef = useRef10(0);
|
|
6299
|
+
const valueRef = useRef10(value || "");
|
|
5123
6300
|
valueRef.current = value || "";
|
|
5124
|
-
const mouseFragmentRef =
|
|
5125
|
-
const cursorRef =
|
|
6301
|
+
const mouseFragmentRef = useRef10("");
|
|
6302
|
+
const cursorRef = useRef10(cursor);
|
|
5126
6303
|
cursorRef.current = cursor;
|
|
5127
6304
|
useEffect8(() => {
|
|
5128
6305
|
setCursor((prev) => {
|
|
@@ -5313,40 +6490,40 @@ var ChatInput = memo10(({
|
|
|
5313
6490
|
}, [isSlashMode, partialCmd, hasArgs]);
|
|
5314
6491
|
const showPreview = isSlashMode && !hasArgs && suggestions.length > 0;
|
|
5315
6492
|
const showCommandHelp = isSlashMode && !hasArgs && suggestions.length === 0 && value.length > 1;
|
|
5316
|
-
const [selectedIdx, setSelectedIdx] =
|
|
6493
|
+
const [selectedIdx, setSelectedIdx] = useState12(0);
|
|
5317
6494
|
const clampedIdx = Math.min(selectedIdx, Math.max(0, suggestions.length - 1));
|
|
5318
|
-
const selectedIdxRef =
|
|
6495
|
+
const selectedIdxRef = useRef11(clampedIdx);
|
|
5319
6496
|
selectedIdxRef.current = clampedIdx;
|
|
5320
|
-
const suggestionsRef =
|
|
6497
|
+
const suggestionsRef = useRef11(suggestions);
|
|
5321
6498
|
suggestionsRef.current = suggestions;
|
|
5322
|
-
const isSlashModeRef =
|
|
6499
|
+
const isSlashModeRef = useRef11(isSlashMode);
|
|
5323
6500
|
isSlashModeRef.current = isSlashMode;
|
|
5324
|
-
const hasArgsRef =
|
|
6501
|
+
const hasArgsRef = useRef11(hasArgs);
|
|
5325
6502
|
hasArgsRef.current = hasArgs;
|
|
5326
|
-
const showPreviewRef =
|
|
6503
|
+
const showPreviewRef = useRef11(showPreview);
|
|
5327
6504
|
showPreviewRef.current = showPreview;
|
|
5328
|
-
const inputRequestRef =
|
|
6505
|
+
const inputRequestRef = useRef11(inputRequest);
|
|
5329
6506
|
inputRequestRef.current = inputRequest;
|
|
5330
|
-
const onChangeRef =
|
|
6507
|
+
const onChangeRef = useRef11(onChange);
|
|
5331
6508
|
onChangeRef.current = onChange;
|
|
5332
|
-
const onRecallQueuedInputRef =
|
|
6509
|
+
const onRecallQueuedInputRef = useRef11(onRecallQueuedInput);
|
|
5333
6510
|
onRecallQueuedInputRef.current = onRecallQueuedInput;
|
|
5334
|
-
const queuedCountRef =
|
|
6511
|
+
const queuedCountRef = useRef11(queuedCount);
|
|
5335
6512
|
queuedCountRef.current = queuedCount;
|
|
5336
|
-
const latestValueRef =
|
|
6513
|
+
const latestValueRef = useRef11(value);
|
|
5337
6514
|
latestValueRef.current = value;
|
|
5338
|
-
const handleLocalChange =
|
|
6515
|
+
const handleLocalChange = useCallback12((newVal) => {
|
|
5339
6516
|
latestValueRef.current = newVal;
|
|
5340
6517
|
onChange(newVal);
|
|
5341
6518
|
}, [onChange]);
|
|
5342
|
-
const latestSecretRef =
|
|
6519
|
+
const latestSecretRef = useRef11(secretInput);
|
|
5343
6520
|
latestSecretRef.current = secretInput;
|
|
5344
|
-
const handleSecretChange =
|
|
6521
|
+
const handleSecretChange = useCallback12((newVal) => {
|
|
5345
6522
|
latestSecretRef.current = newVal;
|
|
5346
6523
|
setSecretInput(newVal);
|
|
5347
6524
|
}, [setSecretInput]);
|
|
5348
|
-
const [inputKey, setInputKey] =
|
|
5349
|
-
const completeCommand =
|
|
6525
|
+
const [inputKey, setInputKey] = useState12(0);
|
|
6526
|
+
const completeCommand = useCallback12((idx) => {
|
|
5350
6527
|
const sug = suggestionsRef.current;
|
|
5351
6528
|
if (!sug.length) return;
|
|
5352
6529
|
const best = sug[Math.min(idx, sug.length - 1)];
|
|
@@ -5356,9 +6533,9 @@ var ChatInput = memo10(({
|
|
|
5356
6533
|
setSelectedIdx(0);
|
|
5357
6534
|
setInputKey((k) => k + 1);
|
|
5358
6535
|
}, []);
|
|
5359
|
-
const onSubmitRef =
|
|
6536
|
+
const onSubmitRef = useRef11(onSubmit);
|
|
5360
6537
|
onSubmitRef.current = onSubmit;
|
|
5361
|
-
const wrappedOnSubmit =
|
|
6538
|
+
const wrappedOnSubmit = useCallback12((_staleVal) => {
|
|
5362
6539
|
const finalValue = latestValueRef.current;
|
|
5363
6540
|
if (showPreviewRef.current) {
|
|
5364
6541
|
const sug = suggestionsRef.current;
|
|
@@ -5378,12 +6555,12 @@ var ChatInput = memo10(({
|
|
|
5378
6555
|
}
|
|
5379
6556
|
onSubmitRef.current(finalValue);
|
|
5380
6557
|
}, [completeCommand]);
|
|
5381
|
-
const onSecretSubmitRef =
|
|
6558
|
+
const onSecretSubmitRef = useRef11(onSecretSubmit);
|
|
5382
6559
|
onSecretSubmitRef.current = onSecretSubmit;
|
|
5383
|
-
const wrappedSecretSubmit =
|
|
6560
|
+
const wrappedSecretSubmit = useCallback12((_staleVal) => {
|
|
5384
6561
|
onSecretSubmitRef.current(latestSecretRef.current);
|
|
5385
6562
|
}, []);
|
|
5386
|
-
const handleSpecialKey =
|
|
6563
|
+
const handleSpecialKey = useCallback12((key) => {
|
|
5387
6564
|
if (inputRequestRef.current.status === "active") return false;
|
|
5388
6565
|
const sug = suggestionsRef.current;
|
|
5389
6566
|
const visible = showPreviewRef.current;
|
|
@@ -5454,7 +6631,7 @@ var ChatInput = memo10(({
|
|
|
5454
6631
|
});
|
|
5455
6632
|
|
|
5456
6633
|
// src/platform/tui/components/footer.tsx
|
|
5457
|
-
import { memo as memo11, useState as
|
|
6634
|
+
import { memo as memo11, useState as useState13, useEffect as useEffect9 } from "react";
|
|
5458
6635
|
import { Box as Box15, Text as Text16 } from "ink";
|
|
5459
6636
|
import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
5460
6637
|
var SECS_PER_HOUR = 3600;
|
|
@@ -5490,7 +6667,7 @@ var Footer = memo11(({
|
|
|
5490
6667
|
const { columns } = useTerminalSize();
|
|
5491
6668
|
const ctxPct = totalTokens > 0 ? Math.round(totalTokens / LLM_LIMITS.streamMaxTokens * 100) : 0;
|
|
5492
6669
|
const canShowHint = !isProcessing && inputText.length === 0;
|
|
5493
|
-
const [showHint, setShowHint] =
|
|
6670
|
+
const [showHint, setShowHint] = useState13(false);
|
|
5494
6671
|
useEffect9(() => {
|
|
5495
6672
|
if (!canShowHint) {
|
|
5496
6673
|
setShowHint(false);
|
|
@@ -5533,7 +6710,7 @@ var Footer = memo11(({
|
|
|
5533
6710
|
var footer_default = Footer;
|
|
5534
6711
|
|
|
5535
6712
|
// src/platform/tui/components/Modal.tsx
|
|
5536
|
-
import { useMemo as useMemo5, memo as memo12, useCallback as
|
|
6713
|
+
import { useMemo as useMemo5, memo as memo12, useCallback as useCallback13, useRef as useRef12 } from "react";
|
|
5537
6714
|
import { Box as Box16, Text as Text17, useInput as useInput3 } from "ink";
|
|
5538
6715
|
import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
5539
6716
|
var MODAL_TITLES = {
|
|
@@ -5548,9 +6725,9 @@ var Modal = memo12(({
|
|
|
5548
6725
|
onScroll,
|
|
5549
6726
|
onClose
|
|
5550
6727
|
}) => {
|
|
5551
|
-
const onScrollRef =
|
|
6728
|
+
const onScrollRef = useRef12(onScroll);
|
|
5552
6729
|
onScrollRef.current = onScroll;
|
|
5553
|
-
const onCloseRef =
|
|
6730
|
+
const onCloseRef = useRef12(onClose);
|
|
5554
6731
|
onCloseRef.current = onClose;
|
|
5555
6732
|
const { columns, rows } = useTerminalSize();
|
|
5556
6733
|
const terminalHeight = rows;
|
|
@@ -5562,7 +6739,7 @@ var Modal = memo12(({
|
|
|
5562
6739
|
() => lines.slice(scrollOffset, scrollOffset + maxHeight),
|
|
5563
6740
|
[lines, scrollOffset, maxHeight]
|
|
5564
6741
|
);
|
|
5565
|
-
useInput3(
|
|
6742
|
+
useInput3(useCallback13((input, key) => {
|
|
5566
6743
|
if (key.escape || input === "q") {
|
|
5567
6744
|
onCloseRef.current();
|
|
5568
6745
|
} else if (key.upArrow || input === "k") {
|
|
@@ -5828,7 +7005,7 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
5828
7005
|
var app_default = App;
|
|
5829
7006
|
|
|
5830
7007
|
// src/platform/tui/components/SplashScreen.tsx
|
|
5831
|
-
import { useEffect as useEffect11, useState as
|
|
7008
|
+
import { useEffect as useEffect11, useState as useState14 } from "react";
|
|
5832
7009
|
import { Box as Box19, Text as Text19 } from "ink";
|
|
5833
7010
|
|
|
5834
7011
|
// src/platform/tui/components/CoinFrames.ts
|
|
@@ -6527,7 +7704,7 @@ var SplashScreen = ({
|
|
|
6527
7704
|
}) => {
|
|
6528
7705
|
const { columns, rows } = useTerminalSize();
|
|
6529
7706
|
const tick = useAnimationTick();
|
|
6530
|
-
const [elapsed, setElapsed] =
|
|
7707
|
+
const [elapsed, setElapsed] = useState14(0);
|
|
6531
7708
|
useEffect11(() => {
|
|
6532
7709
|
const start = Date.now();
|
|
6533
7710
|
const interval = setInterval(() => {
|
|
@@ -6607,6 +7784,10 @@ async function runAction(objective, options) {
|
|
|
6607
7784
|
const shutdown = async (exitCode = 0) => {
|
|
6608
7785
|
await cleanupAllProcesses().catch(() => {
|
|
6609
7786
|
});
|
|
7787
|
+
const reqs = getGlobalRequestCount();
|
|
7788
|
+
const usage = getGlobalTokenUsage();
|
|
7789
|
+
console.log(chalk2.hex(HEX.gray)(`
|
|
7790
|
+
[Session Summary] Requests: ${reqs} | In: ${usage.input_tokens} | Out: ${usage.output_tokens}`));
|
|
6610
7791
|
process.exit(exitCode);
|
|
6611
7792
|
};
|
|
6612
7793
|
process.on("SIGINT", () => shutdown(EXIT_CODES.SIGINT));
|